Customers
Create, update and list the customers you bill — the object invoices and payments attach to.
Customers
A Customer stores the buyer identity you reuse across invoices and payments: email, name, company, tax id, address and metadata. Customers are scoped to your merchant account.
Base URL https://api.southbill.com · Auth Authorization: Bearer sk_live_… · Scopes customers:read, customers:write.
Endpoints
| Method | Path | Scope |
|---|---|---|
POST |
/v1/customers |
customers:write |
GET |
/v1/customers |
customers:read |
GET |
/v1/customers/{id} |
customers:read |
POST |
/v1/customers/{id} |
customers:write |
DELETE |
/v1/customers/{id} |
customers:write |
Create a customer
curl https://api.southbill.com/v1/customers \
-H "Authorization: Bearer $SOUTHBILL_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cus_create_9781" \
-d '{
"email": "jane@example.com",
"name": "Jane Doe",
"company": "Doe Ltd",
"tax_id": "GB123456789",
"address": { "line1": "1 High Street", "postal_code": "EC1A 1BB", "city": "London", "country": "GB" },
"metadata": { "crm_id": "4711" }
}'
const res = await fetch("https://api.southbill.com/v1/customers", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SOUTHBILL_SECRET_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "cus_create_9781",
},
body: JSON.stringify({
email: "jane@example.com",
name: "Jane Doe",
company: "Doe Ltd",
metadata: { crm_id: "4711" },
}),
});
const customer = await res.json();
import os, requests
customer = requests.post(
"https://api.southbill.com/v1/customers",
headers={
"Authorization": f"Bearer {os.environ['SOUTHBILL_SECRET_KEY']}",
"Idempotency-Key": "cus_create_9781",
},
json={"email": "jane@example.com", "name": "Jane Doe", "company": "Doe Ltd"},
timeout=30,
).json()
<?php
$ch = curl_init("https://api.southbill.com/v1/customers");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("SOUTHBILL_SECRET_KEY"),
"Content-Type: application/json",
"Idempotency-Key: cus_create_9781",
],
CURLOPT_POSTFIELDS => json_encode([
"email" => "jane@example.com",
"name" => "Jane Doe",
]),
]);
$customer = json_decode(curl_exec($ch), true);
Response:
{
"id": "cus_9f2c41a0b7e34d9a8c15be22",
"object": "customer",
"livemode": true,
"email": "jane@example.com",
"name": "Jane Doe",
"phone": null,
"company": "Doe Ltd",
"tax_id": "GB123456789",
"address": { "line1": "1 High Street", "postal_code": "EC1A 1BB", "city": "London", "country": "GB" },
"shipping": {},
"metadata": { "crm_id": "4711" },
"deleted": false,
"created": 1756000000
}
At least one of email or name is required. Emails are unique per merchant and mode — a duplicate returns 409 customer_exists.
List, retrieve, update, delete
# list (newest first, cursor paging)
curl "https://api.southbill.com/v1/customers?limit=25&starting_after=cus_9f2c41a0b7e34d9a8c15be22" \
-H "Authorization: Bearer $SOUTHBILL_SECRET_KEY"
# filter by email
curl "https://api.southbill.com/v1/customers?email=jane@example.com" \
-H "Authorization: Bearer $SOUTHBILL_SECRET_KEY"
# update (POST, partial)
curl https://api.southbill.com/v1/customers/cus_9f2c41a0b7e34d9a8c15be22 \
-H "Authorization: Bearer $SOUTHBILL_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "phone": "+44 20 7946 0958" }'
# delete (soft)
curl -X DELETE https://api.southbill.com/v1/customers/cus_9f2c41a0b7e34d9a8c15be22 \
-H "Authorization: Bearer $SOUTHBILL_SECRET_KEY"
const list = await fetch("https://api.southbill.com/v1/customers?limit=25", {
headers: { Authorization: `Bearer ${process.env.SOUTHBILL_SECRET_KEY}` },
}).then((r) => r.json());
await fetch(`https://api.southbill.com/v1/customers/${list.data[0].id}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SOUTHBILL_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ phone: "+44 20 7946 0958" }),
});
import os, requests
h = {"Authorization": f"Bearer {os.environ['SOUTHBILL_SECRET_KEY']}"}
listing = requests.get("https://api.southbill.com/v1/customers", headers=h, params={"limit": 25}).json()
cid = listing["data"][0]["id"]
requests.post(f"https://api.southbill.com/v1/customers/{cid}", headers=h, json={"phone": "+44 20 7946 0958"})
requests.delete(f"https://api.southbill.com/v1/customers/{cid}", headers=h)
Updates are POST (not PUT/PATCH) and partial — only the fields you send change. DELETE is a soft delete: the customer disappears from lists and cannot be attached to new invoices, while existing invoices keep their snapshot.
List shape & paging
{
"object": "list",
"has_more": true,
"data": [ { "id": "cus_…", "object": "customer" } ]
}
| Parameter | Behaviour |
|---|---|
limit |
1–100, default 25. |
starting_after |
Customer id — returns rows created before it. |
email |
Exact match, lowercased. |
Rate limits: 300 reads/min and 60 writes/min per key. Creates accept Idempotency-Key.