# Forms · Content Delivery API

> Content Delivery API reference for forms: GET /forms/{id}, GET /forms/{id}/challenge, POST /forms/{id}/upload-request, POST /forms/{id}/submit.

> Markdown version of https://kanorio.com/en/developers/delivery-api/forms. Append `.md` to any developer docs URL for this format. Index: https://kanorio.com/llms.txt · MCP: https://app.kanorio.com/api/mcp

Base URL: `https://{site}/api/v1` · [Errors](https://kanorio.com/en/developers/basics/errors) · [Pagination](https://kanorio.com/en/developers/basics/pagination)

### Public online form definition (fields, availability, consent)

`GET /forms/{id}`

- Stability: beta
- Authentication: none

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |
| `locale` | query | string | no | Locale for the form name, description, question labels, hints, choice labels, submit button and success message. One of the site's locales from /site (a code such as `en-US` or the URL prefix such as `en`); omit it for the primary locale. Other values, including locales that are still translating, return 400 UNSUPPORTED_LOCALE. Untranslated fields fall back to the primary locale. Field and choice ids are locale-independent, so submissions are unaffected. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `form` | object | yes | — |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORM_NOT_FOUND` | 404 | — |
| `UNSUPPORTED_LOCALE` | 400 | — |

```bash
curl -X GET "https://{site}/api/v1/forms/{id}"
```

### Get the bot check for submitting a form (built-in proof-of-work or the site's third-party widget)

`GET /forms/{id}/challenge`

- Stability: beta
- Authentication: none

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `provider` | "kanorio" \| "turnstile" \| "recaptcha" \| "hcaptcha" | yes | — |
| `entry` | "forms" \| "customer_login" | yes | — |
| `altcha` | object | no | Built-in proof-of-work challenge (provider kanorio only). |
| `altcha.algorithm` | string | yes | — |
| `altcha.challenge` | string | yes | — |
| `altcha.maxnumber` | integer | no | — |
| `altcha.salt` | string | yes | — |
| `altcha.signature` | string | yes | — |
| `siteKey` | string | no | Widget site key (third-party providers). |
| `action` | string | no | Pass this as the widget action; the server checks it. |
| `cData` | string \| null | no | Turnstile cData to pass to the widget; null for other providers. |
| `recaptchaVersion` | "v2" \| "v3" \| null | no | — |
| `visible` | boolean | no | When true, show the check to the visitor instead of running it silently (the site is seeing unusual sign-in volume). |
| `uploadToken` | string | no | Forms with a third-party provider only: send this as _tk (with _ctx set to the form id) when requesting file upload tickets. The widget token is single-use, so it is checked on submit instead. |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORM_NOT_FOUND` | 404 | — |

```bash
curl -X GET "https://{site}/api/v1/forms/{id}/challenge"
```

### Request a signed upload ticket for a form file attachment field

`POST /forms/{id}/upload-request`

- Stability: beta
- Authentication: none

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |

**Request body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `fieldId` | string | yes | — |
| `fileName` | string | yes | — |
| `mimeType` | string | yes | — |
| `fileSize` | integer | yes | — |
| `_hp` | string | no | Honeypot. Send an empty string; any value marks the request as automated. |
| `_wp` | string | no | Second honeypot. Send an empty string. |
| `_co` | string | no | Third honeypot. Send an empty string. |
| `_ts` | integer | no | When the form was rendered, in Unix milliseconds. |
| `_tk` | string | no | Built-in bot check: the solved ALTCHA payload for the challenge (provider kanorio). |
| `_ctx` | string | no | What the built-in challenge was issued for: the form id, or customer-login:{siteId}. |
| `captchaToken` | string | no | Third-party bot check: the token from the provider's widget (provider turnstile, recaptcha or hcaptcha). |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `uploadId` | string | yes | — |
| `uploadUrl` | string | yes | — |
| `headers` | object | no | — |
| `expiresIn` | integer | yes | — |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORM_NOT_FOUND` | 404 | — |
| `INVALID_BODY` | 400 | — |
| `FILE_TOO_LARGE` | 413 | — |
| `FILE_TYPE_NOT_ALLOWED` | 400 | — |
| `UPLOAD_UNAVAILABLE` | 503 | — |

```bash
curl -X POST "https://{site}/api/v1/forms/{id}/upload-request" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### Submit answers to an online form

`POST /forms/{id}/submit`

- Stability: beta
- Authentication: none

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |

**Request body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `answers` | object | yes | Answers keyed by question id (from GET /forms/{id}). |
| `consent` | "checked" \| "submit_statement" | no | How the visitor agreed: ticked the consent box, or submitted under a consent statement. Omit when the form asks for no consent. |
| `consentTextId` | string | no | Id of the consent text the visitor saw (from GET /forms/{id}). |
| `locale` | string | no | — |
| `pagePath` | string | no | Path of the page the form was on, for the submission record. |
| `_hp` | string | no | Honeypot. Send an empty string; any value marks the request as automated. |
| `_wp` | string | no | Second honeypot. Send an empty string. |
| `_co` | string | no | Third honeypot. Send an empty string. |
| `_ts` | integer | no | When the form was rendered, in Unix milliseconds. |
| `_tk` | string | no | Built-in bot check: the solved ALTCHA payload for the challenge (provider kanorio). |
| `_ctx` | string | no | What the built-in challenge was issued for: the form id, or customer-login:{siteId}. |
| `captchaToken` | string | no | Third-party bot check: the token from the provider's widget (provider turnstile, recaptcha or hcaptcha). |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `state` | "closed" \| "not_open" \| "full" | no | — |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORM_NOT_FOUND` | 404 | — |
| `INVALID_BODY` | 400 | — |
| `INVALID_ANSWERS` | 400 | — |
| `VERIFICATION_EXPIRED` | 400 | — |
| `DUPLICATE_SUBMISSION` | 409 | — |

```bash
curl -X POST "https://{site}/api/v1/forms/{id}/submit" \
  -H "Content-Type: application/json" \
  -d '{}'
```
