Skip to content

Customers

Customers represent the people and businesses that owe you money. They are linked to invoices, credit memos, and payment records.

Create a Customer

bash
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_cents is in cents. 5000000 = $50,000.00 credit limit.

The response data is the new customer:

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

bash
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

bash
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

bash
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.):

bash
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:

bash
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:

bash
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 a category for a customer from its name, email, website and address (optional email, website and address query parameters); the response has category, subcategory and confidence. POST /api/v1/customers/auto-categorize schedules categorization for uncategorized customers (up to 500 per call) in the background and returns submitted and status (processing or noop) immediately.
  • POST /api/v1/customers/analyze-payment-behavior summarises 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

bash
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