Consent Management & Receipts
Before you message a contact on a regulated channel you generally need a lawful basis — most often consent. Orbit’s consent API is the system of record for who opted in or out, on which channel, when, and under what legal basis. Every write fans out to the surfaces your sends are gated against, so recording consent here is what actually unblocks (or blocks) a message. All endpoints below are rooted athttps://api.orbit.devotel.io/api/v1/compliance.
Channels and states
Consent is tracked per channel. The supported channel set is:email, fax, instagram, line, messenger, push, rcs,
sms, viber, voice, whatsapp.
A (contact, channel) pair resolves to one of three states:
Recording consent
POST /compliance/consent records an opt-in or opt-out across one or
more channels in a single call. Identify the contact by contact_id
or by identifier (an email, E.164 phone, or WhatsApp ID — Orbit
resolves the type automatically).
201 Created:
Supplying both
valid_until and expires_in_days is ambiguous and
rejected with 422 VALIDATION_ERROR. When you set a window, the 201
response echoes the resolved valid_until (the absolute expiry instant);
it is null for a non-expiring grant or an opt-out. Re-recording an
opt-in with a fresh window extends validity — the original
granted_at is preserved, but the expiry is updated.
What a write does. Each recorded channel updates four
synchronized surfaces: the consent_records audit table, the
contact’s channel_preferences mirror (the read-side fast path your
sends check), the suppression_list (on opt-out), and a short-lived
Redis STOP-fence so in-flight campaign batches honour the change
within ~10 minutes.
Writes are partial-safe: if one channel fails, the others still
apply. Compare
consent_record_ids.length against the number of
channels you requested to detect a partial write. Re-recording an
opt-in for a channel that is already opted-in refreshes the
metadata/proof but keeps the original granted_at.Looking up consent
GET /compliance/consent/lookup returns the current state for one
(contact, channel) pair — use it as a pre-send gate.
state of unknown means no record exists for the pair — your
application decides whether that implies consent (some transactional
flows) or blocks the send (most marketing flows).
The last three fields report time-bounded consent and are always
present:
Finding expiring consent
GET /compliance/consent/expiring sweeps the tenant for opt-ins whose
validity window has lapsed or is about to — the input to a re-permission
(re-confirmation) campaign. Only grants that carry a valid_until are
returned; non-expiring consent never appears.
status
(expired or expiring) so you can split “must re-confirm now” from
“warn before the window closes”. Re-confirmation is an ordinary
POST /compliance/consent opt-in — optionally with a fresh valid_until
or expires_in_days.
Consent history
GET /compliance/consent/history returns the full, paginated audit
trail for a contact — every grant and revocation, most recent first.
Query parameters: contact_id or identifier (one required), an
optional channel filter, limit (≤ 100, default 50), and an opaque
cursor.
Consent receipts (India DPDP) & Consent Managers
India’s Digital Personal Data Protection Act (DPDP) introduces the concept of a Consent Manager — an accountable, registered intermediary that mints cryptographically signed consent receipts on behalf of a data principal. Orbit can register the managers your users go through and verify the receipts they issue.Register a Consent Manager
POST /compliance/consent/managers (admin/owner) registers a manager
and stores its public key (an ECDSA P-256 SPKI PEM) used to verify
every receipt it signs.
GET /compliance/consent/managerslists registered managers (active first).PUT /compliance/consent/managers/{id}updates or deactivates one (partial update; all fields optional).
Store a signed receipt
POST /compliance/consent/receipts verifies a manager-signed receipt
and persists it as consent. The signature (ECDSA P-256 / SHA-256,
IEEE-P1363, base64url) is checked against the registered manager’s
public key over a JCS-inspired, sorted-key JSON canonicalization of the
payload before anything is stored. This canonicalization sorts object
keys ascending by UTF-16 code unit and drops insignificant whitespace, but
it is not a full RFC 8785 implementation — in particular it does not apply
JCS’s mandated number serialization rules. Sign receipts with the same
sorted-key form Orbit uses rather than assuming a spec-complete RFC 8785
verifier will produce a matching hash.
201 with { "id": …, "receipt_id": …, "verified": true }.
A bad signature, or an unregistered/inactive manager, returns 422 CONSENT_RECEIPT_INVALID — the detail notes the payload may have been
tampered with or the manager may have rotated keys.
Re-verify a stored receipt
POST /compliance/consent/receipts/{id}/verify re-checks a
previously stored receipt against the manager’s current key — use
it during an audit to confirm a receipt still validates and whether
its manager remains active. {id} accepts either the consent-record
id or the receipt_id.
Consent receipts require the tenant
consent_managers migration. On
tenants that predate it, the read paths degrade gracefully: the
manager list returns an empty list and the re-verify endpoint returns
404. Minting a receipt is fail-closed, so POST /compliance/consent/receipts returns 422 CONSENT_RECEIPT_INVALID
on pre-migration tenants rather than degrading — run the migration
before issuing receipts.Related references
- Opt-Out & Suppression Lists — bulk-import opt-outs and how the suppression list gates sends.
- DSAR — honouring access/delete requests over the consent record.
- DLT-India Onboarding — the registration layer that pairs with DPDP consent on Indian SMS.
- API Reference → Compliance — full request/response schemas (regenerated from the live API).