Skip to main content

Consent inspector

Consent touches every channel — SMS, voice, email, push, WhatsApp, MMS, fax — and it’s one thing Orbit owns end to end. The Consent inspector (Audience → Consent) shows, for a single contact, the complete consent ledger plus the exact allow/block verdict the platform would compute if a destination tried to deliver to them today. It’s the surface you open before you queue a send, and the surface you open when a send was suppressed and you need to know why. Before this page existed, the only way to read a contact’s consent ledger or preview the destination verdict was a raw API call. The inspector closes that gap — the same two endpoints the compliance team hits with curl, wrapped in a form.

What the inspector shows

Type a contact_id (cnt_… — the identifier shown on the contact’s record page, or the id returned by the Contacts API) into Look up a contact and the page splits into two cards. Consent records — one row per consent ledger entry, newest first: Verdict simulator — pick a channel (and optionally a policy template) and press Run consent check. You get back the same structured verdict the destination dispatch path computes at delivery time — Allowed or Blocked, with the machine-readable reason and human label. The verdict reasons and what to do with each:

When to open it

  • Preflight a single contact before you queue them into a campaign, add them to a destination, or hand them to an agent. The verdict simulator runs the identical decision the destination dispatch path runs at delivery time, so a Blocked answer now is a send that would be suppressed later — catch it in the inspector, not in the delivery log.
  • Debug a suppressed send. When a message was suppressed on a consent gate, paste the contact id and rerun the check against the channel that was suppressed. The reason code tells you which gate fired (opted_out, revoked, expired, policy_mismatch, or the refuse-on-unknown defaults).
  • Compliance audit. For GDPR Art 30 burden-of-proof purposes, the records card exposes the lawful basis, policy template, and captured-at timestamp of every grant and revoke in the ledger — the same evidence the export endpoints can hand to a DPA.

How the verdict is computed

The verdict folds three stores, per the consent and suppression model:
  1. consent_records — the append-only ledger. Every opt-in or opt-out flip inserts a new row; the decision reads the newest active row (revoked_at IS NULL). A partial unique index guarantees at most one active row per (contact, channel), so the verdict is deterministic.
  2. channel_preferences — the contact profile’s per-channel preference flags, consulted only when the ledger is empty: an explicit profile opt-in or opt-out is surfaced so the inspector doesn’t lie “no records on file” about a positive opt-in captured via import, merge, flows, or CRM sync. A top-level global_opt_out flag suppresses every channel and returns opted_out.
  3. Suppression list — the opt-out suppression mirror that the send gate consults for hard blocks.
The per-tenant posture (deny_on_missing or permit_on_missing — see Consent default policy) can invert only the two “no record / unknown state” branches. Four gates are always enforced regardless of tenant posture, because they’re universal-legal: a positive opt-out, a revoked grant, a policy-template mismatch, and an expired validity window.

Fixing a verdict from the surface

The inspector is a read surface — the grant/revoke writes stay on the surfaces that own them. From a Blocked verdict:
  • To consent the contact — open the contact’s profile in the audience workspace and set the channel opt-in; the write lands in the ledger. Rerun the verdict and the reason moves to allowed.
  • To revoke — use the same contact-level opt-out control. The revocation inserts a new ledger row (the ledger is append-only), stamps revoked_at, and the verdict flips to Blocked with revoked.
  • To restore (re-consent after a revocation) — set the opt-in again from the contact profile. The new row supersedes the revoked one and the verdict returns to Allowed.
The member-level permission the page warns about is the same permission the Contact Consents tab enforces — only authorized roles see these controls.

API walkthrough

Two endpoints back the inspector. Both are restricted to owner / admin / developer roles, tenant-scoped, and rate-limited (60 reads/min for the ledger endpoint, 600 verdicts/min for the check endpoint — the check is the dispatch-path hot path).

Read the full ledger for a contact

Response — the full ledger plus any profile-reconciled rows for channels the ledger has never covered:

Run a single destination verdict

Response — the verdict the dispatch path would compute (policy_template is optional; omit it to skip the template gate):
Every check call emits a structured log line (cdp_consent_check) with the inputs and verdict — that’s the forensic trail you can tail instead of an audit row per call.

Grant / revoke from the API

The targeted grant or revoke goes to the compliance consent endpoint. An opt-out also mirrors the channel into the suppression list so the send gate sees it within the same call:
Returns 201 with the new ledger consent_record_ids. You can pass a phone/email identifier instead of contact_id — the endpoint resolves it the same way the lookup does.
  1. Open Audience → Consent inspector.
  2. Paste the contact you were about to send to — say Cnt_4f3a9b1c2d (casing is normalized), press Inspect. The page confirms the profile identity inline so you’re not working against the wrong hash.
  3. In the Verdict simulator, pick sms and press Run consent check. Suppose it comes back Blocked — contact opted out of this channel.
  4. The records card explains why: the newest sms row has state opted_out and a Revoked timestamp.
  5. Check with the contact, then open their profile page and set the sms opt-in back on.
  6. Re-run the check. It now returns Allowed — active grant on record, with the new consent_record_id and granted_at. You’re clear to send.
For a parity check, clear the contact id and paste it back in — the verdict resets with the contact, so a stale verdict never lingers across contacts.

See also