> ## Documentation Index
> Fetch the complete documentation index at: https://inkbox.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 10DLC compliance API

> Register a brand and campaign, upload opt-in evidence, check approval, and select your organization's SMS campaign.

Use these endpoints to manage your organization's 10DLC registration over HTTP. Each organization can have one brand and one campaign at a time.

## Authentication and scope

Send an **admin-scoped API key** or a **claimed agent's API key** in the `X-API-Key` header. Unclaimed agents cannot use these endpoints. You can also manage registration in the [Inkbox Console](https://inkbox.ai/console/phone?tab=compliance).

These operations act on the **key's organization**, even when you use an agent-scoped key. Activation and resubmission affect all eligible local US phone numbers in that organization, including numbers belonging to other agents. Do not pass an organization ID or identity ID.

All paths below start with `https://inkbox.ai/api/v1/compliance`.

| Method | Path | Purpose | Success |
| - | - | - | - |
| `POST` | `/brand` | Register your organization | `201` |
| `GET` | `/brand` | Read your brand and verification status | `200` |
| `POST` | `/opt-in-evidence` | Upload an opt-in screenshot | `201` |
| `POST` | `/campaign` | Submit a messaging campaign | `201` |
| `GET` | `/campaign` | Read your campaign and review status | `200` |
| `POST` | `/campaign/resubmit` | Replace your campaign with a new submission | `201` |
| `GET` | `/campaign/active` | Read your campaign selection | `200` |
| `PATCH` | `/campaign/active` | Change your campaign selection | `202` |

Brand and campaign JSON request fields use **snake\_case**. Their response fields use **camelCase**, such as `brandId`, `identityStatus`, `campaignId`, and `campaignStatus`. Evidence and activation responses use the exact names shown below.

## Registration workflow

1. Register your brand and retain its `brandId`.
2. Check the brand until verification completes. Complete any requested verification in the Console.
3. Upload a screenshot if you need a public URL showing how recipients opt in.
4. Submit a campaign using the brand's UUID as `brand_id`.
5. Check `campaignStatus` and `failureReasons` while the campaign is reviewed.
6. Once approved, select your campaign with `PATCH /campaign/active`.
7. Wait for each phone number's `sms_status` to become `ready` before sending.

Registration is asynchronous: a `201` response confirms submission, not approval. See the [10DLC guide](/docs/capabilities/phone/10dlc.md) for registration requirements.

## Register a brand

```text theme={null}
POST /api/v1/compliance/brand
```

Send a JSON body with your business details. Unknown fields return `422`.

### Required fields

| Field | Type | Constraints |
| - | - | - |
| `entity_type` | string | `PRIVATE_PROFIT`, `PUBLIC_PROFIT`, `NON_PROFIT`, `GOVERNMENT`, or `SOLE_PROPRIETOR` |
| `display_name` | string | 1–100 characters |
| `email` | string | Business contact email; 1–100 characters |
| `vertical` | string | Industry value listed below |
| `country` | string | `US` or `CA` |
| `phone` | string | E.164 contact number; 1–20 characters |
| `website` | string | Public website URL; 1–100 characters |
| `street` | string | 1–100 characters |
| `city` | string | 1–100 characters |
| `state` | string | State or province code; 1–8 characters |
| `postal_code` | string | 1–10 characters |

Supported `vertical` values: `PROFESSIONAL`, `REAL_ESTATE`, `HEALTHCARE`, `HUMAN_RESOURCES`, `ENERGY`, `ENTERTAINMENT`, `RETAIL`, `TRANSPORTATION`, `AGRICULTURE`, `INSURANCE`, `POSTAL`, `EDUCATION`, `HOSPITALITY`, `FINANCIAL`, `POLITICAL`, `GAMBLING`, `LEGAL`, `CONSTRUCTION`, `NGO`, `MANUFACTURING`, `GOVERNMENT`, `TECHNOLOGY`, and `COMMUNICATION`.

### Conditional and optional fields

