Forms Endpoint Reference
This page is the complete, exact contract for submitting to an Attri form endpoint. It exists so a developer — or an AI coding assistant building your site — can integrate a form without guessing. If you’re using an assistant like Claude, Cursor, or Copilot, paste this whole page into its context along with your form key.
Endpoint
Section titled “Endpoint”POST https://form.attri.io/{formKey}{formKey} is the short identifier shown on the form’s page in Attri (e.g. ofidsmmc). GET is not supported. Cross-origin requests are allowed (Access-Control-Allow-Origin: *; preflight OPTIONS is handled).
Request formats
Section titled “Request formats”All three are equivalent — fields are extracted the same way:
| Content type | Notes |
|---|---|
multipart/form-data | What a native HTML form or FormData sends. |
application/x-www-form-urlencoded | Standard form encoding. |
application/json | Flat object of string values. Also accepts _turnstile as an alias for the captcha token field. |
Every non-reserved field is stored as a submission field, as a string, under the name you sent. There is no fixed schema.
Control fields
Section titled “Control fields”Fields whose names start with _ are control fields — they change behavior and are never stored:
| Field | Purpose |
|---|---|
_gotcha | Honeypot. If non-empty, the submission is silently discarded (the response still reports success, so bots don’t adapt). Add it as a hidden, empty field. |
_next | URL to redirect to after a successful native (non-JavaScript) submission. Ignored for AJAX submissions. |
cf-turnstile-response | Turnstile captcha token. Required when the form has captcha enabled. Normally set automatically by Attri’s tracking script — see the captcha requirement below. |
_attri_eid | Attribution event id linking the submission to the visitor’s analytics session. Set automatically by the tracking script; don’t set it manually. |
One regular field also gets special treatment: a field named exactly email is format-validated server-side on every submission.
Response mode: AJAX vs native
Section titled “Response mode: AJAX vs native”The endpoint returns JSON or redirects depending on how the request was made:
- AJAX mode (request has
Sec-Fetch-Mode: cors— any browserfetch— or a JSON content type): responses are JSON with appropriate status codes. - Native mode (regular HTML form navigation): responses are
303redirects — on success to_next, else the form’s configured redirect URL, else back to theReferer; on validation failure back to theRefererwith an_errorsquery parameter containing URL-encoded JSON of per-field errors.
Responses
Section titled “Responses”| Status | Body | Meaning |
|---|---|---|
200 | {"ok": true} | Captured (AJAX mode). Also returned for honeypot-flagged spam. |
303 | — | Success or validation-failure redirect (native mode). |
400 | {"error": "Missing form key"} | No form key in the URL. |
403 | {"error": "Form is disabled"} | The form exists but is turned off in Attri. |
404 | {"error": "Form not found"} | Unknown form key (or the form was deleted). |
422 | {"ok": false, "errors": {"field": "message"}} | A required field is missing/empty, or email failed format validation. |
422 | {"ok": false, "error": "Captcha verification required"} | Form has captcha enabled and no token was sent. |
422 | {"ok": false, "error": "Captcha verification failed"} | Token was sent but invalid or expired. |
429 | {"error": "Too many submissions. Please try again later."} | Rate limit: 10 submissions per minute per IP per form. Retry-After: 60 header included. |
Required fields are configured per form in the Attri app, not in your markup — the server enforces them regardless of client-side validation.
The captcha constraint (read this before enabling captcha)
Section titled “The captcha constraint (read this before enabling captcha)”If the form has Turnstile captcha enabled, the page must have Attri’s tracking snippet installed. The snippet injects the captcha widget and fills cf-turnstile-response; there is no standalone embed. A captcha-enabled form on a page without the snippet fails every submission with 422. Details and other gotchas: Spam Protection & Captcha.
The snippet also only scans for forms at page load — a form rendered later by client-side JavaScript won’t be enhanced (no interception, no captcha widget). Render Attri forms in the initial HTML.
Minimal working examples
Section titled “Minimal working examples”Plain HTML (works with or without the tracking snippet):
<form action="https://form.attri.io/YOUR_FORM_KEY" method="POST"> <input type="text" name="name" /> <input type="email" name="email" required /> <textarea name="message"></textarea> <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" /> <button type="submit">Send</button></form>curl (AJAX mode, via JSON):
curl -X POST https://form.attri.io/YOUR_FORM_KEY \ -H "Content-Type: application/json" \ -d '{"name": "Ada", "email": "ada@example.com", "message": "Hello"}'# → {"ok":true}Can I use Attri forms without any JavaScript? Yes — a plain HTML form POST works and redirects on success. You lose the inline success message, session-linked attribution, and captcha (which requires the tracking snippet).
Does this work in Next.js / React / Vue? Yes, with one constraint: the form must be in the DOM when Attri’s script loads (server-rendered or mounted at page load). Client-side-rendered forms that appear later aren’t enhanced by the script — they’ll still capture via native POST, but without interception or captcha.
What field names should I use?
Any names you like. Only email is special (format-validated), and _-prefixed names are reserved control fields.
Where do submissions go? The Forms section of your Attri workspace, with attribution (referrer, campaign, device, geo) attached. Notifications go to whatever channels you’ve configured — email, Slack, or webhooks.
Is there an API to read submissions? Yes — the REST API exposes forms and their submissions, including export.