Compliance
Generated from
DialerDigital/corecommit6e9f026f0a72— specopenapi/dialer-v1.yamlv0.36.0. Do not edit by hand; see How this reference is built. Download the OpenAPI spec.
Suppression data the tenant owns: its internal do-not-call list, uploaded as CSV into the same list the pre-dial gate reads.
Endpoints
| Method | Path | Summary |
|---|---|---|
POST | /v1/suppressions/import | Upload the tenant's own do-not-call list (CSV) |
GET | /v1/artificial-voice-disclosure | Fetch the tenant NON-AI artificial-voice disclosure |
PUT | /v1/artificial-voice-disclosure | Store the tenant NON-AI artificial-voice disclosure |
Upload the tenant's own do-not-call list (CSV)
POST /v1/suppressions/importLoads the tenant's INTERNAL suppression list from a text/csv body.
Rows join the internal_tenant DNC list that the PRE-DIAL compliance gate already consults on every call and every SMS — the same list SMS STOP writes. There is no second suppression path and nothing to enable: an accepted number is suppressed for the next attempt.
Format: one row per number, number[,reason]. A header row (number, phone, phone_number, telephone, e164, msisdn) is optional; blank lines and lines starting with # are skipped. Numbers are normalised the same way the regulatory dumps are: E.164 is kept, a bare 10-digit or 1+10-digit NANP number becomes +1XXXXXXXXXX.
Malformed rows are REPORTED, not fatal: refusing a whole file over one typo would leave the remaining consumers dialable, which is the expensive direction of the error. Rejected values come back MASKED — the line number is what identifies the row to fix.
Re-uploading a list is idempotent (recorded counts rows actually written, so an overlap reads as an overlap and not as new suppressions).
Limits: 1 000 000 bytes of body, 10 000 data rows, 200 characters of reason, and at most 100 entries in rejected (rejected_count always carries the true total).
Responses
200
Upload outcome. A 200 does NOT mean every row was accepted — rejected_count is authoritative.
| Field | Type | Required | Description |
|---|---|---|---|
received | integer | yes | Data rows the document offered (header/blank lines excluded). |
accepted | integer | yes | Rows that normalised to a distinct E.164 number. |
recorded | integer | yes | Rows actually written. Lower than accepted when the list overlaps suppressions already on file. |
rejected_count | integer | yes | |
rejected | array of object | yes | Up to 100 entries; see rejected_count for the total. |
rejected[].line | integer | yes | 1-based line number in the uploaded document. |
rejected[].value | string | yes | The offending value, masked to its last 4 characters. |
rejected[].reason | not_e164 · duplicate | yes |
{
"received": 3,
"accepted": 2,
"recorded": 2,
"rejected_count": 1,
"rejected": [
{
"line": 4,
"value": "***5551",
"reason": "not_e164"
}
]
}400 — BadRequest
Malformed request (shape/type errors, invalid filters, bad timestamps).
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}413
The CSV body exceeds 1 000 000 bytes.
{
"error": {
"code": "payload_too_large",
"message": "csv body exceeds 1000000 bytes"
}
}415
The request body is not text/csv.
{
"error": {
"code": "unsupported_media_type",
"message": "this endpoint takes a text/csv body"
}
}422 — Unprocessable
Shape is fine, semantics are not (broken domain rule).
{
"error": {
"code": "unprocessable",
"message": "scheduled_at must be at least 10 minutes out"
}
}Fetch the tenant NON-AI artificial-voice disclosure
GET /v1/artificial-voice-disclosureThe stored disclosure for the NON-AI artificial-voice lane (TTS / IVR / survey). A tenant with nothing stored is 404 not_found — NOT a 200 with a null body: "no disclosure stored" is a real compliance state, and a client branching on the status code must not read it as configured.
Responses
200
The stored disclosure.
| Field | Type | Required | Description |
|---|---|---|---|
artificial_voice_disclosure | object (ArtificialVoiceDisclosure) | yes | |
artificial_voice_disclosure.text | string | yes | The stored disclosure text. |
artificial_voice_disclosure.source | api · import · migration | yes | Provenance — how the row got here. Server-set, never taken from the body. |
artificial_voice_disclosure.actor | string | yes | The principal that wrote it, narrowest-first: the acting agent id, else the dashboard user id when the caller is a ddu_ session, else tenant-api-key for a dd_ key that carries no finer identity. Server-set. |
artificial_voice_disclosure.valid | boolean | yes | Whether the stored text STILL passes the content check. Re-computed on every read, not stored: the marker vocabulary can gain a term, and a row written under the older one must not keep vouching for a text the gate would no longer accept. This is the same question the dialing path asks. |
artificial_voice_disclosure.updated_at | string (date-time) | yes |
401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}404 — NotFound
Unknown id, malformed (non-UUID) id OR another tenant's id — RLS returns zero rows, so all three are indistinguishable by design (no existence leak, never a 403 for foreign ids).
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}Store the tenant NON-AI artificial-voice disclosure
PUT /v1/artificial-voice-disclosureUpsert on the tenant (one row, last write wins). The TEXT is judged by the SAME content check the AI lane applies: it must clearly identify the voice as automated, in English or Spanish. A text that does not is 400 bad_request and NOTHING is stored — a gate cannot be handed a true the text has not earned.
source and actor are provenance and are set by the SERVER (api plus the acting principal); sending either is 400 bad_request naming the key. Tenant-management scope (dd_ API key or dashboard login; ddw_ → 403).
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
text | string | yes | Must clearly identify the voice as automated (EN or ES). Anything else is 400 bad_request and nothing is stored. The ceiling exists because the dialing path re-reads this row on every attempt. The EFFECTIVE ceiling is 10000 BYTES (UTF-8), enforced by the server and by a database CHECK. JSON Schema maxLength counts CHARACTERS and has no byte-length keyword, so the maxLength: 10000 above is an UPPER bound, not the real limit: a multibyte text can be refused with 400 bad_request well before reaching 10000 characters. The machine-readable form of the real limit is x-maxLengthBytes. See ADR #74. |
{
"text": "Hello, this is an automated call from Acme Collections."
}Responses
200
The stored disclosure.
| Field | Type | Required | Description |
|---|---|---|---|
artificial_voice_disclosure | object (ArtificialVoiceDisclosure) | yes | |
artificial_voice_disclosure.text | string | yes | The stored disclosure text. |
artificial_voice_disclosure.source | api · import · migration | yes | Provenance — how the row got here. Server-set, never taken from the body. |
artificial_voice_disclosure.actor | string | yes | The principal that wrote it, narrowest-first: the acting agent id, else the dashboard user id when the caller is a ddu_ session, else tenant-api-key for a dd_ key that carries no finer identity. Server-set. |
artificial_voice_disclosure.valid | boolean | yes | Whether the stored text STILL passes the content check. Re-computed on every read, not stored: the marker vocabulary can gain a term, and a row written under the older one must not keep vouching for a text the gate would no longer accept. This is the same question the dialing path asks. |
artificial_voice_disclosure.updated_at | string (date-time) | yes |
400 — BadRequest
Malformed request (shape/type errors, invalid filters, bad timestamps).
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
The tenant is suspended.
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}