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

# Troubleshooting: STIR/SHAKEN attestation stuck at C — spam-likely treatment, delegate certificates, and RCD

> Diagnose outbound calls arriving labeled spam-likely with collapsing answer rates: the C-attestation ceiling on non-owned numbers, expired or rejected delegate certificates, per-number branded-calling overrides, and RCD carrier coverage — and the fix run for each.

# Troubleshooting: STIR/SHAKEN attestation downgrade

An attestation downgrade is the quiet answer-rate failure: the call connects,
the routing is clean, and no error is returned anywhere — but the call leaves
signed at **C (gateway)** instead of A or B, and downstream carriers label it
"Spam Likely." Answer rates collapse and the [error-code catalog](/reference/error-codes)
has nothing to point at, because a downgrade is not a refusal: there is no
structured STIR/SHAKEN error code — the call still dispatches, signed at a
weaker level. This page works the five root causes that produce a downgrade
and the fix run for each. For what the attestation levels mean and where
signing happens, see [STIR/SHAKEN attestation](/channels/voice/stir-shaken);
this page starts where that one ends — at a call that attests lower than you
expected.

<Note>
  A downgrade never blocks a call. Attestation resolution fails open to C
  rather than failing the dial, and the policy flags below — target level,
  downgrade handling, certificate status — are reporting surfaces: they flag
  the gap, they do not gate traffic. Do not look for the failure in a blocked-
  call log.
</Note>

## Symptoms

* **Outbound calls arrive labeled "Spam Likely"** on downstream carriers even
  though your dialing practices, traffic volume, and caller IDs haven't
  changed.
* **Answer-rate drop** on outbound campaigns with no other change in caller ID,
  list quality, or time of day — the signature of a level that silently fell
  from A or B to C.
* **Calls sign at C on numbers you used to reach A or B on** — visible per
  call on the call record (the parsed attestation result is stored on every
  call), or across the org in the
  [Attestation posture](/compliance/attestation) page.
* **Rich Call Data (branded calling) no longer renders** on handsets that
  showed it before — often the same downgrade from the recipient's side: RCD
  only attaches to A-attested calls.

## Reads: measuring the downgrade before fixing it

Two reads tell you the downgrade is real and which numbers it covers. A
posture snapshot is fine-grained enough to name the specific number — don't
work the fix runs below blind.

### Originating: which numbers attest below your target

`GET /compliance/attestation/posture` lists every originating number with the
level calls from it actually attest at, and compares that against your
`target_attestation`:

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

```json theme={null}
{
  "data": {
    "policy": {
      "target_attestation": "A",
      "downgrade_handling": "alert",
      "inbound_min_verification": "B",
      "flag_unverified_inbound": true,
      "updated_at": "2026-08-20T14:03:11.000Z"
    },
    "originating": {
      "summary": {
        "total": 12,
        "meeting_target": 9,
        "below_target": 3,
        "by_level": { "A": 9, "B": 3, "C": 0 }
      },
      "numbers": [
        { "phone_number": "+14155550100", "source": "owned", "attestation": "A", "meets_target": true },
        { "phone_number": "+14155550777", "source": "leased", "attestation": "B", "meets_target": false }
      ],
      "numbers_truncated": false
    },
    "inbound": {
      "window_days": 30,
      "window_start": "2026-07-27T10:15:00.000Z",
      "window_end": "2026-08-26T10:15:00.000Z",
      "summary": {
        "total": 1840,
        "verified": 1712,
        "unverified": 96,
        "spoof_risk": 32,
        "verified_rate": 0.9304
      }
    }
  }
}
```

How to read it for a downgrade:

* **`originating.numbers[]`** — each originating number with the level it
  attests at (`owned` → A, `leased` → B) and `meets_target` against your
  declared target. A row with `meets_target: false` is a downgrade on that
  number; `originating.summary.below_target` counts them. `numbers_truncated`
  tells you when the 500-number list cap truncated the evidence.
