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

# Numbers API

> Search, purchase, release, and configure virtual phone numbers

# Numbers API

Search available phone numbers in 60+ countries, purchase instantly, configure routing for SMS and voice, and release numbers you no longer need.

**Base path:** `/api/v1/numbers`

***

## Search Available Numbers

`GET /api/v1/numbers/available`

Search the Orbit inventory for phone numbers matching your criteria.

<ParamField query="country" type="string" default="US">
  ISO 3166-1 alpha-2 country code (e.g., `US`, `GB`, `TR`). Optional — defaults to `US` when omitted.
</ParamField>

<ParamField query="type" type="string">
  Number type: `local`, `toll_free`, `mobile`
</ParamField>

<ParamField query="capabilities" type="string">
  Comma-separated capabilities filter: `sms`, `voice`, `mms`, `fax`
</ParamField>

<ParamField query="area_code" type="string">
  Filter by area code (e.g., `415` for San Francisco)
</ParamField>

<ParamField query="contains" type="string">
  Pattern match — search for numbers containing specific digits (e.g., `555`)
</ParamField>

<ParamField query="limit" type="integer" default="100">
  Number of results to return (max 500)
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl "https://api.orbit.devotel.io/api/v1/numbers/available?country=US&type=local&capabilities=sms,voice&area_code=415&limit=5" \
    -H "X-API-Key: dv_live_sk_your_key_here"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": [
      {
        "number": "+14155550100",
        "country": "US",
        "type": "local",
        "region": "California",
        "capabilities": ["sms", "voice", "mms"],
        "monthly_cost": "1.50",
        "currency": "USD"
      },
      {
        "number": "+14155550101",
        "country": "US",
        "type": "local",
        "region": "California",
        "capabilities": ["sms", "voice", "mms"],
        "monthly_cost": "1.50",
        "currency": "USD"
      }
    ],
    "meta": {
      "request_id": "req_num_001",
      "timestamp": "2026-03-08T12:00:00Z"
    }
  }
  ```
</ResponseExample>

***

## Pre-purchase discovery

A buyer integration calls these three read endpoints **before** [Purchase a Number](#purchase-a-number) — check what's in stock for a country, then check what documents a regulated country needs — so the buy flow never hits a surprise at order time. All three are read-only and scoped on `numbers:read`.

### Country Capabilities

`GET /api/v1/numbers/country-capabilities`

Per-`(line type × capability)` inventory summary for a single country, so you can see the stock reality (for example, GB has plenty of local-voice numbers but **zero** SMS-capable mobiles) before running a search. Counts are bucketed and clamped at `100` — a value of `100` means "plenty in stock", not exactly one hundred — aggregated across carriers, and cached server-side for 5 minutes.

<ParamField query="country" type="string" required>
  ISO 3166-1 alpha-2 country code to summarise (e.g., `GB`, `US`). A missing or invalid value returns `422`.
</ParamField>

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "country": "GB",
      "summary": {
        "mobile_sms": 100,
        "mobile_voice": 100,
        "local_sms": 0,
        "local_voice": 100,
        "toll_free": 42,
        "toll_free_sms": 0
      },
      "two_way_sms": 100,
      "us_uses_10dlc": false,
      "last_updated": "2026-06-08T10:00:00.000Z"
    },
    "meta": {
      "request_id": "req_num_cc1",
      "timestamp": "2026-06-08T10:00:00Z"
    }
  }
  ```
</ResponseExample>

<Note>
  `two_way_sms` is the authoritative "can this country do two-way SMS" signal (send *and* receive) — it excludes send-only toll-free. `local_sms` is `0` in countries where landlines don't carry SMS (UK, DE, FR, …). For the US, `us_uses_10dlc` is `true` and a `local_sms_via_10dlc` count is added, since A2P SMS there rides 10DLC-registered local numbers.
</Note>

### Regulatory Preview

`GET /api/v1/numbers/regulatory-preview`

A live, single-country preview of what a purchase will require: the identity/address documents and data fields, the expected activation window for regulated countries, and — scoped to your organization — whether an existing compliance profile already satisfies the requirements. Use it to render an at-a-glance "documents needed" disclosure on a specific country and line type before the buy click.

<ParamField query="country" type="string" required>
  ISO 3166-1 alpha-2 country code. A missing or invalid value returns `422`.
</ParamField>

<ParamField query="phone_number_type" type="string" default="local">
  Line type the requirements vary by — one of `local`, `mobile`, `toll_free`.
</ParamField>

<ParamField query="provider" type="string">
  Optional carrier to narrow the schema to — one of `telnyx`, `didww`. When omitted, the requirement sets from every provider that could serve the country are unioned.
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl "https://api.orbit.devotel.io/api/v1/numbers/regulatory-preview?country=GB&phone_number_type=local" \
    -H "X-API-Key: dv_live_sk_your_key_here"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "country": "GB",
      "provider": "platform",
      "phone_number_type": "local",
      "activation_eta": { "typical_minutes": 120, "max_hours": 72 },
      "fields": [
        { "name": "registered_address", "type": "address", "description": "Registered service address", "required": true }
      ],
      "document_roles": ["id_proof", "address_proof"],
      "compliance_profile_satisfies": false,
      "unregulated": false
    },
    "meta": {
      "request_id": "req_num_rp1",
      "timestamp": "2026-06-08T10:00:00Z"
    }
  }
  ```
</ResponseExample>

<Note>
  `compliance_profile_satisfies` is always a boolean: `false` means "complete a compliance profile before buying", and `true` (also returned whenever `unregulated` is `true`) means you can purchase right away. When the country/type activates instantly, `activation_eta` is `null`.
</Note>

### Regulatory Requirements

`GET /api/v1/numbers/regulatory-requirements`

Enumerate the identity/address documents and data fields a purchase will require, so you can gather them up front instead of discovering them via a carrier-side bundle decline after the order. With `?country=` it returns that single country's record; without it, the catalog across every regulated country for the line type. This is a pure catalog read off the compliance registry — no carrier call.

<ParamField query="country" type="string">
  Optional ISO 3166-1 alpha-2 country code. When omitted, returns the catalog across all regulated countries.
</ParamField>

<ParamField query="phone_number_type" type="string" default="local">
  Line type the requirements vary by — one of `local`, `mobile`, `toll_free`.
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl "https://api.orbit.devotel.io/api/v1/numbers/regulatory-requirements?country=GB" \
    -H "X-API-Key: dv_live_sk_your_key_here"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "country": "GB",
      "phone_number_type": "local",
      "regulated": true,
      "identity_type": "business",
      "data_fields": [
        { "key": "registered_address", "label": "Registered service address", "required": true }
      ],
      "documents": [
        { "role": "id_proof", "accepts": ["passport", "national_id"] },
        { "role": "address_proof", "accepts": ["utility_bill", "bank_statement"] }
      ]
    },
    "meta": {
      "request_id": "req_num_rr1",
      "timestamp": "2026-06-08T10:00:00Z"
    }
  }
  ```
