Forms & Booking
Forms and booking share one data model (a flow), so they share one set of endpoints, distinguished by the flow's kind (form or booking). Authoring requires a bearer token; the public render and submit endpoints need no auth.
Flow management (bearer)
GET /dashboard/flows?kind=form— list your flows.GET /dashboard/flows/{slug}— a flow's detail.POST /booking/flows— create a flow (setkindtoformorbooking).PATCH /booking/flows/{flow_id}— update a flow (fields, logic, screens, theme).POST /booking/flows/{flow_id}/publish— publish (draft → active). Gated.Versioning + locking: list versions, restore a version, lock/unlock — mirroring the page-locking model.
A flow's flow object holds its flow (steps + fields), logic, variables, hidden_fields, welcome_screens, thankyou_screens, and theme. See Building a Form for the field and logic model.
Public render (no auth)
GET /api/v1/booking/{flow_id}/render— returns the flow's render projection (fields, logic, variables, screens), honoring theAccept-Languageheader.
The customer-facing page for a flow is served by the renderer at
https://<your-site>/f/<flow_id>— that is the canonical URL to share or embed. The/renderAPI endpoint above is the data the renderer consumes. Never construct/book/<id>for akind='form'flow.
Public submit (no auth)
POST /api/v1/booking/{flow_id}/submit— submit answers for any flow kind. Idempotent (safe to retry). For bookings, this holds and books the slot and sends the calendar invite.
Idempotency-Key and X-Customer-Timezone are required headers on submit. Omit either and the call fails with 400 before the body is read. Send the same Idempotency-Key when you retry and the second call returns the first result rather than creating a duplicate.
Example — submit answers:
curl -X POST "https://spideriq.ai/api/v1/booking/FLOW_ID/submit" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 4f8c1e77-0f2c-4a91-9f0a-7c5e2b1d3a64" \
-H "X-Customer-Timezone: Europe/Berlin" \
-d '{
"answers": { "email": "lead@example.com", "company": "Acme" },
"hidden_fields": { "utm_source": "newsletter" }
}'Submissions are written to your results store and synced into your CRM (per-tenant norm_cli_* schemas) within about a minute, based on each field's CRM mapping.
Submissions (bearer)
GET /dashboard/booking/flows/{flow_id}/submissions— list responses (paginated).GET /dashboard/booking/flows/{flow_id}/submissions.csv— export to CSV.
Templates (bearer)
GET /dashboard/booking/templates/global?kind=form— browse the global form-template library.POST /dashboard/booking/templates/clone— clone a template into your account as a draft.
The shipped form templates are contact-form, lead-gen-quiz, nps-survey, event-rsvp, and job-application.
Embedding
Forms embed three ways — standalone /f/<flow_id>, inline, and popup — using the loader at embed.spideriq.ai/v1/loader.js. See Embedding Forms for the snippets, or use the form_get_embed_snippet MCP tool / spideriq form embed-snippet CLI command to generate them.
Errors
Every 4xx from these endpoints comes back in the same envelope:
{ "error": { "code": "…", "message": "…", "what_you_sent": {}, "what_was_expected": {}, "suggested_action": "…", "suggested_url": "…" } }Only code and message are always present. The other keys appear when the endpoint has something specific to tell you, so read defensively.
400 HTTP_ERROR_400— a required header is missing on submit (Idempotency-Key,X-Customer-Timezone). The message names the header.404 RESOURCE_NOT_FOUND— returned byGET /api/v1/forms/{flow_id}when no form flow has that id. Carrieswhat_you_sentandsuggested_action.404 HTTP_ERROR_404— returned by the public render and submit endpoints for an unknown or archived flow ("Flow not found"/"Booking flow not found."). An archived flow behaves the same as an unknown one.409 WRONG_FLOW_KIND— you called a form endpoint with akind='booking'flow, or the reverse. The envelope'ssuggested_urlnames the endpoint you wanted, so read it rather than guessing.422 SCHEMA_VALIDATION_FAILED— the body failed schema validation.what_was_expected.errorsis the field-by-field validation array; each entry names the failing path. Note the flow-existence check runs first, so a bad body against an unknown flow returns404, not422.423— the flow is being edited elsewhere. The envelope carrieserror_code: "flow_locked"pluslocked_by,locked_atandunlock_endpoint. Wait for the holder, or unlock deliberately.
Next steps
API Overview — base URL, auth, and the dry-run / confirm flow.
Building a Form — the field and logic model behind a flow.
Embedding Forms — standalone, inline, and popup snippets.
Booking — the booking variant of a flow.