Integration reference
Customer accounts API
Manage system accounts, read unpaid balances, and connect customers to accounts. Every endpoint below includes its request contract and complete response example.
Before you start
Follow Get started: create an API key to generate your first credential and make a test request.
Use https://bottlespeed.com and the target system’s slug. Send one system-key header consistently:
x-api-key: <system-api-key>
# Alternative:
Authorization: Bearer <system-api-key>x-api-key takes precedence. Read keys permit reads; Read-Write keys permit reads and writes. The creator must remain an active system Owner or PrimaryOwner for account management. Search also supports active Employee/Manager keys. Application sessions continue to work for the app; third-party integrations use system API keys.
Account balances are signed dollars, not cents. 42.5 means $42.50. Balances can be negative. Read unpaidBalance directly; lifetime return totals can be attributed to a different returning account and do not reconstruct the balance.
Examples use fictional IDs and customer data. Path placeholders must be replaced with actual IDs. JSON shown under Request is the HTTP body; GET and body-free mutations explicitly say so. Timestamps are ISO 8601 strings.
Save the account id, global userId, system membershipId, and assignment accountUserId separately. Change or remove an assignment using accountUserId.
List accounts
/api/system/{systemSlug}/customer-accountRead all accounts in one system, including unpaid balances and assigned users. Active accounts sort first, followed by account name and ID.
Authentication: System API key with Read or Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
GET /api/system/{systemSlug}/customer-account?page=1&pageSize=25
x-api-key: <system-api-key>Path parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
Query parameters
| Field | Type | Details |
|---|---|---|
| page | integer · optional | 1–90,071,992,547,409; defaults to 1 when pageSize is supplied. |
| pageSize | integer · optional | 1–100; defaults to 25 when page is supplied. |
Request body: none.
Response · 200 OK
{
"customerAccounts": [
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"hasAchAccountNumber": false,
"hasAchRoutingNumber": false,
"active": true,
"unpaidBalance": 42.5,
"totalReturnedContainers": 1000,
"totalReturnedValue": 100,
"userCount": 1,
"users": [
{
"accountUserId": "22222222-2222-4222-8222-222222222222",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null,
"customerAccountRole": "Owner"
}
]
}
],
"pagination": {
"page": 1,
"pageSize": 25,
"totalItems": 1,
"totalPages": 1,
"hasPreviousPage": false,
"hasNextPage": false
}
}| Field | Type | Details |
|---|---|---|
| customerAccounts | CustomerAccount[] | Current page, including active and inactive accounts. |
| pagination | Pagination · conditional | Present only when page or pageSize is supplied. |
Omit both query parameters for the unpaginated response containing only customerAccounts. No search/status filters are supported here. An empty system returns [] and totalPages: 0. A page past the end stays at the requested page and returns [].
Increment page until hasNextPage is false. totalItems counts accounts, not users. Pages are live offset-based reads; reconcile by account id if records change between requests.
See response object fields and error responses.
Search accounts
/api/system/{systemSlug}/customer-account/searchSearch account names or assigned users’ email/phone. Results are unpaginated.
Authentication: System API key with Read or Read-Write permission, owned by an active system Owner or PrimaryOwner. Employee and Manager system keys can also search; their responses omit ACH saved-state flags.
Request
GET /api/system/{systemSlug}/customer-account/search?nameQuery=Community&status=both
x-api-key: <system-api-key>Path parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
Query parameters
| Field | Type | Details |
|---|---|---|
| nameQuery | string · required | Trimmed, 1–255 characters. URL-encode the search text. |
| status | enum · optional | active, inactive, or both (default). |
Request body: none.
Response · 200 OK
{
"customerAccounts": [
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"hasAchAccountNumber": false,
"hasAchRoutingNumber": false,
"active": true,
"unpaidBalance": 42.5,
"totalReturnedContainers": 1000,
"totalReturnedValue": 100,
"userCount": 1,
"users": [
{
"accountUserId": "22222222-2222-4222-8222-222222222222",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null,
"customerAccountRole": "Owner"
}
]
}
]
}| Field | Type | Details |
|---|---|---|
| customerAccounts | CustomerAccount[] | Matching accounts; [] when none match. No pagination object. |
See response object fields and error responses.
Create an account
/api/system/{systemSlug}/customer-accountCreate an active account with zero balance and lifetime totals. This does not create a customer, assign an owner, or send an invitation.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
POST /api/system/{systemSlug}/customer-account
x-api-key: <system-api-key>
Content-Type: application/jsonPath parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| Field | Type | Details |
|---|---|---|
| name | string · required | Trimmed, 1–255 characters. Exact duplicate names in this system, including inactive accounts, are rejected. |
| address | string | null · required | Blank strings normalize to null. |
| payee | string | null · required | Blank strings normalize to null. |
| achAccountNumber | string | null · optional | 1–17 digits; use a string to preserve leading zeroes. Blank becomes null. Encrypted before storage. |
| achRoutingNumber | string | null · optional | Exactly 9 digits. Blank becomes null. Encrypted before storage. |
{
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"achAccountNumber": null,
"achRoutingNumber": null
}Add account users separately. Omitted or blank ACH values create no saved bank value.
Response · 200 OK
{
"customerAccount": {
"id": "11111111-1111-4111-8111-111111111111",
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"hasAchAccountNumber": false,
"hasAchRoutingNumber": false,
"active": true,
"unpaidBalance": 0,
"totalReturnedContainers": 0,
"totalReturnedValue": 0,
"userCount": 0,
"users": []
}
}| Field | Type | Details |
|---|---|---|
| customerAccount | CustomerAccount | null | Complete account object; see the field reference below. A concurrent deletion can make this null. |
See response object fields and error responses.
Update or reactivate an account
/api/system/{systemSlug}/customer-account/{accountId}Update account details or reactivate an inactive account. Required name/address/payee fields must be sent even when changing only active status.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
PUT /api/system/{systemSlug}/customer-account/{accountId}
x-api-key: <system-api-key>
Content-Type: application/jsonPath parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
| Field | Type | Details |
|---|---|---|
| name | string · required | Trimmed, 1–255 characters. Exact duplicate names in this system, including inactive accounts, are rejected. |
| address | string | null · required | Blank strings normalize to null. |
| payee | string | null · required | Blank strings normalize to null. |
| achAccountNumber | string | null · optional | 1–17 digits; use a string to preserve leading zeroes. Blank becomes null. Encrypted before storage. |
| achRoutingNumber | string | null · optional | Exactly 9 digits. Blank becomes null. Encrypted before storage. |
| active | boolean · optional | Omit to preserve the current state; true reactivates the account. |
| removeAchAccountNumber | boolean · optional | true explicitly removes the saved account number. |
| removeAchRoutingNumber | boolean · optional | true explicitly removes the saved routing number. |
{
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"active": true,
"achAccountNumber": null,
"achRoutingNumber": null,
"removeAchAccountNumber": false,
"removeAchRoutingNumber": false
}Omitted, null, or blank ACH replacements preserve saved values. A nonblank replacement and its removal flag cannot be sent together. The same name is allowed for this account; another account with that name is rejected.
Response · 200 OK
{
"customerAccount": {
"id": "11111111-1111-4111-8111-111111111111",
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"hasAchAccountNumber": false,
"hasAchRoutingNumber": false,
"active": true,
"unpaidBalance": 42.5,
"totalReturnedContainers": 1000,
"totalReturnedValue": 100,
"userCount": 1,
"users": [
{
"accountUserId": "22222222-2222-4222-8222-222222222222",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null,
"customerAccountRole": "Owner"
}
]
}
}| Field | Type | Details |
|---|---|---|
| customerAccount | CustomerAccount | null | Complete account object; see the field reference below. A concurrent deletion can make this null. |
See response object fields and error responses.
Deactivate an account
/api/system/{systemSlug}/customer-account/{accountId}/deactivateMark the account inactive while preserving its users, balance, and history. Reactivate with the update endpoint.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
POST /api/system/{systemSlug}/customer-account/{accountId}/deactivate
x-api-key: <system-api-key>Path parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
Request body: none.
Response · 200 OK
{
"customerAccount": {
"id": "11111111-1111-4111-8111-111111111111",
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"hasAchAccountNumber": false,
"hasAchRoutingNumber": false,
"active": false,
"unpaidBalance": 42.5,
"totalReturnedContainers": 1000,
"totalReturnedValue": 100,
"userCount": 1,
"users": [
{
"accountUserId": "22222222-2222-4222-8222-222222222222",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null,
"customerAccountRole": "Owner"
}
]
}
}| Field | Type | Details |
|---|---|---|
| customerAccount | CustomerAccount | null | Complete account object; see the field reference below. A concurrent deletion can make this null. |
See response object fields and error responses.
Create or reactivate a customer
/api/system/{systemSlug}/customersProvision a system Customer membership before assigning that customer to an account. This may create a global user or reactivate an existing Customer membership.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
POST /api/system/{systemSlug}/customers
x-api-key: <system-api-key>
Content-Type: application/jsonPath parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| Field | Type | Details |
|---|---|---|
| string | null · optional | Valid email, trimmed and lowercased; blank becomes null. At least one of email/cellphone must be nonblank. | |
| cellphone | string | null · optional | US ten-digit or +1 phone, normalized to E.164. Blank becomes null. Every supplied contact must belong to the same global user. |
| firstName | string · required | Trimmed, 1–255 characters. |
| lastName | string · required | Trimmed, 1–255 characters. |
| notes | string | null · required | Blank becomes null. |
| licensePlateNumber | string | null · optional | Trimmed, up to 255 characters. Stored only when Connecticut reporting is enabled. |
| driversLicenseNumber | string | null · optional | Trimmed, up to 255 characters. Stored only when Connecticut reporting is enabled. |
{
"email": "alex@example.com",
"cellphone": "+12025550123",
"firstName": "Alex",
"lastName": "River",
"notes": null,
"licensePlateNumber": null,
"driversLicenseNumber": null
}Omit the storeId query parameter for this owner integration. The POS store-scoped path has different authorization and a reduced response.
This writes names, notes, and applicable identity fields on existing Customer memberships. Existing global contact ownership must be respected; staff cannot be converted. Managed updates cannot replace verified login contacts. This call does not verify contacts, send an OTP, or add an account assignment.
Response · 200 OK
{
"customerMember": {
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"role": "Customer",
"hourlyRate": 0,
"posPinCode": null,
"notes": null,
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null
}
}| Field | Type | Details |
|---|---|---|
| customerMember | CustomerMember | null | System membership with global user/contact fields; use the saved contact in the account-user endpoint. |
See response object fields and error responses.
Add an account user
/api/system/{systemSlug}/customer-account/{accountId}/usersAssign an existing active Customer in this system to the account. A customer can have multiple account assignments.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
POST /api/system/{systemSlug}/customer-account/{accountId}/users
x-api-key: <system-api-key>
Content-Type: application/jsonPath parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
| Field | Type | Details |
|---|---|---|
| string | null · optional | Valid email, trimmed and lowercased; blank becomes null. At least one of email/cellphone must be nonblank. | |
| cellphone | string | null · optional | US ten-digit or +1 phone, normalized to E.164. Blank becomes null. Every supplied contact must belong to the same global user. |
| customerAccountRole | enum · required | Owner, Admin, or User. This account role grants no system-wide API access. |
{
"email": "alex@example.com",
"cellphone": "+12025550123",
"customerAccountRole": "Owner"
}Email-only and phone-only requests are supported. Every supplied contact must match the same saved user. Missing/inactive customers, staff memberships, conflicting contacts, and duplicate assignments return 400. No login, OTP, or invitation is created.
Response · 200 OK
{
"accountUser": {
"accountUserId": "22222222-2222-4222-8222-222222222222",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null,
"customerAccountRole": "Owner"
}
}| Field | Type | Details |
|---|---|---|
| accountUser | AccountUser | null | Created or updated assignment with its system membership and global contact details. A concurrent deletion can make this null. |
See response object fields and error responses.
Change an account user’s role
/api/system/{systemSlug}/customer-account/{accountId}/users/{accountUserId}Change the role on one account assignment. Demoting the only remaining Owner is rejected.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
PUT /api/system/{systemSlug}/customer-account/{accountId}/users/{accountUserId}
x-api-key: <system-api-key>
Content-Type: application/jsonPath parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
| accountUserId | UUID · required | Assignment id from users[].accountUserId; not the global userId or membershipId. |
| Field | Type | Details |
|---|---|---|
| customerAccountRole | enum · required | Owner, Admin, or User. |
{
"customerAccountRole": "Admin"
}This example assumes another Owner remains. The assignment must belong to this account and system.
Response · 200 OK
{
"accountUser": {
"accountUserId": "22222222-2222-4222-8222-222222222222",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null,
"customerAccountRole": "Admin"
}
}| Field | Type | Details |
|---|---|---|
| accountUser | AccountUser | null | Created or updated assignment with its system membership and global contact details. A concurrent deletion can make this null. |
See response object fields and error responses.
Remove an account user
/api/system/{systemSlug}/customer-account/{accountId}/users/{accountUserId}Remove only the account assignment. Preserve the global user, system membership, other assignments, and account history.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
DELETE /api/system/{systemSlug}/customer-account/{accountId}/users/{accountUserId}
x-api-key: <system-api-key>Path parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
| accountUserId | UUID · required | Assignment id from users[].accountUserId; not the global userId or membershipId. |
Request body: none.
Removal currently permits removing the last account Owner. Deleting the same assignment again returns 400.
Response · 200 OK
{
"success": true,
"accountUserId": "22222222-2222-4222-8222-222222222222"
}| Field | Type | Details |
|---|---|---|
| success | boolean | true after deletion. |
| accountUserId | UUID | The deleted assignment id. |
See response object fields and error responses.
List account transactions
/api/system/{systemSlug}/customer-account/{accountId}/transactionsRead transactions where this account was paid or recorded as the returning account. History can include activity that did not change this account’s balance.
Authentication: System API key with Read or Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
GET /api/system/{systemSlug}/customer-account/{accountId}/transactions?page=1&pageSize=25
x-api-key: <system-api-key>Path parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
Query parameters
| Field | Type | Details |
|---|---|---|
| page | integer · optional | At least 1; default 1. A page past the end is clamped to the last page, or 1 when empty. |
| pageSize | integer · optional | 1–100; default 25. |
Request body: none.
Response · 200 OK
{
"transactions": [
{
"id": "66666666-6666-4666-8666-666666666666",
"createdAt": "2026-09-15T12:00:00.000Z",
"storeName": "Main store",
"grandTotal": -10,
"paidSubTotal": -10,
"salesSubTotal": 0,
"taxesSubTotal": 0,
"containerDepositsSubTotal": 0,
"roundingAdjustmentTotal": 0,
"totalReturnedContainers": 100,
"paymentMethod": "Customer Account",
"isPaidToCustomerAccount": true,
"paidToCustomerAccountId": "11111111-1111-4111-8111-111111111111",
"paidToCustomerAccountName": "Community Returns",
"returnedByCustomerAccountId": "11111111-1111-4111-8111-111111111111",
"returnedByCustomerAccountName": "Community Returns",
"fundraiserId": null,
"fundraiserName": null,
"isTransactionRefunded": false,
"isPaidToLinkedAccount": true,
"isReturnedByLinkedAccount": true
}
],
"pagination": {
"page": 1,
"pageSize": 25,
"totalItems": 1,
"totalPages": 1,
"hasPreviousPage": false,
"hasNextPage": false
}
}| Field | Type | Details |
|---|---|---|
| transactions | AccountTransaction[] | Newest first by createdAt, then id. |
| pagination | Pagination | Always included; totalPages is 0 when empty. |
See response object fields and error responses.
List account payouts
/api/system/{systemSlug}/customer-account/{accountId}/payoutsRead payout and correction adjustments, newest first. This endpoint is unpaginated.
Authentication: System API key with Read or Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
GET /api/system/{systemSlug}/customer-account/{accountId}/payouts
x-api-key: <system-api-key>Path parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
Request body: none.
Response · 200 OK
{
"payouts": [
{
"id": "55555555-5555-4555-8555-555555555555",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"adjustmentType": "Payout",
"totalPayoutAmount": 12.5,
"payoutMethod": "Check",
"checkNumber": "1042",
"notes": null,
"createdAt": "2026-09-15T12:00:00.000Z",
"updatedAt": "2026-09-15T12:00:00.000Z"
}
]
}| Field | Type | Details |
|---|---|---|
| payouts | AccountPayout[] | All adjustments for this account; [] when none exist. adjustmentType distinguishes Payout and Correction. |
See response object fields and error responses.
Record an account payout
/api/system/{systemSlug}/customer-account/{accountId}/payoutsRecord a cash or check payout and reduce the active account’s unpaid balance. This records the payment; it does not send ACH or issue a check.
Authentication: System API key with Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
POST /api/system/{systemSlug}/customer-account/{accountId}/payouts
x-api-key: <system-api-key>
Content-Type: application/jsonPath parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
| accountId | UUID · required | Account id from the collection or search response. Must belong to this system. |
| Field | Type | Details |
|---|---|---|
| totalPayoutAmount | number · required | Dollar amount greater than 0, at most 1 billion, and no greater than the current unpaid balance. |
| payoutMethod | enum · required | Check or Cash. |
| checkNumber | string | null · required | Nonblank for Check; null/blank for Cash. |
| notes | string | null · required | Blank becomes null. |
{
"totalPayoutAmount": 12.5,
"payoutMethod": "Check",
"checkNumber": "1042",
"notes": null
}The balance check and deduction use a database transaction. No idempotency-key contract exists: after an uncertain response, reconcile payout history before retrying. Account and assignment creation also require reconciliation before retrying an uncertain write.
Response · 200 OK
{
"payout": {
"id": "55555555-5555-4555-8555-555555555555",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"adjustmentType": "Payout",
"totalPayoutAmount": 12.5,
"payoutMethod": "Check",
"checkNumber": "1042",
"notes": null,
"createdAt": "2026-09-15T12:00:00.000Z",
"updatedAt": "2026-09-15T12:00:00.000Z"
},
"customerAccount": {
"id": "11111111-1111-4111-8111-111111111111",
"name": "Community Returns",
"address": null,
"payee": "Community Returns",
"active": true,
"unpaidBalance": 30,
"totalReturnedContainers": 1000,
"totalReturnedValue": 100,
"userCount": 1,
"users": [
{
"accountUserId": "22222222-2222-4222-8222-222222222222",
"customerAccountId": "11111111-1111-4111-8111-111111111111",
"membershipId": "33333333-3333-4333-8333-333333333333",
"userId": "44444444-4444-4444-8444-444444444444",
"email": "alex@example.com",
"active": true,
"firstName": "Alex",
"lastName": "River",
"fullName": "Alex River",
"cellphone": "(202)-555-0123",
"licensePlateNumber": null,
"driversLicenseNumber": null,
"activeIdDocument": null,
"customerAccountRole": "Owner"
}
]
}
}| Field | Type | Details |
|---|---|---|
| payout | AccountPayout | null | The recorded adjustment. |
| customerAccount | CustomerAccount | null | Updated account. This endpoint omits hasAchAccountNumber and hasAchRoutingNumber. |
See response object fields and error responses.
Read API audit history
/api/system/{systemSlug}/api-audit-logsRead this system’s recorded API activity. Owners can also browse /application/{systemSlug}/api-audit-logs.
Authentication: System API key with Read or Read-Write permission, owned by an active system Owner or PrimaryOwner.
Request
GET /api/system/{systemSlug}/api-audit-logs?page=1&pageSize=25
x-api-key: <system-api-key>Path parameters
| Field | Type | Details |
|---|---|---|
| systemSlug | string · required | System slug from /application/{systemSlug}/profile. |
Query parameters
| Field | Type | Details |
|---|---|---|
| page | integer · optional | 1–21,474,836; default 1. Clamped to the last page, or 1 when empty. |
| pageSize | integer · optional | 1–100; default 25. |
| method | enum · optional | GET, POST, PUT, PATCH, DELETE, HEAD, or OPTIONS. |
| path | string · optional | Trimmed, up to 255 characters; case-insensitive substring of the recorded route template. |
| fromDate / toDate | YYYY-MM-DD · optional | Valid calendar dates in the system’s time zone. Both boundary days are included; toDate cannot precede fromDate. |
| snapshotAt | ISO timestamp · optional | Timezone required. Reuse the returned value for subsequent pages. Future values are capped at the current time. |
Request body: none.
Response · 200 OK
{
"auditLogs": [
{
"id": "77777777-7777-4777-8777-777777777777",
"systemId": "88888888-8888-4888-8888-888888888888",
"createdAt": "2026-09-15T12:00:00.000Z",
"userId": "44444444-4444-4444-8444-444444444444",
"userName": "Alex River",
"userEmail": "alex@example.com",
"authenticationMethod": "api_key",
"apiKeyId": "99999999-9999-4999-8999-999999999999",
"method": "GET",
"path": "/api/system/[systemSlug]/customer-account",
"statusCode": 200,
"durationMs": 24,
"inputParameters": {
"route": {
"systemSlug": "north-buffalo"
},
"query": {
"page": [
"1"
],
"pageSize": [
"25"
]
},
"body": null
}
}
],
"pagination": {
"page": 1,
"pageSize": 25,
"totalItems": 1,
"totalPages": 1,
"hasPreviousPage": false,
"hasNextPage": false
},
"snapshotAt": "2026-09-15T12:01:00.000Z"
}| Field | Type | Details |
|---|---|---|
| auditLogs | ApiAuditLog[] | Newest first by createdAt and id; strictly before snapshotAt. |
| pagination | Pagination | Page metadata. |
| snapshotAt | ISO timestamp | Upper boundary for this result window. |
Response header: Cache-Control: no-store. System calls include failures and audit reads. Sensitive inputs are redacted; unverified/wrong-system callers have null identity and omitted input values. Headers and response bodies are not captured. Oversized, malformed, or unsupported bodies use omission markers.
Global/counter routes and framework-generated 404/405 responses are outside system audit coverage. Persistence is best effort during database/process failures.
See response object fields and error responses.
Response object fields
CustomerAccount
Money values are signed dollars, not integer cents. unpaidBalance is current; lifetime return totals are separate and must not be used to reconstruct it. There is no separate GET account, balance, or account-users endpoint: locate accounts in collection/search results.
| Field | Type | Details |
|---|---|---|
| id | UUID | Account identifier. |
| name | string | Account name. |
| address / payee | string | null | Optional saved text. |
| hasAchAccountNumber / hasAchRoutingNumber | boolean · optional | Saved-state indicators, never bank values/ciphertext. Included in owner collection/search/create/update/deactivate responses; omitted from payout responses and staff search. |
| active | boolean | Account status. |
| unpaidBalance | number | Current signed dollar balance; may be negative. Account-paid transactions/reversals affect it and payouts reduce it. |
| totalReturnedContainers | number | Lifetime containers attributed to the returning account. |
| totalReturnedValue | number | Lifetime return value in dollars. Returns may credit a different fundraising account. |
| userCount | integer | Number of returned assignments. |
| users | AccountUser[] | Assigned Customer memberships, including inactive memberships; active first, then fullName. |
AccountUser
Account assignment, system membership, and global user IDs are distinct. Connecticut identity fields are null when reporting is disabled.
| Field | Type | Details |
|---|---|---|
| accountUserId | UUID | Account assignment; use in PUT/DELETE user paths. |
| customerAccountId | UUID | Owning account. |
| membershipId | UUID | System membership. |
| userId | UUID | Global user. |
| string | null | Saved global email. | |
| firstName / lastName | string | System membership names. |
| fullName | string | null | Computed display name. |
| cellphone | string | null | Saved global phone formatted as (202)-555-0123. |
| active | boolean | System membership status, not account status. |
| customerAccountRole | Owner | Admin | User | Role on this account. |
| licensePlateNumber / driversLicenseNumber | string | null | Identity fields, visible only for Connecticut-enabled systems. |
| activeIdDocument | IdDocument | null | Active identity-document metadata for Connecticut-enabled systems. |
CustomerMember
Owner customer-provisioning response. It is a system membership rather than an account assignment.
| Field | Type | Details |
|---|---|---|
| membershipId / userId | UUID | System membership id and global user id. |
| email / cellphone | string | null | Saved global contacts; phone uses formatted display text. |
| firstName / lastName | string | System membership names. |
| fullName | string | null | Computed display name. |
| active | boolean | true after successful creation/reactivation. |
| role | Customer | System role. |
| hourlyRate | number | Stored membership rate. |
| posPinCode / notes | string | null | Stored membership fields; a new Customer has no POS PIN. |
| licensePlateNumber / driversLicenseNumber | string | null | Stored identity text when Connecticut reporting is enabled; otherwise null. |
| activeIdDocument | IdDocument | null | Active document metadata or null. |
IdDocument
Metadata only; no file bytes or public download URL.
| Field | Type | Details |
|---|---|---|
| id | UUID | Document id. |
| createdAt | ISO timestamp | Creation instant. |
| fileName | string | Original file name. |
| contentType | string | File MIME type. |
| fileSizeBytes | number | File size in bytes. |
Pagination
Shared pagination envelope; see each endpoint for out-of-range page behavior.
| Field | Type | Details |
|---|---|---|
| page | integer | Returned page number. |
| pageSize | integer | Maximum rows per page. |
| totalItems | integer | Total matching records. |
| totalPages | integer | Ceiling of totalItems/pageSize; 0 for no records. |
| hasPreviousPage / hasNextPage | boolean | Whether an earlier/later page is available according to the endpoint’s pagination rules. |
AccountTransaction
Includes paid-to and returned-by relationships. isPaidToLinkedAccount and isReturnedByLinkedAccount refer to the account requested in the URL. Monetary fields are dollars.
| Field | Type | Details |
|---|---|---|
| id | UUID | Transaction id. |
| createdAt | ISO timestamp | Creation instant. |
| storeName | string | null | Store display name. |
| grandTotal | number | Recorded final signed transaction total. |
| paidSubTotal | number | Recorded paid subtotal. |
| salesSubTotal | number | Sales subtotal. |
| taxesSubTotal | number | Tax total. |
| containerDepositsSubTotal | number | Container deposit total. |
| roundingAdjustmentTotal | number | Recorded rounding adjustment. |
| totalReturnedContainers | number | Returned container count for the transaction. |
| paymentMethod | Cash | Customer Account | Credit Card | Normalized payment method. |
| isPaidToCustomerAccount | boolean | Whether account tender was used. |
| paidToCustomerAccountId / returnedByCustomerAccountId | UUID | null | Receiving account and account associated with the return. |
| paidToCustomerAccountName / returnedByCustomerAccountName | string | null | Corresponding account names. |
| fundraiserId | UUID | null | Related fundraiser. |
| fundraiserName | string | null | Fundraiser name. |
| isTransactionRefunded | boolean | Whether this transaction has been reversed. |
| isPaidToLinkedAccount / isReturnedByLinkedAccount | boolean | Whether each relationship matches the requested account. |
AccountPayout
Adjustment history distinguishes normal Payout records from Correction records.
| Field | Type | Details |
|---|---|---|
| id / customerAccountId | UUID | Adjustment id and owning account id. |
| adjustmentType | Payout | Correction | Adjustment category. |
| totalPayoutAmount | number | Recorded amount in dollars. |
| payoutMethod | Check | Cash | Recorded tender. |
| checkNumber / notes | string | null | Optional saved text. |
| createdAt / updatedAt | ISO timestamp | Creation and last-update instants. |
ApiAuditLog
Sanitized operational request history. Input capture is bounded and may contain redaction, truncation, or omission markers.
| Field | Type | Details |
|---|---|---|
| id / systemId | UUID | Audit record and owning system. |
| createdAt | ISO timestamp | Recorded at request completion. |
| userId | UUID | null | Verified caller; null when unverified for this system. |
| userName / userEmail | string | null | Verified identity snapshot. |
| authenticationMethod | session | api_key | management | unauthenticated | Verified authentication mechanism. |
| apiKeyId | UUID | null | API-key metadata id, never the secret. |
| method / path | string | HTTP method and normalized route template. |
| statusCode / durationMs | integer | Response status and processing duration in milliseconds. |
| inputParameters | object | route, query, and body contain sanitized JSON or omission markers; repeated query values are arrays. |
Error responses
Check the HTTP status before reading success fields. The examples use fictional data. JSON responses can contain null fields; preserve null rather than treating it as an empty string.
| Field | Type | Details |
|---|---|---|
| 400 | Bad request | Invalid fields/UUIDs, missing account or assignment, duplicate names/assignments, contact conflicts, or failed membership/payout preconditions. |
| 401 | Unauthorized | Invalid, expired, revoked or wrong-system key; inactive membership; insufficient system role; or a write with Read permission. |
| 500 | Server error | Unexpected failure, including malformed JSON. An uncertain write may have completed; reconcile before retrying. |
Ordinary error · 400
{
"error": "Customer account not found."
}Validation error · 400
{
"error": "ValidationError",
"issues": [
{
"expected": "string",
"code": "invalid_type",
"path": [
"name"
],
"message": "Invalid input: expected string, received undefined"
}
]
}error is a string. Validation responses also contain issues, an array with code, path (field names or array indices), and message; other issue fields depend on the validation rule. Missing records use 400 and authorization failures use 401.