Customers
Manage customer records and look them up by phone or email
Customers are the people (or businesses) you do work for. Each customer record holds contact details, an optional business name, and a billing address. Customers are scoped per organisation.
The devices you've serviced for a customer are a separate subcollection managed through the Devices endpoints, not an embedded field on the customer.
Endpoints
- GET
/organisations/{orgId}/customers— List customers (paginated) - POST
/organisations/{orgId}/customers— Create customer - GET
/organisations/{orgId}/customers/lookup— Find by phone or email (exact match) - GET
/organisations/{orgId}/customers/{customerId}— Get customer - PUT
/organisations/{orgId}/customers/{customerId}— Update customer - DELETE
/organisations/{orgId}/customers/{customerId}— Delete customer (defaultcascade=true)
List is ordered by firstName (customers created in the app have no
createdAt and would be invisible if we ordered by that field). Delete
cascades devices, jobs, and their calendar events. A cascade that would
delete jobs requires both customers:delete and tickets:delete
(403 insufficient_scope with
requiredScopes: ["customers:delete","tickets:delete"] if either is
missing). Outstanding form gates on any job return 409
FORM_GATE_DELETE. Pass ?cascade=false to refuse when jobs exist
(409 CUSTOMER_HAS_TICKETS).
Customer-form workflow
The dedicated lookup endpoint runs an exact-match query server-side and
is designed for customer-facing intake forms. Use it to find an existing
record before booking a job, then create a job with the returned id. No
composite Firestore index is required — phone and email are queried in
parallel and deduplicated by document id.
Required scopes: customers:read / customers:write / customers:delete
(plus tickets:delete when a cascade would delete jobs).
firstName is required on create. Everything else is optional. On update every
field is optional — send only what you want to change. The list endpoint
supports limit, startAfter (cursor), and search (matches name, email or
business).
Machine-readable version: OpenAPI spec.