Customers
Create, list, retrieve, and update merchant customers in Tokeflow — the canonical buyer records that anchor transactions, orders, and subscriptions.
A Customer is the canonical buyer record for a single merchant. It stores the buyer's identity (email, name, phone, tax document) and a billing address, and it anchors the buyer's transactions, orders, and subscriptions under one record.
Customers are unique by email per merchant. Creating a customer is idempotent on email: if a customer with the same email already exists for the merchant, Tokeflow returns the existing record instead of creating a duplicate. This makes customer creation safe to retry from checkout and server-to-server flows.
Tokeflow is a payment orchestration platform — it standardizes buyer records across every connected provider so you keep one customer identity regardless of which provider ultimately handles a charge. Tokeflow does not process or settle funds; that is handled by your connected providers.
The customer object
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier, prefixed cust_. |
merchant_id | string | Merchant that owns this customer, prefixed mrc_. |
email | string | null | Buyer email. Stored lowercase; unique per merchant. |
name | string | null | Buyer full name. |
phone | string | null | Buyer phone in E.164 format (e.g. +5511999990000). |
document_type | string | null | One of cpf, cnpj, passport, tax_id. |
metadata | object | null | Arbitrary key/value pairs you attach. Max 5 KB serialized. |
created_at | string | Creation timestamp (ISO 8601 UTC). |
updated_at | string | Last-update timestamp (ISO 8601 UTC). |
The tax document number (document_number) and billing_address are accepted on write for compliance and processing, but they are never returned in API responses. The customer object exposes document_type only.
Authentication & scopes
All customer endpoints require a Merchant secret key (sk_live_mer_…). The merchant is inferred from the key, so you never pass a merchant_id. Use the customers:read scope for read operations and customers:write for create/update.
Authorization: Bearer sk_live_mer_xxxxxxxxxxxxxxxxxxxxxxxxSecret keys are server-side only. Never embed an sk_* key in browser or mobile code. See Authentication for key anatomy and scopes.
POST/api/v1/customers
Auth: Merchant key (scope customers:write)
Creates a customer for the merchant resolved from the API key. Idempotent by email: if a customer with the same email already exists, the existing record is returned with HTTP 200; a new customer returns HTTP 201.
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Buyer email. Normalized to lowercase; unique per merchant. |
name | string | Yes | Buyer full name. |
phone | string | Yes | E.164 phone, at least 10 digits incl. country/area code (e.g. +5511999990000). |
document_type | string | Yes | One of cpf, cnpj, passport, tax_id. |
document_number | string | Yes | Tax document (max 40 chars). Stored encrypted at rest; never returned. |
billing_address | object | Yes | Billing address (see below). |
metadata | object | No | Arbitrary key/value pairs. Max 5 KB serialized. |
billing_address object
| Field | Type | Required | Description |
|---|---|---|---|
line_1 | string | Yes | Street and number (max 255). |
line_2 | string | No | Apartment, suite, etc. (max 255). |
zip_code | string | Yes | ZIP/postal code (max 20). |
city | string | Yes | City (max 100). |
state | string | Yes | State/province code (max 100). |
country | string | Yes | ISO 3166-1 alpha-2 country code, e.g. BR (max 2). |
Send an Idempotency-Key header on create. Combined with email idempotency, this guarantees retries never produce duplicate customers and return the original result.
Request
curl -X POST https://api.tokeflow.com/api/v1/customers \
-H "Authorization: Bearer sk_live_mer_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 4f1d8c2a-9b07-4f3e-9a1c-7e2b6d0c5a11" \
-d '{
"email": "joao@example.com",
"name": "Joao da Silva",
"phone": "+5511999990000",
"document_type": "cpf",
"document_number": "123.456.789-00",
"billing_address": {
"line_1": "Av Paulista, 1000",
"line_2": "Apto 42",
"zip_code": "01310-100",
"city": "São Paulo",
"state": "SP",
"country": "BR"
},
"metadata": { "source": "checkout_web" }
}'Response 201 Created
{
"success": true,
"data": {
"id": "cust_a1b2c3d4e5",
"merchant_id": "mrc_9f8e7d6c5b",
"email": "joao@example.com",
"name": "Joao da Silva",
"created_at": "2026-01-15T12:30:00.000Z"
},
"request_id": "req_8f3c1a2b4d",
"timestamp": "2026-01-15T12:30:00.000Z"
}The create response is a confirmation payload containing id, merchant_id, email, name, and created_at. To read the full customer object, call GET /api/v1/customers/:id.
When the email already exists, the identical body is returned with status 200 OK instead of 201.
GET/api/v1/customers
Auth: Merchant key (scope customers:read)
Lists customers for the merchant, newest first. Supports an email filter and pagination.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
email | string | No | Exact, case-insensitive email filter. |
page | integer | No | Page number (default 1, min 1). |
limit | integer | No | Items per page (default 20, max 100). |
Request
curl -X GET "https://api.tokeflow.com/api/v1/customers?page=1&limit=20&email=joao@example.com" \
-H "Authorization: Bearer sk_live_mer_xxxxxxxxxxxxxxxxxxxxxxxx"Response 200 OK
{
"success": true,
"data": [
{
"id": "cust_a1b2c3d4e5",
"merchant_id": "mrc_9f8e7d6c5b",
"email": "joao@example.com",
"name": "Joao da Silva",
"phone": "+5511999990000",
"document_type": "cpf",
"metadata": { "source": "checkout_web" },
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 42,
"total_pages": 3,
"has_next": true,
"has_prev": false
}
},
"request_id": "req_8f3c1a2b4d",
"timestamp": "2026-01-15T12:30:00.000Z"
}Because email is unique per merchant, filtering by email returns at most one customer — a convenient lookup before deciding whether to create.
GET/api/v1/customers/:customerId
Auth: Merchant key (scope customers:read)
Retrieves a single customer by ID.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | Customer ID, prefixed cust_. |
Request
curl -X GET https://api.tokeflow.com/api/v1/customers/cust_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_xxxxxxxxxxxxxxxxxxxxxxxx"Response 200 OK
{
"success": true,
"data": {
"id": "cust_a1b2c3d4e5",
"merchant_id": "mrc_9f8e7d6c5b",
"email": "joao@example.com",
"name": "Joao da Silva",
"phone": "+5511999990000",
"document_type": "cpf",
"metadata": { "source": "checkout_web" },
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
},
"request_id": "req_8f3c1a2b4d",
"timestamp": "2026-01-15T12:30:00.000Z"
}Response 404 Not Found
{
"error": {
"type": "not_found_error",
"code": "RESOURCE_NOT_FOUND",
"message": "Customer with ID \"cust_a1b2c3d4e5\" not found",
"details": {},
"request_id": "req_8f3c1a2b4d",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}PATCH/api/v1/customers/:customerId
Auth: Merchant key (scope customers:write)
Updates a customer. All body fields are optional; only the fields you send are changed. Changing email to one already used by another customer of the merchant returns 409 Conflict.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | Customer ID, prefixed cust_. |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
email | string | No | New email. Must be unique per merchant. |
name | string | No | New name. |
phone | string | No | New E.164 phone. |
document_type | string | No | One of cpf, cnpj, passport, tax_id. |
document_number | string | No | Tax document (max 40 chars). Never returned. |
metadata | object | No | Replaces stored metadata. Max 5 KB serialized. |
billing_address | object | No | Billing address (same shape as on create). |
Request
curl -X PATCH https://api.tokeflow.com/api/v1/customers/cust_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Joao P. da Silva",
"phone": "+5511988887777",
"metadata": { "source": "checkout_web", "tier": "gold" }
}'Response 200 OK
{
"success": true,
"data": {
"id": "cust_a1b2c3d4e5",
"merchant_id": "mrc_9f8e7d6c5b",
"email": "joao@example.com",
"name": "Joao P. da Silva",
"phone": "+5511988887777",
"document_type": "cpf",
"metadata": { "source": "checkout_web", "tier": "gold" },
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T13:05:00.000Z"
},
"request_id": "req_8f3c1a2b4d",
"timestamp": "2026-01-15T13:05:00.000Z"
}Response 409 Conflict
Returned when the new email already belongs to another customer of the merchant. The conflicting customer's ID is included in details.
{
"error": {
"type": "conflict_error",
"code": "CUSTOMER_EMAIL_EXISTS",
"message": "A customer with this email already exists for this merchant",
"details": { "existing_customer_id": "cust_f6e5d4c3b2" },
"request_id": "req_8f3c1a2b4d",
"timestamp": "2026-01-15T13:05:00.000Z"
}
}Errors
Customer endpoints use the standard Tokeflow error envelope. Common cases:
| HTTP | type | When |
|---|---|---|
| 400 | validation_error | Missing/invalid field, bad document_type, malformed phone, or metadata over 5 KB. |
| 401 | authentication_error | Missing or invalid API key. |
| 403 | authorization_error | Key lacks the required scope, or a non-Merchant key was used. |
| 404 | not_found_error | No customer with that ID for this merchant. |
| 409 | conflict_error | Email already in use by another customer (PATCH). |
| 429 | rate_limit_error | Rate limit exceeded — back off exponentially. |
Related
- Transactions — charge a customer.
- Orders — group a customer's purchases.
- Subscriptions — recurring billing for a customer.
- Authentication — keys, scopes, and headers.