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

# Network APIs

> GSMA Open Gateway / CAMARA network-as-a-service — Silent Auth, SIM-swap, KYC-match, device location/status, carrier billing, age & scam-signal checks, a unified risk score, and Quality-on-Demand sessions

# Network APIs

Tap directly into the mobile operator network for **silent, no-OTP identity and fraud signals**. These endpoints wrap the GSMA Open Gateway / CAMARA APIs behind a single, tenant-scoped surface so you can verify a number, detect a recent SIM swap, confirm a device's location or reachability, match KYC attributes, charge the operator bill, and fuse it all into one risk verdict — without ever sending a message to the subscriber.

**Base path:** `/api/v1/numbers/network-apis`

<Note>
  The upstream integration is gated on operator credentials per deployment. When no operator is configured every endpoint **fails closed** — it returns `503 SERVICE_UNAVAILABLE` rather than fabricating an identity result. Call [Operator status](#operator-status) first to check whether the surface is live.
</Note>

All requests are authenticated with your API key and rate-limited on the standard read/write buckets. Phone numbers are E.164 (e.g. `+14155550100`).

***

## Operator status

`GET /api/v1/numbers/network-apis/status`

Report whether GSMA/CAMARA operator credentials are configured for this deployment. Returns a boolean only — never a secret.

<RequestExample>
  ```bash theme={null}
  curl "https://api.orbit.devotel.io/api/v1/numbers/network-apis/status" \
    -H "X-API-Key: dv_live_sk_your_key_here"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "enabled": true }, "meta": { "request_id": "req_naas_001" } }
  ```
</ResponseExample>

***

## Number Verification (Silent Auth)

`POST /api/v1/numbers/network-apis/number-verification:verify`

Verify a network-asserted MSISDN — the silent alternative to an SMS OTP. Fails closed: anything that is not an explicit network-confirmed match resolves to `devicePhoneNumberVerified: false`.

<ParamField body="phoneNumber" type="string">
  E.164 number to verify. Provide exactly one of `phoneNumber` or `hashedPhoneNumber`.
</ParamField>

<ParamField body="hashedPhoneNumber" type="string">
  SHA-256 hash of the E.164 number (privacy-preserving variant).
</ParamField>

<ParamField body="accessToken" type="string">
  Device-bound access token from the 3-legged CIBA / auth-code flow completed over the cellular data bearer. When present it proves device possession.
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/numbers/network-apis/number-verification:verify" \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{ "phoneNumber": "+14155550100" }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "devicePhoneNumberVerified": true }, "meta": { "request_id": "req_naas_002" } }
  ```
</ResponseExample>

***

## SIM Swap

### Check

`POST /api/v1/numbers/network-apis/sim-swap:check`

Was the SIM behind a number swapped within the look-back window? A recent swap is a strong account-takeover signal.

<ParamField body="phoneNumber" type="string" required>
  E.164 number to check.
</ParamField>

<ParamField body="maxAge" type="integer" default="240">
  Look-back window in hours (range `1`–`2400`). Default `240` (10 days).
</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "swapped": false }, "meta": { "request_id": "req_naas_003" } }
  ```
</ResponseExample>

### Retrieve date

`POST /api/v1/numbers/network-apis/sim-swap:retrieve-date`

Return the timestamp of the most recent SIM change, for callers that want the raw date rather than a boolean.

<ParamField body="phoneNumber" type="string" required>
  E.164 number to look up.
</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "latestSimChange": "2026-05-12T08:31:00Z" }, "meta": { "request_id": "req_naas_004" } }
  ```
</ResponseExample>

***

## KYC Match

`POST /api/v1/numbers/network-apis/kyc-match:match`

Match caller-supplied identity attributes against the operator's KYC records. Returns per-attribute match results — never the underlying PII.

<ParamField body="phoneNumber" type="string" required>
  E.164 number whose KYC record to match against.
</ParamField>

<ParamField body="name" type="string">Full name to match.</ParamField>
<ParamField body="givenName" type="string">Given (first) name.</ParamField>
<ParamField body="familyName" type="string">Family (last) name.</ParamField>
<ParamField body="birthdate" type="string">Date of birth (ISO `YYYY-MM-DD`).</ParamField>
<ParamField body="idDocument" type="string">National ID / document number.</ParamField>
<ParamField body="email" type="string">Email address to match.</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "nameMatch": "true", "birthdateMatch": "true" }, "meta": { "request_id": "req_naas_005" } }
  ```
</ResponseExample>

***

## Device Location Verification

