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

# Assemble a Shared Compliance Profile Across Gated Surfaces

> One approved compliance profile backs every gated surface — numbers, Sender IDs, brands, campaigns, RCS, WABA. This walkthrough covers reuse, attach-by-id, rotation without downtime, expiry alerts, and the first-launch checklist.

# Assemble a Shared Compliance Profile Across Gated Surfaces

A **compliance profile** (`cprof_…`) is a reusable KYC packet: the
identity and business data carriers and regulators review, approved
once, then referenced by every surface that asks for it. The packet
itself is documented field-by-field on
[KYC Documents & the Compliance-Profile Lifecycle](/compliance/documents-kyc),
and the four attach/mutate/delete error codes have their own runbook on
[Compliance profile lifecycle errors](/compliance/compliance-profile-lifecycle-errors).
This guide is the end-to-end pattern: why one profile is shared, how it
backs several surfaces at once, how to rotate it without downtime, and
how to see expiry coming before it gates a launch.

<Note>
  Compliance posture is tenant-owned. You choose which profiles back
  which surfaces; Orbit enforces the lifecycle gates, and the approval
  itself always comes from the carrier or regulator reviewing your
  packet. Orbit never mandates that a given surface use a given
  profile.
</Note>

***

## One shared packet, not five per-surface packets

The alternative to a shared profile is the per-surface packet: one KYC
bundle for your German numbers, another for your alphanumeric Sender
ID, another for your US 10DLC brand. That buys you five review queues,
five renewal calendars, and five chances for a document to lapse on
one surface while the other four still work.

A shared profile inverts that: the carrier reviews the packet once,
and every surface referencing its id inherits the approval. A profile
that matches both a use case and a country is fair to reuse — your
`phone_number_purchase` DE profile can back every German number you
buy, and one document library backs profiles for every surface. Reuse
is a choice you make at attach time; nothing attaches a profile
without your id.

The trade to weigh: a shared profile funnels its surfaces through one
review and one expiry clock, so keep profile scope tight enough that a
rejection doesn't idle assets you could have split. Most tenants run a
handful of deliberately-scoped shared profiles rather than one global
one.

***

## Lifecycle states and the two guards

A profile moves through one fixed review lifecycle, with three
side-exits:

```
draft → pending_review → approved → expired
           │                 │
           ├─ rejected       └─ rejected
           └─ partially_rejected
```

Only `draft`, `rejected`, and `partially_rejected` are editable;
everything else is frozen by design. Two guards protect the frozen
states: **LOCKED** (`409 COMPLIANCE_PROFILE_LOCKED`) on any mutation
past `draft`, and **IN\_USE** (`409 COMPLIANCE_PROFILE_IN_USE`) on delete
or document-detach while a downstream asset still references the
profile. The state-by-state map and the unblocked path for each code
are on
[Compliance profile lifecycle errors](/compliance/compliance-profile-lifecycle-errors) —
read it before you treat a 409 as a bug.

***

## One approved profile, many surfaces

Each gated surface reads the same packet. Map the use case to the
surface it opens:

| Surface                                 | Use case                                       | Console                                |
| --------------------------------------- | ---------------------------------------------- | -------------------------------------- |
| Number purchases into regulated markets | `phone_number_purchase`                        | Numbers                                |
| Alphanumeric Sender IDs                 | `sms_sender_id_alphanumeric`                   | Settings → Compliance → Sender IDs     |
| US 10DLC brand / campaign               | `sms_10dlc_brand_us` / `sms_10dlc_campaign_us` | Settings → Compliance → Brand identity |
| US toll-free verification               | `sms_tfv_us`                                   | Numbers                                |
| WhatsApp Business                       | `whatsapp_business_verification`               | Channels → WhatsApp                    |
| RCS brand                               | `rcs_brand_verification`                       | Channels → RCS                         |
| Voice carrier KYC                       | `voice_carrier_kyc`                            | Numbers / SIP trunks                   |

Pass the profile id explicitly at attach time — an explicit
`compliance_profile_id` is always safer than relying on a surface's
auto-pick. For a number purchase:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/purchase \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "country_code": "DE",
    "type": "local",
    "compliance_profile_id": "cprof_abc123"
  }'
