> ## 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.

# Set up branded calling (RCD) end to end

> First-time branded-calling setup: confirm A-attestation, register the brand through the per-carrier wizard, configure the console assets, read the SIP headers on the wire, and verify the render on a live PSTN call.

# Set up branded calling (RCD) end to end

This is the first-time setup walkthrough for **Rich Call Data (RCD)** — the
verified brand (display name, logo, reason-for-call) that renders on the
recipient's incoming-call screen in place of the bare number. The concept and
the field reference live on
[Branded calling](/compliance/branded-calling); this page puts the pieces in
the order a tenant runs them: ownership → registration → configuration →
signal → render → measurement.

<Note>
  Branded calling is **tenant-owned**. You choose the brand assets, the
  numbers they ride on, the carriers you submit to, and any hold-out
  experiment. Orbit records and signals what you set; carrier-side review and
  activation stay with the carrier, signing stays with the carrier of record,
  and regulatory judgment about which calls may display a brand stays with
  you and your counsel. Nothing on this page is legal advice, and no step
  gates, blocks, or reroutes a call.
</Note>

## 1. What RCD is — and why it rides on STIR/SHAKEN

RCD is the ATIS-1000095 extension that travels **inside the signed STIR/SHAKEN
PASSporT itself**, not in a phone-number database side-lookup. That is why RCD
only exists where full attestation exists: the brand is carried
cryptographically by the signing carrier, bound to the signed call.

Two practical consequences:

* The from-number must sign at **A — full attestation**. On B (partial) or C
  (gateway) calls the RCD payload is suppressed every time; the recipient
  sees the attestation label and any CNAM name on file instead. A is reached
  only by an active number your organization owns through Orbit — see
  [Assemble your STIR/SHAKEN attestation posture](/guides/stir-shaken-attestation-guide)
  for the ownership ladder.
* RCD is a **signalling surface, not a signing one**. Orbit resolves your
  brand configuration and signals it on the outbound leg; the Devotel
  wholesale softswitch — the carrier of record — signs the PASSporT and
  attaches the registered payload. Where the downstream carrier does not
  know your brand, the headers are dropped silently and the call proceeds
  with plain attestation.

For the attestation model RCD rides on, see
[STIR/SHAKEN attestation](/channels/voice/stir-shaken).

## 2. Prerequisites — numbers and attestation first

Get these four out of the way before touching a brand asset:

1. **An owned number attesting at A.** Buy or port the outbound caller ID
   into Orbit, then confirm the level on the posture snapshot:

   ```bash theme={null}
   curl "https://api.orbit.devotel.io/api/v1/compliance/attestation/posture" \
     -H "Authorization: Bearer $ORBIT_API_KEY"
   ```

   The number must appear under `originating.numbers[]` with
   `"attestation": "A"`. If it reads `"leased"` or attests at B/C, fix
   ownership first — the
   [attestation posture guide](/guides/stir-shaken-attestation-guide) is the
   walkthrough for that climb. No branded-calling step leapfrogs a non-owned
   caller ID to A.
2. **Owner or admin access.** Every branded-calling write is org-scoped and
   owner/admin gated; reads are not.
3. **A public logo URL over HTTPS.** PNG/JPEG/WebP, square aspect,
   recommended 200×200, publicly reachable without auth headers, served over
   TLS, on a stable URL — the handset fetches it per call, so no expiring
   signed links.
