Numbers
Local and toll-free US numbers that your dial sessions use as caller ID. Search, buy and release numbers, group them in caller ID pools, read their health, start spam cleaning and port in the numbers you already have.
Guide: Numbers.
Endpoints
| GET | /v1/numbers/available | Search available numbers |
| POST | /v1/numbers | Buy a number |
| GET | /v1/numbers | List numbers |
| GET | /v1/numbers/{id} | Retrieve a number |
| PATCH | /v1/numbers/{id} | Update a number |
| POST | /v1/numbers/{id}/retire | Retire a number |
| DELETE | /v1/numbers/{id} | Release a number |
| GET | /v1/numbers/{id}/health | Read number health |
| POST | /v1/numbers/{id}/cleaning | Start spam cleaning |
| GET | /v1/numbers/{id}/cleaning | Retrieve spam cleaning |
| PATCH | /v1/branded_caller_id | Set branded caller ID |
| POST | /v1/caller_id_pools | Create a caller ID pool |
| GET | /v1/caller_id_pools | List caller ID pools |
| PATCH | /v1/caller_id_pools/{id} | Update a caller ID pool |
| POST | /v1/ports | Port numbers in |
| GET | /v1/ports/{id} | Retrieve a port |
| PATCH | /v1/ports/{id} | Update a port |
| POST | /v1/ports/{id}/cancel | Cancel a port |
| POST | /v1/ports/{id}/documents | Add a port document |
The number object
| Field | Type | Description |
|---|---|---|
id | string | Unique ID, starts with num_. |
phone_number | string | The number in E.164 format. |
type | string | local or toll_free. |
area_code | string | The three-digit area code. |
state | string | null | Two-letter state from the rate center. Null for toll-free numbers. |
rate_center | string | null | The rate center of a local number. |
pool_id | string | null | The caller ID pool the number belongs to. |
caller_name | string | null | Caller name (CNAM), up to 15 characters. Needs number reputation on the pool. |
caller_name_status | string | null | pending until the caller name filing is accepted, then registered. |
branded_caller_id | string | pending, approved, rejected or not_registered. |
stir_shaken | string | STIR/SHAKEN attestation on calls from this number: A, B or C. |
daily_call_cap | integer | Outbound calls per day from this number, from its pool. |
status | string | active, resting, callbacks_only, retired or released. A retired number takes only callbacks for 90 days, then is released. See the health endpoint for the reason. |
created_at | string | When the number was bought or ported in, ISO 8601. |
Search available numbers
Returns numbers you can buy. Search by area code, state, city or digits the number contains.
GET/v1/numbers/available
| Query parameters | Type | Description |
|---|---|---|
type | string | local or toll_free. Default local. |
area_code | string | Three-digit area code. For toll-free, a prefix such as 833. |
state | string | Two-letter state. |
city | string | City name, matched to rate centers. |
contains | string | Digits the number must contain, for example 7663. |
limit | integer | 1 to 100. Default 20. |
curl "https://api.liveleg.dev/v1/numbers/available?type=local&area_code=512&limit=3" \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers/available?type=local&area_code=512&limit=3', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.get(
"https://api.liveleg.dev/v1/numbers/available",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
params={
"type": "local",
"area_code": "512",
"limit": 3,
},
).json() {
"object": "list",
"data": [
{
"object": "available_number",
"phone_number": "+15125550199",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN"
},
{
"object": "available_number",
"phone_number": "+15125550142",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN"
},
{
"object": "available_number",
"phone_number": "+15125550176",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "ROUND ROCK"
}
],
"has_more": true
} Buy a number
Buys a number from the search results, or the first available numbers in an area code. New numbers are registered with the call analytics engines before their first call. Numbers are billed monthly from the day you buy them. Every call is signed for STIR/SHAKEN at no charge.
POST/v1/numbers
| Body parameters | Type | Description |
|---|---|---|
phone_number | string | A number from the search results. Or send area_code and quantity to take the first available. |
type | string | local or toll_free. Default local. Used with area_code and quantity. |
area_code | string | Buy in this area code. |
quantity | integer | How many numbers to buy with area_code. Up to 50. With more than one, the response is a list. |
pool_id | string | Adds the numbers to this caller ID pool. |
caller_name | string | Caller name (CNAM), up to 15 characters. Needs number reputation. |
curl https://api.liveleg.dev/v1/numbers \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+15125550199",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOF"
}'const res = await fetch('https://api.liveleg.dev/v1/numbers', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone_number: "+15125550199",
pool_id: "pool_local_presence",
caller_name: "RIDGELINE ROOF"
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/numbers",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"phone_number": "+15125550199",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOF",
},
).json() {
"id": "num_5Tq81",
"object": "number",
"phone_number": "+15125550199",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOF",
"caller_name_status": "registered",
"branded_caller_id": "approved",
"stir_shaken": "A",
"daily_call_cap": 100,
"status": "active",
"created_at": "2026-11-03T14:20:05Z"
} List numbers
Returns the numbers on the account, newest first.
GET/v1/numbers
| Query parameters | Type | Description |
|---|---|---|
pool_id | string | Only numbers in this pool. |
status | string | active, resting, callbacks_only or retired. |
type | string | local or toll_free. |
area_code | string | Only numbers in this area code. |
limit | integer | 1 to 100. Default 20. |
starting_after | string | The last ID from the previous page. |
curl "https://api.liveleg.dev/v1/numbers?pool_id=pool_local_presence&status=active&limit=20" \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers?pool_id=pool_local_presence&status=active&limit=20', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.get(
"https://api.liveleg.dev/v1/numbers",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
params={
"pool_id": "pool_local_presence",
"status": "active",
"limit": 20,
},
).json() {
"object": "list",
"data": [
{
"id": "num_5Tq81",
"object": "number",
"phone_number": "+15125550199",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOF",
"caller_name_status": "registered",
"branded_caller_id": "approved",
"stir_shaken": "A",
"daily_call_cap": 100,
"status": "active",
"created_at": "2026-11-03T14:20:05Z"
}
],
"has_more": true
} Retrieve a number
Returns one number.
GET/v1/numbers/{id}
curl https://api.liveleg.dev/v1/numbers/num_5Tq81 \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers/num_5Tq81', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.get(
"https://api.liveleg.dev/v1/numbers/num_5Tq81",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "num_5Tq81",
"object": "number",
"phone_number": "+15125550199",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOF",
"caller_name_status": "registered",
"branded_caller_id": "approved",
"stir_shaken": "A",
"daily_call_cap": 100,
"status": "active",
"created_at": "2026-11-03T14:20:05Z"
} Update a number
Moves a number to another pool or changes its caller name. Leads who already saw this number keep seeing it.
PATCH/v1/numbers/{id}
| Body parameters | Type | Description |
|---|---|---|
pool_id | string | null | The new caller ID pool. Null takes the number out of all pools, and it only takes calls. |
caller_name | string | Caller name (CNAM), up to 15 characters. Needs number reputation on the pool. |
curl -X PATCH https://api.liveleg.dev/v1/numbers/num_5Tq81 \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOFS"
}'const res = await fetch('https://api.liveleg.dev/v1/numbers/num_5Tq81', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
pool_id: "pool_local_presence",
caller_name: "RIDGELINE ROOFS"
}),
});
const data = await res.json();import os, requests
data = requests.patch(
"https://api.liveleg.dev/v1/numbers/num_5Tq81",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOFS",
},
).json() {
"id": "num_5Tq81",
"object": "number",
"phone_number": "+15125550199",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOFS",
"caller_name_status": "pending",
"branded_caller_id": "approved",
"stir_shaken": "A",
"daily_call_cap": 100,
"status": "active",
"created_at": "2026-11-03T14:20:05Z"
} Retire a number
Stops the number dialing out right away. It keeps taking callbacks for 90 days, routed to the lead's last agent, and is then released. Leads who saw it get another number in the same area code on their next call.
POST/v1/numbers/{id}/retire
curl -X POST https://api.liveleg.dev/v1/numbers/num_5Tq81/retire \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers/num_5Tq81/retire', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/numbers/num_5Tq81/retire",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "num_5Tq81",
"object": "number",
"phone_number": "+15125550199",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOF",
"caller_name_status": "registered",
"branded_caller_id": "approved",
"stir_shaken": "A",
"daily_call_cap": 100,
"status": "retired",
"created_at": "2026-11-03T14:20:05Z",
"release_at": "2027-02-18T18:02:41Z"
} Release a number
Releases a number at once. It stops dialing out and taking calls right away. A released number is kept out of use for at least 90 days before it goes to anyone else. To keep callbacks working, retire the number instead.
DELETE/v1/numbers/{id}
curl -X DELETE https://api.liveleg.dev/v1/numbers/num_5Tq81 \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers/num_5Tq81', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.delete(
"https://api.liveleg.dev/v1/numbers/num_5Tq81",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "num_5Tq81",
"object": "number",
"phone_number": "+15125550199",
"type": "local",
"area_code": "512",
"state": "TX",
"rate_center": "AUSTIN",
"pool_id": "pool_local_presence",
"caller_name": "RIDGELINE ROOF",
"caller_name_status": "registered",
"branded_caller_id": "approved",
"stir_shaken": "A",
"daily_call_cap": 100,
"status": "released",
"created_at": "2026-11-03T14:20:05Z",
"released_at": "2026-11-20T18:02:41Z"
} Read number health
Returns the state, spam label status, caller name, branded caller ID status and answer rate of a number. Checked every day when number reputation is on. state is active, resting, callbacks_only or retired. label_status is clean, labeled or blocked, where blocked means a carrier is rejecting calls from the number. labels lists each label with engine, label and first_seen_at. branded_caller_id is pending, approved, rejected or not_registered. calls_today counts outbound calls in the number's own time zone. answer_rate_7d is the share of calls answered by a person over 7 days, and pool_answer_rate_7d is the same for the whole pool. rest_until is set while the number rests. cleaning_status is set while spam cleaning runs.
GET/v1/numbers/{id}/health
curl https://api.liveleg.dev/v1/numbers/num_5Tq81/health \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers/num_5Tq81/health', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.get(
"https://api.liveleg.dev/v1/numbers/num_5Tq81/health",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"number_id": "num_5Tq81",
"phone_number": "+15125550199",
"state": "active",
"label_status": "clean",
"labels": [],
"caller_name": "RIDGELINE ROOF",
"branded_caller_id": "approved",
"stir_shaken": "A",
"calls_today": 41,
"daily_call_cap": 100,
"answer_rate_7d": 0.138,
"pool_answer_rate_7d": 0.141,
"rest_until": null,
"cleaning_status": null,
"last_checked_at": "2026-11-03T11:00:04Z"
} Start spam cleaning
Files a dispute with each engine that shows a label, registers the number again, rests it and checks it until the label is gone. The number only takes calls while cleaning runs. If the label is not cleared within 14 days, we replace the number at no charge. number.cleaning_updated reports each step.
POST/v1/numbers/{id}/cleaning
curl -X POST https://api.liveleg.dev/v1/numbers/num_5Tq81/cleaning \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers/num_5Tq81/cleaning', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/numbers/num_5Tq81/cleaning",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "cln_2Rx71Bq",
"object": "number_cleaning",
"number_id": "num_5Tq81",
"phone_number": "+15125550199",
"status": "filed",
"engines": [
{
"engine": "engine_b",
"label": "spam_likely",
"status": "filed"
}
],
"filed_at": "2026-11-04T11:00:12Z",
"replace_after": "2026-11-18T11:00:12Z",
"replacement_number_id": null,
"completed_at": null
} Retrieve spam cleaning
Returns the latest spam cleaning for a number. status is filed, in_progress, cleared or replaced. filed: disputes are filed with each engine that shows a label. in_progress: the number is registered again, resting and checked every day. cleared: no engine shows a label and the number dials out again. replaced: the label was not cleared by replace_after, 14 days after filing, and replacement_number_id is the new number.
GET/v1/numbers/{id}/cleaning
curl https://api.liveleg.dev/v1/numbers/num_5Tq81/cleaning \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/numbers/num_5Tq81/cleaning', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.get(
"https://api.liveleg.dev/v1/numbers/num_5Tq81/cleaning",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "cln_2Rx71Bq",
"object": "number_cleaning",
"number_id": "num_5Tq81",
"phone_number": "+15125550199",
"status": "cleared",
"engines": [
{
"engine": "engine_b",
"label": "spam_likely",
"status": "cleared"
}
],
"filed_at": "2026-11-04T11:00:12Z",
"replace_after": "2026-11-18T11:00:12Z",
"replacement_number_id": null,
"completed_at": "2026-11-09T11:00:31Z"
} Set branded caller ID
Sets the name, logo and call reason shown on supported mobile phones, after your business is verified. It applies to every number in the account or subaccount. branded_caller_id on each number is pending until approved, then approved or rejected. Phones without support show the caller name.
PATCH/v1/branded_caller_id
| Body parameters | Type | Description |
|---|---|---|
display_namerequired | string | Your business name as it should appear, up to 32 characters. |
logo_url | string | A square PNG of your logo, at least 256 by 256 pixels. |
call_reason | string | A short reason shown under the name, up to 40 characters. |
curl -X PATCH https://api.liveleg.dev/v1/branded_caller_id \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Ridgeline Roofing",
"logo_url": "https://ridgeline.example/logo.png",
"call_reason": "Your roof inspection"
}'const res = await fetch('https://api.liveleg.dev/v1/branded_caller_id', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
display_name: "Ridgeline Roofing",
logo_url: "https://ridgeline.example/logo.png",
call_reason: "Your roof inspection"
}),
});
const data = await res.json();import os, requests
data = requests.patch(
"https://api.liveleg.dev/v1/branded_caller_id",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"display_name": "Ridgeline Roofing",
"logo_url": "https://ridgeline.example/logo.png",
"call_reason": "Your roof inspection",
},
).json() {
"object": "branded_caller_id",
"display_name": "Ridgeline Roofing",
"logo_url": "https://ridgeline.example/logo.png",
"call_reason": "Your roof inspection",
"status": "pending",
"updated_at": "2026-11-03T16:40:12Z"
} Create a caller ID pool
A pool is a group of numbers that a dial session picks caller IDs from. A number that reaches the daily call cap sits out until the next day in its own time zone.
POST/v1/caller_id_pools
| Body parameters | Type | Description |
|---|---|---|
namerequired | string | A name for your own use. |
id | string | Your own ID for the pool, starting with pool_. Generated when left out. |
daily_call_cap | integer | Outbound calls per number per day. Default 100. |
reputation | boolean | Turns on number reputation for every number in the pool. Default false. |
on_label | string | What happens when a number is labeled: rest, clean, replace or notify. Default rest. |
rest_days | integer | Days a number rests with on_label=rest. Default 7. |
curl https://api.liveleg.dev/v1/caller_id_pools \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "pool_local_presence",
"name": "Local presence",
"daily_call_cap": 100,
"reputation": true,
"on_label": "clean"
}'const res = await fetch('https://api.liveleg.dev/v1/caller_id_pools', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: "pool_local_presence",
name: "Local presence",
daily_call_cap: 100,
reputation: true,
on_label: "clean"
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/caller_id_pools",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"id": "pool_local_presence",
"name": "Local presence",
"daily_call_cap": 100,
"reputation": True,
"on_label": "clean",
},
).json() {
"id": "pool_local_presence",
"object": "caller_id_pool",
"name": "Local presence",
"daily_call_cap": 100,
"reputation": true,
"on_label": "clean",
"rest_days": 7,
"number_count": 0,
"created_at": "2026-11-03T14:10:44Z"
} List caller ID pools
Returns the pools on the account with the number of numbers in each.
GET/v1/caller_id_pools
| Query parameters | Type | Description |
|---|---|---|
limit | integer | 1 to 100. Default 20. |
starting_after | string | The last ID from the previous page. |
curl https://api.liveleg.dev/v1/caller_id_pools \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/caller_id_pools', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.get(
"https://api.liveleg.dev/v1/caller_id_pools",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"object": "list",
"data": [
{
"id": "pool_local_presence",
"object": "caller_id_pool",
"name": "Local presence",
"daily_call_cap": 100,
"reputation": true,
"on_label": "clean",
"rest_days": 7,
"number_count": 24,
"created_at": "2026-11-03T14:10:44Z"
},
{
"id": "pool_tollfree",
"object": "caller_id_pool",
"name": "Toll-free",
"daily_call_cap": 100,
"reputation": false,
"on_label": "rest",
"rest_days": 7,
"number_count": 4,
"created_at": "2026-11-03T14:12:09Z"
}
],
"has_more": false
} Update a caller ID pool
Changes the name, daily call cap or label policy of a pool. Changes apply to the next call.
PATCH/v1/caller_id_pools/{id}
| Body parameters | Type | Description |
|---|---|---|
name | string | A name for your own use. |
daily_call_cap | integer | Outbound calls per number per day. |
reputation | boolean | Number reputation for every number in the pool. |
on_label | string | rest, clean, replace or notify. |
rest_days | integer | Days a number rests with on_label=rest. |
curl -X PATCH https://api.liveleg.dev/v1/caller_id_pools/pool_local_presence \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reputation": true,
"on_label": "rest",
"rest_days": 7
}'const res = await fetch('https://api.liveleg.dev/v1/caller_id_pools/pool_local_presence', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
reputation: true,
on_label: "rest",
rest_days: 7
}),
});
const data = await res.json();import os, requests
data = requests.patch(
"https://api.liveleg.dev/v1/caller_id_pools/pool_local_presence",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"reputation": True,
"on_label": "rest",
"rest_days": 7,
},
).json() {
"id": "pool_local_presence",
"object": "caller_id_pool",
"name": "Local presence",
"daily_call_cap": 100,
"reputation": true,
"on_label": "rest",
"rest_days": 7,
"number_count": 24,
"created_at": "2026-11-03T14:10:44Z"
} Port numbers in
Starts a port of numbers from your current carrier. We email the letter of authorization (LOA) to authorized_email for e-signature, file the port when it is signed and post the confirmed cutover date (foc_date) when the current carrier accepts it. The numbers keep working at the current carrier until the cutover. number.port_updated reports each status change.
POST/v1/ports
| Body parameters | Type | Description |
|---|---|---|
numbersrequired | string[] | The numbers to port, E.164. All from one account at the current carrier. |
account_namerequired | string | The name on the current carrier account, exactly as on the bill. |
account_numberrequired | string | The account number at the current carrier. |
pin | string | The port-out PIN. Most mobile carriers and many VoIP providers require one. |
billing_telephone_number | string | The main number on the account, if it differs from the numbers being ported. |
service_addressrequired | object | The service address on the account: line1, city, state, postal_code. |
authorized_byrequired | string | The name of the person who signs the LOA. They must be allowed to act for the account. |
authorized_emailrequired | string | Where the LOA is sent for e-signature. |
authorized_title | string | Their title at the business. |
requested_date | string | The earliest cutover date you want, YYYY-MM-DD. Default the first date the carrier offers. |
partial | boolean | True when other numbers stay on the account at the current carrier. |
pool_id | string | Adds the numbers to this caller ID pool after cutover. |
curl https://api.liveleg.dev/v1/ports \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"numbers": [
"+16025550190",
"+16025550191"
],
"account_name": "Ridgeline Roofing LLC",
"account_number": "88213-01",
"pin": "4417",
"service_address": {
"line1": "1400 Industrial Blvd",
"city": "Phoenix",
"state": "AZ",
"postal_code": "85004"
},
"authorized_by": "Dana Ruiz",
"authorized_email": "dana@ridgeline.example",
"authorized_title": "Owner",
"pool_id": "pool_local_presence"
}'const res = await fetch('https://api.liveleg.dev/v1/ports', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
numbers: [
"+16025550190",
"+16025550191"
],
account_name: "Ridgeline Roofing LLC",
account_number: "88213-01",
pin: "4417",
service_address: {
line1: "1400 Industrial Blvd",
city: "Phoenix",
state: "AZ",
postal_code: "85004"
},
authorized_by: "Dana Ruiz",
authorized_email: "dana@ridgeline.example",
authorized_title: "Owner",
pool_id: "pool_local_presence"
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/ports",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"numbers": ["+16025550190", "+16025550191"],
"account_name": "Ridgeline Roofing LLC",
"account_number": "88213-01",
"pin": "4417",
"service_address": {
"line1": "1400 Industrial Blvd",
"city": "Phoenix",
"state": "AZ",
"postal_code": "85004",
},
"authorized_by": "Dana Ruiz",
"authorized_email": "dana@ridgeline.example",
"authorized_title": "Owner",
"pool_id": "pool_local_presence",
},
).json() {
"id": "prt_6Gw20Lc",
"object": "port",
"status": "awaiting_signature",
"numbers": [
"+16025550190",
"+16025550191"
],
"account_name": "Ridgeline Roofing LLC",
"account_number": "88213-01",
"billing_telephone_number": "+16025550190",
"service_address": {
"line1": "1400 Industrial Blvd",
"city": "Phoenix",
"state": "AZ",
"postal_code": "85004"
},
"authorized_by": "Dana Ruiz",
"authorized_title": "Owner",
"authorized_email": "dana@ridgeline.example",
"partial": false,
"pool_id": "pool_local_presence",
"loa_url": "https://sign.liveleg.dev/loa/prt_6Gw20Lc",
"documents": [],
"requested_date": null,
"foc_date": null,
"rejection": null,
"created_at": "2026-11-03T16:20:00Z"
} Retrieve a port
Returns a port and its status: awaiting_signature, submitted, foc_confirmed, completed, rejected or canceled. foc_date is the cutover date once the carrier accepts. When the carrier rejects a port, rejection.reason says which field to correct. Fix it with an update and the port is filed again.
GET/v1/ports/{id}
curl https://api.liveleg.dev/v1/ports/prt_6Gw20Lc \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/ports/prt_6Gw20Lc', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.get(
"https://api.liveleg.dev/v1/ports/prt_6Gw20Lc",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "prt_6Gw20Lc",
"object": "port",
"status": "foc_confirmed",
"numbers": [
"+16025550190",
"+16025550191"
],
"account_name": "Ridgeline Roofing LLC",
"account_number": "88213-01",
"billing_telephone_number": "+16025550190",
"service_address": {
"line1": "1400 Industrial Blvd",
"city": "Phoenix",
"state": "AZ",
"postal_code": "85004"
},
"authorized_by": "Dana Ruiz",
"authorized_title": "Owner",
"authorized_email": "dana@ridgeline.example",
"partial": false,
"pool_id": "pool_local_presence",
"loa_url": "https://sign.liveleg.dev/loa/prt_6Gw20Lc",
"documents": [],
"requested_date": null,
"foc_date": "2026-11-12",
"rejection": null,
"created_at": "2026-11-03T16:20:00Z"
} Update a port
Corrects the details of a rejected or unsigned port with the same fields as when you created it. A rejected port is filed again.
PATCH/v1/ports/{id}
| Body parameters | Type | Description |
|---|---|---|
account_number | string | The account number at the current carrier. |
pin | string | The port-out PIN. |
service_address | object | The service address on the account. |
requested_date | string | The earliest cutover date you want. |
curl -X PATCH https://api.liveleg.dev/v1/ports/prt_6Gw20Lc \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pin": "4471"
}'const res = await fetch('https://api.liveleg.dev/v1/ports/prt_6Gw20Lc', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
pin: "4471"
}),
});
const data = await res.json();import os, requests
data = requests.patch(
"https://api.liveleg.dev/v1/ports/prt_6Gw20Lc",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"pin": "4471",
},
).json() {
"id": "prt_6Gw20Lc",
"object": "port",
"status": "submitted",
"numbers": [
"+16025550190",
"+16025550191"
],
"account_name": "Ridgeline Roofing LLC",
"account_number": "88213-01",
"billing_telephone_number": "+16025550190",
"service_address": {
"line1": "1400 Industrial Blvd",
"city": "Phoenix",
"state": "AZ",
"postal_code": "85004"
},
"authorized_by": "Dana Ruiz",
"authorized_title": "Owner",
"authorized_email": "dana@ridgeline.example",
"partial": false,
"pool_id": "pool_local_presence",
"loa_url": "https://sign.liveleg.dev/loa/prt_6Gw20Lc",
"documents": [],
"requested_date": null,
"foc_date": null,
"rejection": null,
"created_at": "2026-11-03T16:20:00Z"
} Cancel a port
Cancels a port before cutover. The numbers stay at the current carrier.
POST/v1/ports/{id}/cancel
curl -X POST https://api.liveleg.dev/v1/ports/prt_6Gw20Lc/cancel \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/ports/prt_6Gw20Lc/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
},
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/ports/prt_6Gw20Lc/cancel",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "prt_6Gw20Lc",
"object": "port",
"status": "canceled",
"numbers": [
"+16025550190",
"+16025550191"
],
"account_name": "Ridgeline Roofing LLC",
"account_number": "88213-01",
"billing_telephone_number": "+16025550190",
"service_address": {
"line1": "1400 Industrial Blvd",
"city": "Phoenix",
"state": "AZ",
"postal_code": "85004"
},
"authorized_by": "Dana Ruiz",
"authorized_title": "Owner",
"authorized_email": "dana@ridgeline.example",
"partial": false,
"pool_id": "pool_local_presence",
"loa_url": "https://sign.liveleg.dev/loa/prt_6Gw20Lc",
"documents": [],
"requested_date": null,
"foc_date": null,
"rejection": null,
"created_at": "2026-11-03T16:20:00Z"
} Add a port document
Adds a document to a port. Some carriers ask for a recent bill that shows the account name, number and service address. Send file_url, or upload the file as multipart form data in file.
POST/v1/ports/{id}/documents
| Body parameters | Type | Description |
|---|---|---|
typerequired | string | bill, signed_loa or other. |
file_url | string | A link we can download the PDF or image from. |
file | file | The file itself, in a multipart upload. Up to 10 MB. |
curl https://api.liveleg.dev/v1/ports/prt_6Gw20Lc/documents \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "bill",
"file_url": "https://files.example.com/ridgeline/october-bill.pdf"
}'const res = await fetch('https://api.liveleg.dev/v1/ports/prt_6Gw20Lc/documents', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: "bill",
file_url: "https://files.example.com/ridgeline/october-bill.pdf"
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/ports/prt_6Gw20Lc/documents",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"type": "bill",
"file_url": "https://files.example.com/ridgeline/october-bill.pdf",
},
).json() {
"id": "doc_9Fk22Lm",
"object": "port_document",
"port_id": "prt_6Gw20Lc",
"type": "bill",
"status": "received",
"created_at": "2026-11-03T16:05:30Z"
}