API reference

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

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

  • limitintegerOptional

    Page size: 20 when absent or ≤ 0, at most 100 (a larger one is clamped, not refused)

  • cursorstringOptional

    The previous page's nextCursor, verbatim; omit for the first page

    max=200

  • emailstringOptional

    Only the customers with this email, whatever its casing (several may share one)

    emailmax=254

  • referencestringOptional

    Only the customer with this reference (0 or 1)

    referencemax=100

  • verifiedbooleanOptional

    true: 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
  • uuidstring

    Its id (a UUIDv7)

  • firstNamestring

    Customer's first name (lastName holds the last name, when given)

  • lastNamestringOptional

    Customer's last name; absent when not given

  • emailstring

    Customer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)

  • verifiedboolean

    Verified: 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.

  • phoneNumberstringOptional

    Optional fields Customer's phone number; absent when not given

  • addressstringOptional

    Customer's physical address; absent when not given

  • referencestringOptional

    The merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one

  • metadatamapOptional

    Metadata 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

  • createdAtstring

    When the customer was created

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

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

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 Customer

Body

  • firstNamestringRequired

    Customer's first name (lastName holds the last name; see the name validator)

    namemin=2max=100

  • lastNamestringOptional

    Customer's last name, optional

    namemin=1max=100

  • emailstringRequired

    Customer's email address: stored lowercase. Not unique: several customers of a space may share one

    emailmax=254

  • phoneNumberstringOptional

    Optional fields Customer's phone number (see the phone validator)

    max=32phone

  • addressstringOptional

    Customer's physical address

    max=500text

  • referencestringOptional

    Optional merchant reference (e.g., your own customer id). Unique per owner and mode

    referencemax=100

  • metadatamapOptional

    Metadata 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
  • uuidstring

    Its id (a UUIDv7)

  • firstNamestring

    Customer's first name (lastName holds the last name, when given)

  • lastNamestringOptional

    Customer's last name; absent when not given

  • emailstring

    Customer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)

  • verifiedboolean

    Verified: 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.

  • phoneNumberstringOptional

    Optional fields Customer's phone number; absent when not given

  • addressstringOptional

    Customer's physical address; absent when not given

  • referencestringOptional

    The merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one

  • metadatamapOptional

    Metadata 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

  • createdAtstring

    When the customer was created

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

Errors

Get customer#

GET /customers/:uuid

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

request.sh

curl "https://api.qbitflow.app/v2/customers/$CUSTOMER_UUID" \  -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a Customer

Path parameters

  • uuidstringRequired

Response

A Customer.

12 fields of Customer
  • uuidstring

    Its id (a UUIDv7)

  • firstNamestring

    Customer's first name (lastName holds the last name, when given)

  • lastNamestringOptional

    Customer's last name; absent when not given

  • emailstring

    Customer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)

  • verifiedboolean

    Verified: 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.

  • phoneNumberstringOptional

    Optional fields Customer's phone number; absent when not given

  • addressstringOptional

    Customer's physical address; absent when not given

  • referencestringOptional

    The merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one

  • metadatamapOptional

    Metadata 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

  • createdAtstring

    When the customer was created

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

Update customer#

PATCH /customers/:uuid

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

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 Customer

Path parameters

  • uuidstringRequired

Body

  • firstNamestringOptional

    Customer's first name

    namemin=2max=100

  • lastNamestringOptional

    Customer's last name, or "" to clear it

    max=100len=0|name

  • emailstringOptional

    Customer's email address (stored lowercase)

    emailmax=254

  • phoneNumberstringOptional

    Customer's phone number (see the phone validator), or "" to clear it

    max=32len=0|phone

  • addressstringOptional

    Customer's physical address, or "" to clear it

    max=500len=0|text

  • metadatamapOptional

    Metadata 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
  • uuidstring

    Its id (a UUIDv7)

  • firstNamestring

    Customer's first name (lastName holds the last name, when given)

  • lastNamestringOptional

    Customer's last name; absent when not given

  • emailstring

    Customer's email address, lowercase (stored and matched case-insensitively). Several customers of a space may share one (see verified)

  • verifiedboolean

    Verified: 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.

  • phoneNumberstringOptional

    Optional fields Customer's phone number; absent when not given

  • addressstringOptional

    Customer's physical address; absent when not given

  • referencestringOptional

    The merchant's reference of the customer (e.g. its id in the merchant's system), unique per space; absent without one

  • metadatamapOptional

    Metadata 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

  • createdAtstring

    When the customer was created

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

