Set Consents
PUT https://api.firmly.work/api/v2/domains/{domain}/cart/consents
Overview
Updates the consent preferences for a buyer’s cart session. Grants or revokes consent for the IDs returned by Get Consents. Each grant captures a server-side signature for compliance.
Authentication
x-firmly-authorization(string, required) — Device access token from Browser Session
Path Parameters
domain(string, required) — Domain of the merchant website (e.g.,staging.luma.gift)
Request Body
consents(object[], required) — Array of consent updates to apply. Each entry references a consent returned by Get Consents.Consent update properties
id(string, required) — Unique identifier of the consent to update — must match anidreturned by Get Consents.revoke(boolean, defaultfalse) — Set totrueto revoke a previously granted consent. Omit or set tofalseto grant.
Response
Returns an updated array of all consent objects with the same structure as the Get Consents response, reflecting the changes made.
Code Examples
Grant Single Consent
curl -X PUT https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents \-H "x-firmly-authorization: YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"consents": [{"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"}]}'
const response = await fetch('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents', {method: 'PUT',headers: {'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},body: JSON.stringify({consents: [{id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479'}]})});const updatedConsents = await response.json();
import requestsresponse = requests.put('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents',headers={'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},json={'consents': [{'id': 'f47ac10b-58cc-4372-a567-0e02b2c3d479'}]})updated_consents = response.json()
$data = ['consents' => [['id' => 'f47ac10b-58cc-4372-a567-0e02b2c3d479']]];$ch = curl_init();curl_setopt($ch, CURLOPT_URL, 'https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents');curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-firmly-authorization: YOUR_TOKEN','Content-Type: application/json']);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$response = curl_exec($ch);$updatedConsents = json_decode($response, true);curl_close($ch);
Revoke Consent
curl -X PUT https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents \-H "x-firmly-authorization: YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"consents": [{"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479","revoke": true}]}'
const response = await fetch('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents', {method: 'PUT',headers: {'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},body: JSON.stringify({consents: [{id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',revoke: true}]})});
Update Multiple Consents
curl -X PUT https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents \-H "x-firmly-authorization: YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"consents": [{"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"},{"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"},{"id": "8c9e3f12-4567-8901-2345-678901234567","revoke": true}]}'
const response = await fetch('https://api.firmly.work/api/v2/domains/staging.luma.gift/cart/consents', {method: 'PUT',headers: {'x-firmly-authorization': 'YOUR_TOKEN','Content-Type': 'application/json'},body: JSON.stringify({consents: [{ id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479' }, // Grant{ id: '6ba7b810-9dad-11d1-80b4-00c04fd430c8' }, // Grant{ id: '8c9e3f12-4567-8901-2345-678901234567', revoke: true } // Revoke]})});
Response Example
[{"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479","ui_slot": "UNDER_EMAIL_INPUT","text": "I would like to receive marketing emails about special offers and new products.","type": "MARKETING","explicit": true,"required": false,"revokable": true,"signed": true},{"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8","ui_slot": "ABOVE_PLACE_ORDER_BUTTON","text": "I agree to the Terms of Service and Privacy Policy.","html": "I agree to the <a href='/terms'>Terms of Service</a> and <a href='/privacy'>Privacy Policy</a>.","type": "TERMS_AND_CONDITIONS","explicit": true,"required": true,"revokable": false,"signed": true}]
Consent Rules
- Consents with
explicit: truerequire an opt-in action from the buyer; sending theid(withoutrevoke) records that opt-in. - Required consents must be signed before placing an order —
place-orderrejects the request if anyrequired && explicitconsent is unsigned. - Only consents with
revokable: truecan be revoked. Sendingrevoke: truefor a non-revokable consent fails the entire request with400 BadRequest(the description identifies the offending IDs).
Error Responses
Errors return a JSON body with code, error, and description. Program against the error value — descriptions are human-readable and may change.
400 — MissingAuthHeader
The x-firmly-authorization header is missing or empty.
{ "code": 400, "error": "MissingAuthHeader", "description": "x-firmly-authorization header is missing or invalid." }
400 — InvalidToken
The authorization token is not a valid JWT structure.
{ "code": 400, "error": "InvalidToken", "description": "Jwt token is invalid." }
400 — InvalidAPIToken
The API token (server-to-server secret) is invalid or has been revoked.
{ "code": 400, "error": "InvalidAPIToken", "description": "API token is invalid." }
400 — BadRequest
All of these conditions share the same error (BadRequest) and code (400), so the description is the only way to tell them apart. Unlike consent-object descriptions (which are merchant-authored display text and may change), these BadRequest descriptions are Firmly-emitted and stable enough to branch on. Prefer matching on a stable substring (e.g. invalid, non revokable, no consents) rather than the whole string:
- No consents available — the session has no consents to sign. Call Get Consents first.
{ "code": 400, "error": "BadRequest", "description": "There are no consents to sign" }
- Unknown consent IDs — one or more
ids in the request body do not match any consent from Get Consents. Description lists the offending IDs.{ "code": 400, "error": "BadRequest", "description": "The consents <id1>,<id2> are invalid." } - Attempt to revoke a non-revokable consent — at least one consent has
revokable: false. Description lists the offending IDs.{ "code": 400, "error": "BadRequest", "description": "The consents <id> are non revokable" } - Server-to-server auth headers missing or malformed (different code path, same wire error).
{ "code": 400, "error": "BadRequest", "description": "Bad request." }
401 — Unauthorized
The authorization token was rejected.
{ "code": 401, "error": "Unauthorized", "description": "Unauthorized." }
401 — InvalidJWTToken
The device JWT signature does not verify, or required claims are missing.
{ "code": 401, "error": "InvalidJWTToken", "description": "Jwt token is invalid." }
404 — PartnerNotFound
The appid claim on the device JWT does not map to a known partner / tenant.
{ "code": 404, "error": "PartnerNotFound", "description": "Partner not found." }
404 — DomainNotFound
The {domain} path parameter does not match any merchant configured with Firmly, or the merchant has been disabled.
{ "code": 404, "error": "DomainNotFound", "description": "This domain was not found in firmly servers." }
412 — OperationNotSupported
The merchant’s platform does not support this operation.
{ "code": 412, "error": "OperationNotSupported", "description": "This operation is not supported for this store." }
400 — InvalidInputBody
Request body fails schema validation. Each entry under consents must have a string id; revoke is optional and boolean.
{ "code": 400, "error": "InvalidInputBody", "description": "The body is not processable" }