Pages accept both GET and POST requests. Combined with backend scripts, this lets you build data-collection endpoints, custom forms, and mini-apps using regular ElasticFunnels pages — no external server needed.
The pattern
- Create a page (e.g.
/weight/checkin)
- Add a
<script scope="backend"> that checks for POST, validates request.body with the Schema validator, and stores the data
- Your frontend uses
fetch() to POST JSON data to that page
- The backend script responds with JSON — the page HTML is skipped when
response.json() is called
For reusable validation logic across multiple pages, use shared backend script modules — create a module with exported functions and import them anywhere.
Full example: Weight check-in
1. Create a shared validation module
Create a backend script module (code: validators) with shared schemas:
2. Create the page /weight/checkin
3. Read the data back on another page
On any page (e.g. /dashboard), read the CRM data:
How it fits together
CRM functions reference
slug (CRM entity) vs reference
slug — required on every call. This is the CRM entity slug from entity settings (e.g. pattern-tracker). It is not the same as referenceType (customer, order, …).
referenceType / referenceId — which record entries attach to. Omit or null for 'customer' and the session customer_id.
Calls must use a single options object (no positional lists).
Parameter types
Moving through CRM workflow
moveCrmToStage and moveCrmToPipeline are the workflow transition functions. You can move:
- Stage to stage in the same pipeline
- Pipeline to pipeline (by setting a stage that belongs to the new pipeline)
These only change workflow placement (pipeline_id, stage_id). They do not map one field key into another.
For field mapping (for example quiz_email -> email), do it explicitly with reads/writes:
Or move across entities in one call:
All inputs are strictly validated before data is stored:
slug must match a CRM entity configured for your brand; unknown slug → TypeError
fieldKey must match /^[a-zA-Z0-9_.-]+$/
referenceType must match /^[a-zA-Z0-9_-]+$/
referenceId rejects {}[]"\ characters
- String values are capped at 10,000 characters
- Numbers must be finite (no
NaN, Infinity)
- Objects are JSON-serialized and capped at 10 KB
- Missing
slug, fieldKey, or value (set) throws TypeError
- Other invalid inputs still cause
false / null / [] where applicable
Quiz / anonymous visitor → customer CRM migration
A common pattern: save a quiz or funnel payload with setCrmField({ referenceType: 'quiz_visitor', referenceId: sessionUuid, ... }) while the visitor is anonymous, then after members login call reassignCrmReference so the same CRM entries attach to customer + customer_id. fromReferenceId must match session.get('quiz_visitor_id') and the target must be the current session customer — see ReassignCrmReference on the Data Functions page. Clear quiz_visitor_id from the session after a successful migration to keep the flow idempotent.
Security checklist
Always validate request.body in your backend script. The body comes from the visitor’s browser and can contain anything.
- Validate with Schema — use the built-in Schema validator to enforce types, lengths, and allowed values
- Check authentication — verify
is_customer before storing data
- Use
response.json() — this stops page rendering and returns JSON directly
- Use
response.status() — set proper HTTP status codes (400 for validation errors, 401 for auth)
- Share validation logic — create a backend script module for reusable schemas and auth checks
POST body limits