Bulk-import a suppression list from CSV
Use the bulk import when you are bringing a suppression ledger from another provider, applying a batch from a CRM, or remediating a known exposure. The endpoint writes each accepted address to your tenant’s suppression ledger and returns enough detail to reconcile the file without guessing which rows were new. This is an operator-side control. Devotel Orbit does not decide which recipients your tenant must suppress or replace your legal process. Review the three suppression entry points when you need to choose between a CSV backfill, a live Consent API event, or a recipient-managed Preference Center.Before you import
Prepare a UTF-8 RFC-4180 CSV with a header and confirm that your source system has already identified the correct country for any national-format phone numbers. The endpoint accepts one multipart file per request and requires an owner or admin credential. The operation isPOST /api/v1/compliance/suppression-list/import.
default_country is optional. Use it when the CSV contains national-format
phone numbers such as 4155550101; it is a two-letter ISO 3166-1 alpha-2
country hint. You, as the operator, are responsible for supplying the right
country. Orbit cannot safely infer whether an unqualified number belongs to
one country or another. A number that cannot be normalized is returned as
invalid_phone instead of being written.
Use dry_run=true with the same file and defaults to parse and classify rows
without writing them. A dry run returns a preview, but it does not create an
audit-chain import record because no suppression write occurred.
CSV contract
The first row is the header. Parsing follows RFC 4180: quote a field that contains a comma or newline, escape a literal quote by doubling it, and use CRLF or LF row endings. A UTF-8 BOM is accepted and removed from the first header. Header matching is case-insensitive, ignores spaces, hyphens, and underscores, and accepts the aliases below. Header order does not matter.
A row may contain more than one address. Each populated address becomes its
own suppression entry, with the row’s
channel override applied to each one.
For example, this file creates one all phone entry and one email entry on
the first data row:
channel must be one of all, sms, voice, whatsapp,
email, push, telegram, messenger, or rcs. An invalid override is
classified as a row-level invalid result; it does not make the entire file
fail.
Address normalization
- Phone: Orbit calls its phone normalizer. Valid input is stored in the
canonical E.164 representation. Pass
default_countryfor national-format input, and verify the resulting country before importing. Do not prepend a country code based on a guess in a batch script. - WhatsApp ID: Supply the recipient’s numeric MSISDN, with an optional
leading
+. Orbit validates the 7-to-15-digit E.164 range and stores the canonical+form. A bare national number is not made unambiguous by thewa_idcolumn; convert it before upload. - Email: Orbit lowercases the address and requires a practical RFC 5321 mailbox shape. Addresses longer than 254 characters are invalid.
file_sha256 together so you can prove exactly which bytes were
submitted.
Understand channel scope before committing
The suppression key is(channel, address). The default is intentionally based
on the address type, not on the transport that delivered the original opt-out:
The override is a deliberate narrowing or selection, not a declaration of
where the CSV came from. A phone row with
channel=email can be stored, but it
will only match email sends. If a recipient still receives a message after an
accepted import, first inspect the row’s scope and follow
Troubleshooting: suppression scope mismatch.
A scope mismatch is different from a failed import. Export the active ledger,
check the channel value, and compare it with the send channel. Re-uploading
the same (channel, address) is idempotent; it does not replace an
existing row with a different scope. If you intentionally need both scopes,
import the address with the additional key, and if you need to correct an
existing scope, follow the revocation or correction procedure in the
troubleshooting guide rather than assuming a duplicate changed it.
Walk through an import response
The successful response is an envelope whosedata contains the run summary.
The endpoint classifies bad rows and continues with the valid ones:
accepted counts newly inserted suppression keys. duplicates counts keys
that were already active before this request. intra_file_duplicates counts
repeated keys that were collapsed before the database write. invalid is the
complete invalid count; only the first 100 invalid rows are expanded in
errors. by_channel counts newly accepted rows by their stored scope.
Invalid and empty rows
A row with no value in any recognized address column returns a row-level classification, not an HTTP failure:missing_address reason is used when a populated row has an invalid
channel override. Fix the source row and re-upload it. If the header has
no recognized address column at all, the request instead returns HTTP 422
with error code MISSING_ADDRESS_COLUMN; add phone, email, or wa_id (or
one of the aliases in the schema table). Other row-level reasons include
invalid_phone, invalid_email, invalid_wa_id, row_too_long, and
too_many_columns.
Re-run a strict subset safely
The database’s active partial unique key makes a re-run safe. A second upload of a key that is already active is a no-op, and the response exposes it as aduplicates count. This lets you retry a timed-out or overlapping batch
without creating a second active suppression row.
File 1: the original ledger
reason does not rewrite an active row. duplicates means “it
was already active before this run,” while intra_file_duplicates means “the
same key appeared more than once in this file.” Keep the second run’s
run_id and hash as evidence of the retry even though it added no rows.
Capture audit-chain evidence
Every committing import writes one tamper-evident audit-chain row with the operationcompliance.suppression_list_import. The row is associated with
the tenant and contains:
For a regulator or discovery request, preserve the original CSV, the response,
and the audit export containing this run. Match the audit row’s hash to the
file, match its counts to the response, and export the active suppression
ledger for the affected time window. The ledger proves which entries were
active; the audit row proves who ran the import and what the operator
submitted. Do not send a raw CSV as the only evidence: it does not establish
that the file was actually imported.
Post-incident remediation example
If a GDPR or CCPA review finds that a segment was exposed to an unintended campaign, use a repeatable tenant-owned sequence:- Discover the exposure. Freeze the affected segment in your own incident process, identify the recipient identifiers and the communication window, and preserve the source report.
- Build the CSV. Deduplicate by intended
(channel, address)scope. Use E.164 phone values, canonical WhatsApp IDs, and lowercase email values. Leave phone andwa_idrows untagged when the incident requires a cross-channel fence; addchannelonly for a documented channel-specific restriction. - Preview and import. Run the exact file with
dry_run=true, fix everyinvalidrow, then commit the clean file with a reason such aspost_incident_remediation_2026_10_11. - Capture evidence. Save the
run_id,file_sha256, response counts, and the audit-chain export. Export the active ledger and verify representative rows have the intended scope. - Close the loop. Attach the evidence bundle to the tenant’s incident record and update the outbound audience process that admitted the exposed recipients. A suppression import is the containment step, not a substitute for the tenant’s privacy, consent, or incident-response work.
Limits and HTTP failures
Split larger ledgers into batches. A batch that hits the 60-second budget may
have already written rows; its
408 IMPORT_TIMEOUT response identifies the
partial counts, and a retry is safe because active keys are idempotent.
These request-level failures write no normal completion summary:
400 NO_FILEorMULTIPLE_FILES: attach exactly one multipartfile.400 CSV_PARSE_ERROR: fix malformed quoting or the missing header.413 PAYLOAD_TOO_LARGEorTOO_MANY_ROWS: split the source file.415 UNSUPPORTED_MEDIA_TYPE: export CSV rather than XLSX.422 MISSING_ADDRESS_COLUMN: add a recognized address header.422 VALIDATION_ERROR: correctdefault_countryor the form-leveldefault_reason.
errors[], counted in invalid,
and leaves the other rows eligible for import.
How this differs from other opt-out paths
All of these inputs converge on the tenant’s suppression ledger, but they serve different operational jobs. Keep one row per channel of truth in your source systems and avoid treating one input as a replacement for the others.
Use the suppression three-entry-points guide
for the selection decision. Use the Consent vs. suppression model
and TCPA reasonable revocation
to align your tenant’s consent and revocation records with the suppression
fence.