Customers
- 5 endpoints
Identifiers are UUIDs.
List customers#
GET /customers
Lists the customers of the caller's space, newest first, optionally only those with an email (whatever its casing: several may share one), the one with a reference (0 or 1 item), or only the verified (or unverified) ones.
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl https://api.qbitflow.app/v2/customers \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a page of Customer ({ items, hasMore, nextCursor? })const page = await client.customers.list({ limit: 10 });for (const customer of page.items) { console.log(`${customer.uuid} ${customer.email}`);}if (page.hasMore) { // Pass it back, verbatim, as the cursor param for the next page. console.log(`next cursor: ${page.nextCursor}`);}page = client.customers.list(limit=20)for customer in page.items: print(customer.uuid, customer.email, customer.reference)if page.has_more: print("next page: cursor =", page.next_cursor) # pass it back, verbatim, as cursor=page, err := client.Customers.List(ctx, &qbitflow. CustomerListParams{Limit: 20})if err != nil { log.Fatal(err)}for _, c := range page.Items { fmt.Println(c.UUID, c.Email)}if page.HasMore { // Pass it back, verbatim, as CustomerListParams.Cursor for the next page. fmt.Println("next cursor:", page.NextCursor)}$page = $client->customers->list(new \QBitFlow\Params\CustomerListParams( limit: 20));foreach ($page->items as $customer) { echo "{$customer->uuid} {$customer->email}\n";}if ($page->hasMore) { $cursor = $page->nextCursor; // pass it back, verbatim, as `cursor` for the next page}Query parameters
limitintegerOptionalPage size: 20 when absent or ≤ 0, at most 100 (a larger one is clamped, not refused)
cursorstringOptionalThe previous page's nextCursor, verbatim; omit for the first page
max=200emailstringOptionalOnly the customers with this email, whatever its casing (several may share one)
emailmax=254referencestringOptionalOnly the customer with this reference (0 or 1)
referencemax=100verifiedbooleanOptionaltrue: only the customers the merchant created; false: only those created from an email a payer typed at checkout
Response
A page: { "items": Customer[], "hasMore": …, "nextCursor"?: … }. Pass nextCursor back as cursor for the next page; it is absent when hasMore is false.
12 fields of each Customer
uuidstringIts id (a UUIDv7)
firstNamestringCustomer's first name (lastName holds the last name, when given)
lastNamestringOptionalCustomer's last name; absent when not given
emailstringCustomer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)
verifiedbooleanVerified: the merchant created this customer (the API, the dashboard), so the merchant vouches for its details. false: QBitFlow created it from the email a payer typed at a checkout that named no customer: nobody proved the payer owns that address. A checkout never links a typed email to a verified customer: it reuses the space's unverified customer with that email, or creates one. Customers created before this field existed are verified.
phoneNumberstringOptionalOptional fields Customer's phone number; absent when not given
addressstringOptionalCustomer's physical address; absent when not given
referencestringOptionalThe merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one
metadatamapOptionalMetadata is the merchant's own data on the customer (naming: 50 keys, keys of 1–40 characters, values of at most 500). Always present ({} when empty). Never copied onto the customer's payments or subscriptions
Only for the resource's owner
createdAtstringWhen the customer was created
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Create customer#
POST /customers
Creates a customer in the caller's space (verified: the merchant vouches for it).
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Accepted: send one per operation
- Success
- 201
request.sh
curl https://api.qbitflow.app/v2/customers \ -H "X-API-Key: $QBITFLOW_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "firstName": "Ada", "lastName": "Lovelace", "email": "ada@example.com", "reference": "crm-5521" }'# 201: a CustomerBody
firstNamestringRequiredCustomer's first name (lastName holds the last name; see the name validator)
namemin=2max=100lastNamestringOptionalCustomer's last name, optional
namemin=1max=100emailstringRequiredCustomer's email address: stored lowercase. Not unique: several customers of a space may share one
emailmax=254phoneNumberstringOptionalOptional fields Customer's phone number (see the phone validator)
max=32phoneaddressstringOptionalCustomer's physical address
max=500textreferencestringOptionalOptional merchant reference (e.g., your own customer id). Unique per owner and mode
referencemax=100metadatamapOptionalMetadata is the merchant's own data (optional): at most 50 keys of 1–40 characters (letters, digits, _ - . :), values of at most 500 characters; a key set to "" isn't stored
metadata
Response
A Customer.
12 fields of Customer
uuidstringIts id (a UUIDv7)
firstNamestringCustomer's first name (lastName holds the last name, when given)
lastNamestringOptionalCustomer's last name; absent when not given
emailstringCustomer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)
verifiedbooleanVerified: the merchant created this customer (the API, the dashboard), so the merchant vouches for its details. false: QBitFlow created it from the email a payer typed at a checkout that named no customer: nobody proved the payer owns that address. A checkout never links a typed email to a verified customer: it reuses the space's unverified customer with that email, or creates one. Customers created before this field existed are verified.
phoneNumberstringOptionalOptional fields Customer's phone number; absent when not given
addressstringOptionalCustomer's physical address; absent when not given
referencestringOptionalThe merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one
metadatamapOptionalMetadata is the merchant's own data on the customer (naming: 50 keys, keys of 1–40 characters, values of at most 500). Always present ({} when empty). Never copied onto the customer's payments or subscriptions
Only for the resource's owner
createdAtstringWhen the customer was created
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
Errors
- 409idempotency_
key_ in_ use the
Idempotency-Key's first request is still running (idempotency): retry once it answered - 422idempotency_
key_ reused the
Idempotency-Keywas used for another request (idempotency): send a new key - 413request_
too_ large an
Idempotentroute's body over 1 MiB (idempotency)
Also the common codes, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Get customer#
GET /customers/
A customer of the caller's space by its uuid.
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl "https://api.qbitflow.app/v2/customers/$CUSTOMER_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a CustomerPath parameters
uuidstringRequired
Response
A Customer.
12 fields of Customer
uuidstringIts id (a UUIDv7)
firstNamestringCustomer's first name (lastName holds the last name, when given)
lastNamestringOptionalCustomer's last name; absent when not given
emailstringCustomer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)
verifiedbooleanVerified: the merchant created this customer (the API, the dashboard), so the merchant vouches for its details. false: QBitFlow created it from the email a payer typed at a checkout that named no customer: nobody proved the payer owns that address. A checkout never links a typed email to a verified customer: it reuses the space's unverified customer with that email, or creates one. Customers created before this field existed are verified.
phoneNumberstringOptionalOptional fields Customer's phone number; absent when not given
addressstringOptionalCustomer's physical address; absent when not given
referencestringOptionalThe merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one
metadatamapOptionalMetadata is the merchant's own data on the customer (naming: 50 keys, keys of 1–40 characters, values of at most 500). Always present ({} when empty). Never copied onto the customer's payments or subscriptions
Only for the resource's owner
createdAtstringWhen the customer was created
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Update customer#
PATCH /customers/
Changes a customer of the caller's space: only the fields sent (metadata merges).
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl -X PATCH "https://api.qbitflow.app/v2/customers/$CUSTOMER_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "lastName": "King" }'# 200: a CustomerPath parameters
uuidstringRequired
Body
firstNamestringOptionalCustomer's first name
namemin=2max=100lastNamestringOptionalCustomer's last name, or "" to clear it
max=100len=0|nameemailstringOptionalCustomer's email address (stored lowercase)
emailmax=254phoneNumberstringOptionalCustomer's phone number (see the phone validator), or "" to clear it
max=32len=0|phoneaddressstringOptionalCustomer's physical address, or "" to clear it
max=500len=0|textmetadatamapOptionalMetadata updates the merchant's data by merging: a key set to a value sets it, a key set to "" deletes it, keys not named are kept (absent: unchanged). At most 50 keys after the merge
metadata
Response
A Customer.
12 fields of Customer
uuidstringIts id (a UUIDv7)
firstNamestringCustomer's first name (lastName holds the last name, when given)
lastNamestringOptionalCustomer's last name; absent when not given
emailstringCustomer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)
verifiedbooleanVerified: the merchant created this customer (the API, the dashboard), so the merchant vouches for its details. false: QBitFlow created it from the email a payer typed at a checkout that named no customer: nobody proved the payer owns that address. A checkout never links a typed email to a verified customer: it reuses the space's unverified customer with that email, or creates one. Customers created before this field existed are verified.
phoneNumberstringOptionalOptional fields Customer's phone number; absent when not given
addressstringOptionalCustomer's physical address; absent when not given
referencestringOptionalThe merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one
metadatamapOptionalMetadata is the merchant's own data on the customer (naming: 50 keys, keys of 1–40 characters, values of at most 500). Always present ({} when empty). Never copied onto the customer's payments or subscriptions
Only for the resource's owner
createdAtstringWhen the customer was created
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Delete customer#
DELETE /customers/
Deletes a customer of the caller's space (204). Its payments and subscriptions keep naming it (customer.deleted).
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 204
request.sh
curl -X DELETE "https://api.qbitflow.app/v2/customers/$CUSTOMER_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 204: no contentPath parameters
uuidstringRequired
Response
No JSON body: the edge cases below say what it answers.
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Edge cases
The contract's rules for these routes: Authentication & roles · Spaces · The customer · Validation · References and emails · Verified customers · Reads · Update (PATCH, partial) · Delete · Customers and checkouts.
Read all 10
Authentication & roles
- There is no role gate.
Spaces
- Every customer belongs to one space (spaces): the organization's or a member's, in one mode. Every route works in the request's space: the team (and organization keys) sees the organization's customers, a member (and their keys) their own, and an organization key with
On-Behalf-Of: <userUuid>(or the dashboard switched into the member's space) the member's. - A customer of another space, of another organization or of the other mode is never listed (the
emailandreferencefilters included: an empty list), and answers404by uuid: existence is never revealed.
The customer
firstName(it wasname),email(lowercase) andverifiedare always present;lastName,phoneNumber,addressandreferenceare absent when not set (never"").metadata: the merchant's own data; always present,{}when empty.verified:truefor a customer the merchant created (this API, the dashboard);falsefor one QBitFlow recorded from the email a payer typed at a checkout that named no customer (§ Verified customers).userUuidis present for a member's customer and absent for the organization's own. A customer names no organization.deletedAtis never emitted.- The
customerobject transactions carry (Payment.customer,Subscription.customer, …,customer.Summary) names it the same way:uuid,firstName,lastName?,email,reference?,deleted(always present:trueonce the merchant deleted it).
Validation
- Create (
CreateCustomerDto) requiresfirstNameandemail;lastName,phoneNumber,address,referenceandmetadataare optional. The v1nameis not read (a create withoutfirstName→400onfirstName). Success →201with the createdCustomer. - Create takes an
Idempotency-Key(idempotency): a create retried with the same key answers the first customer and creates no second one. firstName(length 2–100) andlastName(length 1–100): thenamevalidator: any letters, digits, spaces and punctuation on one line (O’Brien,Smith & Co,Dr. Who (II)), but no line break, no bidirectional control and none of< > { } [ ] ` \ | ; " ~ ^, so HTML/markup is rejected.email: a valid email address, at most 254 characters. It is stored lowercase (Ada@X.comis returned asada@x.com).phoneNumber: optional,phonevalidator (an optional leading+, then at least 6 of digits andspace . - ( ), starting and ending with a digit), at most 32 characters.address: optional,textvalidator, at most 500 characters.reference: optional on create,referencevalidator (1–100 characters: ASCII letters, digits and. _ : @ -); immutable afterwards (see below).metadata: at most 50 keys; a key is 1–40 characters ofA–Z a–z 0–9 _ - . :; a value a string of at most 500 characters without control or bidirectional characters. Too many keys or a bad key → onmetadata; a bad value → onmetadata.<key>. On create, a key set to""is not stored.- Every violation →
400validation_failednaming the field.
References and emails
- A
referenceis unique per space (the organization's, each member's, per mode) among its live customers: a deleted customer doesn't hold it. Reusing it within the same space →409(code: "unique_violation",details.field: "reference"; the value is never echoed). The same reference is allowed in another space (another member, another organization, the other mode). Several customers without a reference never collide. - Emails aren't unique (founder decision D17b): the merchant may create several customers with one email, in any casing. Only the unverified customers are one per email and space (§ Verified customers): changing an unverified customer's email to another unverified customer's →
409unique_violationonemail, the record unchanged.
Verified customers
- A checkout that names no customer records the payer's typed email as the space's one unverified customer with that email (any casing), created the first time, reused afterwards; it never links a payment to a customer the merchant created (verified), whatever its casing.
GET /customers?verified=true|falselists one kind only.
Reads
GET /customers/:uuidreturns a customer of the caller's space. Unknown, soft-deleted, or another space's uuid →404. A malformed:uuid→400validation_failedonuuid.GET /customersreturns the list envelope (pagination:{ items, hasMore, nextCursor? }) of the space's customers, newest first (createdAt, thenuuid), soft-deleted ones excluded: pages oflimit(default 20, at most 100: clamped, never refused; not an integer →400onlimit), the next page with?cursor=<nextCursor>(one the API didn't give →400oncursor). A full last page answershasMore: false(no empty trailing page). Filters, kept across pages:email=<address>: the customers with that email, whatever its casing (several may share one); not an email →400onemail;reference=<ref>: the customer with that reference: 0 or 1 item; not a reference →400onreference;verified=true|false.
- The v1 lookups
GET /customer/email/:emailandGET /customer/reference/:referenceare gone (no personal data or reference in a path): use the filters.
Update (PATCH, partial)
- Every field of
UpdateCustomerDtois optional: only the fields present in the body change, every omitted field is left unchanged. An empty body is a200no-op. Answers the customer as stored. - Cleared with
"":lastName,phoneNumberandaddress(then absent from the customer).firstNameandemailcan't be cleared:""→400on the field. - Present fields are validated exactly as on create. A present-but-invalid field →
400, and nothing of the update is applied. - A new
emailis stored lowercase. APATCHdoesn't changeverified. metadatamerges: absent → unchanged;{"k": "v"}setsk;{"k": ""}deletesk; keys not named are kept. The 50-key limit is checked on the merged result (over →400onmetadata, nothing applied).referenceis NOT updatable — areferencesent in the body is ignored; the other fields still apply (200).- Mass-assignment is prevented:
userUuid,uuid,test,verifiedandcreatedAtin the body are ignored; ownership and identity never change. - An unknown, deleted or another space's uuid →
404; a malformed:uuid→400.PUTis not a route.
Delete
DELETE /customers/:uuid→204with no body (it answered a message). It is a soft delete: the customer is removed from every read (404by uuid, absent from the list and its filters). Deleting it again, an unknown or another space's uuid →404; a malformed one →400.- A deleted customer frees its
emailandreference: a new customer may take them, and a checkout whose payer types that email records a new customer (the deleted one is never revived). Its past payments and subscriptions keep naming it (customer.deleted: true).
Customers and checkouts
- A checkout creates a customer only from the details its customer types (when the merchant named none: Checkout sessions § The customer), when the payment or subscription is recorded.
- A checkout's
customerReferencecreates no customer: the space's customer with that reference is linked if there is one, and the payment keeps the reference either way (customerReference). - Customer metadata is never copied onto its payments or subscriptions.
The codes named here
- 400validation_
failed path/query/body failed binding or validation, or a business rule about one input (a field named in
errors) - 409unique_
violation a unique value already exists (e.g. a duplicate
referenceoremail)