Customers
Customers represent the people and businesses that owe you money. They are linked to invoices, credit memos, and payment records.
Create a Customer
curl -X POST "https://api.ondayzero.com/api/v1/customers" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"email": "billing@acme.com",
"phone": "+1-555-0100",
"address": "123 Main St, San Francisco, CA 94105",
"credit_limit_cents": 5000000
}'
credit_limit_centsis in cents.5000000= $50,000.00 credit limit.
The response data is the new customer:
{
"success": true,
"data": {
"id": "019ab37c-c057-7000-8000-000000000001",
"business_id": "019ab37c-b051-7000-8000-000000000001",
"name": "Acme Corp",
"email": "billing@acme.com",
"phone": "+1-555-0100",
"address": "123 Main St, San Francisco, CA 94105",
"credit_limit_cents": 5000000,
"status": "active",
"stripe_customer_id": null,
"is_deleted": false,
"created_at": "2026-10-14T15:02:11Z",
"updated_at": "2026-10-14T15:02:11Z"
}
}name (max 255 chars) and email (a valid address) are required, and each email must be unique within the business: creating a second customer with an existing email returns 400. If Stripe is connected, a matching Stripe customer is created and its ID is returned as stripe_customer_id. Optional: tax_id, website, notes, category, status (active — the default — inactive or blocked), default_payment_term_id (see Payment Terms) and invoice_footer (text printed on this customer's invoices).
default_payment_term_id must be a term of this business (otherwise 404); PUT can change or clear it (see Update a Customer). The app's invoice form uses it to suggest the due date for this customer, but the invoice API does not apply it: send due_date, and payment_term_id if you want the term recorded, on each invoice. The Payment Terms page is switched off in the DayZero app for now and new businesses start with no terms, so create or seed them through the Payment Terms API before setting this field.
List Customers
curl "https://api.ondayzero.com/api/v1/customers?search=acme&limit=25" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID"Filter Parameters
| Parameter | Type | Description |
|---|---|---|
search |
string | Fuzzy (trigram) search by name (1-200 chars) |
email |
string | Filter by email address |
status |
string | active, inactive or blocked |
id |
UUID | Get specific customer by ID |
Plus the standard pagination parameters (sort_by accepts any customer column, e.g. name or created_at). Soft-deleted customers are left out of the list.
Get a Customer
curl "https://api.ondayzero.com/api/v1/customers/{customer_id}" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID"The response includes the fields you set plus id, business_id, created_at, updated_at, stripe_customer_id, registry_id / registry_name (the linked counterparty registry entry, if any), is_deleted and contacts.
Update a Customer
curl -X PUT "https://api.ondayzero.com/api/v1/customers/{customer_id}" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corporation",
"website": "https://acme.com"
}'Send only the fields that change. PUT applies name, email, address, phone, tax_id, website, notes, credit_limit_cents, status, category, invoice_footer and default_payment_term_id; an empty name, email or address is ignored, so those cannot be cleared. On update, invoice_footer sets the footer; "" clears it (invoices fall back to the business footer); omitted or null leaves it. default_payment_term_id must be a term of this business (otherwise 404); send null to clear it. If Stripe is connected, a new name or email is also pushed to the Stripe customer (best effort).
Contacts
Each customer can have multiple contacts (billing, technical, etc.):
curl -X POST "https://api.ondayzero.com/api/v1/customers/{customer_id}/contacts" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Smith",
"email": "jane@acme.com",
"role": "Accounts Payable",
"is_primary": true
}'Only name is required; email, phone, role (max 100 chars) and notes are optional.
List contacts: GET /api/v1/customers/{customer_id}/contacts. Fetch, update or remove one with GET / PUT / DELETE /api/v1/customers/{customer_id}/contacts/{contact_id}.
Bulk Upload
Download the CSV template with GET /api/v1/customers/bulk-upload/template (returns the CSV as csv_content_stringified and csv_content_base64), then import:
curl -X POST "https://api.ondayzero.com/api/v1/customers/bulk-upload" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID" \
-F "file=@customers.csv"CSV columns: name, email, phone, address, tax_id, website, notes, category, status, credit_limit (in dollars, not cents).
name and email are required in every row, and a file may hold up to 1,000 data rows. Rows whose email appears earlier in the file or already exists are skipped rather than failing the upload. The response includes created and skipped counts and any row-level errors.
Duplicate Detection
Before creating a customer, check for potential duplicates:
curl -X POST "https://api.ondayzero.com/api/v1/customers/check-duplicates?name=Acme%20Corp&email=billing@acme.com" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID"Returns has_duplicates and a duplicates list of similar existing customers with similarity_score (0.0-1.0) and match_type (name, email, tax_id or phone). name is required; email, tax_id and exclude_id (ignore one customer, e.g. the one you're editing) are optional.
AI Helpers
POST /api/v1/customers/categorize?name=…suggests acategoryfor a customer from its name, email, website and address (optionalemail,websiteandaddressquery parameters); the response hascategory,subcategoryandconfidence.POST /api/v1/customers/auto-categorizeschedules categorization for uncategorized customers (up to 500 per call) in the background and returnssubmittedandstatus(processingornoop) immediately.POST /api/v1/customers/analyze-payment-behaviorsummarises how promptly customers pay, from their invoice history.
Customer Statements
GET /api/v1/customer-statements/{customer_id} builds an A/R statement (opening balance, invoice, payment and credit activity with a running balance, closing balance and an aging summary). Optional query parameters: start_date (defaults to one year ago), end_date (defaults to today) and as_of_date (aging date, defaults to end_date). POST /api/v1/customer-statements/{customer_id}/send emails it to the customer's address on file; the body accepts the same three dates plus custom_message and cc_email.
Delete a Customer
curl -X DELETE "https://api.ondayzero.com/api/v1/customers/{customer_id}" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID"Deletion is a soft delete: the customer is hidden from list results but remains retrievable by ID so invoices and other records that reference it stay intact. Deleting an already-deleted customer returns 404.
MCP Tools
Regent and MCP clients can read and write customers with list_customers, get_customer, create_customer, bulk_create_customers and update_customer, and read balances with get_customer_balance, get_customer_cash_receipts and get_customer_statement. The customer list is also available through export_dataset with data_type set to customers. See MCP Server.
Errors
| Status | code |
Cause |
|---|---|---|
400 |
CONFLICT_001 |
A customer with this email already exists in the business (create) |
400 |
GEN_002 |
Bulk upload: file is not UTF-8, has no name/email header, has no data rows, or has more than 1,000 |
404 |
NOT_FOUND_008 |
Customer not found, soft-deleted (on delete), or owned by another business |
404 |
NOT_FOUND_001 |
default_payment_term_id does not exist in this business |
400 |
GEN_002 |
Body failed validation: missing name/email, invalid email, status not active/inactive/blocked, negative credit_limit_cents |