| Field | Type | When to send |
| - | - | - |
| `company_name` | string | Required except for sole proprietors; up to 100 characters |
| `ein` | string | Required except for sole proprietors; exactly nine digits, without a hyphen |
| `first_name`, `last_name` | string | Required for sole proprietors; up to 100 characters each |
| `mobile_phone` | string | Required for sole proprietors; E.164 number, up to 20 characters |
| `stock_symbol` | string | Required for public companies; up to 10 characters |
| `business_contact_email` | string | Required for public companies; up to 255 characters |
| `stock_exchange` | string | Optional exchange value listed below |

Supported `stock_exchange` values: `NYSE`, `NASDAQ`, `AMEX`, `AMX`, `ASX`, `B3`, `BME`, `BSE`, `FRA`, `ICEX`, `JPX`, `JSE`, `KRX`, `LON`, `NSE`, `OMX`, `SEHK`, `SSE`, `STO`, `SWX`, `SZSE`, `TSX`, `TWSE`, and `VSE`.

### Example

The values below are fictional. Replace them with your registered business details before submitting.

```bash theme={null}
curl -X POST "https://inkbox.ai/api/v1/compliance/brand" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "PRIVATE_PROFIT",
    "display_name": "Example Support",
    "company_name": "Example Company LLC",
    "ein": "123456789",
    "email": "support@example.com",
    "vertical": "TECHNOLOGY",
    "country": "US",
    "phone": "+15555550123",
    "website": "https://example.com",
    "street": "123 Example Street",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94105"
  }'
```

### Response (201)

Returns a brand object. Fields you will use in the registration workflow include:

| Field | Type | Meaning |
| - | - | - |
| `brandId` | string | Brand UUID; use this as the campaign's `brand_id` |
| `displayName` | string | Brand display name |
| `identityStatus` | string or null | Verification state; `VERIFIED` or `VETTED_VERIFIED` indicates a verified brand |
| `failureReasons` | string, array, or null | Review feedback; array entries can contain `fields` and `description` |

An existing brand returns `409` with `detail.error: "brand_already_exists"`.

## Check brand status

```bash theme={null}
curl "https://inkbox.ai/api/v1/compliance/brand" \
  -H "X-API-Key: YOUR_API_KEY"
```

Returns the same brand object with current verification information. No request body or brand ID is needed. If your organization has no brand, the endpoint returns `404` with `detail.error: "brand_not_found"`.

Sole-proprietor phone verification and other requested verification steps remain available through the Console or an admin-scoped integration. Those separate verification endpoints, along with brand/campaign update and delete endpoints, require an admin-scoped API key or the Console.

## Upload opt-in evidence

```text theme={null}
POST /api/v1/compliance/opt-in-evidence
```

Register a brand first. Upload one image as the `file` part of a `multipart/form-data` request. The image must be no larger than 5 MiB (5,242,880 bytes) or 16 million pixels.

```bash theme={null}
curl -X POST "https://inkbox.ai/api/v1/compliance/opt-in-evidence" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@opt-in-form.png"
```

Let your HTTP client set the multipart boundary. A successful response is `201`:

```json theme={null}
{
  "url": "https://inkbox.ai/api/v1/compliance/opt-in-evidence/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
```

The URL is publicly readable without an API key. Upload only an image you intend to share publicly. Include the returned URL in your campaign's `message_flow` to show the opt-in experience. There is no separate evidence field in the campaign request.

## Register a campaign

```text theme={null}
POST /api/v1/compliance/campaign
```

Send the following JSON fields. Unknown fields return `422`.

### Required fields

| Field | Type | Constraints |
| - | - | - |
| `brand_id` | string | UUID from your organization's `brandId`, not the short registry identifier |
| `usecase` | string | Use-case value listed below |
| `description` | string | 40–4,096 characters |
| `message_flow` | string | How recipients opt in, including evidence URLs; 40–2,048 characters |
| `help_message` | string | 20–320 characters |
| `sample1`, `sample2`, `sample3`, `sample4` | string | Four sample messages; 20–320 characters each |
| `optin_keywords`, `optout_keywords`, `help_keywords` | string | Comma-separated keywords; 1–320 characters each |
| `optin_message`, `optout_message` | string | Auto-reply text; 20–320 characters each |
| `privacy_policy_link`, `terms_and_conditions_link` | string | Public URLs; 1–512 characters each |