* **`policy.target_attestation`** — what you intend. If it's `A` and your
  traffic runs on leased pool numbers (B) or BYON numbers (C), the below-target
  count lights up — that is the surface flagging the gap, not a blocked call.

### Inbound: verification summary against your floor

The same snapshot carries the inbound half: `inbound.summary` classifies the
last 30 days of calls arriving on your DIDs against your
`inbound_min_verification` floor — verified share, spoof-risk count, and the
`verified_rate` (or `null`, not zero, when the window had no inbound traffic).
A drop in `verified_rate` or a spike in `spoof_risk` is the inbound-side
signal worth reading before tuning the floor.

## Root causes and fixes

Work the causes in order — the top two produce the spam-likely symptom on
your own traffic; the bottom two produce it on the recipient's handset even
when the attestation level itself is fine.

### Root cause 1: calling from a number you don't own in Orbit (the C ceiling)

The dial-time attestation resolver mints A/B/C purely from ownership: a
number your org owns through Orbit attests **A**, an active pool lease
attests **B**, and anything else — a verified external caller ID, a hosted
number, a BYON number you legitimately control but haven't ported — attests
**C**. That C is the "dreaded C ceiling": no configuration raises a non-owned
number to A. A is ownership-only, per
[STIR/SHAKEN attestation](/channels/voice/stir-shaken) — and a delegate
certificate (root cause 2) caps at B by design, because a self-registered
certificate the platform doesn't cryptographically chain-validate must never
be able to spoof full attestation.

**Diagnostic.** Pull the posture snapshot. The column that matters is
`source`: a number showing `attestation: "C"` (or "B" against an "A" target)
on a source the org doesn't hold as owned is living under the ceiling.

**Fix run.**

1. **Own the number.** Purchase or port the numbers into Orbit
   ([Number porting](/numbers/porting)) so your org owns them outright. This
   is the only path from C to A. A delegate certificate covers the gap while
   the port completes (root cause 2), but the floor to A is ownership.
2. **Move your outbound traffic onto the owned DIDs.** Until ownership lands,
   expect and accept a B target for BYON/leased traffic — or cover BYON
   numbers with a delegate certificate (root cause 2).
3. **Re-read the posture snapshot.** The moved numbers now resolve to A, and
   `meeting_target` closes the `below_target` gap.

Do not `PUT /attestation/policy` a higher target and expect calls to sign
higher: the softswitch signs exactly the level Orbit attests and no more — a
worse-compared-to-target posture is a reporting flag, not a gate.

### Root cause 2: delegate certificate expired, pending, or rejected

A delegate certificate lifts the specific numbers it covers from C to B —
the answer for BYON / verified external numbers you hold certificates for.
The failure modes are the certificate's own lifecycle, and they are all
silent: coverage falls to C the moment the certificate stops being
**active**, with no call blocked and no error.

| Symptom in the registry             | Effective status          | Result                                                                         |
| ----------------------------------- | ------------------------- | ------------------------------------------------------------------------------ |
| `valid from` still in the future    | `pending`                 | Coverage has not begun; covered numbers attest at ownership level (C for BYON) |
| `valid until` has passed            | `expired`                 | Coverage ended; covered numbers fall back to C                                 |
| You clicked Revoke                  | `revoked`                 | Same immediate fallback as expired                                             |
| Chain not parseable at registration | `422 INVALID_CERTIFICATE` | The certificate never registered at all                                        |

**Diagnostic.** List the registered certificates and read the
**effective status** column (the registry derives it live, not from what it
was last told):

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

A certificate showing `pending`, `expired`, or `revoked` is one whose
covered numbers have silently dropped to C — this is the most common way a
previously-working call route degrades.

**Fix run.**

