# nothanumber phone numbers, SMS and voice for AI agents

**Audience: an LLM or coding agent integrating this API.** Read the whole file once, then use the
section headings as a lookup table. Everything here is meant to be unambiguous: where something is
not built yet it says `PLANNED` and you must not call it.

- Document version: 2026-11-08 (updated with prepaid balance fields and voice pricing)
- "The administrator" below means the nothanumber staff who set up your account (support@nothanumber.com).
- Base URL: `https://app.nothanumber.com/api/v1/wholesale`
- Format: JSON request and response bodies, UTF-8, HTTPS only
- Money: **integers in minor units** (pence for GBP). `priceMinor: 3` means £0.03. Never use floats.
- Time: ISO 8601 UTC strings (`2026-11-08T21:00:00.000Z`).

## 0. Status: what exists today

| Capability | Status | Notes |
|---|---|---|
| Send an SMS (text) | **LIVE** | `POST /sms` |
| Receive SMS and delivery receipts | **LIVE** | poll `GET /sms`, or webhooks |
| List and read messages | **LIVE** | `GET /sms`, `GET /sms/{id}` |
| List your numbers and senders | **LIVE** | `GET /numbers` (each says whether it can do `sms` and `voice`) |
| Check balance | **LIVE** | `GET /balance` |
| Log in with a simple API key | **LIVE** | `Authorization: Bearer …` |
| Log in with signed requests | **LIVE** | more secure alternative (section 3) |
| Get a phone number | **LIVE, by hand** | an administrator assigns it. You cannot buy one by API yet |
| Voice-enable a number | **LIVE as a setting** | the administrator turns it on per number, and only on a number assigned to you; it is shown as `voice: true` in `GET /numbers`. Calls themselves are PLANNED |
| Place and receive voice calls | **PLANNED** | section 9. `/voice/*` returns 404 until it ships |
| Event stream (WebSocket) | **PLANNED** | section 10. Use polling or webhooks today |
| Buy a number by API, top up by API | **PLANNED** | section 10 |

If a path is marked `PLANNED` and you call it, expect `404`. Treat a `404` on `/voice/*` as "not
available yet", not as a bug in your code.

## 1. The offer in six sentences

1. nothanumber sells **real phone numbers, SMS and (soon) voice calls** to software, including AI agents.
2. You are an **invited customer**. Accounts are created by an administrator; there is no public sign-up.
3. **You pre-pay.** Charges come out of a wallet. When the wallet cannot cover a message, the API
   refuses it with `402`. Nothing is billed on credit unless the administrator has set a credit limit.
4. An SMS is charged **per segment** at the price agreed for your account. The price is returned in
   every send response, so you always know what you paid.
5. A **two-way number** is a normal phone number that can send and receive texts, and, if the
   administrator has turned **voice** on for it, make and receive calls. **Calls can only ever be made
   from a number we assigned to you**, so you cannot spoof a caller ID, and without an assigned
   two-way number there is no voice at all.
6. An **alphanumeric sender ID** (for example `ACME`) is a brand name you may send *from*. It is
   one-way: people cannot reply to it, and it cannot make calls.

## 1a. Getting access (invite-only for now)

There is no public sign-up while we are in a controlled launch. To request an account, email
**support@nothanumber.com** with: who you are (company or project), what your agent will do (for
example appointment reminders, one-time passcodes, customer service calls), the countries you will
send to, and expected volume. We may ask for identity and business details before activating an
account. You will receive an API key and at least one assigned number. Pricing is agreed per account.

## 2. Quick start (do these in order)

You need two things from the administrator first: an **API key** (`nthk_live_<id>_<secret>`) and at
least one **sender**: a two-way number (e.g. `+447537000002`) or an approved sender ID.

Set the key in an environment variable. Never put it in code, logs or a URL.

```sh
export NTH_API_KEY='nthk_live_…'
export NTH=https://app.nothanumber.com/api/v1/wholesale
```

**Step 1. Check the key works and see your money.** Free and read-only. Do this first.
```sh
curl -sS "$NTH/balance" -H "Authorization: Bearer $NTH_API_KEY"
# {"balanceMinor":5000,"creditLimitMinor":0,"availableMinor":5000,"currency":"GBP",
#  "lowBalance":false,"lowBalanceThresholdMinor":500,"topUpUrl":null}
```
`200` means the key is good. `401` means it is not (section 7).