Supported `usecase` values: `ACCOUNT_NOTIFICATION`, `AGENTS_FRANCHISES`, `CARRIER_EXEMPT`, `CHARITY`, `CUSTOMER_CARE`, `DELIVERY_NOTIFICATION`, `EMERGENCY`, `FRAUD_ALERT`, `HIGHER_EDUCATION`, `K12_EDUCATION`, `LOW_VOLUME`, `MARKETING`, `MIXED`, `POLITICAL`, `POLLING_VOTING`, `PROXY`, `PUBLIC_SAFETY_RESTRICTED`, `PUBLIC_SERVICE_ANNOUNCEMENT`, `SECURITY_ALERT`, `SOCIAL`, `SWEEPSTAKE`, `2FA`, `UCAAS_LOW`, `M2M`, `SOLE_PROPRIETOR`, `TRIAL`, and `UCAAS_HIGH`. Registry eligibility still applies to each use case.

### Optional fields

| Field | Type | Default / constraints |
| - | - | - |
| `sample5` | string | Omitted; 20–320 characters when supplied |
| `embedded_link` | boolean | `false`; whether messages contain links |
| `embedded_link_sample` | string | Omitted; example link, up to 320 characters |
| `embedded_phone` | boolean | `false`; whether messages contain phone numbers |
| `age_gated` | boolean | `false` |
| `direct_lending` | boolean | `false` |
| `sub_usecases` | array of strings | Omitted; classifications for a mixed campaign |

### Example

Replace `brand_id` with the UUID returned when you registered your brand. Replace the example content and URLs with your actual messaging flow.

```bash theme={null}
curl -X POST "https://inkbox.ai/api/v1/compliance/campaign" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "brand_id": "123e4567-e89b-42d3-a456-426614174000",
    "usecase": "CUSTOMER_CARE",
    "description": "Customer support updates for users who request help from Example Company.",
    "message_flow": "Users check an optional SMS consent box at https://example.com/support before submitting a support request. The form explains message frequency and opt-out instructions.",
    "help_message": "Example Support: contact support@example.com for help. Reply STOP to opt out.",
    "sample1": "Example Support: we received your request and will follow up shortly. Reply STOP to opt out.",
    "sample2": "Example Support: your support appointment is confirmed for tomorrow. Reply STOP to opt out.",
    "sample3": "Example Support: your replacement is ready for collection. Reply STOP to opt out.",
    "sample4": "Example Support: your support request has been resolved. Reply STOP to opt out.",
    "optin_keywords": "START,YES",
    "optout_keywords": "STOP,END,QUIT",
    "help_keywords": "HELP,INFO",
    "optin_message": "Example Support: you are subscribed to support updates. Reply STOP to opt out.",
    "optout_message": "Example Support: you are unsubscribed and will receive no further messages.",
    "privacy_policy_link": "https://example.com/privacy",
    "terms_and_conditions_link": "https://example.com/terms"
  }'
```

### Response (201)

Returns a campaign object. Important fields include:

| Field | Type | Meaning |
| - | - | - |
| `campaignId` | string | Campaign UUID |
| `brandId` | string | Parent brand UUID |
| `usecase` | string | Registered use case |
| `campaignStatus` | string or null | Review/provisioning status listed below |
| `failureReasons` | string, array, or null | Review feedback; array entries can contain `fields` and `description` |

An existing campaign returns `409` with `detail.error: "campaign_already_exists"`. Use resubmission to replace an existing campaign.

## Check campaign status

```bash theme={null}
curl "https://inkbox.ai/api/v1/compliance/campaign" \
  -H "X-API-Key: YOUR_API_KEY"
```

Returns the campaign object with current review information. No request body or campaign ID is needed. If your organization has no campaign, the endpoint returns `404` with `detail.error: "campaign_not_found"`.

