# Suppress a number (idempotent)

Source: https://docs.usetone.ai/reference/compliance/dnc.add

> Adding a number already on the list is a no-op rather than an error, so a replayed webhook or a retried job cannot fail here.

`POST /v1/dnc`
Adding a number already on the list is a no-op rather than an error, so a replayed webhook or a retried job cannot fail here. Use `lockout90d` for an opt-out, which becomes contactable again on a date, rather than a permanent suppression.

Requires the `write` scope.

## Request body

| Field | Type | | Meaning |
|---|---|---|---|
| `e164` | string | required | The number to suppress, in E.164. Adding one already on the list is a no-op, not an error. |
| `lockout90d` | boolean | optional | Apply the TCCCPR 90-day re-consent lockout instead of suppressing permanently — the number becomes contactable again on a specific date rather than never. Only meaningful for an opt-out. |
| `reason` | string | optional | Why this number was suppressed. Free text, kept for the audit trail. |
| `source` | string | optional | Where the suppression came from. Recorded on the entry and shown in the evidence pack. Open set — handle an unrecognised value rather than throwing. Today: `manual`, `csv`, `api`, `caller_request`, `complaint`, `carrier_rejection`. |

### Example request

```json
{
  "e164": "+919876543210",
  "lockout90d": false,
  "reason": "Asked to be removed during a call on 2026-08-20.",
  "source": "manual"
}
```

## Response

| Field | Type | | Meaning |
|---|---|---|---|
| `createdAt` | string | required | When this number was suppressed, RFC 3339 UTC. |
| `e164` | string | required | The suppressed number, in E.164. |
| `expiresAt` | string | null | required | Null is permanent. A date is the end of the 90-day opt-out lockout. |
| `id` | string | required | The suppression entry’s id. Pass it to `POST /v1/dnc/{id}/remove` to lift the suppression — a dashboard-session action that requires a written reason. |
| `reason` | string | null | required | Why this number was suppressed. |
| `removedAt` | string | null | required | Non-null means the suppression was LIFTED — the entry no longer blocks, and survives only as the record of the removal. |
| `removedReason` | string | null | required | The written justification the operator gave when lifting it. |
| `source` | string | required | How it got on the list. Open set — handle an unrecognised value rather than throwing. Today: `manual`, `csv`, `api`, `caller_request`, `complaint`, `carrier_rejection`. |
| `sourceCallId` | string | null | required | The call that produced this suppression, when it was captured in-call (DTMF, verbal opt-out) or from a carrier rejection. |

### Example response

```json
{
  "data": {
    "createdAt": "2026-08-25T09:14:22Z",
    "e164": "+919876543210",
    "expiresAt": "2026-11-23T09:14:22Z",
    "id": "8814",
    "reason": "Asked to be removed during a call on 2026-08-20.",
    "removedAt": null,
    "removedReason": null,
    "source": "manual",
    "sourceCallId": null
  }
}
```
