> ## 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. Observe the inbound call, then call back

Have the tester call the 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.