1. **Re-register with a fresh chain.** Obtain the renewed delegate certificate
   chain (PEM) from your service provider (ATIS-1000092), then re-register
   with the numbers listed in E.164:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/compliance/attestation/delegate-certs" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "friendly_name": "BYON caller-ID coverage",
    "certificate_pem": "-----BEGIN CERTIFICATE-----\n…\n-----END CERTIFICATE-----",
    "covered_numbers": ["+14155551234", "+14155550987"],
    "covered_ranges": [{ "start": "+14155555000", "end": "+14155555999" }],
    "not_before": "2026-09-01T00:00:00Z",
    "not_after": "2027-09-01T00:00:00Z"
  }'
```

2. **Match the coverage to the numbers you actually dial.** Coverage falls
   to C per-number: a chain registered with one range or list doesn't cover a
   number outside it. A registered certificate only raises the numbers it
   explicitly covers; E.164 the whole outbound set.
3. **Re-register before the window closes.** Watch the **valid until**
   column on the dashboard section and re-register *before* it flips to
   `expired` — the flip is when the drop happens, not after.

Limits to plan around: an org holds at most 50 certificates
(`409 DELEGATE_CERT_LIMIT` at the limit), re-registering the same fingerprint
returns `409 DELEGATE_CERT_DUPLICATE`, and a revoked certificate cannot be
un-revoked — re-register a fresh chain instead. Certificate writes are
admin-gated (owner/admin role); certificate reads and the posture snapshot
are not.

### Root cause 3: certificate chain rejected at registration

A rejected chain never enters the registry, so the numbers it was meant to
cover silently attest at ownership level (C) while you believed they were
covered. The registration endpoint rejects two shapes with a `422` before any
coverage exists:

* **`422 INVALID_CERTIFICATE`** — `certificate_pem` held no parseable
  PEM-encoded certificate block (wrong encoding, truncated chain, or a
  non-certificate payload pasted in).
* **`422 INVALID_COVERAGE_RANGE`** — a `covered_ranges` entry had
  `start > end`.

**Diagnostic.** Re-issue the `POST` and read the response **`error.code`**
directly — the two codes above name the failure exactly. A `422` at
registration time means your numbers were never covered; the calls themselves
carry no mark of that.

**Fix run.** Obtain the chain again from the provider and re-submit with a
well-formed PEM body and valid ranges. If your provider issued a chain that
won't parse, escalate to the provider — the registry records the
authorization artifact; it does not repair the chain.

### Root cause 4: per-number branded-calling override blocking RCD

This one bites from the recipient side even when attestation itself is A.
RCD (branded calling) renders only on **A-attested** calls; a per-number
branded-caller profile set on a DID can override the org-level profile for
that DID, and a profile that is disabled or incomplete drops RCD for that
number **without touching the attestation level** — the call still signs A,
but the handset renders plain attestation plus CNAM, and recipients who
associate the brand with the rendered name see a bare "Spam Likely"-adjacent
treatment on the numbers they expected branded.

**Diagnostic.** Check the DID's own branded-calling surface before blaming
the org default: if a per-number profile exists on the number, it overrides
the org configuration for that number, and a disabled/blank profile on it
removes RCD for that DID only.

**Fix run.**

1. **Confirm attestation first.** RCD is downstream of A — a downgraded
   number breaks RCD regardless of profile (work root cause 1/2 first).
2. **Lift the per-number profile to the org default.** Unless the DID needs
   its own brand, remove the per-DID override and let the org-level branded
   calling apply. Per the
   [Branded calling](/compliance/branded-calling) page, the org-level surface
   is `PUT /api/v1/settings/branded-calling`; a per-DID profile that was set
   as a one-off and left behind is the common culprit.
3. **Verify**. Place a test call from the number to a carrier where your
   brand registration completed; confirm the brand renders instead of the
   bare caller ID.

### Root cause 5: RCD-capable carrier coverage mismatch

RCD is **carrier-dependent**: the branded-calling treatment only renders on
carriers where your per-carrier brand registration completed, and elsewhere
the RCD payload silently drops — but the call still carries full attestation.
A registration gap across the RCD-capable carrier list is therefore a
downgrade *of the branded treatment*, not of attestation: a recipient on a
non-registered carrier sees plain attestation plus CNAM while a recipient on
a registered carrier sees the brand. The confusing part is that the payload
is silently dropped for that call, and no error surfaces.

**Diagnostic.** Check the per-carrier registration status on the branded-
calling surface. RCD renders only where the Devotel branded-calling team has
finished the registration side-band — an incomplete or not-yet-registered
carrier drops the RCD while the attestation level still shows A on the call
record.

**Fix run.**

1. **Verify A-attestation first** — RCD can't ride a downgraded call (fix
   root cause 1/2, or the org branded-calling `enabled` toggle).
2. **Complete carrier registration.** Registration is an operator-managed
   side-band with the Devotel branded-calling team, not something a tenant
   self-serves — until it completes on a downstream carrier, RCD stays silent
   there by design.
3. **Re-check after save.** Remember a config save on the branded-calling
   surface — a changed display name, logo URL, or reason-for-call — resets
   the brand-verification state to unverified, so the revised brand goes back
   through review before it renders again.

### What not to do

* **Do not chase a blocked call.** A downgrade never blocks — fail-open to
  C is deliberate (a recoverable reporting downgrade beats a blocked call).
  Work the level reads, not a call-failure log.
* **Do not PUT a higher policy target and wait for calls to improve.** The
  target is reporting intent; the softswitch signs at exactly the attested
  level and no more. Policy changes make the gap more visible, never smaller.
* **Do not re-register a revoked delegate certificate's same chain.** A
  revoked record can't be un-revoked — obtain and re-register a fresh chain.
* **Do not treat "RCD renders nowhere" as an attestation outage.** Check the
  branded-calling surface first: RCD is a signalling surface on top of A,
  not the attestation level itself.

## Error-code map

No structured STIR/SHAKEN refusal exists — the call dispatches regardless —
but three real codes from the compliance surface are worth recognizing when
they do appear:

| Code                      | HTTP | Where it surfaces                             | What it means                                                                              |
| ------------------------- | ---- | --------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `INVALID_CERTIFICATE`     | 422  | `POST /compliance/attestation/delegate-certs` | The PEM chain had no parseable certificate block — coverage never started                  |
| `INVALID_COVERAGE_RANGE`  | 422  | `POST /compliance/attestation/delegate-certs` | A covered range had `start > end` — rejected before registration                           |
| `DELEGATE_CERT_LIMIT`     | 409  | `POST /compliance/attestation/delegate-certs` | The 50-per-org certificate cap is full — revoke an unused certificate first                |
| `DELEGATE_CERT_DUPLICATE` | 409  | `POST /compliance/attestation/delegate-certs` | This chain fingerprint is already registered — covered numbers already attest at its level |

`VALIDATION_ERROR` (422) on the policy/cert write endpoints means a malformed
field, not a downgrade. For everything else — the C ceiling, an expired cert,
a per-number override, a registration gap — there is no error: the signal
lives in the posture snapshot and the branded-calling status, not the
[error-code catalog](/reference/error-codes).

## See also

* [STIR/SHAKEN attestation](/channels/voice/stir-shaken) — what the A/B/C
  levels mean, where signing happens, and the controls you own.
* [Attestation posture](/compliance/attestation) — the policy, delegate-cert
  registry, and posture snapshot controls page this run uses.
* [Branded calling (Rich Call Data)](/compliance/branded-calling) — the RCD
  configuration surface, and why it only pays off on A-attested calls.
* [ITG traceback](/compliance/itg-traceback) — the downstream obligation
  that comes with signed traffic, worth knowing once attestation actually
  attributes calls to you.
* [Error codes](/reference/error-codes) — every structured refusal the API
  returns, including the two attestation-write codes above.
