Customer Management
The customer management endpoints let a merchant owner fetch, update, and erase their enrolled clients (cardholders). All four endpoints are owner-only and operate on a single merchant identified by a required merchant_id query parameter.
Owner credentials required
These endpoints require merchant owner (admin) credentials. System user tokens used by POS integrations are rejected with 403 OWNER_REQUIRED.
merchant_id must be a merchant you own
Every request must include a merchant_id query parameter. It is verified against the authenticated owner's merchants — passing a merchant you do not own returns 403 MERCHANT_NOT_AUTHORIZED. This explicit scoping exists because one owner can own multiple merchants, so the target merchant can never be inferred.
Endpoints
Customers are keyed by customer_id. A customer exists for this API as long as they hold any card instance of any status (active, expired, or archived) under the verified merchant.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/customers | Paginated, per-customer grouped list with card summaries. Search supported. |
| GET | /api/v1/customers/{customerId} | One customer: profile plus all their cards under this merchant. |
| PATCH | /api/v1/customers/{customerId} | Update the customer's contact and demographic profile fields. |
| DELETE | /api/v1/customers/{customerId} | Erase profile PII and archive the customer's cards under this merchant. |
Listing & Search
The list endpoint returns the merchant's customers grouped per customer_id, each with a card_count, a first_enrollment_date (earliest enrollment across their cards), and a summary of every card they hold under this merchant.
Query Parameters
merchant_id— Required. The merchant to list customers for. Must be a merchant you own.page— 1-based page number. Defaults to 1.page_size— Results per page, between 1 and 50. Values above 50 are rejected.search— Case-insensitive filter matching customer name, email, phone, or card barcode. Minimum 3 characters.
GET /api/v1/customers?merchant_id=8f14e45f-ceea-467e-8b0f-1a2b3c4d5e6f&page=1&page_size=20&search=jane
Authorization: Bearer <access_token>{
"success": true,
"data": {
"customers": [
{
"customer_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"customer_name": "Jane Doe",
"email": "jane@example.com",
"card_count": 2,
"first_enrollment_date": "2025-11-02T09:15:00.000Z",
"cards": [
{
"card_instance_id": "d4c3b2a1-f6e5-0987-dcba-0987654321fe",
"barcode_value": "FRL-2026-000841",
"card_name": "Lunch Cashback",
"card_type": "cashback",
"status": "active",
"current_balance": 12.5,
"max_balance": null,
"currency_symbol": "$",
"enrollment_date": "2026-01-10T14:00:00.000Z"
},
{
"card_instance_id": "c1d2e3f4-a5b6-7890-abcd-ef1234567890",
"barcode_value": "FRL-2025-000317",
"card_name": "Coffee Rewards",
"card_type": "punch",
"status": "active",
"current_balance": 4,
"max_balance": 10,
"currency_symbol": null,
"enrollment_date": "2025-11-02T09:15:00.000Z"
}
]
}
],
"total_count": 1,
"total_pages": 1,
"current_page": 1
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-01-15T10:30:00.000Z"
}
}Updating a Customer Profile
PATCH updates the customer's profile across all of their card profiles under the verified merchant. Only contact and demographic fields may be changed. locale and consent_marketing remain customer-controlled and are rejected here.
Editable Fields
customer_name— Display name. Non-empty, up to 100 characters. Cannot be cleared (non-nullable).email— Email address. Up to 255 characters, or null to clear.phone— Phone number. Up to 20 characters, or null to clear.birthday— ISO date (YYYY-MM-DD), or null to clear.gender— One of male, female, other, prefer_not_to_say, or null to clear.
Null-clear and empty-body semantics
email, phone, birthday, and gender accept null to clear the field. customer_name is non-nullable and cannot be set to null. An empty body ({}) is rejected with 400 VALIDATION_ERROR — at least one field is required. Any unknown or forbidden key (including locale and consent_marketing) is also rejected with 400.
PATCH edits profile PII only. It never changes the customer's login identity (their auth account) — editing the profile email does not change the email they sign in with.
After an erase, the customer still exists (their archived cards remain) but has no profile rows, so PATCH returns 200 with updated: 0.
PATCH /api/v1/customers/a1b2c3d4-e5f6-7890-abcd-ef1234567890?merchant_id=8f14e45f-ceea-467e-8b0f-1a2b3c4d5e6f
Content-Type: application/json
Authorization: Bearer <access_token>
{
"customer_name": "Jane A. Doe",
"email": "jane.doe@example.com",
"phone": null
}{
"success": true,
"data": {
"updated": 2
},
"meta": {
"request_id": "req_def456",
"timestamp": "2026-01-15T10:31:00.000Z"
}
}Deleting Customer Data
DELETE is a merchant-scoped client removal, not a GDPR-complete erasure. In a single transaction it erases the customer's profile PII and archives their cards under this merchant. It is idempotent: a repeat DELETE returns 200 with zero counts.
What DELETE removes
- Profile PII (customer_name, email, phone, birthday, gender) — the profile rows are deleted.
- The customer's cards under this merchant are archived (status set to archived), not hard-deleted.
- Push registrations (device tokens) for those cards are deleted.
- Merchant-scoped customer sessions (which hold IP address and user agent) are deleted.
What DELETE intentionally retains
- Card transactions, including any free-text transaction notes — financial history is never cascade-wiped.
- Punch rewards earned by the customer.
- The customer's login identity (their auth account) — they may hold cards at other merchants.
Wallet pass behavior
Apple Wallet passes receive no final push notification on DELETE (the device tokens are deleted in the same transaction). Apple passes pick up the archived state on their next pass web service poll. Google Wallet passes are updated through the registration-independent path.
Response
DELETE returns four counts: cards_archived, profiles_deleted, push_registrations_deleted, and sessions_deleted. A second DELETE returns all zeros and sends no wallet notifications.
DELETE /api/v1/customers/a1b2c3d4-e5f6-7890-abcd-ef1234567890?merchant_id=8f14e45f-ceea-467e-8b0f-1a2b3c4d5e6f
Authorization: Bearer <access_token>{
"success": true,
"data": {
"cards_archived": 2,
"profiles_deleted": 2,
"push_registrations_deleted": 3,
"sessions_deleted": 1
},
"meta": {
"request_id": "req_ghi789",
"timestamp": "2026-01-15T10:32:00.000Z"
}
}Error Codes
These endpoints may return the following endpoint-specific error codes.
| Code | Status | Description |
|---|---|---|
| OWNER_REQUIRED | 403 | The caller is not a merchant owner (a system user token, or a user owning no merchant). |
| MERCHANT_NOT_AUTHORIZED | 403 | The caller is an owner but does not own the requested merchant_id. |
| CUSTOMER_NOT_FOUND | 404 | The customer has no card instance of any status under the verified merchant. |
| VALIDATION_ERROR | 400 | A query parameter or request body failed validation (missing merchant_id, empty PATCH body, unknown field, or an invalid value). |