| `campaignStatus` | Meaning |
| - | - |
| `TCR_PENDING` | Registry review pending |
| `TCR_ACCEPTED` | Accepted by the registry; not yet fully provisioned |
| `TCR_FAILED` | Registry review failed; inspect `failureReasons` |
| `TCR_SUSPENDED`, `TCR_EXPIRED` | Registration suspended or expired |
| `CSP_ACCEPTED` | Accepted for carrier processing |
| `CSP_FAILED` | Processing failed; inspect `failureReasons` |
| `MNO_PENDING` | Carrier review pending |
| `MNO_ACCEPTED` | Accepted by carriers; provisioning is not yet complete |
| `MNO_REJECTED` | Rejected by carriers |
| `MNO_PROVISIONED` | Carrier provisioning complete; eligible for activation with a verified brand |
| `MNO_PROVISIONING_FAILED` | Carrier provisioning failed |
| `null` | A status has not yet been reported |

Approval, campaign selection, and number readiness are separate states. `campaignStatus` describes approval; `/campaign/active` describes your organization’s selection; each number's `sms_status` describes whether it is ready to send.

## Resubmit a campaign

```text theme={null}
POST /api/v1/compliance/campaign/resubmit
```

Send the **complete campaign registration body**, with the same fields as `POST /campaign`. This is not a partial update. For example, save your corrected JSON as `campaign.json`:

```bash theme={null}
curl -X POST "https://inkbox.ai/api/v1/compliance/campaign/resubmit" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @campaign.json
```

A successful response is `201` with a new campaign object and a **new `campaignId`**. Your local numbers return to Inkbox's default campaign while the replacement is reviewed. Check its status and select your campaign again after approval. If no campaign exists to replace, the endpoint returns `404`.

Do not blindly retry registration or resubmission after a network timeout. First retrieve the current campaign and check whether the submission succeeded. Resubmission creates a replacement, rather than updating the same campaign ID.

## Read campaign selection

```bash theme={null}
curl "https://inkbox.ai/api/v1/compliance/campaign/active" \
  -H "X-API-Key: YOUR_API_KEY"
```

Response (`200`):

```json theme={null}
{ "active": true }
```

`active: true` means your organization selected its own campaign. `false` means it selected Inkbox's default. This does not confirm that every number has finished switching. If no customer campaign exists, this endpoint returns `404`.

## Change campaign selection

```bash theme={null}
curl -X PATCH "https://inkbox.ai/api/v1/compliance/campaign/active" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": true}'
```

The required boolean `active` selects your campaign (`true`) or Inkbox's default (`false`). Activating requires a verified brand and a campaign with `MNO_PROVISIONED` status. If readiness has not yet been confirmed, the endpoint returns `409` with `detail.error: "customer_campaign_not_ready"`; check status again before retrying.

Response (`202`):

```json theme={null}
{
  "active": true,
  "local_numbers_scheduled_for_migration": 2
}
```

The count is the number of eligible local US numbers scheduled to switch. Switching runs asynchronously across the organization. It does not change non-US or toll-free numbers. Check [phone number status](/docs/api/phone/numbers.md) before sending; sends while a number is pending return `409 sender_sms_pending`.

## Errors

| HTTP status | Meaning |
| - | - |
| `400` | Invalid brand UUID in campaign registration or resubmission |
| `401` | Missing or invalid credentials |
| `403` | The API key is not permitted, including unclaimed-agent keys |
| `404` | No brand or campaign exists for a requested read, or no campaign exists to resubmit |
| `409` | Brand/campaign already exists, campaign brand does not belong to your organization, evidence upload has no registered brand, or activation is not ready |
| `413` | Evidence file, image dimensions, or upload request is too large |
| `415` | Evidence is not a decodable image |
| `422` | Invalid request fields or registration validation failure |
| `429` | Registration service is rate-limited; retry later |
| `502` | Registration or evidence storage could not be completed; inspect the returned error |

Registration conflict responses contain `detail.error` and `detail.message`. Upload errors may instead contain a string in `detail`. Validation errors can use an array of field errors. Inspect the response body rather than assuming one error shape for every endpoint.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.