**Step 2. See which senders you can use and what each can do.**
```sh
curl -sS "$NTH/numbers" -H "Authorization: Bearer $NTH_API_KEY"
# {"numbers":[{"e164":"+447537000002","countryCode":"GB","twoWay":true,"sms":true,"voice":false}],
#  "senderIds":[{"senderId":"ACME","twoWay":false,"sms":true,"voice":false}]}
```
Use one of these values as `from`. Anything else gets `403`.

**Step 3. Send one SMS to a phone you control.**
```sh
curl -sS -X POST "$NTH/sms" \
  -H "Authorization: Bearer $NTH_API_KEY" -H "content-type: application/json" \
  -d '{"from":"+447537000002","to":"+447700900123","body":"Hello from my agent","clientRef":"test-1"}'
# 201 {"id":"6f1c…","status":"SENT","from":"+447537000002","to":"+447700900123",
#      "segments":1,"encoding":"GSM7","priceMinor":3,"currency":"GBP",
#      "availableMinor":4997,"lowBalance":false}
```

**Step 4. Confirm delivery.** `status` moves `SENT` → `DELIVERED` (or `FAILED`).
```sh
curl -sS "$NTH/sms/6f1c…" -H "Authorization: Bearer $NTH_API_KEY"
```

**Step 5. Read replies.** Text your number from your phone, then:
```sh
curl -sS "$NTH/sms?direction=inbound&limit=10" -H "Authorization: Bearer $NTH_API_KEY"
```

## 3. Authentication

Two ways. Use **one** per request. Both are checked against the database on every request, so
revoking a credential takes effect on the very next call.

### 3a. API key (simplest; use this unless told otherwise)
```
Authorization: Bearer nthk_live_<id>_<secret>
```
The key is shown to you **once**; we keep only a hash. Treat it like a password: anyone holding it can
spend your balance. If it leaks, ask the administrator to revoke it and issue a new one.

### 3b. Signed requests (more secure; optional)
For when you can hold a private key. Send four headers on every request: `X-Nth-Key-Id`,
`X-Nth-Timestamp` (Unix seconds, within 5 minutes), `X-Nth-Nonce` (16-64 chars of `A-Za-z0-9_-`, never
reused) and `X-Nth-Signature` (base64 Ed25519 / ECDSA P-256 signature, or HMAC-SHA256, of this string
joined with `\n`, no trailing newline):
```
NTH-SIG-V1
<HTTP METHOD, uppercase>
<path plus query string exactly as sent, e.g. /api/v1/wholesale/sms?limit=10>
<timestamp>
<nonce>
<lowercase hex SHA-256 of the exact request body bytes; SHA-256 of "" for GET>
```
Prefer the API key unless you can protect a private key.

### Auth failures are deliberately vague
Every authentication failure is the same response: `401 {"error":"Request signature not accepted"}`.
Do not try to tell the causes apart from the response. Check, in this order: the key is complete and
unmodified; it is sent as `Authorization: Bearer <key>` (not in a URL or other header); you did not
also send a signature for an API key; the administrator has not revoked, paused or expired it.

### Reference helper (copy this)
```js
// Node 18+. Reads the key from the environment.
const BASE = "https://app.nothanumber.com/api/v1/wholesale";
const KEY = process.env.NTH_API_KEY;
export async function nth(method, path, payload) {       // path like "/sms" or "/sms?limit=10"
  const res = await fetch(BASE + path, {
    method,
    headers: { authorization: `Bearer ${KEY}`, ...(payload ? { "content-type": "application/json" } : {}) },
    body: payload ? JSON.stringify(payload) : undefined,
  });
  return { status: res.status, retryAfter: res.headers.get("retry-after"), json: await res.json().catch(() => null) };
}
```
```python
import os, requests
BASE = "https://app.nothanumber.com/api/v1/wholesale"
def nth(method, path, payload=None):
    return requests.request(method, BASE + path, json=payload,
                            headers={"Authorization": f"Bearer {os.environ['NTH_API_KEY']}"}, timeout=30)
```

## 4. Concepts you must get right

- **Sender** = what you put in `from`. Either a *two-way number* (E.164, e.g. `+447537000002`) or an
  *alphanumeric sender ID* (e.g. `ACME`, up to 11 letters, digits or spaces). Only senders the
  administrator assigned to your account work.
- **E.164** = international phone format: `+`, country code, number, no spaces. `+447700900123`.
  `07700 900123` is **wrong**; always include the country code.
