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

# Number Health and Spam Test sessions

> Inspect carrier-specific evidence, organize Number Sets, and persist human spam-test sessions across devices.

Number Health provides the shared evidence behind the app's fleet view and the **revdesk.dev Spam
Test**. A handset observation means “observed on this carrier at this time,” not a guarantee that
a number is clear on every network. Read the server's evidence state and recommended action rather
than calculating readiness or storing authoritative results in localStorage.

<Note>
  The APIs below cover fleet evidence, Number Sets, saved sessions, and audited observations.
  They do not yet provide tester-invite redemption, automatic inbound-to-browser handoff, alternate
  provisioning, billing resolution, or scheduled retirement. Availability follows the Number Health
  rollout; a documented operation does not imply that these additional workflows are enabled.
</Note>

## Authentication and permissions

Use an organization-scoped bearer credential. Creating sets, updating sets, starting sessions, and
attaching calls also require an associated user for attribution. Keep long-lived API keys on your
server; use the [client-token flow](/api-reference/client-tokens) for browser integrations.

| Operation | Required scope |
| - | - |
| Read policy, fleet, sets, sessions, or inbound evidence; preview set resolution | `caller_trust:read` |
| Create/update sessions, attach attempts, record/correct observations | `caller_trust:write` |
| Create/update Number Sets | `phone_numbers:write` |
| Place a phone-bridge call with `/v1/calls/dial` | `calls:write` |
| Request a human browser call ticket with `/v1/webrtc-token` | `voice:webrtc` |

Responses use `{ "data": ... }`; list responses also include `meta.total_results`. Set, session,
and attempt creation return **201**. Other Number Health operations return **200** on success.

## Read policy and fleet evidence

Fetch `GET /v1/number-health/policy` for the canonical policy version, evidence thresholds, UTC
deduplication rule, and current fresh-check/remediation prices. Use these values in cost previews
and guidance instead of duplicating constants in clients.

`GET /v1/number-health/numbers` returns owned numbers, identity status, provider scores and
freshness, set membership, carrier observations, `health_warning`, and `recommended_action`.
Optional filters are `team_id`, `number_set_id`, `area_code`, and `state`.

| `evidence_state` | Meaning |
| - | - |
| `CLEAN_OBSERVED` | Fresh LOW risk, complete setup, and recent non-negative handset evidence. Not a cross-carrier guarantee. |
| `NEEDS_DATA` | Insufficient or stale evidence. Unknown is not the same as clean. |
| `WATCH` | MEDIUM provider risk requiring monitoring. |
| `FLAGGED` | HIGH risk or recent negative handset evidence, including wrong name, blocked, or no ring. |
| `SETUP_INCOMPLETE` | Missing identity, registration, monitoring, or inbound-routing setup. |
| `RETIRED` | Inactive number or retired set membership; historical evidence is retained. |

Inspect `observations` for `carrier`, `outcome`, `observed_label`, `observed_at`, duration, and
callback evidence. `NUMBER_ONLY` and `LOCATION` are non-spam observations, but do not confirm the
correct clinic name. Null scores or timestamps mean unavailable evidence, not zero risk.

Fleet reads use cached reputation. A paid refresh is a separate explicit
`GET /v1/caller-trust/reputation/numbers/{phone}?fresh=true` request. Remediation uses the existing
Caller Trust remediation API. Preview the cost and obtain approval before issuing paid operations;
creating a test session does not request a fresh reputation check.

## Organize Number Sets

A Number Set groups numbers for one clinic or calling purpose. Area code is only a visual fallback;
sharing an area code does not make two numbers replacements for one another.

Create a set with `POST /v1/number-health/sets`:

```json theme={null}
{
  "name": "Downtown clinic",
  "use_case": "Appointment confirmations",
  "identification_guidance": "Identify the clinic at the start of each call.",
  "members": [
    { "phone_id": 101, "role": "PRIMARY", "state": "ACTIVE", "outbound_enabled": true },
    { "phone_id": 102, "role": "ALTERNATE", "state": "PREPARING", "outbound_enabled": false }
  ]
}
```

Use actual owned phone IDs from the fleet response. `GET /v1/number-health/sets` returns sets and
their members. `PATCH /v1/number-health/sets/{id}` updates fields and upserts supplied members;
omitted members are not removed. To make a member ineligible for selection, set `outbound_enabled`
to `false` or use an ineligible lifecycle state. `RETIRED` is accepted by the update endpoint.

These membership changes affect outbound selection only. They do not buy/release numbers, change
billing, disable inbound service, or delete call history. Marking a member `RETIRED` is not carrier
number retirement.

Preview selection without placing a call:

```http theme={null}
POST /v1/number-health/sets/SET_ID/resolve
Content-Type: application/json
Authorization: Bearer YOUR_SERVER_CREDENTIAL

{ "destination_number": "+12025550123" }
```

The response includes `from_number`, `phone_id`, `evidence_state`, `health_warning`, and
`selection_source: "number_set_sticky_health"`. Selection considers active, outbound-enabled
`ACTIVE`/`STANDBY` members, preferring the healthiest tier, then priority and a recipient-sticky
tie-breaker. Health or membership changes can change the selected caller ID. If all eligible
members are flagged, selection still succeeds with a warning. No eligible member returns **409**.

