Customers
For platforms (SaaS, agent builders, agencies) that send on behalf of other
businesses. A customer is a label you control: assign numbers to it, and
every message, verification, event and webhook from those numbers carries its
customer_id, with usage you can read back per customer to rebill.
Entirely optional. Accounts that never create a customer see no change.
Create a customer
POST /v1/customers
curl -X POST https://api.joinsimplesms.com/v1/customers \
-H "Authorization: Bearer ssms_sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Acme Dental", "external_id": "acct_42", "metadata": { "plan": "pro" } }'{
"id": "cus_a1B2c3D4e5F6g7",
"object": "customer",
"name": "Acme Dental",
"external_id": "acct_42",
"metadata": { "plan": "pro" },
"created_at": "2026-10-01T12:00:00.000Z",
"updated_at": "2026-10-01T12:00:00.000Z"
}| Parameter | Type | Notes |
|---|---|---|
name | string | Optional, up to 200 characters. |
external_id | string | Optional. Your id for them; unique per account (409 if taken). |
metadata | object | Optional. Up to 50 string values. Keys up to 40 characters, no . # $ / [ ]. |
Retrieve, update, delete, list
GET /v1/customers/{id}also returnsphone_numbersassigned to it.PATCH /v1/customers/{id}changes only the fields you send;metadatareplaces the whole object,nullclears a field.DELETE /v1/customers/{id}deletes it and unassigns its numbers (they stay on your account). Messages, events and usage already attributed keep the id: they record what happened.GET /v1/customers?limit=25&cursor=...lists newest first;?external_id=acct_42finds one by your id.
Attribute traffic
- Numbers:
POST /v1/numberswithcustomer_id, orPATCH /v1/numbers/{number}with{ "customer_id": "cus_..." }. - Outbound messages inherit the customer of their
fromnumber, or take an explicitcustomer_id. A number assigned to one customer can't send as another (400); unassigned numbers can send for anyone. - Inbound messages (and STOP/HELP replies) inherit the customer of the number they arrived on.
- Verifications take an optional
customer_idonPOST /v1/verify. - Events and webhooks about any of the above carry
data.customer_id. GET /v1/messages?customer_id=cus_...lists one customer's messages.
Usage
GET /v1/customers/{id}/usage?start=2026-10-01&end=2026-10-31
{
"object": "customer_usage",
"customer_id": "cus_a1B2c3D4e5F6g7",
"mode": "live",
"start": "2026-10-01",
"end": "2026-10-31",
"messages_sent": 1204,
"messages_failed": 3,
"messages_received": 377,
"verifications_sent": 88,
"verifications_approved": 81,
"numbers": 2,
"phone_numbers": ["+14155550132", "+14155550133"],
"daily": [{ "date": "2026-10-01", "messages_sent": 40, "...": 0 }]
}UTC days, inclusive, up to 366 days; the default is this month to date.
Usage follows the key you call with: a test key reports sandbox traffic and a
live key live traffic, so test runs never show up in what you rebill.
numbers is the count assigned right now.
With the SDKs
const acme = await sms.customers.create({ name: 'Acme Dental', externalId: 'acct_42' });
await sms.numbers.update('+14155550132', { customerId: acme.id });
await sms.messages.list({ customerId: acme.id });
await sms.customers.usage(acme.id, { start: '2026-10-01', end: '2026-10-31' });acme = client.customers.create(name="Acme Dental", external_id="acct_42")
client.numbers.update("+14155550132", customer_id=acme["id"])
client.messages.list(customer_id=acme["id"])
client.customers.usage(acme["id"], start="2026-10-01", end="2026-10-31")Responsibility
A customer label is bookkeeping for you. It does not register the business with carriers, make it our customer, or change who is responsible for its traffic: you are, as if it were your own (Terms §2.4, Messaging Policy §7). Each end business still needs its own brand and campaign registration.