Delete customer#

DELETE /customers/:uuid

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

request.sh

curl -X DELETE "https://api.qbitflow.app/v2/customers/$CUSTOMER_UUID" \  -H "X-API-Key: $QBITFLOW_API_KEY"# 204: no content

Path parameters

  • uuidstringRequired

Response

No JSON body: the edge cases below say what it answers.

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 email and reference filters included: an empty list), and answers 404 by uuid: existence is never revealed.

The customer

  • firstName (it was name), email (lowercase) and verified are always present; lastName, phoneNumber, address and reference are absent when not set (never "").
  • metadata: the merchant's own data; always present, {} when empty.
  • verified: true for a customer the merchant created (this API, the dashboard); false for one QBitFlow recorded from the email a payer typed at a checkout that named no customer (§ Verified customers).
  • userUuid is present for a member's customer and absent for the organization's own. A customer names no organization. deletedAt is never emitted.
  • The customer object transactions carry (Payment.customer, Subscription.customer, …, customer.Summary) names it the same way: uuid, firstName, lastName?, email, reference?, deleted (always present: true once the merchant deleted it).

Validation

  • Create (CreateCustomerDto) requires firstName and email; lastName, phoneNumber, address, reference and metadata are optional. The v1 name is not read (a create without firstName → 400 on firstName). Success → 201 with the created Customer.
  • 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) and lastName (length 1–100): the name validator: 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 &lt; > { } [ ] ` \ | ; " ~ ^, so HTML/markup is rejected.
  • email: a valid email address, at most 254 characters. It is stored lowercase (Ada@X.com is returned as ada@x.com).
  • phoneNumber: optional, phone validator (an optional leading +, then at least 6 of digits and space . - ( ), starting and ending with a digit), at most 32 characters.
  • address: optional, text validator, at most 500 characters.
  • reference: optional on create, reference validator (1–100 characters: ASCII letters, digits and . _ : @ -); immutable afterwards (see below).
  • metadata: at most 50 keys; a key is 1–40 characters of A–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 → on metadata; a bad value → on metadata.<key>. On create, a key set to "" is not stored.
  • Every violation → 400 validation_failed naming the field.

References and emails

  • A reference is 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 → 409 unique_violation on email, 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|false lists one kind only.

Reads

  • GET /customers/:uuid returns a customer of the caller's space. Unknown, soft-deleted, or another space's uuid → 404. A malformed :uuid → 400 validation_failed on uuid.
  • GET /customers returns the list envelope (pagination: { items, hasMore, nextCursor? }) of the space's customers, newest first (createdAt, then uuid), soft-deleted ones excluded: pages of limit (default 20, at most 100: clamped, never refused; not an integer → 400 on limit), the next page with ?cursor=<nextCursor> (one the API didn't give → 400 on cursor). A full last page answers hasMore: 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 → 400 on email;
    • reference=<ref>: the customer with that reference: 0 or 1 item; not a reference → 400 on reference;
    • verified=true|false.
  • The v1 lookups GET /customer/email/:email and GET /customer/reference/:reference are gone (no personal data or reference in a path): use the filters.

Update (PATCH, partial)

  • Every field of UpdateCustomerDto is optional: only the fields present in the body change, every omitted field is left unchanged. An empty body is a 200 no-op. Answers the customer as stored.
  • Cleared with "": lastName, phoneNumber and address (then absent from the customer). firstName and email can't be cleared: "" → 400 on 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 email is stored lowercase. A PATCH doesn't change verified.
  • metadata merges: absent → unchanged; {"k": "v"} sets k; {"k": ""} deletes k; keys not named are kept. The 50-key limit is checked on the merged result (over → 400 on metadata, nothing applied).
  • reference is NOT updatable — a reference sent in the body is ignored; the other fields still apply (200).
  • Mass-assignment is prevented: userUuid, uuid, test, verified and createdAt in the body are ignored; ownership and identity never change.
  • An unknown, deleted or another space's uuid → 404; a malformed :uuid → 400. PUT is not a route.

Delete

  • DELETE /customers/:uuid → 204 with no body (it answered a message). It is a soft delete: the customer is removed from every read (404 by 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 email and reference: 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 customerReference creates 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 reference or email)