Skip to main content

10DLC brand & campaign compliance profiles

For US A2P SMS over 10-digit long codes, registration is a pair of compliance profiles, not one: a brand profile (who the sender is) and a campaign profile (what the traffic is for). In Orbit both profiles live on the shared compliance-profile model — they follow the same lifecycle, reuse the same document library, and answer the same status endpoints as every other packet. This page is the 10DLC-specific walkthrough of that model; the concept anchor is 10DLC: the US A2P sender-registration model.
Compliance posture is tenant-owned. You choose which 10DLC profiles back which US traffic; Orbit enforces the lifecycle gates and the send-time check, and the approval itself always comes from The Campaign Registry and the carriers. Orbit never mandates that a US long-codes lane use a particular brand or campaign.

1. sms_10dlc_brand_us vs sms_10dlc_campaign_us — what each profile controls

The two use cases register separately on GET /api/v1/compliance/compliance-profiles/use-cases and carry different packets:
  • sms_10dlc_brand_us — the legal entity behind the traffic. The structured fields are the TCR brand schema: entity_type (PRIVATE_PROFIT / PUBLIC_PROFIT / NON_PROFIT / GOVERNMENT / SOLE_PROPRIETOR), display and legal company names, the EIN (required for every entity type except sole proprietor from the profile side), industry vertical, website, address, and contact details. No file documents are required for an organization brand — TCR grades the entity fields. The vetting outcome on the brand bounds the throughput your campaigns can request (T-Mobile brand daily cap, AT&T per-campaign MPM class).
  • sms_10dlc_campaign_us — the declared use case a brand sends under. The packet references an already-approved brand id and carries the use case (MIXED, MARKETING, ACCOUNT_NOTIFICATION, CUSTOMER_CARE, DELIVERY_NOTIFICATION, FRAUD_ALERT, PUBLIC_SERVICE_ANNOUNCEMENT), a 40–4096-character description, the consent/message flow text, sample messages, the HELP and STOP keyword responses, and embedded-link / embedded-phone / age-gated flags.
At send time the distinction matters: the campaign is the gate, the brand alone does not open US A2P traffic. An unregistered or non-approved campaign holding a from on a US long code is held before dispatch; the brand profile on its own never satisfies it. Conversely, a campaign can never reference a brand whose profile is not approved — resolver refuses the attach.

2. Lifecycle walkthrough — submit brand, then campaign, then poll status

Work this order for a scripted filing (the 10DLC registration guide and the profile wizard walkthrough cover the console flow; the 10DLC wizard handles sole-prop OTP):
  1. Create the brand profile as a draft with use_case = "sms_10dlc_brand_us" and brand fields in structured data:
  2. Validate before submit — every use case exposes its requirement set on GET /api/v1/compliance/compliance-profiles/requirements?use_case=sms_10dlc_brand_us; miss a required field and submit answers 422 with a missing list without ever reaching TCR.
  3. Submit the brand profile (POST /api/v1/compliance/compliance-profiles/:id/submit); it enters pending_review and fans out to the TCR adapter.
  4. Wait for the brand to reach approved. TCR brand vetting is typically 24–48h.
  5. Create the campaign profile with use_case = "sms_10dlc_campaign_us"; its brand_id field carries the approved TCR brand id the first profile returned, plus the use case, description, message flow, sample messages, and HELP/STOP responses. Submit it the same way.
  6. Poll status on GET /api/v1/compliance/compliance-profiles — filter by use_case and read the status of both profiles:
    TCR does not send webhooks for review transitions; Orbit reconciles non-webhook providers every three hours, so a status read returns the last reconciled value. The manual POST /api/v1/compliance/compliance-profiles/:id/resync forces a re-read if you do not want to wait for the next pass.
Both profiles follow the lifecycle map on Assemble a Shared Compliance Profile Across Gated Surfaces — the same draft → pending_review → approved guard states, the same LOCKED/IN_USE error codes on mutations past draft.

3. Rejection handling — common TCR codes and where they surface

A rejected brand or campaign profile moves to rejected / partially_rejected (both stay editable) and carries the registry’s reason. The most common TCR rejection families:
  • DUPLICATE-BRAND — a brand with the same entity/EIN is already on the registry; returned with resubmit_allowed: false, so the fix is a fresh filing, not an edit.
  • BRAND-DCA-FAIL — the direct-carrier audit on the brand failed; also burns the brand id (resubmit_allowed: false).
  • Use-case mismatch — the samples describe one kind of traffic while the declared use case says another (MARKETING samples under a CUSTOMER_CARE filing is the canonical form).
  • Consent/copy lint — missing HELP/STOP responses, a message_flow that does not describe the opt-in, or an over-short description.
For a raw code string, decode it with POST /api/v1/compliance/10dlc/decode-rejection — it returns the fixed fix card the wizard banner uses, including whether the id is burned. The full registry-vs-carrier split lives on 10DLC rejections and re-vet, and Troubleshooting: 10DLC campaign rejected is the operator runbook. The wizard pre-lints against the known rejection patterns before you spend a submission.

4. Doc requirements — which doc_… ids carry over

Documents live in the tenant-wide library independently of any profile (KYC Documents). The two 10DLC use cases are unusually document-light:
  • sms_10dlc_brand_us requires no file documents for an organization brand — TCR grades the entity fields themselves. A sole- proprietor brand verifies by phone OTP (walked on the wizard guide), not an uploaded document.
  • sms_10dlc_campaign_us accepts one optional other role — opt-in proof: a screenshot or copy of the consent flow a recipient sees before the brand messages them. Attach it with the same doc_… id + role attach any other profile uses.
Because the library is tenant-wide, the same doc_… id can back both this campaign profile and any other packet that takes the other role — and the business registration you uploaded for a phone_number_purchase or sms_sender_id_alphanumeric profile stays available if a reviewer asks for more than TCR’s minimum.

5. US-market interplay — 10DLC next to toll-free verification

The US accepts exactly two sender identities for A2P SMS, and they pick on different packets:
  • Long code (10-digit) → the 10DLC pair — sms_10dlc_brand_us + sms_10dlc_campaign_us, registered through TCR.
  • Toll-free → sms_tfv_us, Toll-Free Verification filed per DID on the dedicated TFV surface (Settings → Compliance → Toll-Free Verification), not through the shared-profile submit flow — the /use-cases catalogue carries an available_via deep-link for it.
The sender-identity classifier resolves each from to its class (long_code vs toll_free come from different branches) and only then picks the regime — a US long code cannot pass through TFV, and a toll-free number is never graded by TCR. The decision tree lives on Sender-identity classification model; the Regime registry row for US ten_dlc vs toll_free reads back on Country Compliance Requirements.

6. Where the gate fires at send time — and why registration is tenant-owned

The 10DLC check runs before dispatch, like every gate on Send Gates: when a from resolves to a US long code, Orbit verifies the brand-and-campaign pair’s terminal status and holds traffic the registry has not approved. The campaign profile — not the brand, not the packet metadata — is what the gate answers against. Registration is tenant-owned for the same reason every other packet is: Orbit files what you submit and enforces the lifecycle transitions, but it never auto-registers a US lane on your behalf and never decides that a given sender must use a given brand. The carrier and registry approvals are also theirs, not Orbit’s — the throughput calculator page makes the same point about TCR’s trust score.