# 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

```bash
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" }
  }'
```

```node
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();
```

```python
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
<?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:

```json
{
  "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

```bash
# 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"
```

```node
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" }),
});
```

```python
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

```json
{
  "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`.