4. **A CNAM fallback name on the DID.** Where RCD cannot render (a carrier
   that doesn't support it, an unregistered carrier, a non-A call) the
   recipient falls back to plain attestation plus the CNAM string on file.
   Register the same display name with CNAM so the fallback is never the
   bare number — see [CNAM & caller ID](/numbers/cnam).

## 3. Register the brand with the carriers

RCD renders only on carriers where your brand registration completed — each
US carrier runs its own Rich Call Data program, and a brand must be
submitted to each independently. Orbit exposes that as a per-carrier
provisioning state machine:

| Provisioning status | Meaning |
| - | - |
| `not_submitted` | Initial. The brand has not been submitted to this carrier. |
| `submitted` | You submitted the brand to this carrier (a wizard action). |
| `pending_carrier` | The carrier received it and is reviewing. |
| `active` | The carrier activated it — the brand renders on that network's handsets. |
| `rejected` | The carrier declined; re-submittable |

Read all three states (AT\&T, Verizon, T-Mobile) at once:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/settings/branded-calling/rcd-provisioning" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

The response carries the org-wide brand readiness (`brand_ready` /
`verification_status`) plus one provisioning record per carrier. **Submit to
each carrier explicitly; activation reconciles automatically** — when the
operator-managed registration lands, the carrier's record flips to `active`
without another write from you. A carrier the carrier itself has not
activated never reports `active`, no matter what the wizard says — the
reconcile reads the operator's authoritative registration list
(`registered_with_carriers` on the branded-calling settings object), so a
tenant cannot self-attest a carrier to active.

What the carrier-side review decides is out of tenant reach: a rejected
carrier row carries a machine-readable `reason`, and re-submission starts
the same endpoint. Until at least one carrier flips to `active`, headers
still go out — downstream carriers silently drop the payload, and the
handset renders plain attestation plus your CNAM fallback.

## 4. Configure the brand in Numbers → Voice

With the carrier registration in flight, set the brand itself — **Settings →
Voice → Branded calling** in the console, or the API:

```bash theme={null}
curl -X PUT "https://api.orbit.devotel.io/api/v1/settings/branded-calling" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "display_name": "Acme Support",
    "logo_url": "https://assets.example.com/logo.png",
    "reason_for_call": "Appointment reminder",
    "holdout_pct": 10
  }'
```

The write-time validators reject out-of-spec values with `422` before
anything stores (the failure modes table below enumerates them). Two
patch-style behaviours matter on the save:

* **Changing any rendered asset resets verification.** A new display name,
  logo, or reason-for-call flips the brand back to unverified — it re-enters
  review before the handset renders it again. Toggling `enabled` or
  adjusting `holdout_pct` alone does not.
* **Omitted `holdout_pct` preserves a running experiment.** A save that
  edits only brand assets must not silently collapse a branded-vs-unbranded
  measurement mid-cohort. Send an explicit `0` to end the experiment; omit
  the field to leave it untouched.

Run several brands over one org (an agency dialing per client, a multi-brand
programme)? Leave the org default as the catch-all and set a **per-number
profile** on the DIDs that should wear a different brand — the profile
overrides the org default for that one number when it is enabled and fully
specified (name + logo), and falls back to the org default unchanged when it
is incomplete. The per-carrier registration list always stays
organization-level; a per-number profile can never self-attest a carrier.

## 5. What goes on the wire — the SIP/SDP signal

RCD signalling is internal to the platform's outbound path — headers move on
the existing Devotel outbound leg; nothing here changes origination or
routing, and a signalling failure never blocks the call. At dial time the
platform resolves the branded-calling configuration (the per-number profile
if one is set, else the org default), merges the attestation level, and
signals the softswitch on the INVITE:

```
INVITE sip:+14155581998@… SIP/2.0
From: "Acme Support" <sip:+14155580100@…>;tag=…
To: <sip:+14155581998@…>
X-Devotel-Attest: A
X-Devotel-RCD-DisplayName: Acme Support
X-Devotel-RCD-Logo: https://assets.example.com/logo.png
X-Devotel-RCD-Reason: Appointment reminder
X-Devotel-RCD-Carriers: att,verizon
```

* **`X-Devotel-Attest`** — the resolved STIR/SHAKEN level (A/B/C). RCD
  headers attach only on A; on B or C calls only this attestation header
  goes out.
* **`X-Devotel-RCD-DisplayName` / `X-Devotel-RCD-Logo` / `X-Devotel-RCD-Reason`**
  — the brand assets. `display_name` and `logo_url` are always present when
  the resolver attaches RCD; `reason_for_call` only when non-blank.
* **`X-Devotel-RCD-Carriers`** — the operator-managed registration list,
  echoed for the softswitch's delivery decision.

On the receive side, read what the terminating carrier actually did with the
signalling by inspecting the inbound INVITE you accept from an upstream
carrier — a branded inbound call that survived the chain carries the
passport token in SIP:

```
INVITE sip:+14155580100@… SIP/2.0
Identity: eyJhbGciOiJFUzI1NiIs…<base64url PASSporT token>…;info=<https://…/stir-shaken-cert.pem>;alg=ES256;ppt="shaken"
From: <sip:+14155582001@…>;tag=…
To: <sip:+14155580100@…>
X-Verstat: TN-Validation-Passed
```

The `Identity` header is the signed PASSporT; the `rcd` claim set inside it
is where the brand assets travel end-to-end once a carrier stamps them.
Your own telemetry never reads the token — Orbit parses `Verstat` (or the
raw `Identity`) at the inbound edge and classifies the level for the
posture snapshot; the headers above are the ones your logs and packet
captures can match against.

## 6. What the handset actually shows

When a downstream carrier accepts the RCD payload, the subscriber's
incoming-call screen renders three fields above the plain number:

| Slot | Source field | Hard constraint |
| - | - | - |
| **Brand name** (up to 40 chars, typically on one or two lines) | `display_name` | Carrier-side truncate at the wire cap — write 40 chars or fewer |
| **Logo** (square tile beside or above the name) | `logo_url` | Handset fetches the URL per call — must stay public, TLS, stable; PNG/JPEG/WebP, recommended 200×200 |
| **Reason-for-call sub-line** ("Appointment reminder") | `reason_for_call` | Up to 60 chars render on every carrier that supports the slot — blank shows brand alone |

Everything else on the screen — the phone number, the attestation badge the
carrier draws, the spam-likely label where it applies — falls out of the
attestation result, not RCD. When RCD drops for any reason (unregistered
carrier, non-A call, blank profile), the subscriber sees exactly the
attestation-plus-CNAM treatment they would have seen without branded
calling. RCD is an addition, never a replacement for the caller-ID floor.

Verizon's spec allows an 80-character reason; AT\&T truncates at 60, and the
platform caps at the strictest — 60 characters render everywhere, 80 render
only where the carrier honors the longer field.

## 7. Test the render on a live PSTN call

The settle check is a real call on a registered carrier, not a simulator —
the per-carrier `active` flag is the only signal that the carrier itself
stamped your brand on the call.

1. **Pick one registered carrier.** Read `…/rcd-provisioning` and confirm
   at least one carrier row reads `active` — the brand only renders on
   networks where your registration completed.
2. **Place an outbound call from the A-attested number you wired in Step 2
   to a handset on that carrier's network** with RCD enabled and a complete
   brand set. Cell coverage, a VoLTE/5G connection, and an unmodified
   incoming-call screen matter — carriers draw the branded card only on
   their own branded-calling-enabled devices.
3. **Inspect the answering handset.** The verified brand should render —
   display name, logo, reason line above the number. If it does not, walk
   the fallback ladder: the attestation badge plus the CNAM string means
   RCD dropped (carrier unregistered, non-A call, incomplete config); the
   bare number means both RCD and CNAM are absent.
4. **Confirm the hold-out if one is running.** With `holdout_pct` set,
   roughly that percentage of calls must land **without** the brand — that
   is the control half of the experiment, not a defect.
5. \*\*Read the cohort split after both cohorts fill.
   `GET /api/v1/numbers/:id/branded-calling-cohort` splits branded-vs-holdout
   answer rate out of actual call logs once the two cohorts reach size, so
   the lift is a real randomized read rather than a before/after time-series
   confounded by campaign mix.

## 8. Failure modes — 422s and render gaps

Write-time validation on `PUT /settings/branded-calling` is the tight spot —
an out-of-spec value is a `422` at save, never a stored-and-later-failing
config:

| Status | Code | Field | Fix |
| - | - | - | - |
| 422 | `VALIDATION_ERROR` | `display_name` | Empty string, or over 40 characters — set 1–40 chars (carriers truncate longer at the wire cap anyway). |
| 422 | `VALIDATION_ERROR` | `logo_url` | `http://` instead of `https://`, an internal/private host (loopback or RFC-1918 address), or a URL over 2000 chars — point the field at a public, TLS-served, stable URL. |
| 422 | `VALIDATION_ERROR` | `reason_for_call` | Over 60 characters — shorten it; AT\&T truncates at 60 and the platform caps at the strictest. |
| 422 | `VALIDATION_ERROR` | `holdout_pct` | Below 0 or above 50 — send 0–50; `0` ends the experiment, omit the field to keep the stored value. |
| 409 | (409 Conflict) | carrier submit | Brand not yet approved when you submit a carrier — the org-wide brand verification must be approved first; submit again after review. |

Render-side gaps (the call proceeds with plain attestation instead of an
error) and their fixes:

* **Recipient sees no brand on one carrier only.** That carrier is not in
  `active` — the operator-managed registration list drives the render, and
  until the carrier itself activates the brand the headers drop silently.
  Check `…/rcd-provisioning` for the `active` flag per network.
* **Recipient sees no brand anywhere, every call.** The from-number attests
  at B or C — RCD is suppressed on non-A calls. Walk the
  [attestation posture guide](/guides/stir-shaken-attestation-guide) to A on
  the owned number.
* **The brand rendered last week, stopped after an edit.** Saving a changed
  name, logo, or reason resets verification; the revised brand re-enters
  review before it renders again. Check the org-wide `verification_status`
  on the branded-calling surface.
* **The logo URL saved fine but the handset shows no logo.** The URL was
  reachable at save time and has since gone stale — an expiring signed link
  or an auth-gated host. The write-time check guards scheme and internal
  hosts, not future expiry; re-point `logo_url` at a stable public URL.
* **The per-number profile didn't take over.** An incomplete profile
  (missing name or logo) falls back to the org default quietly. Set both
  fields on the DID, or clear the profile to return to the org default
  deliberately.

For the reference walkthrough of each one — including the RCD-vs-CNAM
fallback ladder and the carrier-cap matrix — see
[Branded calling](/compliance/branded-calling) and
[Troubleshooting: STIR/SHAKEN attestation downgrade](/troubleshooting/stir-shaken-attestation-downgrade).

## See also

* [Branded calling](/compliance/branded-calling) — the concept and field
  reference this walkthrough runs.
* [Assemble your STIR/SHAKEN attestation posture](/guides/stir-shaken-attestation-guide) —
  the ownership climb RCD's A-attestation prerequisite needs.
* [STIR/SHAKEN attestation](/channels/voice/stir-shaken) — where Orbit
  signals attestation and where the Devotel softswitch signs it.
* [CNAM & caller ID](/numbers/cnam) — the LIDB fallback label the render
  drops to when RCD cannot ride.
* [Brand identity & trust score](/concepts/brand-identity-trust-score) —
  how branded calling rolls into the trust surfaces the score aggregates.


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