Skip to main content

Export Consent & Suppression Records

Recording consent and suppressing opt-outs is only half of the audit problem. When a regulator, an auditor, or opposing counsel asks you to prove who opted in or out, when, on which channel, and from what source, you need the whole trail back out as a structured file — not a per-contact lookup or a manual database pull. Two endpoints produce that proof. Both are limited to owners and admins, default to CSV (the file an auditor opens in a spreadsheet), also speak JSON, and are audited themselves: every export run is written to your audit log with the filters and row count, so the export is part of the evidence trail. All endpoints below are rooted at https://api.orbit.devotel.io/api/v1/compliance.
GET /compliance/consent/export returns one row per consent event — opt-in, opt-out, or unknown — joined to the contact’s email, phone, and WhatsApp ID so a record maps back to a person.
Query parameters: Every CSV export uses the same fixed column order, in this sequence (missing values are empty cells — start with the wide export, then narrow): The JSON response carries the same fields and also returns the column list in columns, the row count in count, the applied filters in filters, and the truncation flag in truncated.

Suppression ledger

GET /compliance/suppression-list/export is the export counterpart to the bulk import — it returns the durable (channel, address) STOP signals, including bulk-imported addresses that have no linked contact.
Query parameters:

Suppression row schema

Every CSV export uses the same fixed column order, in this sequence: The active set is the audit default because it is the set every send gate enforces; switch to revoked to prove an address was re-permissioned, or all for the full ledger.

Worked example: prove a number is suppressed since a date

An auditor asks for evidence that +15555550100 has been suppressed since May 1, 2026. Bound the export window to end at that date — any row returned is proof the address was on the ledger on or before the cut-off:
Then locate the row:
The row shows the address entered the ledger on 2026-05-01 via an inbound STOP keyword (reason: stop_keyword, source: api), is still active (status: active, empty revoked_at), and — because status=all was passed — would also surface had someone since revoked it. Quote the row and the audit entry for the export run together as the evidence package. The same window-bounding works on the consent export: filter to a single contact_id, or set to to the audit cut-off so the file contains only what was true at that point in time.

Truncation, and pulling past the 50k cap

Both exports are synchronous and hard-capped at 50,000 rows per request — the same ceiling as the bulk import. When your ledger exceeds the cap, the CSV response sets the X-Export-Truncated: true header and JSON responses report truncated: true. Never assume a flagged file is complete. To page past the cap, pull consecutive date windows and keep each window small enough to fit under 50,000 rows:
Because rows are ordered by suppressed_at (consent: created_at) ascending and to is inclusive of the day, adjacent windows tile with no gaps and no duplicates. Concatenate the files (dropping the repeated header lines) for the full ledger. If a single day still exceeds the cap, narrow further by channel and pull one file per channel per window.