POST
/v1/customersCustomers
First-class end-user primitive. Tracks spend, enforces budgets, and scopes Client Tokens. Look up an existing customer by your own ID via GET /v1/customers?external_id=… (list filter) or GET /v1/customers/ext:<external_id> (direct fetch — also works on /wallet, /usage, /conversations, /client_tokens, /margin subroutes).
Request
- HTTP
- POST
- URL
- /v1/customers
- Auth
- api_key
Try it
# Create
curl https://api.fightclub.pro/v1/customers \
-H "Authorization: Bearer $FC_API_KEY" \
-d '{"external_id":"u_42","display_name":"Alice","monthly_budget_usd":50}'
# Look up by your external_id (two ways)
# 1. List filter — returns {data, has_more, next_cursor}
curl "https://api.fightclub.pro/v1/customers?external_id=u_42" \
-H "Authorization: Bearer $FC_API_KEY"
# 2. Direct fetch with ext: prefix — single object, also works on subroutes
curl https://api.fightclub.pro/v1/customers/ext:u_42 \
-H "Authorization: Bearer $FC_API_KEY"
curl https://api.fightclub.pro/v1/customers/ext:u_42/wallet \
-H "Authorization: Bearer $FC_API_KEY"Example response
{
"id": "cus_42",
"external_id": "tenant_42",
"display_name": "Acme Corp",
"status": "active",
"monthly_budget_usd": 50.0,
"rpm_limit": null,
"tpm_limit": null,
"default_billable_markup_pct": 20.0,
"default_billable_amount_usd": null,
"wallet_balance_usd": null,
"lifetime_spend_usd": 3.71,
"created_at": "2026-06-24T12:00:00.000Z"
}A representative 200 body. Ids and timestamps are illustrative.
Body parameters
| Name | Type | Default | Description |
|---|---|---|---|
| external_id | string | optional | Your own id for this customer (your tenant or user id). Lets you address them later by ext:<external_id> on any customer route. Unique within your account. |
| display_name | string | optional | Human-readable name shown in the dashboard. |
| monthly_budget_usd | number | optional | Monthly spend ceiling in USD (>= 0). When attributed spend would exceed it, calls for this customer return 402 customer_budget_exceeded — your wallet still has money. |
| rpm_limit | integer | optional | Per-customer requests-per-minute cap (positive integer). |
| tpm_limit | integer | optional | Per-customer tokens-per-minute cap (positive integer). |
| default_billable_markup_pct | number | optional | Markup added to raw cost to compute what this customer is billed (e.g. 20 = +20%). >= 0. Mutually exclusive with default_billable_amount_usd. |
| default_billable_amount_usd | number | optional | A flat billable amount per call instead of a markup. >= 0. Mutually exclusive with default_billable_markup_pct. |
| metadata | object | optional | Free-form JSON, up to 4 KB serialized. |
* required.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 20 | Page size for GET /v1/customers. |
| after | string | optional | Cursor: the id of the last row from the previous page. |
| status | string | optional | Filter by status: active | archived. |
| external_id | string | optional | Filter to the customer with this external_id (returns a single-item list). |
| order_by | string | created_desc | created_asc | created_desc | last_active_desc. |
Headers
| Header | Dir | Description |
|---|---|---|
| FC-Customer | req → | On any metered call (chat, embeddings, runs, file_search…): attribute that call's cost + usage to this customer id. |
| X-Ringside-Wallet-Balance | ← res | The customer's prepaid wallet balance after the call (present only when the customer carries a wallet). |
| X-Ringside-Wallet-Deducted | ← res | Amount debited from the customer wallet for this call. |
Response fields
| Name | Type | Description |
|---|---|---|
| id | string | Customer id (cus_*). |
| external_id | string | null | The external_id you set, or null. |
| display_name | string | null | The display name, or null. |
| status | string | active | archived. |
| monthly_budget_usd | number | null | Budget ceiling, or null if none. |
| rpm_limit | integer | null | Requests-per-minute cap, or null. |
| tpm_limit | integer | null | Tokens-per-minute cap, or null. |
| default_billable_markup_pct | number | null | Markup percent, or null. |
| default_billable_amount_usd | number | null | Flat billable amount, or null. |
| wallet_balance_usd | number | null | Prepaid wallet balance, or null if no wallet. |
| lifetime_spend_usd | number | Total attributed spend to date. |
| created_at | string | ISO 8601 timestamp (a string, not a unix integer). |
Errors
- 401
missing_tokenNo Authorization header was sent. - 401
invalid_auth_schemeThe scheme was neither Bearer nor Client. - 401
invalid_token_formatA Bearer token not prefixed ko_. - 401
invalid_tokenThe API key is unknown, revoked or expired. - 403
insufficient_scopeThe key is valid but lacks the required scope. - 400
invalid_rpm_limitrpm_limit must be a positive integer. - 400
invalid_tpm_limittpm_limit must be a positive integer. - 400
invalid_monthly_budget_usdmonthly_budget_usd must be a non-negative number. - 400
invalid_default_billable_markup_pctMust be a non-negative number. - 400
invalid_default_billable_amount_usdMust be a non-negative number. - 400
billable_defaults_mutexmarkup_pct and amount_usd are mutually exclusive — set at most one. - 400
metadata_too_largemetadata exceeds 4 KB serialized. - 404
resource_not_foundNo customer with that id or external_id. Also returned for cross-tenant access, so ids are not enumerable.
See the full error reference.
Related routes
| GET | /v1/customers | List customers (cursor pagination + the query filters above). |
| GET | /v1/customers/:id | Retrieve one. Accepts ext:<external_id> as the id. |
| PATCH | /v1/customers/:id | Update display_name, budgets, limits, billable defaults, status or metadata. |
| DELETE | /v1/customers/:id | Archive the customer (status becomes archived). |
| GET | /v1/customers/:id/wallet | Prepaid wallet balance + full transaction history. |
| POST | /v1/customers/:id/wallet/topup | Add credit to the customer wallet. |
| POST | /v1/customers/:id/wallet/adjust | Manual balance adjustment (credit or debit). |
| GET | /v1/customers/:id/usage | Usage + cost rollups for this customer. |
| GET | /v1/customers/:id/margin | Billable vs raw cost (your margin) for this customer. |
| POST | /v1/customers/:id/client_tokens | Mint a browser Client Token scoped to this customer. |
| POST | /v1/customers/:id/client_tokens/revoke_all | Revoke every active token for this customer. |
| DELETE | /v1/customers/:id/client_tokens/:jti | Revoke one token by its jti. |
| GET | /v1/customers/:id/conversations | List this customer's stateful conversations. |
Notes
- ·Every id route also accepts ext:<external_id> in place of the cus_ id — e.g. GET /v1/customers/ext:tenant_42/wallet — so you never have to store our id if you keep your own.
- ·default_billable_markup_pct and default_billable_amount_usd are mutually exclusive: set one to control what the customer is billed per call, or neither to bill raw cost.
- ·Cross-tenant access returns 404, not 403, so customer ids cannot be probed for existence.
- ·All body fields are optional on create; a bare POST /v1/customers makes an unnamed customer you can attribute spend to immediately.