```

The surface resolves the id, reads the profile's status, and opens
only on `approved`; any other status answers the same 422 as a missing
profile. Sender IDs, brands, and campaigns attach from their console
surfaces or their own attach endpoints; numbers also accept
`POST /api/v1/numbers/:id/attach-compliance-profile` after the fact.

***

## Upload documents once, reference by `doc_…`

Documents live in a tenant-wide library, not inside any profile.
Upload the file once, then attach its `doc_…` id to as many profiles
as accept it — with a role (`id_proof`, `address_proof`,
`business_doc`, `authorization`, `other`) that may differ per profile.
The same business registration can back a German phone-number profile
today and a Sender-ID filing tomorrow. The upload call, accepted
types, and the renewal-by-re-upload pattern are on
[KYC Documents & the Compliance-Profile Lifecycle](/compliance/documents-kyc).

***

## Rotate without downtime

The old profile keeps serving traffic while its replacement clears
review — a swap is never a gap. Work this order:

1. **Clone the frozen profile** — clone copies fields, metadata, and
   document attachments into a new editable `draft`:

   ```bash theme={null}
   curl -X POST https://api.orbit.devotel.io/api/v1/compliance/compliance-profiles/cprof_abc123/clone \
     -H "Authorization: Bearer $ORBIT_API_KEY"
   ```

2. **Fix and submit the clone** — edit the still-editable draft against
   the reviewer's notes (or the country's checklist), submit, and wait
   for `approved`. Use the same sequence for an `expired` profile you
   need to re-verify.

3. **Re-attach downstream** — point each number, sender, brand, and
   campaign at the new id (`POST /api/v1/numbers/:id/attach-compliance-profile`
   for numbers; the console attach surface for everything else).

4. **Detach and delete the old profile** — only once nothing
   references it. A delete attempt while an asset still points at it
   answers `409 COMPLIANCE_PROFILE_IN_USE`; the detach walk clears it.

***

## See expiry before it gates a launch

Orbit derives per-number expiry alerts from the timestamps it already
stores — each attached document's `expires_at`, and the carrier
verify-by deadline on numbers waiting at `pending_compliance`:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/numbers/document-expiry-alerts" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

Each alert names the number, the soonest binding expiry, the source
(document or carrier deadline), the days until it lapses, and a
`suggested_action`:

* `renew` — still valid but inside your alert window; upload the
  replacement document now.
* `renew_or_release` — already lapsed; renew immediately, or decide to
  let the number go.

The look-ahead window defaults to 30 days; tune it with the
`numbers.document_expiry_alert_days` setting or the `?days=` query
parameter. Field details are on
[KYC Documents & the Compliance-Profile Lifecycle](/compliance/documents-kyc).

***

## First-launch checklist

Work this order the first time you wire a gated surface:

1. **Read the destination's country rules** — the per-market packet a
   profile is graded against, on
   [Country Compliance Requirements](/compliance/country-requirements).
2. **Preview the requirements** — run
   [regulatory preview](/numbers/regulatory-preview) so the exact fields
   and documents are settled before you create the profile.
3. **Upload documents** to the library; get back `doc_…` ids.
4. **Create the profile** (`POST /api/v1/compliance/compliance-profiles`
   or Settings → Compliance → Profiles), attach documents by id with
   their roles, and fill the structured fields.
5. **Submit and wait for `approved`.**
6. **Attach by id** at the surface — explicit `compliance_profile_id`
   on the purchase or registration.
7. **Set the expiry window** — `numbers.document_expiry_alert_days`,
   sized to your renewal lead time.
8. **Know the rotation path** — clone, fix, re-attach, detach-old —
   before a reviewer note or an expiry alert forces it.

***

## Related references

* [KYC Documents & the Compliance-Profile Lifecycle](/compliance/documents-kyc) —
  the document library, roles, and renewal this guide reuses.
* [Compliance profile lifecycle errors](/compliance/compliance-profile-lifecycle-errors) —
  REQUIRED / NOT\_APPROVED / LOCKED / IN\_USE, with the unblocked path
  for each.
* [Assemble your tenant's compliance posture](/guides/compliance-profiles-assemble) —
  where profiles compose with country rules and vertical packs.
* [Your Tenant Compliance Posture: The Toggle Map](/compliance/posture-overview) —
  the tenant-owned control model the posture note above plugs into.
* [Country Compliance Requirements](/compliance/country-requirements) —
  the per-market checklist step 1 grades against.
* [API Reference → Compliance](/api-reference/endpoints/compliance) —
  the endpoint surface, including clone.
