Skip to main content
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

  1. Create a page (e.g. /weight/checkin)
  2. Add a <script scope="backend"> that checks for POST, validates request.body with the Schema validator, and stores the data
  3. Your frontend uses fetch() to POST JSON data to that page
  4. 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:

Input validation

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.
  1. Validate with Schema — use the built-in Schema validator to enforce types, lengths, and allowed values
  2. Check authentication — verify is_customer before storing data
  3. Use response.json() — this stops page rendering and returns JSON directly
  4. Use response.status() — set proper HTTP status codes (400 for validation errors, 401 for auth)
  5. Share validation logic — create a backend script module for reusable schemas and auth checks

POST body limits