Skip to content
Forms API

Submit a form

Submit forms to the Qubeforms API with JSON or multipart data. Send fields and files, configure reserved parameters, and handle responses, redirects, and retries.

1. Prepare an active form

Copy the public endpoint from Settings → General. Use POST https://api.qubeforms.io/v1/{publicKey}/submissions with the cf_pk_ key in the path. No bearer key is required. Configure the website’s allowed origin and any recipients first. Public keys identify a form; they do not grant read access.

2. Send JSON

Replace the endpoint and Origin in this example. Send a JSON object containing your named fields and request JSON with Accept: application/json. Include a fresh CAPTCHA token if your form requires one; this minimal example assumes it does not.

curl --fail-with-body \
  'https://api.qubeforms.io/v1/cf_pk_YOUR_KEY/submissions' \
  --header 'Origin: https://www.example.com' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"email":"alex@example.com","message":"Please contact me.","fax_number":""}'

3. Send HTML fields or files

Ordinary HTML forms can use URL-encoded POST data. For files, use multipart/form-data with named file fields, or JavaScript FormData without manually setting Content-Type. JSON filenames are not uploads. Attachments require Starter or higher and allow up to 10 files of 1 byte to 5 MB each. See Adding file uploads for complete HTML, JavaScript, and curl examples.

4. Use reserved fields correctly

fax_number is the default empty spam trap; use the configured name if changed. _cf_started_at is a Unix timestamp in seconds for form-start timing. CAPTCHA tokens can use _cf_captcha_token, h-captcha-response, g-recaptcha-response, or cf-turnstile-response. _cf_redirect requests an allowlisted thank-you destination. _cf_draft_id and _cf_draft_token complete a saved draft. These control fields, _token, _gotcha, and _cf_step are excluded from stored customer field data.

5. Respect field normalization

Include at least one non-reserved field. Prefer simple unique names using letters, numbers, underscores, or hyphens; unsupported characters are removed from names. Names longer than 191 characters are skipped. Strings are trimmed, stripped of null bytes, and limited to 20,000 characters; nested arrays are limited to 100 entries per level and excessive nesting is rejected. The encoded field payload limit is 512 KB, separate from the default 51 MB overall request limit. Browser required attributes do not define a custom server-side field validation schema.

6. Interpret receipt and redirects

Successful JSON responses use HTTP 201 and the shape below; the values are illustrative. Receipt does not guarantee inbox placement or email delivery. Check status and inspect delivery separately. A permitted redirect returns HTTP 303 when Accept does not contain application/json. JSON clients get a redirect_url value instead. Invalid redirects can cause 422, so remove or correct a disallowed target before trying again.

{
  "ok": true,
  "submission_id": "EXAMPLE_SUBMISSION_ID",
  "status": "inbox",
  "trust_grade": "A",
  "redirect_url": null,
  "warnings": []
}

7. Add error and retry handling

Check HTTP status and parse errors defensively. Preserve visitor input until success is confirmed. Use API errors and response handling to distinguish validation from throttling, and Idempotent submissions before adding automatic retries. Do not send a read key from your browser to inspect a submission.

Keep private credentials private

Public form keys can appear in browser code. Read keys, management credentials, and webhook signing secrets must stay on the server.