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.