- **Capabilities.** Each entry in `GET /numbers` has `sms` and `voice` booleans. `voice: true` means the
  number may place and receive calls (once voice ships). Sender IDs are always `voice: false`. Calls
  can only be made from a number assigned to your account with `voice: true`; see section 9.
- **Segment.** One billed SMS unit. A plain-text (GSM-7) message is 160 characters, or 153 per part
  when longer. A message with emoji or other non-GSM characters (UCS-2) is 70, or 67 per part. **You
  may send 1 to 3 segments**: up to 459 plain characters, or 201 with emoji. Longer is refused with `400`.
- **Wallet.** `balanceMinor` is your balance. `creditLimitMinor` is how far below zero you may go
  (normally `0`: you pre-pay). **`availableMinor = balanceMinor + creditLimitMinor` is what you can
  spend.** If `availableMinor` is less than the price, the send is refused.
- **`clientRef`.** Your own label (up to 100 characters) echoed back on the message and its receipts.
  It is **not** an idempotency key (section 8).

## 5. Endpoint reference (all LIVE)

All paths are relative to the base URL. All need authentication (section 3).

### `POST /sms`: send one SMS
Request:
```json
{ "from": "+447537000002", "to": "+447700900123", "body": "Your code is 123456", "clientRef": "order-42" }
```
| Field | Rules |
|---|---|
| `from` | required; one of your senders (section 4). Case-insensitive for sender IDs |
| `to` | required; E.164 international number. Premium-rate and special-service numbers are refused with `400` |
| `body` | required; 1 to 3 segments (section 4) |
| `clientRef` | optional; up to 100 characters |

Success `201`:
```json
{ "id": "6f1c…", "status": "SENT", "from": "+447537000002", "to": "+447700900123",
  "segments": 1, "encoding": "GSM7", "priceMinor": 3, "currency": "GBP",
  "availableMinor": 4997, "lowBalance": false }
```
`availableMinor` is what you can still spend after this message. `lowBalance` is `true` when it is at
or below the low-balance threshold (500 minor units, i.e. £5.00, unless agreed otherwise): **when you
see it, ask a human to top up soon**, before sends start failing.

The charge is taken when the gateway accepts the message. A later delivery failure is **not refunded**.
Sending to another nothanumber number is delivered internally.

### `GET /sms/{id}`: one message
Returns `{ "message": <Message> }`, or `404` if it is not yours.

### `GET /sms`: list messages, newest first
Query: `direction=inbound|outbound`, `since=<ISO date>`, `limit=1..100` (default 50), `cursor=<id>`.
Returns `{ "messages": [<Message>, …], "nextCursor": "<id>" | null }`. Pass `nextCursor` back as
`cursor` to get the next page; `null` means no more. This is also how you **recover anything a webhook
missed**.

### Message object
```json
{ "id": "6f1c…", "direction": "outbound", "from": "ACME", "to": "+447700900123",
  "body": "Your code is 123456", "status": "DELIVERED", "segments": 1, "encoding": "GSM7",
  "priceMinor": 3, "currency": "GBP", "clientRef": "order-42", "errorCode": null,
  "sentAt": "2026-11-08T12:00:00.000Z", "deliveredAt": "2026-11-08T12:00:04.000Z" }
```
`direction` is `outbound` or `inbound`. `status` is one of:

| status | meaning |
|---|---|
| `QUEUED` | accepted, not yet with the network |
| `SENT` | with the network, waiting for a receipt |
| `DELIVERED` | delivered to the handset |
| `FAILED` | not delivered; see `errorCode` |
| `RECEIVED` | an inbound text (direction `inbound`) |

### `GET /numbers`: your senders and what each can do
```json
{ "numbers":   [ { "e164": "+447537000002", "countryCode": "GB", "twoWay": true,  "sms": true, "voice": true } ],
  "senderIds": [ { "senderId": "ACME",                           "twoWay": false, "sms": true, "voice": false } ] }
```
Only active numbers and un-revoked sender IDs are listed.

### `GET /balance`
```json
{ "balanceMinor": 4950, "creditLimitMinor": 0, "availableMinor": 4950, "currency": "GBP",
  "lowBalance": false, "lowBalanceThresholdMinor": 500, "topUpUrl": null }
```
A negative `balanceMinor` is money you owe (only possible with a credit limit). `lowBalance` is true
when `availableMinor` is at or below `lowBalanceThresholdMinor`. `topUpUrl` is a link where the
account owner can add money, or `null` if there is none yet (then ask the administrator).

