Customers API
Customer profile management, identity resolution, timeline, and notes.
Customer Profiles
List Customers
GET /customers
Query Parameters
| Parameter | Type | Description |
|---|---|---|
tenantId | string | Tenant ID (required) |
status | string | Filter: ACTIVE, INACTIVE, MERGED, DELETED |
lifecycleStage | string | Filter: SUBSCRIBER, LEAD, CUSTOMER, MEMBER, ADVOCATE, CHURNED |
search | string | Search by name, email, phone |
segmentId | string | Filter by segment membership |
engagementMin | number | Min engagement score (0-100) |
churnRiskMin | number | Min churn risk (0-100) |
emailOptIn | boolean | Filter by opt-in |
skip | number | Offset (default: 0) |
take | number | Limit (default: 25, max: 100) |
Response: 200 OK
{
"data": [{
"id": "uuid",
"email": "john@example.com",
"firstName": "John",
"lastName": "Smith",
"status": "ACTIVE",
"engagementScore": 75,
"churnRiskScore": 15,
"lastActivityAt": "2025-12-14T10:00:00Z"
}],
"meta": { "total": 1250, "skip": 0, "take": 25, "hasMore": true }
}
Get Customer
GET /customers/:id
Response: 200 OK
{
"id": "uuid",
"tenantId": "tenant_123",
"email": "john@example.com",
"phoneNumber": "+27821234567",
"firstName": "John",
"lastName": "Smith",
"status": "ACTIVE",
"lifecycleStage": "MEMBER",
"membershipTier": "Gold",
"membershipStatus": "ACTIVE",
"handicap": 12.5,
"homeClubId": "club_123",
"homeClubName": "Randpark Golf Club",
"engagementScore": 75,
"engagementScoreAt": "2025-12-14T02:00:00Z",
"churnRiskScore": 15,
"churnRiskScoreAt": "2025-12-14T02:00:00Z",
"lifetimeValue": 250000,
"emailOptIn": true,
"smsOptIn": true,
"pushOptIn": false,
"lastActivityAt": "2025-12-14T10:00:00Z",
"lastBookingAt": "2025-12-10T06:30:00Z",
"createdAt": "2024-01-15T00:00:00Z",
"updatedAt": "2025-12-14T10:00:00Z"
}
Create Customer
POST /customers
Request Body
{
"tenantId": "tenant_123",
"email": "john@example.com",
"phoneNumber": "+27821234567",
"firstName": "John",
"lastName": "Smith",
"lifecycleStage": "LEAD"
}
Response: 201 Created
Update Customer
PATCH /customers/:id
Request Body
{
"firstName": "Jonathan",
"lastName": "Smith-Jones",
"homeClubId": "club_456"
}
Response: 200 OK
Merge Customers
Merges two customer profiles, combining all data into the winner profile.
POST /customers/:winnerId/merge
Request Body
{
"loserId": "uuid-to-merge"
}
Response: 200 OK
{
"winnerId": "uuid-winner",
"loserId": "uuid-loser",
"mergedFields": ["activities", "notes", "segmentMemberships"],
"mergedAt": "2025-12-14T10:00:00Z"
}
Note: The loser profile is marked with status MERGED and references the winner.
Identity Resolution
Resolve Customer
Finds or creates a customer profile based on identity keys.
GET /customers/resolve
Query Parameters
| Parameter | Type | Description |
|---|---|---|
tenantId | string | Required |
authUserId | string | IDP user ID |
email | string | |
phone | string | Phone (E.164 format) |
Response: 200 OK
{
"profile": {
"id": "uuid",
"email": "john@example.com",
"firstName": "John",
"lastName": "Smith"
},
"resolution": {
"isNew": false,
"matchedBy": ["email"],
"confidence": 95
}
}
Resolution Priority:
authUserId- 100% confidenceemail- 95% confidencephone- 90% confidence
Activity Timeline
Get Timeline
GET /customers/:id/activities
Query Parameters
| Parameter | Type | Description |
|---|---|---|
categories | string[] | Filter by categories |
startDate | string | ISO 8601 date |
endDate | string | ISO 8601 date |
skip | number | Offset |
take | number | Limit |
Categories:
MEMBERSHIP- Tier changes, renewalsBOOKING- Tee time bookingsCOMMUNICATION- Messages sent/receivedENGAGEMENT- Email opens, clicksFINANCIAL- Payments, invoicesGOLF- Scores, handicap updatesMARKETING- Campaign interactionsSOCIAL- Social media engagementSYSTEM- Profile updates
Response: 200 OK
{
"data": [{
"id": "activity_123",
"type": "TEETIME_BOOKED",
"category": "BOOKING",
"title": "Tee Time Booked",
"description": "Booked for Bushwillow Course at 06:30",
"occurredAt": "2025-12-14T10:00:00Z",
"metadata": {
"clubName": "Randpark Golf Club",
"courseName": "Bushwillow Course",
"startTime": "2025-12-20T06:30:00Z"
}
}],
"meta": { "total": 45, "skip": 0, "take": 25 }
}
Add Activity
POST /customers/:id/activities
Request Body
{
"type": "NOTE_ADDED",
"category": "SYSTEM",
"title": "Manual Activity",
"description": "Customer called to inquire about membership",
"occurredAt": "2025-12-14T10:00:00Z",
"metadata": {
"source": "phone_call",
"duration": 300
}
}
Response: 201 Created
Customer Notes
List Notes
GET /customers/:id/notes
Query Parameters
| Parameter | Type | Description |
|---|---|---|
type | string | Filter: GENERAL, SUPPORT, SALES, COMPLAINT |
Response: 200 OK
{
"data": [{
"id": "note_123",
"content": "Customer interested in Gold membership upgrade",
"type": "SALES",
"isPinned": true,
"createdBy": "user_456",
"createdByName": "Sarah Admin",
"createdAt": "2025-12-14T10:00:00Z",
"updatedAt": "2025-12-14T10:00:00Z"
}]
}
Create Note
POST /customers/:id/notes
Request Body
{
"content": "Note content",
"type": "GENERAL",
"isPinned": false
}
Response: 201 Created
Update Note
PATCH /notes/:id
Request Body
{
"content": "Updated note content",
"isPinned": true
}
Response: 200 OK
Delete Note
DELETE /notes/:id
Response: 204 No Content