# Submit a form

URL: /docs/api/storefront/forms/submit-a-form

## POST /v1/forms/{handle}

```http
POST https://api.elaraone.app/api/v1/forms/{handle}
```

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `handle` | path | string | yes |  |
| `X-Client-IP` | header | string |  | Optional. IP address of the shopper (one IPv4/IPv6, no port, no list). Honoured only together with a valid X-Tenant-Secret; ignored otherwise. Used for rate limits and recorded with form submissions. |
| `X-Tenant-Secret` | header | string |  | Optional here. Server secret of the store: server-to-server only, never from a browser. Enables X-Client-IP. |
| `Idempotency-Key` | header | string |  | Optional; recommended (a UUID made once per user action, reused only to retry that action). Only a success is stored (24 hours): the same key with the same body, URI and credentials (Authorization, X-Cart-Token, X-Chat-Token) replays it (`Idempotent-Replayed: true`); anything else under that key is 422 `IDEMPOTENCY_KEY_REUSED`. An error is never stored: after a 4xx or 5xx, retrying with the same key runs the request again. |
| `X-Session-Id` | header | string |  | Optional. The shopper's `sid` cookie value (8-128 of letters, digits, `.` `_` `:` `-`). Decides the consent the lead rests on: whether the `lead_submitted` event keeps its session and visitor link, and whether the lead may be sent to ad platforms as a conversion. A lead has no basket, so this request is the only chance to read it. Read for this request only, compared as a SHA-256 hash, never stored raw. Send it on every write; a malformed value is ignored (never a 422), and leaving it out means "no choice known". |
| `X-Visitor-Id` | header | string |  | Optional. The `vid` cookie value, when the shopper has one. Decides the consent the lead rests on: whether the `lead_submitted` event keeps its session and visitor link, and whether the lead may be sent to ad platforms as a conversion. A lead has no basket, so this request is the only chance to read it. Read for this request only, compared as a SHA-256 hash, never stored raw. Send it on every write; a malformed value is ignored (never a 422), and leaving it out means "no choice known". |
| `X-Consent-Token` | header | string |  | Optional. The `consent` cookie value (the `consent_token` from `PUT consent`). Decides the consent the lead rests on: whether the `lead_submitted` event keeps its session and visitor link, and whether the lead may be sent to ad platforms as a conversion. A lead has no basket, so this request is the only chance to read it. Read for this request only, compared as a SHA-256 hash, never stored raw. Send it on every write; a malformed value is ignored (never a 422), and leaving it out means "no choice known". |

### Responses

- **202**: Success.
- **402**: Payment required. Branch on `error.code`: - `TENANT_PAYMENT_REQUIRED`: this store is on payment hold. Show a "store temporarily unavailable" page; do not retry the request.
- **404**: Not found: `FEATURE_NOT_ENABLED` when this store does not have the CRM feature on.
- **409**: `IDEMPOTENCY_IN_PROGRESS`: a request with this Idempotency-Key is still being processed; retry shortly with the same key.
- **422**: Validation error Also 422 `IDEMPOTENCY_KEY_REUSED`: this Idempotency-Key already belongs to a different request (body, URI or credentials). Also 422 `VALIDATION_FAILED` on a File question (its answer is the list of `token`s `POST forms/{handle}/files` returned): a token that was never issued for this form and question, has expired (2 hours after the upload) or was already used by another submission ("Upload it again"); the same token twice; more than the question's `max_files`; or more than 25 MB of files in the whole submission. Nothing is stored and no upload is used up, so the visitor can fix the answer and send again. A retry with the same `Idempotency-Key` replays the first answer.
- **503**: `CAPTCHA_UNAVAILABLE`: the captcha service could not be reached (only for stores whose forms are captcha-protected); nothing was stored. Retry with a fresh captcha token.