Agreements vs. consents
The catalog distinguishes two kinds of item, mirroring their lawful basis:- Agreements: contract-basis, non-optional documents (terms of service and the like). A member with an un-accepted agreement at its required version is gated: they must accept before proceeding when they next sign in. Agreements are versioned; a major version change re-gates.
- Consents: freely-given opt-ins (e.g. location use). Optional by definition, revocable at any time, and their absence simply switches the associated behavior off.
scope (for example data.geo) and has published versions.
Append-only evidence
Every grant and revocation is a new immutable record: who, which item, which version, when, through what channel, with supporting evidence. Nothing is ever updated in place: re-consenting after a revocation appends a fresh record, so the full history of a member’s choices is always reconstructible. At most one grant per item is active at a time; that active record is what gates behavior.Recording consent from your backend
If members give consent on your own site or app, record it with your organization API key. A grant names the item’sscope and the version the member saw, and the platform accepts it only while that item is live and that version is currently in effect. Granting another version of an item supersedes the active grant for the old one. Every record carries a source: grants made through the API are api, those made in 4D’s own screens are ui.
A member can withdraw a consent at any time, and your backend can record that too. An agreement cannot be withdrawn through the API; it is refused, because accepting an agreement is a condition of membership rather than an opt-in. The endpoints are in the reference at the end of this page.
Consent actually gates behavior
The point of the trail is that platform features check it. The concrete example: geo enrichment of engagement events. A coarse country/region is stamped onto a member’s events only while they hold an activedata.geo consent. The lookup is transient (the raw IP address is
never persisted) and for a member without that consent, location is never
derived at all.
Imported members
Bulk-imported members receive no consent records at import time: an import never fabricates consent evidence. Like every member, they encounter the consent gate (pending agreements, offered opt-ins) the first time they sign in. See Migrate an existing member base.API reference
All three routes authenticate with your organization API key as a bearer header (see Authentication). The member must belong to your organization: a user id that isn’t a member returns404 NOT_FOUND. Errors use the standard envelope: { "error": { "code", "status", "message", "details" } }.
Grant a consent
POST /v1/users/{userId}/consents with a JSON body:
201 Created when a new grant was recorded:
200 OK with the existing record. A grant for a different version of the same scope records a new grant and withdraws the old one. Idempotency-Key is optional; a retry with the same key and body replays the first response.
List a member’s consents
GET /v1/users/{userId}/consents returns the member’s full record, active and withdrawn, for platform-wide items and your organization’s items, newest grant first. It is a JSON array, not an object.
200 OK:
revoked_at: null is active. A member with no records returns [].
Withdraw a consent
DELETE /v1/users/{userId}/consents/{scope} withdraws the member’s active grant for that scope. It takes no body. Idempotency-Key is optional.
200 OK returns the withdrawn record, with revoked_at set. The record stays in the member’s history.