See [caller-ID selection when dialing](/api-reference/calling-paths#caller-id-selection-and-health-warnings)
for exact-number precedence and the `/v1/calls/dial` request.

## Run a resumable Spam Test

### 1. Create the session

Obtain the tester's consent before submitting their phone number. The consent flag records the
operator's assertion; it is not an OTP verification or an invitation redemption flow.

```http theme={null}
POST /v1/number-health/sessions
Content-Type: application/json
Authorization: Bearer YOUR_SERVER_CREDENTIAL

{
  "tester": {
    "alias": "Consented mobile tester",
    "phone_number": "+12025550123",
    "carrier": "TMOBILE",
    "consented": true
  },
  "mode": "RAPID_AUDIT",
  "scope": { "kind": "ALL" },
  "compare_alternates": true
}
```

Carrier values are `ATT`, `TMOBILE`, `VERIZON`, `OTHER`, and `UNKNOWN`. Scope kinds are `ALL`,
`TEAM`, `NUMBER_SET`, `AREA_CODE`, `FLAGGED`, and `NEEDS_DATA`. For `TEAM`, `NUMBER_SET`, and
`AREA_CODE`, supply the matching identifier or three-digit area code in `scope.value`.

| Mode | Purpose |
| - | - |
| `RAPID_AUDIT` | Quick carrier-specific handset audit. Does not count toward readiness. |
| `HUMAN_READINESS` | Qualifying human evidence across testers, days, and carriers. |
| `CONNECTIVITY_QA` | Routing/audio/lifecycle checks; synthetic evidence never counts toward readiness. |

The returned `data.id` identifies the saved session. `GET /v1/number-health/sessions/{id}` returns
its selected `phone_ids` and attempts. Use those server records to resume on another device.
`PATCH` the same URL with `{ "status": "PAUSED" }`, `ACTIVE`, `COMPLETED`, or `CANCELLED` to
change its lifecycle without deleting evidence. The selected queue is saved when the session starts.

### 2. Place the label-test call

For a **Quick fleet scan**, call the tester directly from the candidate number using the human
browser WebRTC flow below. Inbound checks are optional, and a label audit has no 60-second target.
There is no need to sit through an AI or prerecorded greeting to unlock the outbound test.

For **Human Readiness**, have the tester call a human-handled candidate RevDesk number first. Read
`GET /v1/number-health/sessions/{id}/inbound?phone_id=101` for server-confirmed progress:
`call_id`, `status`, `answered_at`, `ended_at`, `duration_seconds`, `callback_verified`, and `human`.
Only calls from this tester to a queued number after session creation are considered.

<Warning>
  This endpoint observes the number's existing inbound routing. It does not redirect the call to
  the browser operator. Arrange human inbound handling before a readiness test. An AI answering
  an inbound call is not qualifying human callback evidence.
</Warning>

For a human browser callback, use the existing [WebRTC flow](/api-reference/webrtc-security) with
the exact candidate `from_number` and the consented tester as `to_number`. No assigned AI agent is
required. Disable duplicate placement while a call request is pending.

Attach the resulting call to the session using `POST /v1/number-health/sessions/{id}/attempts`:

```json theme={null}
{ "phone_id": 101, "call_id": "CALL_ID_FROM_THE_CALL_FLOW" }
```

This endpoint attaches an existing call; it does not dial. The server checks ownership, queue
membership, session timing, and tester destination. Human modes reject AI calls. Wait for carrier
completion events before recording the observation; a browser stopwatch is not duration evidence.

### 3. Record exactly what appeared

Submit to `POST /v1/number-health/attempts/{id}/observation`:

```json theme={null}
{
  "outcome": "SPAM",
  "observed_label": "Spam likely",
  "handset": { "manufacturer": "Apple", "model": "iPhone", "os": "iOS" }
}
```

Outcomes are `CLEAN` (correct name), `NUMBER_ONLY`, `LOCATION`, `WRONG_NAME`, `SPAM`, `SCAM`,
`BLOCKED`, `NO_RING`, or `UNKNOWN`. Supply the exact display text, not a guessed carrier verdict.
The server measures durations and derives callback evidence from actual calls. Do **not** set
`callback_verified: true` automatically: that optional field is an explicit, audited operator
attestation. Reposting a corrected observation preserves a before/after audit snapshot.

If the call has not reached a terminal carrier state, the endpoint returns **400** with retry
guidance. After saving, reload the session and fleet for evidence eligibility, exclusion reasons,
and the next recommended action. Save the session ID; localStorage is not the results database.

## Readiness is evidence, not a warm-up guarantee

The current policy targets 8 qualifying human calls, 5 consented testers, 4 active days, and a
60-second conversational duration. Readiness also needs clean observations on AT\&T, T-Mobile,
and Verizon, a verified callback, LOW provider risk, and no recent negative display.

Only one qualifying number/tester interaction per UTC calendar day contributes to readiness.
There is no 48-hour rejection of legitimate extra calls. These are RevDesk evidence criteria,
not a carrier promise that call length or a fixed number of calls removes spam labels. Do not
disguise synthetic calls or automate repetitive warm-up loops. The QA mode records exclusion;
it is not a dedicated AI/prerecorded scenario runner.

## Divide testing across the team

Staff can start separate sessions for their own consented handset and carrier, scope the scan by
area code or evidence state, and select the numbers they actually use from the queue. In Spam Test,
**Leave untested and next** moves to another number without inventing an observation or marking the
skipped number clean. Skipped numbers remain untested when the saved session is resumed.

If a readiness session encounters an automated inbound route, **Set up Quick fleet scan instead**
preserves the old session and opens setup for a new label-only audit. It does not convert synthetic
calls into human readiness evidence. An authenticated shared session link resumes the same tester's
session; it is not an invitation for a different handset or carrier.


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