# Record a consent choice

URL: /docs/api/storefront/consent-and-privacy/record-a-consent-choice

Records the shopper's choice from the banner, the "Cookie settings" link or the account page:
a new consent record (the previous one is kept, marked superseded). Withdrawing is the same call
with the category set to false. Answers `201` with the consent now in force, like `GET consent`,
plus `consent_token`: keep it in the HttpOnly `consent` cookie for 12 months and forward it as
`X-Consent-Token` from then on. A visitor id (`X-Visitor-Id`) is linked only when analytics is
granted.

## PUT /v1/consent

```http
PUT https://api.elaraone.app/api/v1/consent
```

Records the shopper's choice from the banner, the "Cookie settings" link or the account page:
a new consent record (the previous one is kept, marked superseded). Withdrawing is the same call
with the category set to false. Answers `201` with the consent now in force, like `GET consent`,
plus `consent_token`: keep it in the HttpOnly `consent` cookie for 12 months and forward it as
`X-Consent-Token` from then on. A visitor id (`X-Visitor-Id`) is linked only when analytics is
granted.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `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 | yes | Required here. Server secret of the store: this route is server-to-server only, never from a browser. Also 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 | yes | Required. The shopper's `sid` cookie value (8-128 of letters, digits, `.` `_` `:` `-`). |
| `X-Visitor-Id` | header | string |  | Optional. The `vid` cookie value, when the shopper has one (it exists only with analytics consent). On `PUT`, linked to the record only when analytics is granted. |
| `X-Consent-Token` | header | string |  | Optional. The `consent` cookie value: the `consent_token` an earlier `PUT consent` returned. Remembers the choice across sessions, including "reject all". |

### Request body (`application/json`)

| Field | Type | Required | Description |
|---|---|---|---|
| `categories` | object | yes | The choice for each category. Strictly necessary is not a choice and is ignored if sent. |
| `source` | `banner` · `settings_link` · `account_page` | yes | Where the choice was made: the banner (first visit or a re-prompt), the persistent "Cookie settings" link, or the customer's account page. (`login` is the engine's own linking record at sign-in and is never accepted here.) |
| `banner_version` | integer |  | The banner version the shopper was shown (`banner.version` of `GET consent`). Default: the current version. |

### Responses

- **201**: 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.
- **403**: `TENANT_SECRET_INVALID`: `X-Tenant-Secret` is missing or wrong. This route is server-to-server only: call it from the storefront server, never from a browser.
- **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).