Docs / API

The form-to-email API

Base URL https://api.conform.centrst.com. No API key, no account. The machine-readable source of truth is the served OpenAPI document; /.well-known/conform.json describes the deployment, and this page is the human summary of both.

Endpoints

Method & pathPurpose
POST /v1/routesCreate a form endpoint. Accepts an Idempotency-Key header — retries return the same endpoint. Returns form_id, endpoint, status, a one-time management_token, and next_action.
GET /v1/routes/{form_id}Poll status. Refreshes verification, so the route activates as soon as the inbox owner confirms.
DELETE /v1/routes/{form_id}Hard-delete a route. Requires Authorization: Bearer <management_token>.
GET /v1/routes/{form_id}/installReady-to-install accessible form code with the endpoint baked in. ?framework=html|js|react|vue|svelte|astro|nextjs, &raw=1 for the bare file.
POST /f/{form_id}Submit. JSON, URL-encoded, or multipart text fields. Browser posts get an HTML result page; _redirect sends a 303.
GET / · /openapi.json · /llms.txtDiscovery: deployment descriptor, full contract, plain-text summary.

Verification — the one human step

The destination inbox must confirm a verification email before delivery starts. While pending, submissions return 409 inbox_not_verified with a next_action block telling you exactly what to poll. The endpoint URL never changes, so install the form immediately and poll until status is "active".

Errors are a contract

Every non-2xx response is JSON with the same envelope:

{ "success": false, "error": "inbox_not_verified", "message": "…", "retryable": true }

error is a stable machine code from the x-conform-error-codes table in the OpenAPI document; retryable: true means the identical request may succeed later. Codes carry extra fields where useful: rate_limited adds retry_after_seconds, monthly_allowance_exhausted adds used/limit/resets_at. The table, the spec, and the runtime are locked together by CI in the open-source engine.

Reserved submission fields

FieldEffect
_subjectSets the email subject.
_format=jsonAttaches the fields as submission.json.
emailA valid value becomes the reply-to address.
_redirectAbsolute https:// URL → 303 See Other after delivery.
_testMarks a test: delivered for real with a [Test] subject, consumes one quota unit, response carries test: true and echoes a non-boolean value. A test response without test: true means it was spam-filtered.
_gotcha, botcheckSpam traps. Keep them empty; never remove them from generated forms.

Signed webhooks

Add delivery: {"mode": "webhook"|"both", "webhook": {"url": "https://…"}} at creation to deliver submissions as signed JSON events. The response reveals a whsec_ signing secret exactly once (and again on idempotent replays). Deliveries carry webhook-id/webhook-timestamp/webhook-signature headers in the Standard Webhooks format — deduplicate on webhook-id. Email verification remains required in every mode, and the verification email discloses the webhook URL. There is no durable retry queue, because redelivery would require storing submission content: webhook-only routes deliver synchronously and roll back quota on failure (502 webhook_delivery_failed, safe to retry); in both mode the email is authoritative and the webhook is best-effort.

Quota

Every form delivering to the same inbox shares one monthly allowance (250 deliveries free). Successful responses report used and limit; exhaustion returns 429 with resets_at.