</ResponseExample>

<Note>
  Without `?country=`, `data` is the catalog instead — `{ phone_number_type, count, requirements: [ … ] }`, one record per regulated country. Countries that need nothing come back with `regulated: false`.
</Note>

***

## Purchase a Number

`POST /api/v1/numbers/purchase`

Purchase an available number and add it to your account. The number is billed monthly starting immediately.

<ParamField body="phone_number" type="string" required>
  The phone number to purchase in E.164 format (from the search results)
</ParamField>

<ParamField body="country_code" type="string" default="US">
  ISO 3166-1 alpha-2 country code for the number (2 letters)
</ParamField>

<ParamField body="capabilities" type="string[]">
  Capabilities to provision on the number — any of `sms`, `voice`, `whatsapp`, `rcs`
</ParamField>

<ParamField body="compliance_profile_id" type="string">
  ID of an approved compliance profile to attach before the carrier order. For regulated countries, attaching a profile that covers the target country lets the number activate immediately instead of entering a regulatory hold.
</ParamField>

<ParamField body="id" type="string">
  Provider-issued inventory row id returned by `GET /api/v1/numbers/available`. Forward it for placeholder (carrier-allocated) numbers; omit it to purchase by `phone_number` alone.
</ParamField>

<Note>
  Purchase only acquires the number. Set its label and inbound webhook routing afterward with the [Configure a Number](#configure-a-number) endpoint.
</Note>

<Warning>
  Most EU/EEA numbers (for example DE, FR, NL, DK, AT, BE, IT, ES, PL, IE, and GB) require a verified end-user address on the order. Purchase with a `compliance_profile_id` that covers the destination country — without one, the order returns `422` or enters a regulatory hold until the required identity documents are provided.
</Warning>

<RequestExample>
  ```bash theme={null}
  curl -X POST https://api.orbit.devotel.io/api/v1/numbers/purchase \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "phone_number": "+14155550100",
      "country_code": "US",
      "capabilities": ["sms", "voice"]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={null}
  {
    "data": {
      "id": "num_abc123",
      "number": "+14155550100",
      "country": "US",
      "type": "local",
      "capabilities": ["sms", "voice", "mms"],
      "status": "active",
      "monthly_cost": "1.50",
      "currency": "USD",
      "created_at": "2026-03-08T12:00:00Z"
    },
    "meta": {
      "request_id": "req_num_002",
      "timestamp": "2026-03-08T12:00:00Z"
    }
  }
  ```
</ResponseExample>

***

## List Your Numbers

`GET /api/v1/numbers`

Retrieve all phone numbers on your account with cursor-based pagination.

<ParamField query="cursor" type="string">
  Cursor for pagination (returned in previous response)
</ParamField>

<ParamField query="limit" type="integer" default="25">
  Number of results per page (max 200)
</ParamField>

<ParamField query="status" type="string" default="active">
  Filter by lifecycle status. Pass a single value, a comma-separated subset, or
  `all` to return every status. Recognised values: `active`,
  `pending_compliance`, `inactive`, `released`, `parked`, `suspended`. When
  omitted, only `active` numbers are returned.
</ParamField>

<ParamField query="search" type="string">
  Case-insensitive partial match against the phone number or its label.
</ParamField>

<ParamField query="tags" type="string">
  Comma-separated tag labels (e.g. `sales,vip`). Returns numbers carrying at
  least one of the supplied tags.
</ParamField>

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": [
      {
        "id": "num_abc123",
        "number": "+14155550100",
        "country": "US",
        "type": "local",
        "label": "Main Support Line",
        "capabilities": ["sms", "voice", "mms"],
        "status": "active",
        "monthly_cost": "1.50",
        "currency": "USD",
        "created_at": "2026-03-08T12:00:00Z"
      }
    ],
    "meta": {
      "request_id": "req_num_003",
      "timestamp": "2026-03-08T12:00:00Z",
      "pagination": {
        "cursor": "cur_num_abc",
        "has_more": false,
        "total": 1
      }
    }
  }
  ```
</ResponseExample>

***

## Get Number Details

`GET /api/v1/numbers/{id}`

Retrieve the full configuration and status of a specific number.

<ParamField path="id" type="string" required>
  Number ID (e.g., `num_abc123`)
</ParamField>

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "id": "num_abc123",
      "number": "+14155550100",
      "country": "US",
      "type": "local",
      "label": "Main Support Line",
      "capabilities": ["sms", "voice", "mms"],
      "status": "active",
      "sms_webhook_url": "https://yourapp.com/webhooks/sms",
      "voice_webhook_url": "https://yourapp.com/webhooks/voice",
      "agent_id": null,
      "monthly_cost": "1.50",
      "currency": "USD",
      "created_at": "2026-03-08T12:00:00Z"
    },
    "meta": {
      "request_id": "req_num_004",
      "timestamp": "2026-03-08T12:00:00Z"
    }
  }
  ```
