Subaccounts
A subaccount is a separate account under yours for one of your clients, with its own numbers, reputation, do-not-call list, recordings, keys and billing view. Each subaccount is reviewed as its own business. Features turn on one by one as their checks pass.
Guide: Subaccounts.
Endpoints
| POST | /v1/accounts | Create a subaccount |
| GET | /v1/accounts/{id} | Retrieve a subaccount |
| GET | /v1/accounts | List subaccounts |
| PATCH | /v1/accounts/{id} | Update a subaccount |
| GET | /v1/accounts/{id}/requirements | Retrieve requirements |
| POST | /v1/accounts/{id}/documents | Upload a document |
| POST | /v1/accounts/{id}/attestations | Record an attestation |
| POST | /v1/accounts/{id}/onboarding_links | Create a setup link |
The account object
| Field | Type | Description |
|---|---|---|
id | string | Unique ID, starts with acc_. |
name | string | The client business name. Used for caller name and registrations. |
legal_name | string | null | The registered company name, if it differs from name. |
parent_id | string | The account that created this subaccount. |
status | string | in_review, active or closed. |
capabilities | object | Status of each feature: inbound, outbound_consumer, outbound_business, sms, whatsapp and recording. Each is pending, active or inactive. All start pending. No capability turns on, inbound included, until the client accepts the subaccount terms and signs its compliance certification (attestations.subaccount_terms and attestations.seller_compliance). After that each one turns on when its own checks pass. |
requirements | object | currently_due lists what you or your client still need to send. pending_verification lists what we are checking. |
contact_email | string | Where we send setup questions and compliance notices for this client. |
billing | string | parent puts usage on the parent invoice. own gives the subaccount its own invoice. |
onboarding_url | string | A setup link for your client. See Create a setup link. |
metadata | object | Up to 20 keys of your own. |
livemode | boolean | false for subaccounts created with a test key. |
created_at | string | When the subaccount was created, ISO 8601. |
Create a subaccount
Creates a subaccount in in_review and starts the business review. Send what you have now and the rest later with an update, document uploads or the setup link.
POST/v1/accounts
| Body parameters | Type | Description |
|---|---|---|
namerequired | string | The client business name. Used for caller name and registrations. |
legal_name | string | The registered company name, if it differs. |
ein | string | The client company tax ID, for carrier and registry filings. |
contact_email | string | Where we send setup questions and compliance notices for this client. |
entity_type | string | llc, corporation, partnership, sole_proprietor or nonprofit. |
formation | object | state, date and entity_id of the state registration. |
address | object | The business address: line1, line2, city, state, postal_code. |
website | string | The client website. |
representative | object | The person who signs for the client: name, title, email and phone. |
owners | array | Each owner with 25% or more, with name and percent. |
industry | string | What the client does, for example health_care_provider or home_services. |
offer_description | string | What the client sells, in a sentence. |
audience | consumer | business | both | Who the client calls. |
calling_states | string[] | Two-letter states the client calls into. |
channels | string[] | Any of live_voice, parallel_dialing, sms, whatsapp, recording. |
lead_sources | string[] | Where the client's leads come from. |
dnc.san | string | The client's own National Do Not Call Registry subscription account number. |
state_registrations | array | Telemarketer registrations by state with state and number, or state and the exemption the client relies on. |
billing | parent | own | Which invoice the usage goes on. Default parent. |
metadata | object | Up to 20 keys of your own. |
curl https://api.liveleg.dev/v1/accounts \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Hollis Family Dental",
"legal_name": "Hollis Family Dental PLLC",
"ein": "00-0000000",
"contact_email": "ops@hollisdental.example",
"entity_type": "llc",
"formation": {
"state": "CO",
"date": "2014-03-18",
"entity_id": "20141188301"
},
"address": {
"line1": "1420 Larimer St",
"city": "Denver",
"state": "CO",
"postal_code": "80202"
},
"website": "https://hollisdental.example",
"representative": {
"name": "Sam Hollis",
"title": "Owner",
"email": "sam@hollisdental.example",
"phone": "+17205550134"
},
"owners": [
{
"name": "Sam Hollis",
"percent": 100
}
],
"industry": "health_care_provider",
"offer_description": "Cleanings, exams and recall visits for existing patients.",
"audience": "consumer",
"calling_states": [
"CO"
],
"channels": [
"parallel_dialing",
"sms",
"recording"
],
"lead_sources": [
"existing_patients",
"website_form"
],
"dnc": {
"san": "0000000000"
},
"billing": "parent"
}'const res = await fetch('https://api.liveleg.dev/v1/accounts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: "Hollis Family Dental",
legal_name: "Hollis Family Dental PLLC",
ein: "00-0000000",
contact_email: "ops@hollisdental.example",
entity_type: "llc",
formation: {
state: "CO",
date: "2014-03-18",
entity_id: "20141188301"
},
address: {
line1: "1420 Larimer St",
city: "Denver",
state: "CO",
postal_code: "80202"
},
website: "https://hollisdental.example",
representative: {
name: "Sam Hollis",
title: "Owner",
email: "sam@hollisdental.example",
phone: "+17205550134"
},
owners: [
{
name: "Sam Hollis",
percent: 100
}
],
industry: "health_care_provider",
offer_description: "Cleanings, exams and recall visits for existing patients.",
audience: "consumer",
calling_states: [
"CO"
],
channels: [
"parallel_dialing",
"sms",
"recording"
],
lead_sources: [
"existing_patients",
"website_form"
],
dnc: {
san: "0000000000"
},
billing: "parent"
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/accounts",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"name": "Hollis Family Dental",
"legal_name": "Hollis Family Dental PLLC",
"ein": "00-0000000",
"contact_email": "ops@hollisdental.example",
"entity_type": "llc",
"formation": {
"state": "CO",
"date": "2014-03-18",
"entity_id": "20141188301",
},
"address": {
"line1": "1420 Larimer St",
"city": "Denver",
"state": "CO",
"postal_code": "80202",
},
"website": "https://hollisdental.example",
"representative": {
"name": "Sam Hollis",
"title": "Owner",
"email": "sam@hollisdental.example",
"phone": "+17205550134",
},
"owners": [
{
"name": "Sam Hollis",
"percent": 100,
},
],
"industry": "health_care_provider",
"offer_description": "Cleanings, exams and recall visits for existing patients.",
"audience": "consumer",
"calling_states": ["CO"],
"channels": ["parallel_dialing", "sms", "recording"],
"lead_sources": ["existing_patients", "website_form"],
"dnc": {
"san": "0000000000",
},
"billing": "parent",
},
).json() {
"id": "acc_hollis_dental",
"object": "account",
"name": "Hollis Family Dental",
"legal_name": "Hollis Family Dental PLLC",
"parent_id": "acc_ridgeline",
"status": "in_review",
"capabilities": {
"inbound": "pending",
"outbound_consumer": "pending",
"sms": "pending",
"recording": "pending"
},
"requirements": {
"currently_due": [
"attestations.subaccount_terms",
"attestations.seller_compliance",
"documents.ein_letter",
"documents.consent_form",
"documents.script"
],
"pending_verification": [
"owners[0].screening"
]
},
"contact_email": "ops@hollisdental.example",
"billing": "parent",
"onboarding_url": "https://liveleg.dev/onboard/acc_hollis_dental?t=...",
"metadata": {},
"livemode": true,
"created_at": "2026-11-02T17:20:41Z"
} Retrieve a subaccount
Returns the subaccount with its current capabilities and requirements.
GET/v1/accounts/{id}
curl https://api.liveleg.dev/v1/accounts/acc_hollis_dental \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/accounts/acc_hollis_dental', {
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/accounts/acc_hollis_dental",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"id": "acc_hollis_dental",
"object": "account",
"name": "Hollis Family Dental",
"legal_name": "Hollis Family Dental PLLC",
"parent_id": "acc_ridgeline",
"status": "active",
"capabilities": {
"inbound": "active",
"outbound_consumer": "active",
"sms": "pending",
"recording": "active"
},
"requirements": {
"currently_due": [
"sms.brand_registration"
],
"pending_verification": []
},
"contact_email": "ops@hollisdental.example",
"billing": "parent",
"metadata": {},
"livemode": true,
"created_at": "2026-11-02T17:20:41Z"
} List subaccounts
Returns the subaccounts under your account, newest first.
GET/v1/accounts
| Query parameters | Type | Description |
|---|---|---|
status | string | in_review, active or closed. |
limit | integer | 1 to 100. Default 20. |
starting_after | string | The last ID from the previous page. |
curl "https://api.liveleg.dev/v1/accounts?status=in_review&limit=20" \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/accounts?status=in_review&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/accounts",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
params={
"status": "in_review",
"limit": 20,
},
).json() {
"object": "list",
"data": [
{
"id": "acc_hollis_dental",
"object": "account",
"name": "Hollis Family Dental",
"status": "in_review",
"capabilities": {
"inbound": "pending",
"outbound_consumer": "pending",
"sms": "pending",
"recording": "pending"
},
"created_at": "2026-11-02T17:20:41Z"
}
],
"has_more": false
} Update a subaccount
Changes any field you can set on create. Changes to legal_name, ein, owners or calling_states are checked again and show in requirements until they are verified.
PATCH/v1/accounts/{id}
| Body parameters | Type | Description |
|---|---|---|
contact_email | string | Where we send setup questions and compliance notices. |
calling_states | string[] | States the client calls into. Adding a state that requires registration adds it to requirements. |
state_registrations | array | Registrations by state, or the exemption the client relies on. |
billing | parent | own | Which invoice the usage goes on. Takes effect on the next billing period. |
metadata | object | Up to 20 keys. Send an empty string to remove a key. |
curl -X PATCH https://api.liveleg.dev/v1/accounts/acc_hollis_dental \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"calling_states": [
"CO",
"WY"
],
"state_registrations": [
{
"state": "WY",
"exemption": "existing_customers"
}
],
"metadata": {
"crm_client_id": "cl_4471"
}
}'const res = await fetch('https://api.liveleg.dev/v1/accounts/acc_hollis_dental', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
calling_states: [
"CO",
"WY"
],
state_registrations: [
{
state: "WY",
exemption: "existing_customers"
}
],
metadata: {
crm_client_id: "cl_4471"
}
}),
});
const data = await res.json();import os, requests
data = requests.patch(
"https://api.liveleg.dev/v1/accounts/acc_hollis_dental",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"calling_states": ["CO", "WY"],
"state_registrations": [
{
"state": "WY",
"exemption": "existing_customers",
},
],
"metadata": {
"crm_client_id": "cl_4471",
},
},
).json() {
"id": "acc_hollis_dental",
"object": "account",
"name": "Hollis Family Dental",
"status": "active",
"capabilities": {
"inbound": "active",
"outbound_consumer": "active",
"sms": "pending",
"recording": "active"
},
"requirements": {
"currently_due": [
"sms.brand_registration"
],
"pending_verification": [
"state_registrations.WY"
]
},
"billing": "parent",
"metadata": {
"crm_client_id": "cl_4471"
},
"livemode": true,
"created_at": "2026-11-02T17:20:41Z"
} Retrieve requirements
Lists what is still due for each feature, what we are checking, what was rejected and what needs to be sent again soon. account.requirements_updated is sent when this changes.
GET/v1/accounts/{id}/requirements
curl https://api.liveleg.dev/v1/accounts/acc_hollis_dental/requirements \
-H "Authorization: Bearer $LIVELEG_API_KEY"const res = await fetch('https://api.liveleg.dev/v1/accounts/acc_hollis_dental/requirements', {
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/accounts/acc_hollis_dental/requirements",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
).json() {
"object": "account_requirements",
"account_id": "acc_hollis_dental",
"currently_due": [
"attestations.subaccount_terms",
"attestations.seller_compliance",
"documents.consent_form",
"sms.brand_registration"
],
"pending_verification": [
"documents.ein_letter"
],
"by_capability": {
"inbound": {
"status": "pending",
"currently_due": [
"attestations.subaccount_terms",
"attestations.seller_compliance"
]
},
"recording": {
"status": "pending",
"currently_due": [
"attestations.subaccount_terms",
"attestations.seller_compliance"
]
},
"outbound_consumer": {
"status": "pending",
"currently_due": [
"attestations.subaccount_terms",
"attestations.seller_compliance",
"documents.consent_form"
]
},
"sms": {
"status": "pending",
"currently_due": [
"attestations.subaccount_terms",
"attestations.seller_compliance",
"sms.brand_registration"
]
}
},
"errors": [
{
"item": "documents.script",
"reason": "The script does not name the business at the start of the call."
}
],
"reverification_due": [
{
"item": "dnc.san",
"due_at": "2027-01-31"
}
]
} Upload a document
Uploads one file for review. Send it as multipart/form-data with type and file. PDF, PNG or JPEG up to 10 MB. account.document_verified or account.document_rejected follows.
POST/v1/accounts/{id}/documents
| Body parameters | Type | Description |
|---|---|---|
typerequired | string | ein_letter, formation_document, dnc_subscription, dnc_policy, consent_form, script, state_registration, license or other. |
filerequired | file | The document. |
state | string | Two-letter state, for state_registration and license. |
curl https://api.liveleg.dev/v1/accounts/acc_hollis_dental/documents \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-F type=dnc_subscription \
-F file=@dnc-subscription.pdfimport { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('type', 'dnc_subscription');
form.append('file', new Blob([await readFile('dnc-subscription.pdf')]), 'dnc-subscription.pdf');
const res = await fetch('https://api.liveleg.dev/v1/accounts/acc_hollis_dental/documents', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LIVELEG_API_KEY}` },
body: form,
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/accounts/acc_hollis_dental/documents",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
data={
"type": "dnc_subscription",
},
files={"file": open("dnc-subscription.pdf", "rb")},
).json() {
"id": "doc_2Lm40Rt",
"object": "document",
"account_id": "acc_hollis_dental",
"type": "dnc_subscription",
"file_name": "dnc-subscription.pdf",
"status": "pending_verification",
"created_at": "2026-11-02T17:31:09Z"
} Record an attestation
Records a statement your client signed in your own app instead of the setup link. The signer must be the client's representative. We store the text version, the signer, the time and the IP address.
POST/v1/accounts/{id}/attestations
| Body parameters | Type | Description |
|---|---|---|
typerequired | string | subaccount_terms or seller_compliance. |
versionrequired | string | The version of the text that was shown, from the setup link or the console. |
signed_byrequired | object | name, title and email of the person who signed. |
signed_atrequired | string | When they signed, ISO 8601. |
iprequired | string | The signer's IP address. |
user_agent | string | The signer's browser. |
curl https://api.liveleg.dev/v1/accounts/acc_hollis_dental/attestations \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "seller_compliance",
"version": "2026-10",
"signed_by": {
"name": "Sam Hollis",
"title": "Owner",
"email": "sam@hollisdental.example"
},
"signed_at": "2026-11-02T18:04:52Z",
"ip": "198.51.100.24",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6)"
}'const res = await fetch('https://api.liveleg.dev/v1/accounts/acc_hollis_dental/attestations', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: "seller_compliance",
version: "2026-10",
signed_by: {
name: "Sam Hollis",
title: "Owner",
email: "sam@hollisdental.example"
},
signed_at: "2026-11-02T18:04:52Z",
ip: "198.51.100.24",
user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6)"
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/accounts/acc_hollis_dental/attestations",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"type": "seller_compliance",
"version": "2026-10",
"signed_by": {
"name": "Sam Hollis",
"title": "Owner",
"email": "sam@hollisdental.example",
},
"signed_at": "2026-11-02T18:04:52Z",
"ip": "198.51.100.24",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6)",
},
).json() {
"id": "att_9Fz13Qe",
"object": "attestation",
"account_id": "acc_hollis_dental",
"type": "seller_compliance",
"version": "2026-10",
"signed_by": {
"name": "Sam Hollis",
"title": "Owner",
"email": "sam@hollisdental.example"
},
"signed_at": "2026-11-02T18:04:52Z",
"ip": "198.51.100.24",
"created_at": "2026-11-02T18:04:53Z"
} Create a setup link
Creates a link for your client to fill in what is still due, upload documents and sign our subaccount terms and their compliance statement. A link works for 7 days and for one client.
POST/v1/accounts/{id}/onboarding_links
| Body parameters | Type | Description |
|---|---|---|
return_url | string | Where your client goes after they finish. |
send_to | string | An email address. We send the link there with your business name on it. |
curl https://api.liveleg.dev/v1/accounts/acc_hollis_dental/onboarding_links \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"return_url": "https://app.example.com/clients/hollis/done",
"send_to": "sam@hollisdental.example"
}'const res = await fetch('https://api.liveleg.dev/v1/accounts/acc_hollis_dental/onboarding_links', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
return_url: "https://app.example.com/clients/hollis/done",
send_to: "sam@hollisdental.example"
}),
});
const data = await res.json();import os, requests
data = requests.post(
"https://api.liveleg.dev/v1/accounts/acc_hollis_dental/onboarding_links",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"return_url": "https://app.example.com/clients/hollis/done",
"send_to": "sam@hollisdental.example",
},
).json() {
"object": "onboarding_link",
"account_id": "acc_hollis_dental",
"url": "https://liveleg.dev/onboard/acc_hollis_dental?t=...",
"sent_to": "sam@hollisdental.example",
"expires_at": "2026-11-09T18:10:00Z"
}