Customers API
Create, find, update, and delete team-scoped customer records through public API v1.
Summary
Customer operations use customers.read or customers.write and return records only from the credential's team.
Create, read, update, and delete return the customer representation, including computed activity and engagement fields. Customer writes also require writable workspace billing.
Capabilities
Endpoints
| Method | Endpoint | Scope | Purpose |
|---|---|---|---|
| GET | /customers | customers.read | List and filter customers |
| POST | /customers | customers.write | Create or upsert a customer |
| GET | /customers/{id} | customers.read | Retrieve one customer |
| PATCH | /customers/{id} | customers.write | Upsert by path ID with name and email |
| DELETE | /customers/{id} | customers.write | Delete a customer |
Prefix each endpoint with the environment base URL, such as https://api.eigenn.io/v1.
Prerequisites
Use a bearer credential for the intended team with the scope required by the operation. User-linked credentials also depend on current team membership; Viewer membership removes write scopes. Use an isolated test customer before connecting a production import.
Concepts
Important Field Rules
- Email fields use email format validation.
- Use an ISO 3166-1 alpha-2 countryCode, such as US. The request field is a string; it does not validate membership in the ISO list.
- peppolId requires four digits, a colon, and an identifier containing letters, numbers, hyphens, periods, underscores, or tildes.
- Tags are objects with an existing team tag's UUID and a name. Supplying a name does not create or rename a tag.
- customFields accepts a map of string, number, boolean, or null values. Sending this map replaces the stored map; include values you want to retain.
- Cursor values are opaque.
Workflow
List Customers
GET /customers supports:
- q text search
- sort tuple
- cursor
- pageSize from 1 to 100
- tag IDs
- country codes
- tab filters: all, first-time, repeat, recent, unpaid, or paid
- start and end creation dates in YYYY-MM-DD format, inclusive in UTC
Country filters match a stored country name or code. First-time and repeat use invoice counts including drafts, scheduled, and canceled invoices. The recent tab covers customers created in the last 30 days. These filters do not describe a customer's payment history by themselves.
The response contains data and cursor metadata. It does not include a total count.
curl --request GET \
--url "https://api.eigenn.io/v1/customers?pageSize=25&q=acme" \
--header "Authorization: Bearer $EIGENN_API_TOKEN"Create or Upsert
POST /customers needs:
- name
Optional fields include billing email, contact, phone, website, address, country code, VAT number, Peppol ID, note, and tags.
curl --request POST \
--url https://api.eigenn.io/v1/customers \
--header "Authorization: Bearer $EIGENN_API_TOKEN" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: customer-acme-2026-07-11" \
--data '{
"name": "Acme Corporation",
"email": "[email protected]",
"billingEmail": "[email protected]",
"countryCode": "US"
}'Upsert matches a supplied id, not a name or email address. Without an ID, repeated creates can produce separate customers with the same name and email. An unused supplied UUID creates a record; an ID owned by another team is rejected. Keep your external-to-Eigenn ID mapping.
POST returns 201 with the stored record, matching the OpenAPI contract. If a network or server error prevents you from receiving a response, reconcile the customer list before retrying a create.
The public request schema does not advertise an arbitrary externalId or metadata object. Store joins with documented fields and the returned Eigenn customer ID.
Retrieve a Customer
GET /customers/{id} returns the team's customer details, tags, custom fields, counts, overdue amount, and computed activity and engagement fields using the same representation as the list. Missing and other-team IDs return 404 Customer not found.
Update a Customer
PATCH /customers/{id} requires name and email, just like POST. The path ID takes precedence over a body ID. An unused path UUID can create a customer; this is not an update-only operation.
Omitted optional contact and address fields stay unchanged on an existing record. Omitting tags preserves existing associations. Sending a tag array replaces the selected set; send [] to remove all tags. A supplied customFields map replaces the map rather than merging individual keys. Explicit null clears nullable fields.
Use a new Idempotency-Key for each logical change. For current cached responses, the guard compares a hash of the exact request body and returns 422 if it differs; even JSON whitespace or field order can matter. Older cache entries without a body hash can still replay their original response. Use one key for one unchanged request. Failed responses are not cached. Reconcile stored state before retrying after a network or server failure.
Delete a Customer
DELETE /customers/{id} returns the customer details captured before deletion. Missing and other-team IDs return 404.
Deletion returns 409 when the customer has scheduled, unpaid, or overdue invoices, an active or paused recovery sequence, or any Tracker project. Resolve those dependencies before retrying. After an allowed deletion, retained invoices lose their customer link; customer notes, tasks, file records, contacts, and completed recovery records can be deleted through dependent relationships. Stored file objects are not removed by this operation. Review the customer deletion guidance before removal.
Behavior specification
Common Errors
| Status | Cause |
|---|---|
| 401 | Missing or invalid bearer token |
| 402 | Read-only workspace billing; code BILLING_WRITE_BLOCKED |
| 403 | Missing customers.read or customers.write |
| 404 | Customer is not found in the authenticated team |
| 400 or 422 | Request validation failed; inspect the returned body |
| 409 | Customer deletion is blocked by dependencies, or another keyed request is still in progress |
| 429 | Resource limit exceeded |
| 500 | Server failure; reconcile stored state before retrying a write |
| 503 | The idempotency guard is unavailable |
Billing restrictions
POST, PATCH, and DELETE return 402 with code: "BILLING_WRITE_BLOCKED" before changing a customer when billing is read-only. GET and list requests remain available. Active and trialing subscriptions permit writes, as does past-due billing during its seven-day grace period. A workspace without a subscription is read-only. See Billing and Usage for the full state policy and recovery steps.
Diagrams
Rendering diagram…
Screenshots
Not applicable: this page describes an API or a non-visual workflow rather than an application screen.
Verification
- With writable billing, create one synthetic customer and confirm a 201 response with its ID.
- Retrieve the customer, update its name, and confirm the returned name matches the saved record.
- Delete the disposable customer and confirm the response identifies it and the customer is absent from the team's records.
- With read-only billing, confirm writes return 402 with BILLING_WRITE_BLOCKED and leave records unchanged, while reads still work.
- Restore billing and confirm the same credential can write again, subject to its scopes and role.