## 6. Pricing and billing

- **SMS out:** your agreed price per segment for the destination country, taken when the message is
  accepted. The exact charge is `priceMinor` in the send response.
- **SMS in:** free unless your agreement says otherwise.
- **Numbers:** a monthly rental, usually taken from the wallet when the number is assigned (the
  administrator can waive the first month) and then monthly. Turning voice on does not change the rental.
- **Voice (PLANNED):** charged at **the same per-minute rates as the nothanumber app**, by destination
  (including the on-net rate for calls to other nothanumber numbers). Calls are billed in **whole
  minutes, rounded up, with a one-minute minimum for any answered call**. Calls that are not answered
  are not charged. **There is no monthly fee for enabling voice**: you pay the number's monthly rental
  and the per-minute call charges, nothing else.
- If a charge cannot be covered the request is refused (`402`); you are never silently overdrawn.
- Your price list is agreed with the administrator. There is no public price list yet.

## 7. Errors: what each means and what to do

Errors are JSON: `{ "error": "<message or object>", …details }`.

| HTTP | Meaning | What you should do | Retry? |
|---|---|---|---|
| 400 | A field is invalid, or the message is over 3 segments | Fix the request using the `error` details | No |
| 401 | Not authenticated (always the same vague message) | Check the key (section 3). Do not loop | No |
| 402 | Not enough available balance. Body has `code` (`wallet_empty` for a prepaid account, `credit_limit_reached` for one on credit), `retryable: false`, `balanceMinor`, `creditLimitMinor`, `availableMinor`, `requiredMinor`, `currency`, `topUpUrl` (or `null`) and `nextStep` | **Stop sending.** Tell a human to top up the wallet (use `topUpUrl` if present), then retry. Do not retry in a loop | After a top-up |
| 403 | `from` is not one of your senders | Call `GET /numbers` and use a listed value | No |
| 404 | Not found, or not yours | Check the id | No |
| 422 | No price is configured for that destination | Ask the administrator to add a price | No |
| 429 | Rate limit, or another send from your account is in progress. Has a `Retry-After` header (seconds) | Wait `Retry-After`, then retry the same request | **Yes** |
| 502 | Our SMS gateway is down. **Nothing was sent or charged** | Retry with exponential backoff (1s, 2s, 4s … up to 5 times) | **Yes** |
| 503 | The API is switched off or briefly unavailable | Retry later with backoff | Yes |

## 8. Limits, retries and idempotency

- **Rate limit:** 60 requests per minute per account by default (higher on request).
- **Sends are processed one at a time per account.** Send sequentially, or treat `429` as normal and retry.
- **There is no idempotency key.** If a `POST /sms` times out or you lose the response, **do not just
  resend**: you may send, and pay for, the text twice. First look for it:
  `GET /sms?direction=outbound&since=<a minute before you sent>` and match on `clientRef`, `to` and `body`.
  Put a unique `clientRef` on every send so this lookup is reliable.
- A `502` means nothing was sent, so resending is safe. A `429` means the request was not processed.

## 9. Voice (PLANNED: draft contract, do not call yet)

> Everything in this section is a **draft design** so you can plan. These endpoints **do not exist
> yet** and return `404`. Details may change before launch; this section will be updated when voice ships.

**Model.** You never handle phone signalling. You ask for a call over HTTP, then open **one outgoing
WebSocket** that carries the call's audio both ways. It works from a laptop behind a home router: no
inbound ports, no VPN, no SIP.

**Prerequisite and the caller ID rule (a security rule, not negotiable).** You can only call **from a
two-way number that we assigned to your account, with `voice: true` in `GET /numbers`**. Therefore:
- **No assigned two-way number means no voice at all.** Ask the administrator for a number first,
  then ask them to enable voice on it (a per-number switch; the monthly rental is unchanged).
- **You cannot choose or change the caller ID.** The caller ID of every call is the number you called
  `from`, and `from` must be one of your voice-enabled numbers. There is no caller ID override, no
  "bring your own number", and no anonymous or withheld caller ID. A sender ID such as `ACME` can
  never make calls.
- Anything else in `from` is refused with `403` and a `code`: `from_not_permitted` (not one of your
  numbers, or not active) or `voice_not_enabled` (your number, but voice is off: ask the
  administrator). Do not retry with another value to "find one that works".