</ResponseExample>

***

## Get Number Health Score

`GET /api/v1/numbers/{id}/health`

Read the per-DID deliverability health score for one of your numbers. The score is produced by a daily background tick over a rolling window of message outcomes — the route returns the most recent persisted snapshot and never re-computes inline. Returns `null` until the scheduler has scored the number.

<Note>
  This is internal **deliverability** health, distinct from `GET /api/v1/numbers/{id}/reputation`, which reports the carrier-displayed caller-ID label (Hiya, TNS, First Orion, …) for outbound voice.
</Note>

<ParamField path="id" type="string" required>
  Number ID
</ParamField>

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "phone_number_id": "num_abc123",
      "phone_number": "+14155550100",
      "score": 92,
      "tier": "healthy",
      "last_computed_at": "2026-06-27T00:05:00Z",
      "breakdown": {
        "delivery_rate": 0.987,
        "complaint_rate": 0.0004,
        "sample_size": 4210
      },
      "lookback_days": 30,
      "min_sample_size": 50
    },
    "meta": {
      "request_id": "req_num_health_001",
      "timestamp": "2026-06-28T12:00:00Z"
    }
  }
  ```
</ResponseExample>

***

## Get Number Warming State

`GET /api/v1/numbers/{id}/warming`

Read the current 10DLC warming-tier state for a number. The `current_day_count` is the live counter the pre-send gate enforces against, so today's progress is reflected immediately rather than at the next reset. Returns `null` for numbers that are not being warmed (non-10DLC, alphanumeric, or voice-only DIDs).

<ParamField path="id" type="string" required>
  Number ID
</ParamField>

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "phone_number_id": "num_abc123",
      "phone_number": "+14155550100",
      "trust_score": 0.78,
      "daily_cap": 2000,
      "current_day_count": 1450,
      "current_day_window_start": "2026-06-28T00:00:00Z",
      "warming_started_at": "2026-06-14T00:00:00Z",
      "warming_phase": "ramp",
      "last_phase_advance_at": "2026-06-21T00:00:00Z",
      "days_warmed": 14
    },
    "meta": {
      "request_id": "req_num_warming_001",
      "timestamp": "2026-06-28T12:00:00Z"
    }
  }
  ```
</ResponseExample>

***

## Get Number Warming Progression

`GET /api/v1/numbers/{id}/warming/progression`

Read the warming progression for a number: its `warming_state`, the mirrored daily ceiling, the live day count, and a 15-day `forecast` (day 0 through day 14) of the growth-curve ceiling so a client can render the projection without a per-day round-trip. The row is returned even when warming is `off`, so the UI can show an enable-warming state.

<ParamField path="id" type="string" required>
  Number ID
</ParamField>

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "phone_number_id": "num_abc123",
      "phone_number": "+14155550100",
      "warming_state": "warming",
      "warming_started_at": "2026-06-14T00:00:00Z",
      "warming_max_daily_sms": 2000,
      "current_day_count": 1450,
      "days_warmed": 14,
      "ceiling": 3000,
      "base": 200,
      "growth": 1.25,
      "forecast": [
        { "day": 0, "ceiling": 200 },
        { "day": 14, "ceiling": 3000 }
      ]
    },
    "meta": {
      "request_id": "req_num_warming_prog_001",
      "timestamp": "2026-06-28T12:00:00Z"
    }
  }
  ```
</ResponseExample>

***

## Configure a Number

`PUT /api/v1/numbers/{id}`

Update the configuration for a number — change its label, status, capabilities, forwarding, webhook delivery, compliance profile, or tags. All body fields are optional; only the keys you send are updated. The request body is validated strictly, so unknown keys are rejected with `422`.

<Note>
  To configure inbound AI-agent routing (which agent answers calls/messages on this number), use `PUT /api/v1/numbers/{phoneNumber}/routing` instead — agent assignment is not part of this endpoint.
</Note>

<ParamField path="id" type="string" required>
  Number ID
</ParamField>

<ParamField body="label" type="string">
  Human-friendly label for the number (max 100 characters)
</ParamField>

<ParamField body="status" type="string">
  Number status — one of `active` or `inactive`
</ParamField>

<ParamField body="capabilities" type="string[]">
  Enabled capabilities, any of `sms`, `mms`, `voice`, `fax`. Toggling depends on carrier support (for example `fax`/T.38 is fixed at provisioning time); unsupported toggles return `422`.
</ParamField>

<ParamField body="forwarding_number" type="string">
  E.164 number to forward inbound voice calls to. Send `""` to clear.
</ParamField>

<ParamField body="webhook_url" type="string | null">
  Single webhook URL for inbound event delivery (SMS and voice). Send `""` or `null` to detach.
</ParamField>

<ParamField body="sms_forwarding" type="string">
  E.164 number to forward inbound SMS to. Send `""` to clear.
</ParamField>

<ParamField body="compliance_profile_id" type="string | null">
  Per-number compliance profile to attach. Send `""` or `null` to detach.
</ParamField>

<ParamField body="tags" type="string[]">
  Up to 10 lowercase-slug tags (`^[a-z0-9][a-z0-9_-]{0,63}$`). Sent as the full resolved tag set; an empty array clears all tags.
</ParamField>

<ParamField body="channel_config" type="object">
  Provider-specific channel configuration overrides.
</ParamField>

<ParamField body="messaging_mps_cap" type="integer | null">
  Hard ceiling on this number's outbound SMS throughput, in messages per second (`1`–`1000`). Sends that would exceed this rate are rejected with `429` and the `NUMBER_MPS_EXCEEDED` error code; retry after one second. Send `null` to remove the cap (uncapped by default).
</ParamField>

<ParamField body="monthly_spend_cap_cents" type="integer | null">
  Hard ceiling on this number's outbound SMS spend for the current UTC calendar month, in US cents (`1`–`100000000`, up to \$1,000,000). Sends that would exceed the remaining budget are rejected with `402` and the `NUMBER_MONTHLY_SPEND_CAP_EXCEEDED` error code. Send `null` to remove the cap (uncapped by default).
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl -X PUT https://api.orbit.devotel.io/api/v1/numbers/num_abc123 \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "label": "Main Support Line",
      "status": "active",
      "webhook_url": "https://yourapp.com/webhooks/inbound-v2",
      "forwarding_number": "+14155550111",
      "tags": ["support", "us-west"]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "id": "num_abc123",
      "number": "+14155550100",
      "label": "Main Support Line",
      "capabilities": ["sms", "voice", "mms"],
      "status": "active",
      "webhook_url": "https://yourapp.com/webhooks/inbound-v2",
      "forwarding_number": "+14155550111",
      "tags": ["support", "us-west"],
      "updated_at": "2026-03-08T12:10:00Z"
    },
    "meta": {
      "request_id": "req_num_005",
      "timestamp": "2026-03-08T12:10:00Z"
    }
  }
  ```
