Numbers and caller IDs
Rent a phone number for a customer, or display a number they already own. Both rent monthly against your balance, and both are listed here.
Which one to sell
A number is one Kleos obtains in your customer’s country. It is the default, and the only option when your customer has no business line of their own. $15 a month.
A caller ID displays your customer’s existing business number instead. Nothing is bought; their ownership of it is verified. Sell this one when their prospects already have the number saved — the pickup rate is the whole argument. $12 a month.
What you are charged, and when
The first month is taken when the order succeeds, and nothing is charged if nothing was obtained. After that it renews monthly on the same day. A number rented on the 31st renews on the 30th, the 28th or the 31st — never on the 1st of the following month.
Coverage is checked before your balance. If we cannot obtain a number in that country, you are told that instead of being sent to top up for a country we could not have served anyway.
What happens if the balance runs out
A number is never taken away without warning. There are three steps, and each one is an event:
- Warned. Your balance will not cover the next renewal. Nothing is at risk yet and the number keeps working. You get
number.payment_due. - Grace. A renewal could not be taken. The number keeps working for another 14 days, and the response and event now carry
release_due_on— the exact day it would be lost. Top up before then and everything is stood back down, including on the last morning. - Released. The grace period ran out. The number goes back to the pool and
number.releasedis sent.
A release is permanent. There is no undo, no restore and no appeal — somebody else rents the number next, and anything your customer printed it on is pointing at a stranger. Treat number.payment_due as the one Kleos event worth paging somebody on.
For the same reason, deleting a number refuses on the URL alone. Send confirm_phone_number set to the exact number: a DELETE can be produced by a retry loop or a copied command, but the number in the body can only be produced by somebody who read which number this is.
In test mode
Ordering returns a reserved, unroutable number and moves no money, so you can build the whole flow — the list, the ladder fields, the release — without owning anything.
The numbers and caller IDs this customer rents
GET/v1/customers/{id}/numbers
Includes released ones. That is deliberate: a released number is the only permanent thing this API does, and it is the record you will need the first time a customer asks what happened to theirs. Read `release_due_on` — when it is set, the number is lost on that date unless the balance covers the rent before then.
Permission: numbers:read
Parameters
| Name | In | Type | |
|---|---|---|---|
id * | path | string | Kleos id or your `external_ref`. |
Response
{
"object": "list",
"data": [
{
"object": "number",
"id": "6f1b9c22-4e0a-4a77-9c31-2d5f8ab10e44",
"customer_id": "0f2a6c4e-96a2-4f2e-8f2b-6c1a0e73d8b1",
"kind": "number",
"country": "US",
"phone_number": "+13105550142",
"status": "active",
"monthly_cents": 1500,
"currency": "usd",
"next_charge_on": "2026-09-06",
"release_due_on": null,
"released_at": null,
"release_reason": null,
"mode": "live",
"created_at": "2026-08-06T09:12:44.000Z"
}
],
"has_more": false
}Refusals
| Status | Code | When |
|---|---|---|
| 404 | customer_not_found | No customer with that id in this mode. |
One rented number
GET/v1/customers/{id}/numbers/{number_id}
The same object the list returns, for a single rental. Useful after an order, and after a release: a released rental stays readable here with its `released_at` and `release_reason`.
Permission: numbers:read
Parameters
| Name | In | Type | |
|---|---|---|---|
id * | path | string | Kleos id or your `external_ref`. |
number_id * | path | string | The rental id. |
Response
{
"object": "number",
"id": "6f1b9c22-4e0a-4a77-9c31-2d5f8ab10e44",
"customer_id": "0f2a6c4e-96a2-4f2e-8f2b-6c1a0e73d8b1",
"kind": "number",
"country": "US",
"phone_number": "+13105550142",
"status": "active",
"monthly_cents": 1500,
"currency": "usd",
"next_charge_on": "2026-09-06",
"release_due_on": null,
"released_at": null,
"release_reason": null,
"mode": "live",
"created_at": "2026-08-06T09:12:44.000Z"
}Refusals
| Status | Code | When |
|---|---|---|
| 404 | number_not_found | No such number for this customer in this mode. |
Rent a number, or display your customer's own
POST/v1/customers/{id}/numbers
`kind: "number"` rents one in the customer's country at $15/month. `kind: "caller_id"` displays a number your customer already owns, at $12/month — send it as `display_number`. The first month is charged on success; nothing is charged if nothing was obtained. Coverage is checked before your balance, so you are never sent to top up for a country we could not have served anyway. Send an `Idempotency-Key`: this is the one endpoint where a retry can cost you a second number and a second monthly bill.
Permission: numbers:order
Parameters
| Name | In | Type | |
|---|---|---|---|
id * | path | string | Kleos id or your `external_ref`. |
Idempotency-Key | header | string | Your own unique string. Replaying it returns the first response for 24 hours. |
Body
| Field | Type | |
|---|---|---|
kind | string | "number" (default) or "caller_id". |
country | string | ISO-2. Defaults to the customer's own country. |
display_number | string | Required for `caller_id` — your customer's own number, in E.164. |
Response
{
"object": "number",
"id": "6f1b9c22-4e0a-4a77-9c31-2d5f8ab10e44",
"customer_id": "0f2a6c4e-96a2-4f2e-8f2b-6c1a0e73d8b1",
"kind": "number",
"country": "US",
"phone_number": "+13105550142",
"status": "active",
"monthly_cents": 1500,
"currency": "usd",
"next_charge_on": "2026-09-06",
"release_due_on": null,
"released_at": null,
"release_reason": null,
"mode": "live",
"created_at": "2026-08-06T09:12:44.000Z",
"charged_cents": 1500
}Refusals
| Status | Code | When |
|---|---|---|
| 404 | customer_not_found | No customer with that id in this mode. |
| 400 | invalid_number_kind | Send "number" or "caller_id". |
| 422 | number_unavailable_in_country | We cannot obtain a number there today. The message says what is missing and who owes it. |
| 409 | number_already_rented | This customer already has one of these. Release it first, or you would be billed for both. |
| 402 | insufficient_balance | The first month cannot be covered. Nothing was obtained. |
| 422 | number_not_obtained | Nothing was obtained and nothing was charged. |
Give a number back — permanently
DELETE/v1/customers/{id}/numbers/{number_id}
**There is no undo.** The number returns to the pool, somebody else rents it, and anything your customer printed it on is pointing at a stranger. Because a DELETE can be produced by a retry loop or a copied command, this endpoint refuses on the URL alone: send `confirm_phone_number` set to the exact number. Billing stops at the release; the rental stays readable afterwards with its `released_at`.
Permission: numbers:order
Parameters
| Name | In | Type | |
|---|---|---|---|
id * | path | string | Kleos id or your `external_ref`. |
number_id * | path | string | The rental id. |
Body
| Field | Type | |
|---|---|---|
confirm_phone_number * | string | The number itself, exactly as returned. The confirmation. |
Response
{
"object": "number",
"id": "6f1b9c22-4e0a-4a77-9c31-2d5f8ab10e44",
"customer_id": "0f2a6c4e-96a2-4f2e-8f2b-6c1a0e73d8b1",
"kind": "number",
"country": "US",
"phone_number": "+13105550142",
"status": "released",
"monthly_cents": 1500,
"currency": "usd",
"next_charge_on": null,
"release_due_on": null,
"released_at": "2026-08-06T11:40:02.000Z",
"release_reason": "requested",
"mode": "live",
"created_at": "2026-08-06T09:12:44.000Z"
}Refusals
| Status | Code | When |
|---|---|---|
| 404 | number_not_found | No such number for this customer in this mode. |
| 400 | release_not_confirmed | `confirm_phone_number` was missing or did not match. Nothing was released. |
| 409 | release_needs_support | This number cannot be released automatically. It has NOT been released. |
| 502 | release_failed | The number could not be handed back. Your rental is unchanged. |