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

# Number Lookup

> Validate any phone number and enrich it with carrier, portability, SIM-swap, and roaming intelligence

# Number Lookup

Look up any E.164 phone number to validate it and enrich it with carrier, line-type, portability, SIM-swap, roaming, and live-reachability intelligence. Orbit uses Devotel HLR as the primary source and Telnyx as a fallback.

Lookup is a billable operation — see [Billing](#billing) below.

## Single Lookup (quick validation)

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/lookup \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "phone": "+14155550100" }'
```

Returns validity, country, line type, and HLR enrichment. The `phone` field must be valid E.164 (`+` followed by 2–15 digits, the first non-zero).

## Full Intelligence Lookup

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/numbers/lookup/%2B14155550100" \
  -H "X-API-Key: dv_live_sk_..."
```

URL-encode the leading `+` as `%2B` (an unencoded `+` is also accepted for back-compat).

### Response

```json theme={null}
{
  "data": {
    "phoneNumber": "+14155550100",
    "valid": true,
    "callingCountryCode": "US",
    "nationalFormat": "(415) 555-0100",
    "carrier": { "name": "Verizon", "type": "mobile", "mcc": "310", "mnc": "004" },
    "portability": { "ported": false, "mnp_original_carrier": null },
    "simSwap": { "swapped": null, "lastSwapDate": null, "riskLevel": "unknown" },
    "roaming": { "roaming": null, "servingMcc": null, "homeMcc": null, "servingCountry": null },
    "liveStatus": "yes",
    "reachable": true,
    "cached": false
  }
}
```

## Data Packages (Twilio Lookup v2 compatible)

Add a `fields=` selector (comma-separated, or a JSON array) to request per-field intelligence packages — the same selector shape Twilio Lookup v2 uses, so migration scripts work unchanged.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/numbers/lookup/%2B14155550100?fields=line_type_intelligence,sim_swap" \
  -H "X-API-Key: dv_live_sk_..."
```

The selector is case-insensitive, deduped server-side, and capped at **16 fields** per request. An unknown field name rejects the whole request with `422`.

### Available fields

| Field                        | Status today                                                            | Source                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ---------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `line_type_intelligence`     | available                                                               | Devotel HLR (`type`, `network`, `mcc`, `mnc`)                                                                                                                                                                                                                                                                                                                                                                           |
| `operator_name`              | available                                                               | Devotel HLR home-network operator name                                                                                                                                                                                                                                                                                                                                                                                  |
| `phone_number_quality_score` | available                                                               | derived from reachability + portability + carrier signals                                                                                                                                                                                                                                                                                                                                                               |
| `sim_swap`                   | available when carrier signal present                                   | CAMARA SIM Swap (GSMA Open Gateway) — carrier-asserted last-SIM-change; Telnyx Lookup v2 best-effort fallback                                                                                                                                                                                                                                                                                                           |
| `identity_match`             | available when a CAMARA operator is configured (coming\_soon otherwise) | CAMARA KYC Match (GSMA Open Gateway) — operator-asserted phone→identity match. Supply the attributes to verify (`identity_name`, `identity_given_name`, `identity_family_name`, `identity_birthdate`, `identity_email`, `identity_id_document`) and receive per-attribute match verdicts, never the underlying PII. Selecting the field with no attribute returns a per-field `error` (`identity_attributes_required`). |
| `caller_name`                | available (coming\_soon if Telnyx unconfigured)                         | Telnyx Lookup v2 caller-name dip (CNAM subscriber display; pairs with `cnam`)                                                                                                                                                                                                                                                                                                                                           |
| `cnam`                       | available (coming\_soon if Telnyx unconfigured)                         | Telnyx Lookup v2 caller-name dip                                                                                                                                                                                                                                                                                                                                                                                        |
| `reassigned_number`          | available (US-only; requires `consent_date`)                            | FCC Reassigned Numbers Database (reassigned.us)                                                                                                                                                                                                                                                                                                                                                                         |
| `call_forwarding`            | available (derived, low confidence)                                     | Devotel HLR reachability — best-effort divert inference, `derived: true`                                                                                                                                                                                                                                                                                                                                                |
| `number_reputation`          | available (derived, devotel-hlr)                                        | HLR-derived risk score — line-type + reachability + portability signals; `data.risk_score` (0-1), `data.risk_bucket`, `data.derived: true`. Contracted Telnyx / Trust Hub feed supersedes additively.                                                                                                                                                                                                                   |
| `sms_pumping_risk`           | available (derived, devotel-hlr)                                        | HLR-derived SMS-pumping (AIT) fraud score — line-type (VOIP/virtual signal) + on-network reachability + portability churn fused into a 0-100 score + bucket, with the contributing signals exposed and `data.derived: true`. Dip it before granting an OTP send. A contracted SMS-pumping feed supersedes the derivation additively.                                                                                    |

Each entry in the response `dataPackages` map carries a discriminated `status`:

| Status            | Meaning                                                                                                                                                                                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `available`       | The provider returned real data in `data`.                                                                                                                                                                                                                   |
| `coming_soon`     | The field is advertised in the catalog but the upstream provider is **not yet contracted** (`reason: "upstream_provider_not_contracted"`). Replaying will not change the answer until the vendor work lands — do not design integrations assuming a payload. |
| `not_implemented` | No provider has wired this field yet (transient).                                                                                                                                                                                                            |
| `error`           | The provider call failed for this specific field; the base lookup may still have succeeded (per-field isolation).                                                                                                                                            |

```json theme={null}
{
  "dataPackages": {
    "line_type_intelligence": {
      "status": "available",
      "reason": "hlr_live",
      "provider": "devotel-hlr",
      "data": { "type": "mobile", "carrier_name": "Verizon", "mobile_country_code": "310", "mobile_network_code": "004" }
    },
    "cnam": {
      "status": "available",
      "reason": "telnyx_lookup_v2_caller_name",
      "provider": "telnyx",
      "data": { "caller_name": "ACME CORP" }
    },
    "call_forwarding": {
      "status": "available",
      "reason": "derived_from_hlr_reachability",
      "provider": "devotel-hlr",
      "data": {
        "call_forwarding": true,
        "forwarded": true,
        "confidence": "low",
        "derived": true,
        "signal": "hlr_reachability",
        "reachable": false,
        "line_status": "no"
      }
    }
  }
}
```

<Note>
  `call_forwarding` is a **derived, best-effort** signal, not an operator-asserted
  forwarding flag. It infers an active unconditional divert from the HLR
  reachability dip Orbit already runs — a valid line reported as unreachable on its
  home network is the classic proxy for calls forwarding elsewhere (a known
  SIM-swap / account-takeover indicator). Treat it as a low-confidence input
  (`confidence: "low"`, `derived: true`), fuse it with other signals, and do not
  use it as ground truth. It falls back to `coming_soon` when HLR returns no usable
  reachability signal.
</Note>

## Bulk Lookup

Validate up to **100 numbers** in a single request.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/bulk-lookup \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "numbers": ["+14155550100", "+14155550101"] }'
```