</ResponseExample>

***

## Release a Number

`DELETE /api/v1/numbers/{id}`

Release a number from your account. The number is immediately removed from your inventory and billing stops at the end of the current billing cycle.

<Warning>
  This action is irreversible. The number may be reassigned to another customer and cannot be guaranteed for repurchase.
</Warning>

<ParamField path="id" type="string" required>
  Number ID to release
</ParamField>

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

**Response:** `204 No Content`

***

## Port a Number

`POST /api/v1/numbers/porting`

Submit a request to port existing numbers from another carrier to Orbit.

<ParamField body="numbers" type="string[] | string" required>
  One or more phone numbers in E.164 format to port. Accepts a single string or an array.
</ParamField>

<ParamField body="currentCarrier" type="string" required>
  Name of the current carrier the numbers are leaving. You may send `carrier` as an alias; if both are present, `currentCarrier` wins.
</ParamField>

<ParamField body="authorizedSigner" type="string">
  Name of the person authorized to sign the Letter of Authorization on the current carrier account.
</ParamField>

<ParamField body="accountNumber" type="string">
  Account number with the current carrier.
</ParamField>

<ParamField body="accountPin" type="string">
  Account PIN or passcode, if the current carrier requires one to release the numbers.
</ParamField>

<ParamField body="loaFileUrl" type="string">
  Signed URL to the uploaded Letter of Authorization PDF. Omit to submit in manual mode, where our operations team processes the LoA by hand.
</ParamField>

<ParamField body="country" type="string">
  ISO 3166-1 alpha-2 country code of the numbers being ported (for example, `US`). Drives carrier selection. Omit to store the request without dispatching to a carrier.
</ParamField>

<ParamField body="contactName" type="string">
  Name of the contact for this porting request.
</ParamField>

<ParamField body="contactEmail" type="string">
  Email address for porting status updates.
</ParamField>

<ParamField body="serviceAddress" type="object">
  Service address on file with the current carrier. Some carriers reject ports when this does not match their records.

  <Expandable title="serviceAddress">
    <ParamField body="line1" type="string">Street address, line 1.</ParamField>
    <ParamField body="line2" type="string">Street address, line 2.</ParamField>
    <ParamField body="city" type="string">City.</ParamField>
    <ParamField body="state" type="string">State or province.</ParamField>
    <ParamField body="postalCode" type="string">Postal or ZIP code.</ParamField>
    <ParamField body="countryCode" type="string" required>ISO 3166-1 alpha-2 country code.</ParamField>
  </Expandable>
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl -X POST https://api.orbit.devotel.io/api/v1/numbers/porting \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "numbers": ["+14155551234", "+14155551235"],
      "currentCarrier": "Acme Telecom",
      "authorizedSigner": "John Doe",
      "accountNumber": "ACC-12345",
      "accountPin": "4821",
      "country": "US",
      "contactEmail": "ops@example.com"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={null}
  {
    "data": {
      "id": "port_abc123",
      "numbers": ["+14155551234", "+14155551235"],
      "status": "submitted",
      "submittedAt": "2026-03-08T12:00:00Z"
    },
    "meta": {
      "request_id": "req_port_001",
      "timestamp": "2026-03-08T12:00:00Z"
    }
  }
  ```
</ResponseExample>

<Note>
  A new request starts at `status: "submitted"`. From there it moves through
  `reviewing`, then `approved`, and finally `completed` once the numbers are
  live on Orbit; a port the carrier declines lands in `rejected`. Branch on
  these values — there is no `pending` state.

  Porting typically takes 7–14 business days depending on the country and carrier.
  You will receive a `number.ported` webhook event when the port is complete.
</Note>

***

## SMS Short Codes

Short codes are 3–6 digit numbers leased for high-volume application-to-person SMS programs (2FA, alerts, marketing). Acquiring one is a multi-week, carrier-vetted lifecycle: you open an **application**, attach a **program brief** that carriers review, submit it to the Common Short Code Administration (CSCA) / aggregator, track per-carrier vetting, and — once approved — provision the lease.

**Base path:** `/api/v1/numbers/short-codes`

An application moves through these statuses:

| Status            | Meaning                                                                                                        |
| ----------------- | -------------------------------------------------------------------------------------------------------------- |
| `draft`           | Application created; the program brief is still being assembled and can be edited.                             |
| `submitted`       | Forwarded to the CSCA / aggregator; the code is reserved and the brief is queued for carrier review.           |
| `carrier_vetting` | Under independent per-carrier review (in the US: AT\&T, T-Mobile, Verizon, US Cellular). Typically 6–10 weeks. |
| `provisioned`     | Approved and live — the lease window is open and inbound SMS on the code is recognised.                        |
| `rejected`        | The aggregator or a carrier rejected the program (see `rejection_reason`).                                     |
| `cancelled`       | You relinquished the application or lease.                                                                     |

<Note>
  Short codes are **leased, not owned** (3, 6, or 12-month terms with a CSCA quarterly minimum), so a provisioned application carries a `leaseEndsAt` renew-by date. This surface governs short-code **provisioning only** — outbound message sending is unchanged and continues through the Messaging API.
</Note>

Read operations (`list`, `get`, `timeline`) and the non-mutating `preflight` lints are available to any role on the organization. Tenant-initiated writes (`create`, `update brief`, `submit`, `cancel`) require the `numbers:write` scope and an `owner`, `admin`, or `developer` role. The carrier-confirmed transitions (`vetting`, `provision`, `reject`) relay an upstream decision and are restricted to `owner` / `admin`.

### List Short-Code Applications

`GET /api/v1/numbers/short-codes`

Return every short-code application for your organization.

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": [
      {
        "id": "scapp_abc123",
        "countryCode": "US",
        "leaseType": "dedicated",
        "selection": "vanity",
        "requestedCode": "247365",
        "leaseTermMonths": 12,
        "status": "carrier_vetting",
        "businessName": "Acme Inc.",
        "carrierVetting": [
          { "carrier": "AT&T", "status": "approved", "updatedAt": "2026-05-20T10:00:00Z" },
          { "carrier": "T-Mobile", "status": "pending", "updatedAt": "2026-05-12T10:00:00Z" }
        ],
        "createdAt": "2026-05-01T12:00:00Z",
        "updatedAt": "2026-05-20T10:00:00Z",
        "submittedAt": "2026-05-04T09:00:00Z"
      }
    ],
    "meta": {
      "request_id": "req_sc_001",
      "timestamp": "2026-06-01T12:00:00Z"
    }
  }
  ```
