For ops teams who outgrew their patched-together intake stack

Forms with a backbone.

Real conditional logic, AI-assisted building, and a production-grade backend — distributed wherever your users are via public link, iframe, script tag, or web component.

One rule. Two evaluation surfaces. Same JsonLogic.
Server-enforced Client-mirrored
Renderer · Vite + React 18 step 3 / 4
Employment status answered
Self-employed
↳ this answer triggers the rule
Annual income answered
$148,200
Upload most recent 1099 * revealed by rule
Drop PDF or click to browse
Employer name skipped · hidden
HR contact email skipped · hidden
questions[2].rules[0] action · show
{
"==": [
{"var": "q_0_1"},
"self_employed"
]
}
JSONLOGIC
server EVAL
json-logic-qubit
visible → required
client EVAL
json-logic-js
rendered live

You can't bypass the rule from the client. The server runs the same JsonLogic on submit. Hidden questions are never validated as required; visible ones always are.

The problem The stack you patched together
A form. A glue layer. A spreadsheet. A chat webhook. Brittle the moment you need conditional logic, multi-step flows, file collection, or governance. One engine replaces the entire chain.
02 · What's in the box Five pillars, not thirty features

Five things the page orients around. The inventory comes later.

Buyers care about the wedge — not the 60-row feature matrix. Every section below serves one of these. Receipts, not adjectives.

01 19 question types

A real form engine, not a survey tool.

19 typed question primitives, composable sections, two display modes. Designed for forms that have to do something — not just collect rows.

short_text
select
file_upload
rating
+ 15 more primitives →
02 4 rule actions

Logic that runs everywhere.

JsonLogic on the server and the renderer. Required-ness, visibility, skip-to are part of the model.

03 2 LLM providers

AI that builds, then steps back.

A grounded form-building agent (Anthropic or Gemini) emits a typed FormExport. Schema-validated before it lands.

04 3 embed modes

Distribution is a first-class product.

Public link, iframe, script tag, or web component. Branded theming, draft-saving, URL prefill, graceful pause states.

05 Self-hostable

Production-grade backend.

FastAPI + Postgres + RS256 JWT with JWKS rotation. Multi-tenant, per-tenant quotas, HMAC-signed webhooks, self-hostable.

03 · Conditional logic · the wedge Where survey tools collapse

If they answered X, ask Y. If not, skip ahead.

Rules are stored as JsonLogic expressions on the question record. The same expression evaluates on both surfaces — no drift. The submission endpoint runs the rules itself to decide which questions are required.

4 actions × 2 targets

The grammar of the rule engine

showreveal a question / section on match
hideremove from the rendered flow
skip_tojump to a later question
requireflip required-ness
Target · question or section · multiple rules per question · deterministic order
Portable refs · JSON round-trip
// internal
question_id = 4127
// exported
question_ref = "q_0_3"
// section
section_ref = "s_2"

Numeric IDs become portable refs at export. Move a form between workspaces without rewriting any expression.

04 · Question types · 19 typed primitives Each one a typed primitive · renderer + validator + admin config

Eight you'll use every day. Eleven more for the long tail.

Every type is a typed primitive with its own renderer component, validator, and admin config UI. Answers are stored as JSONB so a new type can land without a schema migration.

short_text

single-line text · validated

long_text

multi-line textarea

email

RFC-validated email input

currency

locale-rendered currency code

date

native or polyfilled date picker

select

radio · single value

rating

star scale, configurable max

file_upload

direct to object storage

+ 11 more · number percentage phone time address dropdown checkbox yes_no scale terms_and_condition image_notes
Read the docs ↗
Repeater

Dynamic question groups

Trigger question controls the count; the engine produces N copies with their own validation and storage. One-at-a-time mode turns each item into a named step.

Interpolation

Reference earlier answers

"Hi {name}, when did you start at {company}?" — rendered live as the respondent moves through. Works in labels and descriptions.

Coming soon

Calculated fields

A read-only question type derived from other fields via a friendly template syntax: {price} * {quantity}. Hidden-mode allowed; usable as a downstream value.

05 · AI form builder · grounded, typed Agent generates → schema validates → form lands

The agent builds it. Then gets out of the way.

No free-text JSON dumps. The agent calls a typed generate_form tool whose payload is validated against the FormExport schema — and imported through the same path as a JSON upload.