`POST /api/v1/numbers/network-apis/device-location:verify`

Confirm whether a device is within a claimed geofence (a circle) **without** revealing its coordinates.

<ParamField body="phoneNumber" type="string" required>E.164 number of the device.</ParamField>
<ParamField body="latitude" type="number" required>Geofence centre latitude (WGS-84, `-90`–`90`).</ParamField>
<ParamField body="longitude" type="number" required>Geofence centre longitude (WGS-84, `-180`–`180`).</ParamField>
<ParamField body="radius" type="integer" required>Geofence radius in metres (`2000`–`200000`).</ParamField>
<ParamField body="maxAge" type="integer">Max age in seconds of the location fix the operator may use (`60`–`3600`).</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "verificationResult": "TRUE", "matchRate": 95 }, "meta": { "request_id": "req_naas_006" } }
  ```
</ResponseExample>

`verificationResult` is one of `TRUE`, `FALSE`, `PARTIAL`, `UNKNOWN`.

***

## Device Status reachability

`POST /api/v1/numbers/network-apis/device-status:reachability`

Surface the operator's network-asserted reachability for a device without contacting it. `NOT_CONNECTED` is a corroborating fraud signal.

<ParamField body="phoneNumber" type="string" required>E.164 number of the device.</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "reachabilityStatus": "CONNECTED_DATA" }, "meta": { "request_id": "req_naas_007" } }
  ```
</ResponseExample>

`reachabilityStatus` is one of `CONNECTED_DATA`, `CONNECTED_SMS`, `NOT_CONNECTED`.

***

## Carrier Billing

`POST /api/v1/numbers/network-apis/carrier-billing:charge`

Charge a one-time payment to the end-user's mobile-operator bill (Direct Carrier Billing). The per-transaction amount is capped server-side; a request above the cap is rejected with `422` before it reaches the operator.

<ParamField body="phoneNumber" type="string" required>E.164 number to bill.</ParamField>
<ParamField body="amount" type="number" required>Charge amount in major currency units (e.g. `1.99`). Positive and finite.</ParamField>
<ParamField body="currency" type="string" required>ISO-4217 alphabetic currency code (e.g. `EUR`).</ParamField>
<ParamField body="description" type="string" required>Human-readable description shown on the operator-bill line (max 140 chars).</ParamField>
<ParamField body="merchantReference" type="string">Your own reconciliation reference, echoed to the operator.</ParamField>
<ParamField body="idempotencyKey" type="string">Idempotency key so a retried charge is not double-billed.</ParamField>

<ResponseExample>
  ```json 201 theme={null}
  { "data": { "paymentId": "pay_abc123", "status": "captured" }, "meta": { "request_id": "req_naas_008" } }
  ```
</ResponseExample>

***

## Age Verification

`POST /api/v1/numbers/network-apis/age-verification:verify`

Operator-asserted check that a subscriber is at least `ageThreshold` years old, for age-gated commerce. Returns a verdict only — never a date of birth — and fails closed (anything but an explicit affirmative reads as not old enough).

<ParamField body="phoneNumber" type="string" required>E.164 number of the subscriber.</ParamField>
<ParamField body="ageThreshold" type="integer" required>Minimum age to assert, in years (`13`–`120`).</ParamField>
<ParamField body="name" type="string">Optional identity attribute that raises match confidence.</ParamField>
<ParamField body="givenName" type="string">Optional given name.</ParamField>
<ParamField body="familyName" type="string">Optional family name.</ParamField>
<ParamField body="birthdate" type="string">Optional date of birth.</ParamField>
<ParamField body="idDocument" type="string">Optional ID / document number.</ParamField>
<ParamField body="email" type="string">Optional email.</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  { "data": { "ageVerified": true, "ageThreshold": 18, "ageCheck": "true" }, "meta": { "request_id": "req_naas_009" } }
  ```
</ResponseExample>

***

## Scam Signal

`POST /api/v1/numbers/network-apis/scam-signal:assess`

Real-time anti-APP-fraud network-signal dip for a number. Returns a normalised, fail-safe verdict — the three anonymised signals plus a coarse risk level — so a bank/fintech can make the final allow/review/block call at the moment of a live transaction. No call content is ever accessed.

<ParamField body="phoneNumber" type="string">E.164 number. Provide exactly one of `phoneNumber` or `hashedPhoneNumber`.</ParamField>
<ParamField body="hashedPhoneNumber" type="string">SHA-256 hash of the E.164 number.</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "scam_signal_detected": false,
      "risk_level": "low",
      "signals": { "active_call": false, "call_forwarding": false, "sim_swap": false }
    },
    "meta": { "request_id": "req_naas_010" }
  }
  ```
