Skip to main content
Consent in 4D is not a checkbox column. It’s an append-only evidence trail that the platform consults before acting on member data.

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.
Items can be platform-wide or specific to your organization. Each item is identified by its 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. If members give consent on your own site or app, record it with your organization API key. A grant names the item’s scope 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. 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 active data.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 returns 404 NOT_FOUND. Errors use the standard envelope: { "error": { "code", "status", "message", "details" } }.
Every route returns consent records in this shape: POST /v1/users/{userId}/consents with a JSON body:
201 Created when a new grant was recorded:
If the member already holds an active grant for that scope at that version, nothing new is written and the response is 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:
A record with revoked_at: null is active. A member with no records returns []. 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.