prompt
01 SSE stream
Chat surface
"Build me an intake form for new hires — emergency contact, T-shirt size, and a 1099 upload if they're self-employed."
tool call
02 schema-validated
Typed FormExport
{
"title": "New hire intake",
"sections": [ … ],
"questions": 12,
"rules": 3
}
import_form_from_export
03 ready to edit
Form preview
Legal name
DOB
Employment
1099 upload
✓ Anthropic or Gemini · pluggable ✓ Invalid JSON → asks the agent to retry ✓ Same import path as JSON upload ✓ Workspace quota-aware
06 · Distribution · first-class Three ways to ship the same renderer

One form schema. Three places it can live.

Public URL, iframe, or web component — all three drive the same Vite + React renderer. Theming, draft-saving, and prefill behave identically. Pause a workspace and every surface returns a graceful 409 form_unavailable — never a blank screen.

Public link standalone
r.your-brand.com/intake
r.your-brand.com/intake

A URL anyone can hit.

Per-form slug, branded on a subdomain you control. URL query-params seed the form: ?email=…&plan=pro.

iframe embedded
/embed/intake
<iframe src="…/embed/intake" />

Margin-stripped renderer.

A dedicated /embed/:slug route sized for an iframe. No outer chrome, themed at fill-time, draft-saving still works.

Web component shadow DOM
<quantum-form>
<quantum-form slug="intake" />

Drop into any page.

Renders inside a Shadow DOM so host CSS never bleeds in. Same renderer, same draft-saving, same prefill — one custom tag.

Theming

Primary, background, font, logo, custom CSS — scoped to the form root.

Draft-saving

Respondents get an X-Session-ID cookie; in-progress answers + step position persist. Restore toast on return.

Pause states

Form-level or workspace-level pause returns 409 form_unavailable — your host page shows a real message, not a 404.

07 · What you get out of the box The non-marquee surface · all shipped

Twelve things, already shipped.

The narrative above explains the wedge. This is the rest of the surface — analytics, identity, governance, dispatch. Available in the workspace on day one.

Per-form analytics

Total / today / week, response trend, per-question breakdowns. Histograms for categorical, distributions for numeric.

PDF export

Per-response PDF with every answer rendered. jspdf-autotable in the admin.

Multi-tenant

Workspaces with owner + member roles. Tenant-active state cached 30s on every backend instance.

Invitations

Email-based, signed token, preview-before-accept. Resend / revoke / accept flows. Last owner can't be demoted.

Identity, 3 ways

Google OAuth, magic link with 6-digit fallback, and SSO / passkeys via your existing IdP.

Per-tenant API tokens

Long-lived X-API-Key, hashed at rest, labeled and individually revocable. Default token minted on workspace create.

Quotas & tiering

max_forms and max_responses per workspace as opaque policy. -1 = unlimited. Counter updates transactional with the insert.

Drafts & restore

Anonymous draft-saving by session cookie; auto-restore toast on return. Required validation respects conditional logic.

HMAC-signed webhooks

form.submitted today, extensible. SHA-256 signature on every payload. Async via background tasks.

RS256 JWT

Access tokens 15-min TTL, refresh 30-day with family-tracked rotation and reuse detection. JWKS endpoint for verifiers.

JSON import / export

Forms round-trip as portable refs (q_0_3, s_2). The agent imports through the same path you upload to.

File uploads

file_upload posts directly to object storage. Answer record holds the reference, not the bytes.

08 · Where we're not the right fit Honesty about scope

Three plain-language sentences, so we don't waste each other's time.

If you need offline data collection on mobile devices,
→ THEN
we're not it today — the renderer assumes live network.
If you need HIPAA or SOC 2 attestations today,
→ THEN
we're production-grade but un-attested — talk to us about scope and timeline first.
If you need hundreds of boutique third-party integrations,
→ THEN
webhooks and the underlying API cover the long tail — but a dedicated automation layer is the better fit.

Replace the form-plus-glue-plus-spreadsheet chain. With one engine.

A 30-minute walkthrough on your most-painful intake flow — what the rules would look like, where the embed would go, what comes out the webhook end.

Thanks — message received.

We'll be in touch within a business day.

Setupone afternoon
Hostingours or yours
EjectJSON export · Postgres schema