**Place a call.**
```
POST /voice/calls
{ "from": "+447537000002", "to": "+447700900123", "clientRef": "call-1",
  "audio": { "format": "pcm16_16000" } }
201 { "id": "call_…", "status": "QUEUED", "from": "…", "to": "…", "holdMinor": 150, "currency": "GBP" }
```
`audio.format` is `pcm16_16000` (16-bit little-endian PCM, 16 kHz, mono; best for speech models) or
`mulaw_8000` (G.711 µ-law, 8 kHz, mono; the phone network's native format).

**Open the audio stream.**
```
POST /stream-token   { "purpose": "call", "callId": "call_…" }
200 { "url": "wss://…", "token": "…", "expiresInSeconds": 60 }
```
Open the WebSocket to `url` with header `Sec-WebSocket-Protocol: nth.v1, <token>`. The token works once and
expires in 60 seconds. **Never put your API key in a WebSocket URL.**

**Messages are JSON text frames.**
Server to you:
```json
{ "event": "started", "callId": "call_…" }
{ "event": "media", "seq": 42, "audio": "<base64 audio in the call's format>" }
{ "event": "dtmf", "digit": "5" }
{ "event": "ended", "reason": "completed", "durationSeconds": 73, "priceMinor": 120, "currency": "GBP" }
```
You to server:
```json
{ "event": "media", "audio": "<base64 audio>" }
{ "event": "clear" }
{ "event": "dtmf", "digits": "123#" }
{ "event": "hangup" }
```
`clear` drops any audio you queued but that has not played yet. **Use it when the caller interrupts
(barge-in)** so your agent stops talking immediately. Send audio in chunks of about 20 ms and keep
the stream real-time; sending much faster than real time is not buffered.
`ended.reason` is one of `completed`, `no_answer`, `busy`, `failed`, `hangup`, `insufficient_balance`,
`max_duration`.

**Money and limits (voice).** When a call starts we **hold** its maximum cost from your wallet, then
release the unused part when it ends. A call ends by itself when its hold is used up
(`insufficient_balance`). At first each account can run **one call at a time**. Place a call when
`availableMinor` is too low and you get `402`, exactly as with SMS.

**Incoming calls.** When someone calls a voice-enabled number you receive a `call.incoming` event
(`{ callId, from, to }`) on the event stream (section 10) and have a short time to answer:
```
POST /voice/calls/{id}/answer     then open the audio stream as above
POST /voice/calls/{id}/reject
```
**Read calls.** `GET /voice/calls/{id}` and `GET /voice/calls?since=…&cursor=…`, shaped like the SMS reads.

## 10. Other PLANNED features

- **Event stream.** One outgoing WebSocket (`POST /stream-token` with `{"purpose":"events"}`) that
  delivers inbound texts, delivery receipts and call events, so a laptop needs no public webhook. Events
  are stored for 48 hours; reconnect with `?after=<eventId>` or poll `GET /events?after=<eventId>` to
  catch up on anything missed while asleep or offline.
- **Buy and release numbers by API**, paid from the wallet.
- **Card top-up by link.** `topUpUrl` already appears in `GET /balance` and in `402` responses but is
  `null` until card top-up is switched on; until then the administrator adds money for you.
- **Per-key daily spend cap.** Hitting it will return `429` with the code `key_spend_cap_reached` so
  you do not mistake it for an empty wallet (`402`, `wallet_empty`).
- **MCP server** wrapping these endpoints as tools, an OpenAPI file and `llms.txt`.

Until then use **polling** or **webhooks**.

## 11. Receiving texts and delivery receipts today

**Option A: poll (works from anywhere, nothing to host).** Every few seconds call
`GET /sms?direction=inbound&since=<time of the newest message you have seen>` and de-duplicate on `id`.
Delivery receipts: re-read the outbound message with `GET /sms/{id}` until `status` is `DELIVERED` or `FAILED`.

**Option B: webhooks.** The administrator sets a callback URL (public `https://`, port 443; private,
Tailscale `100.x` and localhost addresses are refused). We `POST` JSON there:
```json
{ "type": "message.status",  "id": "6f1c…", "clientRef": "order-42", "status": "DELIVERED",
  "from": "ACME", "to": "+447700900123", "deliveredAt": "…", "errorCode": null, "timestamp": "…" }
{ "type": "message.inbound", "id": "a81d…", "from": "+447700900300", "to": "+447537000002",
  "body": "STOP", "segments": 1, "receivedAt": "…", "timestamp": "…" }
```
Headers: `x-nothanumber-event`, `x-nothanumber-timestamp` (Unix seconds),
`x-nothanumber-signature: sha256=<hex HMAC-SHA256(webhook secret, timestamp + "." + rawBody)>`.
Verify the signature over the **raw body** before trusting it, compare in constant time, and reject
timestamps over 5 minutes old. Reply `2xx` within 5 seconds. Failures retry after 1, 5, 15, 60 and 180
minutes, then are dropped, so **de-duplicate on `id`** and run the polling call now and then to catch gaps.
If your agent runs on a laptop, use polling.

## 12. Checklist before you call it done

1. `GET /balance` returns `200` with the key from an environment variable (not hard-coded).
2. `GET /numbers` was called and `from` comes from that list.
3. Numbers are E.164 (`+` and country code). Money is integer minor units.
4. Every `POST /sms` has a unique `clientRef`.
5. `429` and `502/503` are retried with backoff; `400/401/402/403/422` are **not** retried blindly.
6. A timed-out `POST /sms` is reconciled by listing messages, not blindly resent.
7. On `402` the agent stops and asks a human to top up; it does not loop. It also watches
   `lowBalance` and asks for a top-up before the wallet is empty.
8. Replies and receipts are read by polling, or by a verified webhook with de-duplication on `id`.
9. The first real test goes to a phone you control, one message.
10. You honour `STOP` and other opt-outs on your side.

## 13. Common mistakes

- Sending from a number you merely own elsewhere. Only senders listed by `GET /numbers` work.
- Using `07700…` instead of `+447700…`.
- Putting the key in a URL, a query string, logs or a repository.
- Re-serialising the JSON after signing it (signed requests only).
- Treating `priceMinor: 3` as £3. It is 3 pence.
- Expecting a reply to an alphanumeric sender ID. It is one-way.
- Calling `/voice/*` today. It is not live; it returns `404`.
- Trying to call "from" a sender ID, a number you own elsewhere, or any value that is not one of your
  voice-enabled numbers. The caller ID cannot be set to anything else.
- Looping on `402`. Only a human top-up fixes it.

## 14. Machine-readable endpoint index

```json
{
  "baseUrl": "https://app.nothanumber.com/api/v1/wholesale",
  "auth": { "bearer": "Authorization: Bearer nthk_live_<id>_<secret>", "signed": "X-Nth-* headers, see section 3b" },
  "moneyUnit": "minor units (integers)",
  "endpoints": [
    { "method": "GET",  "path": "/balance",  "status": "live" },
    { "method": "GET",  "path": "/numbers",  "status": "live" },
    { "method": "POST", "path": "/sms",      "status": "live" },
    { "method": "GET",  "path": "/sms",      "status": "live" },
    { "method": "GET",  "path": "/sms/{id}", "status": "live" },
    { "method": "POST", "path": "/stream-token",         "status": "planned" },
    { "method": "GET",  "path": "/events",               "status": "planned" },
    { "method": "POST", "path": "/voice/calls",          "status": "planned" },
    { "method": "GET",  "path": "/voice/calls",          "status": "planned" },
    { "method": "GET",  "path": "/voice/calls/{id}",     "status": "planned" },
    { "method": "POST", "path": "/voice/calls/{id}/answer", "status": "planned" },
    { "method": "POST", "path": "/voice/calls/{id}/reject", "status": "planned" }
  ],
  "retryable": [429, 502, 503],
  "neverRetryBlindly": [400, 401, 402, 403, 404, 422]
}
```

## 15. Glossary

- **E.164**: international phone number format, `+<country code><number>`.
- **Minor units**: the smallest currency unit (pence, cents). Money in this API is always an integer of these.
- **Segment**: one billed SMS unit (section 4).
- **Two-way number**: a number that can send and receive texts (and calls, if voice is enabled).
- **Sender ID**: a brand name used as the sender of an SMS; one-way.
- **Wallet / balance**: prepaid money from which charges are taken.
- **Hold**: money reserved while a call is in progress, released when it ends.
- **DTMF**: the tones a caller makes by pressing keypad digits.
- **Barge-in**: the caller speaking over your agent; use `clear` to stop your audio.
- **Webhook**: a message we send to your web address when something happens.