Bulk lookup requires the HLR provider to be configured (see [Configuration](#configuration)); when it isn't, the response carries `available: false` with an explanatory message and an empty `results` array. The endpoint is rate-limited more tightly than single lookup (a 100-number batch checks for \~\$1.00 of wallet headroom up front).

## Configuration

Lookup resolves its upstream from two environment variables on the API service:

| Variable              | Required           | Default                                   | Purpose                                                                                                                                                                                                                                                                                                  |
| --------------------- | ------------------ | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEVOTEL_HLR_API_KEY` | Yes, to enable HLR | *(unset)*                                 | API key for Devotel's HLR service. This is the credential that turns HLR on — it is the primary source for single lookup and the **only** source for bulk lookup. While it is unset, single lookup falls back to Telnyx (if `DEVOTEL_TELNYX_API_KEY` is set) and bulk lookup returns `available: false`. |
| `DEVOTEL_HLR_API_URL` | No                 | `https://hlr.orbit.devotel.io/api/v1/hlr` | Base URL of the HLR service. Override only when pointing at a non-default HLR endpoint.                                                                                                                                                                                                                  |

Set `DEVOTEL_HLR_API_KEY` (and, if needed, `DEVOTEL_HLR_API_URL`) on the API service and restart it. Once the key is present, bulk lookup begins returning results and single lookup uses HLR as its primary source.

## Billing

Each lookup that an upstream actually answers is charged to your wallet on the `number_lookup` channel (fallback **1¢/query**; per-org overrides apply). Key rules:

* **Single lookup** is charged *after* the upstream returns. A request that falls through to local E.164 detection (no upstream call) is **free**.
* **Bulk lookup** pre-checks your balance for `N × unit price` and fails fast with `402` before fanning out, then charges per successful result.
* A per-tenant **daily cost cap** (default \$50/day) protects against runaway spend — exceeding it returns `429`.
* **Sandbox** requests (`testMode`) are never billed.

Insufficient balance returns `402 INSUFFICIENT_BALANCE` with `balance_cents` and `cost_cents` so you can prompt a top-up.