</ResponseExample>

***

## Identity Risk Score

`POST /api/v1/numbers/network-apis/risk:score`

Fuse the operator CAMARA signals (SIM swap, device status, device location, number verification) together with caller-held signals (roaming, reputation, call forwarding) into **one** weighted transaction-risk verdict with reason codes. Callable at login / signup / checkout / payout — not just on an OTP send. Each live signal is fetched fail-soft: an unconfigured or failing signal is reported `unavailable` and contributes nothing rather than failing the request.

<ParamField body="phoneNumber" type="string" required>E.164 number to score.</ParamField>
<ParamField body="context" type="object">Optional business context (`action`, `reference`) echoed back, not scored.</ParamField>
<ParamField body="claimedLocation" type="object">Optional claimed geofence (`latitude`, `longitude`, `radius`, `maxAge`) that drives Device-Location Verification.</ParamField>
<ParamField body="simSwapMaxAge" type="integer" default="240">SIM Swap look-back window in hours (`1`–`2400`).</ParamField>
<ParamField body="accessToken" type="string">Device-bound Silent-Auth token; when present, possession is checked.</ParamField>
<ParamField body="reputationRiskLevel" type="string">Caller-held line reputation: `low`, `medium`, `high`, `unknown`.</ParamField>
<ParamField body="roaming" type="boolean">Caller-held roaming state.</ParamField>
<ParamField body="callForwardingUnconditional" type="boolean">Caller-held unconditional call-forwarding state.</ParamField>
<ParamField body="weights" type="object">Per-request weight overrides (0–100 each).</ParamField>
<ParamField body="thresholds" type="object">Per-request decision-threshold overrides (`review`, `deny`).</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "decision": "allow",
      "risk_score": 12,
      "risk_level": "low",
      "reasons": [],
      "signals_evaluated": ["simSwapped", "deviceReachable"],
      "signals_unavailable": ["deviceLocation"]
    },
    "meta": { "request_id": "req_naas_011" }
  }
  ```
</ResponseExample>

***

## Quality-on-Demand (QoD) sessions

Programmable QoS data bearers: request a low-latency / high-throughput data session for a device on demand, read its status, extend it, and tear it down. QoD shapes a **data** bearer only.

### Create a session

`POST /api/v1/numbers/network-apis/qod-sessions`

<ParamField body="phoneNumber" type="string" required>E.164 number of the target device.</ParamField>
<ParamField body="applicationServerIpv4" type="string" required>Application server IPv4 the optimized bearer targets.</ParamField>
<ParamField body="applicationServerIpv6" type="string">Optional application server IPv6.</ParamField>
<ParamField body="qosProfile" type="string" required>Operator QoS profile id (e.g. `QOS_E` low-latency, `QOS_L` throughput).</ParamField>
<ParamField body="duration" type="integer" default="3600">Requested session duration in seconds (`1`–`86400`).</ParamField>

<ResponseExample>
  ```json 201 theme={null}
  { "data": { "sessionId": "qod_abc123", "qosStatus": "AVAILABLE", "expiresAt": "2026-06-28T13:00:00Z" }, "meta": { "request_id": "req_naas_012" } }
  ```
</ResponseExample>

### Get a session

`GET /api/v1/numbers/network-apis/qod-sessions/{sessionId}`

Read the current status of a session.

### Extend a session

`POST /api/v1/numbers/network-apis/qod-sessions/{sessionId}/extend`

<ParamField body="requestedAdditionalDuration" type="integer" required>Additional seconds to add to the live session (`1`–`86400`).</ParamField>

### Delete a session

`DELETE /api/v1/numbers/network-apis/qod-sessions/{sessionId}`

Tear down a session. Returns `204 No Content`.

***

## Errors

<ResponseField name="SERVICE_UNAVAILABLE" type="503">
  No GSMA/CAMARA operator is configured for this deployment. The surface fails closed — call [Operator status](#operator-status) to check before integrating.
</ResponseField>

<ResponseField name="VALIDATION_ERROR" type="422">
  The request body failed validation, the operator could not process it (e.g. no subscriber for the number), or a Carrier-Billing `amount` exceeded the per-transaction cap.
</ResponseField>

<ResponseField name="UPSTREAM_ERROR" type="502">
  The operator / aggregator returned an unexpected error. Phone numbers in error details are masked.
</ResponseField>