</ResponseExample>

### Create a Short-Code Application

`POST /api/v1/numbers/short-codes`

Open a new application in `draft`. Requires the `numbers:write` scope.

<ParamField body="countryCode" type="string" required>
  ISO 3166-1 alpha-2 country code for the code (e.g., `US`).
</ParamField>

<ParamField body="leaseType" type="string" required>
  `dedicated` (exclusive) or `shared` (multi-tenant).
</ParamField>

<ParamField body="selection" type="string" required>
  `random` (the CSCA assigns any free code) or `vanity` (a specific requested code at the higher vanity rate).
</ParamField>

<ParamField body="requestedCode" type="string">
  The desired 3–6 digit code. **Required** when `selection` is `vanity`; omit for `random`.
</ParamField>

<ParamField body="leaseTermMonths" type="integer" required>
  Lease term — one of `3`, `6`, or `12` months.
</ParamField>

<ParamField body="businessName" type="string" required>
  The brand / registrant name on the program (CSCA registrant).
</ParamField>

<ParamField body="programBrief" type="object" required>
  The CTIA-style program brief carriers vet:

  * `useCase` (string) — e.g. `2FA`, `Marketing`, `Alerts`.
  * `description` (string) — plain-English description of the program / call-to-action.
  * `sampleMessages` (string\[]) — 1–5 production sample messages.
  * `messageFrequency` (string) — how often subscribers receive messages (e.g. `5 msgs/week`).
  * `optInDescription` (string) — the consent flow carriers verify.
  * `supportContact` (string) — contact surfaced in the HELP response.
  * `privacyPolicyUrl` (string, optional) — public privacy-policy URL.
  * `termsUrl` (string, optional) — public terms-and-conditions URL.
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl -X POST https://api.orbit.devotel.io/api/v1/numbers/short-codes \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "countryCode": "US",
      "leaseType": "dedicated",
      "selection": "vanity",
      "requestedCode": "247365",
      "leaseTermMonths": 12,
      "businessName": "Acme Inc.",
      "programBrief": {
        "useCase": "2FA",
        "description": "One-time passcodes for Acme account sign-in.",
        "sampleMessages": ["Your Acme code is 123456. Reply STOP to opt out."],
        "messageFrequency": "Up to 5 msgs/week",
        "optInDescription": "Users opt in during checkout by checking the SMS consent box.",
        "supportContact": "support@acme.com"
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={null}
  {
    "data": {
      "id": "scapp_abc123",
      "countryCode": "US",
      "leaseType": "dedicated",
      "selection": "vanity",
      "requestedCode": "247365",
      "leaseTermMonths": 12,
      "status": "draft",
      "businessName": "Acme Inc.",
      "carrierVetting": [],
      "createdAt": "2026-06-01T12:00:00Z",
      "updatedAt": "2026-06-01T12:00:00Z"
    },
    "meta": {
      "request_id": "req_sc_002",
      "timestamp": "2026-06-01T12:00:00Z"
    }
  }
  ```
</ResponseExample>

### Get a Short-Code Application

`GET /api/v1/numbers/short-codes/{id}`

Fetch a single application by id.

<ParamField path="id" type="string" required>
  Short-code application id (e.g., `scapp_abc123`).
</ParamField>

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

### Get the Carrier-Vetting Timeline

`GET /api/v1/numbers/short-codes/{id}/timeline`

Return a structured, stage-by-stage view of the multi-week review (submitted → CSCA reservation → program-brief review → carrier vetting → provisioned), including a per-carrier snapshot and the calendar days elapsed since submission.

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "applicationId": "scapp_abc123",
      "status": "carrier_vetting",
      "stages": [
        { "stage": "submitted", "state": "completed", "label": "Submitted to CSCA", "expectedDurationDays": 1 },
        { "stage": "csca_reservation", "state": "completed", "label": "Short code reserved", "expectedDurationDays": 2 },
        { "stage": "program_brief_review", "state": "completed", "label": "Program brief review", "expectedDurationDays": 5 },
        { "stage": "carrier_vetting", "state": "current", "label": "Carrier vetting", "expectedDurationDays": 60, "detail": "1/4 carriers approved" },
        { "stage": "provisioned", "state": "pending", "label": "Provisioned (leased)", "expectedDurationDays": 0 }
      ],
      "carrierVetting": [
        { "carrier": "AT&T", "status": "approved", "updatedAt": "2026-05-20T10:00:00Z" }
      ],
      "elapsedDays": 28
    },
    "meta": {
      "request_id": "req_sc_003",
      "timestamp": "2026-06-01T12:00:00Z"
    }
  }
  ```
</ResponseExample>

### Update the Program Brief

`PATCH /api/v1/numbers/short-codes/{id}/brief`

Replace the program brief while the application is still in `draft`. Once submitted the brief is locked (carriers vet the snapshot) and this returns `409` — open a new application to change it. Requires `numbers:write`.

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

<ParamField body="programBrief" type="object" required>
  The full replacement program brief (same shape as on create).
</ParamField>

### Preflight a Program Brief

`POST /api/v1/numbers/short-codes/preflight`

Lint a program brief against the carrier rejection-pattern catalog **before** you open an application. Carriers reject roughly a third of first-round briefs on deterministic, well-documented grounds — SHAFT-C content, ambiguous calls-to-action, public URL shorteners, a missing `STOP` opt-out in the samples, and a call-to-action that omits the message-frequency, "Message and data rates may apply", `STOP`, or `HELP` disclosures — and each rejection restarts the multi-week vetting queue. This endpoint scores those patterns in milliseconds and returns per-field findings, each with the offending text fragment and a concrete rewrite.

Preflight runs entirely against the brief you send: nothing is stored, no application is created, and no message is sent. It is available to any role and does not require the `numbers:write` scope.

<Note>
  A `pass` verdict means "no known rejection patterns matched" — not "carriers will approve". Preflight catches the deterministic majority of rejections; the application still goes through the normal submit → carrier-vetting flow. Run it, fix the findings, then open (or update) the application.
</Note>

<ParamField body="programBrief" type="object" required>
  The program brief to lint (same shape as on create). Each field is checked defensively, so you can preflight a partial brief while you are still assembling it.
</ParamField>

<ParamField body="businessName" type="string">
  The brand / registrant name. When present, the linter also checks that at least one sample message identifies the sender — brand ambiguity is a top carrier rejection reason.
</ParamField>

The response `data` object contains:

* `score` (integer) — `0`–`100`; `100` means no known rejection patterns matched. Each finding deducts by severity (`error` −25, `warn` −8, `info` −2, floored at `0`).
* `verdict` (string) — the aggregate gate: `block` when any `error` finding is present, `warn` when the score is below `75`, otherwise `pass`.
* `findings` (array) — one entry per issue, each with a stable `ruleId` (e.g. `R-SC-SAMPLE-OPTOUT`), a `severity` (`error` | `warn` | `info`), the dot-path `field` it applies to, a human-readable `message`, an optional `match` (the exact offending text, for inline highlighting), and an optional `suggestion` (a rewrite that resolves it).
* `engine` (string) — the rule-engine version (e.g. `shortcode-preflight/v1`), so you can correlate scores over time.

<RequestExample>
  ```bash theme={null}
  curl -X POST https://api.orbit.devotel.io/api/v1/numbers/short-codes/preflight \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "businessName": "Acme",
      "programBrief": {
        "useCase": "Marketing",
        "description": "Weekend promotional offers for Acme subscribers who opt in at checkout.",
        "sampleMessages": ["Acme: 20% off this weekend only. Click here to shop."],
        "messageFrequency": "Up to 5 msgs/week",
        "optInDescription": "Users opt in on acme.com/join by checking the SMS consent box. Msg & data rates may apply. Reply STOP to cancel, HELP for help.",
        "supportContact": "support@acme.com",
        "privacyPolicyUrl": "https://acme.com/privacy",
        "termsUrl": "https://acme.com/terms"
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "score": 59,
      "verdict": "block",
      "findings": [
        {
          "ruleId": "R-SC-SAMPLES-COUNT",
          "severity": "warn",
          "field": "programBrief.sampleMessages",
          "message": "A single sample message reduces approval rate. Carriers vet 2-5 distinct samples covering the program's real scenarios.",
          "suggestion": "Provide 2-5 distinct sample messages (e.g. welcome, reminder, confirmation) so the carrier sees the full program."
        },
        {
          "ruleId": "R-SC-SAMPLE-CLICK-HERE",
          "severity": "warn",
          "field": "programBrief.sampleMessages[0]",
          "message": "Ambiguous call-to-action: \"click here\" is flagged by carrier filters. Name the destination instead.",
          "match": "Click here",
          "suggestion": "Replace \"click here\" with the destination noun (\"view your order\", \"open the form\")."
        },
        {
          "ruleId": "R-SC-SAMPLE-OPTOUT",
          "severity": "error",
          "field": "programBrief.sampleMessages",
          "message": "No sample message includes opt-out language. Carriers reject programs whose samples never show how a subscriber stops messages.",
          "suggestion": "Add \"Reply STOP to opt out.\" to at least one sample message."
        }
      ],
      "engine": "shortcode-preflight/v1"
    },
    "meta": {
      "request_id": "req_sc_005",
      "timestamp": "2026-06-01T12:00:00Z"
    }
  }
  ```
</ResponseExample>

### Preflight a Stored Application

`POST /api/v1/numbers/short-codes/{id}/preflight`

Run the same lint against an application you have already created, using its persisted program brief and registrant `businessName`. This is the convenient check to run on a `draft` right before you submit — no request body is needed. Available to any role; it does not mutate the application.

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

The response is identical to [Preflight a Program Brief](#preflight-a-program-brief) — a `score`, a `verdict`, the per-field `findings`, and the `engine` version.

<RequestExample>
  ```bash theme={null}
  curl -X POST https://api.orbit.devotel.io/api/v1/numbers/short-codes/scapp_abc123/preflight \
    -H "X-API-Key: dv_live_sk_your_key_here"
  ```
</RequestExample>

### Submit for Vetting

`POST /api/v1/numbers/short-codes/{id}/submit`

Submit a `draft` application to the CSCA / aggregator, transitioning it to `submitted`. Requires `numbers:write`.

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

### Record Carrier Vetting

`POST /api/v1/numbers/short-codes/{id}/vetting`

Record a per-carrier vetting decision relayed from the aggregator. The first call moves the application from `submitted` to `carrier_vetting` and seeds the per-carrier tracker. Restricted to `owner` / `admin`.

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

<ParamField body="carrier" type="string" required>
  Carrier name (e.g., `AT&T`).
</ParamField>

<ParamField body="status" type="string" required>
  `pending`, `approved`, or `rejected`.
</ParamField>

<ParamField body="note" type="string">
  Optional carrier-supplied note (e.g., conditional-approval or rejection detail).
</ParamField>

### Provision the Lease

`POST /api/v1/numbers/short-codes/{id}/provision`

Open the lease once carrier vetting is complete, transitioning `carrier_vetting` → `provisioned`. Stamps the assigned code and computes the renew-by date from the lease term. Restricted to `owner` / `admin`.

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

<ParamField body="assignedCode" type="string" required>
  The assigned 3–6 digit short code.
</ParamField>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "id": "scapp_abc123",
      "status": "provisioned",
      "assignedCode": "247365",
      "provisionedAt": "2026-06-01T12:00:00Z",
      "leaseEndsAt": "2027-06-01T12:00:00Z",
      "updatedAt": "2026-06-01T12:00:00Z"
    },
    "meta": {
      "request_id": "req_sc_004",
      "timestamp": "2026-06-01T12:00:00Z"
    }
  }
  ```
</ResponseExample>

### Reject an Application

`POST /api/v1/numbers/short-codes/{id}/reject`

Record an aggregator / carrier rejection (`submitted` | `carrier_vetting` → `rejected`). Restricted to `owner` / `admin`.

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

<ParamField body="rejectionReason" type="string" required>
  The reason supplied by the aggregator or carrier.
</ParamField>

### Cancel an Application

`DELETE /api/v1/numbers/short-codes/{id}`

Cancel / relinquish an application or an active lease (any non-terminal state → `cancelled`). Requires `numbers:write`.

<Warning>
  Cancelling a provisioned lease relinquishes the short code. Re-acquiring it later means a fresh application and the full carrier-vetting cycle.
</Warning>

<ParamField path="id" type="string" required>
  Short-code application id.
</ParamField>

***

## Hosted Messaging

Hosted messaging (also called "Hosted SMS" or "text-enabling") turns an existing landline, toll-free, or mobile number into an SMS-capable number **while its voice service stays with another carrier** — no porting required. This is the equivalent of the Twilio "Hosted Messaging" / Bandwidth "Hosted SMS" flow. You open an order, sign a Letter of Authorization (LOA) proving you control the number, submit it to the hosting carrier, and — once active — inbound SMS to the number is delivered to the route you configure.

**Base path:** `/api/v1/numbers/hosted-messaging`

<Note>
  Hosted messaging governs **inbound SMS only**. The number's voice service is untouched and stays with your current carrier, and outbound (MT) sending is unchanged — it continues through the Messaging API.
</Note>

An order moves through these statuses:

| Status       | Meaning                                                                                     |
| ------------ | ------------------------------------------------------------------------------------------- |
| `draft`      | Order created; awaiting the signed LOA.                                                     |
| `loa_signed` | The authorizing party signed the LOA in-platform. The carrier has not been notified yet.    |
| `submitted`  | The signed order was forwarded to the hosting carrier, which begins ownership verification. |
| `active`     | The carrier confirmed hosting; inbound SMS on the number now routes per `inboundRoute`.     |
| `rejected`   | The carrier or operations team rejected the order (see `rejectionReason`).                  |
| `cancelled`  | You cancelled the order before activation.                                                  |

Read operations (`list`, `get`) are available to any role on the organization. Writes (`create`, `sign LOA`, `submit`, `update route`, `cancel`) require the `numbers:write` scope and an `owner`, `admin`, or `developer` role.

### Inbound route

Several endpoints take an `inboundRoute` object describing where inbound SMS on the hosted number is delivered once the order is `active`:

<ParamField body="type" type="string" required>
  Delivery target type — one of `webhook`, `inbox`, or `messaging_service`.
</ParamField>

<ParamField body="target" type="string" required>
  The destination. For `webhook` this must be an `https` URL the platform POSTs each inbound message to; for `inbox` / `messaging_service` it is the id of the destination resource.
</ParamField>

### List Hosted-Messaging Orders

`GET /api/v1/numbers/hosted-messaging`

Return every hosted-messaging order for your organization.

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

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": [
      {
        "id": "hostedMessagingOrder_abc123",
        "phoneNumber": "+14155550100",
        "voiceCarrier": "Acme Telecom",
        "numberType": "landline",
        "status": "active",
        "inboundRoute": { "type": "webhook", "target": "https://yourapp.com/webhooks/sms" },
        "businessName": "Acme Inc.",
        "createdAt": "2026-06-08T12:00:00Z",
        "updatedAt": "2026-06-10T09:00:00Z"
      }
    ],
    "meta": {
      "request_id": "req_hm_001",
      "timestamp": "2026-06-10T09:00:00Z"
    }
  }
  ```
</ResponseExample>

### Create a Hosted-Messaging Order

`POST /api/v1/numbers/hosted-messaging`

Open a new order in `draft`. A second non-terminal order for the same number is refused with `409`. Requires the `numbers:write` scope.

<ParamField body="phoneNumber" type="string" required>
  The number to text-enable, in E.164 format. Its voice service stays with your current carrier.
</ParamField>

<ParamField body="voiceCarrier" type="string" required>
  Name of the carrier of record that keeps voice service for the number (informational, aids LOA matching).
</ParamField>

<ParamField body="numberType" type="string" required>
  Number class — one of `landline`, `tollfree`, or `mobile`. Drives carrier eligibility on the hosting side.
</ParamField>

<ParamField body="inboundRoute" type="object" required>
  Where inbound SMS is delivered once the order is active. See [Inbound route](#inbound-route).
</ParamField>

<ParamField body="businessName" type="string">
  The brand / business name on the carrier-of-record account, used for LOA matching.
</ParamField>

<RequestExample>
  ```bash theme={null}
  curl -X POST https://api.orbit.devotel.io/api/v1/numbers/hosted-messaging \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "phoneNumber": "+14155550100",
      "voiceCarrier": "Acme Telecom",
      "numberType": "landline",
      "inboundRoute": { "type": "webhook", "target": "https://yourapp.com/webhooks/sms" },
      "businessName": "Acme Inc."
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={null}
  {
    "data": {
      "id": "hostedMessagingOrder_abc123",
      "phoneNumber": "+14155550100",
      "voiceCarrier": "Acme Telecom",
      "numberType": "landline",
      "status": "draft",
      "inboundRoute": { "type": "webhook", "target": "https://yourapp.com/webhooks/sms" },
      "businessName": "Acme Inc.",
      "createdAt": "2026-06-08T12:00:00Z",
      "updatedAt": "2026-06-08T12:00:00Z"
    },
    "meta": {
      "request_id": "req_hm_002",
      "timestamp": "2026-06-08T12:00:00Z"
    }
  }
  ```
</ResponseExample>

### Get a Hosted-Messaging Order

`GET /api/v1/numbers/hosted-messaging/{id}`

Fetch a single order by id.

<ParamField path="id" type="string" required>
  Hosted-messaging order id (e.g., `hostedMessagingOrder_abc123`).
</ParamField>

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

### Sign the LOA

`POST /api/v1/numbers/hosted-messaging/{id}/loa/sign`

Capture an in-platform Letter of Authorization signature, transitioning the order `draft` → `loa_signed`. Requires the `numbers:write` scope.

<ParamField path="id" type="string" required>
  Hosted-messaging order id.
</ParamField>

<ParamField body="signerName" type="string" required>
  Full legal name of the party authorized on the carrier-of-record account.
</ParamField>

<ParamField body="signerEmail" type="string" required>
  Signer email address.
</ParamField>

<ParamField body="acknowledgement" type="string" required>
  The verbatim acknowledgement text the signer accepted (20–4000 characters), retained for the audit trail.
</ParamField>

<ParamField body="loaFileUrl" type="string">
  Optional signed URL of the signed LOA artefact. Must be an Orbit-issued Google Cloud Storage signed URL (`https://storage.googleapis.com/...`); other origins are rejected. Omit to have operations attach the LOA out-of-band.
</ParamField>

### Submit to the Carrier

`POST /api/v1/numbers/hosted-messaging/{id}/submit`

Forward the signed order to the hosting carrier, transitioning `loa_signed` → `submitted`. The LOA must be signed first. Requires the `numbers:write` scope.

<ParamField path="id" type="string" required>
  Hosted-messaging order id.
</ParamField>

### Update the Inbound Route

`PATCH /api/v1/numbers/hosted-messaging/{id}/route`

Change where inbound SMS is delivered, before the order is `active`. Once active the route is locked — open a new order to change live routing. Requires the `numbers:write` scope.

<ParamField path="id" type="string" required>
  Hosted-messaging order id.
</ParamField>

<ParamField body="inboundRoute" type="object" required>
  The replacement inbound route. See [Inbound route](#inbound-route).
</ParamField>

### Cancel an Order

`DELETE /api/v1/numbers/hosted-messaging/{id}`

Cancel an order that has not reached a terminal state (any non-terminal status → `cancelled`). Requires the `numbers:write` scope.

<ParamField path="id" type="string" required>
  Hosted-messaging order id.
</ParamField>

***

## Number Types

| Type         | Description                                | Availability   |
| ------------ | ------------------------------------------ | -------------- |
| `local`      | Geographic number tied to a city or region | 60+ countries  |
| `toll_free`  | Free for callers, costs billed to you      | US, CA, UK, AU |
| `mobile`     | Mobile number for SMS and voice            | 40+ countries  |
| `short_code` | 5-6 digit number for high-volume SMS       | US, CA, UK     |

## Number Capabilities

| Capability | Description                            |
| ---------- | -------------------------------------- |
| `sms`      | Send and receive SMS messages          |
| `voice`    | Make and receive voice calls           |
| `mms`      | Send and receive MMS (images, media)   |
| `whatsapp` | Register as a WhatsApp Business number |

## Examples

### Node.js

```javascript theme={null}
const orbit = new Devotel({ apiKey: 'dv_live_sk_xxxx' })

const available = await orbit.numbers.search({
  country: 'US',
  type: 'local',
  capabilities: ['sms', 'voice'],
  area_code: '415',
})

const purchased = await orbit.numbers.purchase({
  number: available.data[0].number,
})
```

<Note>
  A first-party Python SDK is on the roadmap but not yet shipped. Call the REST endpoints above with `requests` / `httpx` / any HTTP client.
</Note>
