Create, read, update, and delete customer records.
Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/teams/{teamId}/customers |
List customers |
POST |
/api/v1/teams/{teamId}/customers |
Create a customer |
GET |
/api/v1/teams/{teamId}/customers/{customerId} |
Get a customer |
PATCH |
/api/v1/teams/{teamId}/customers/{customerId} |
Update a customer |
DELETE |
/api/v1/teams/{teamId}/customers/{customerId} |
Delete a customer |
The single-resource endpoints (/customers/{customerId}) accept both prefixed IDs (cus_abc123) and UUIDs. List filters operate on the underlying UUID columns, so tag and search use literal values, not prefixed IDs.
List Customers
GET /api/v1/teams/{teamId}/customers
Query Parameters
| Parameter | Type | Description |
|---|---|---|
page |
integer | Page number (default: 1) |
pageSize |
integer | Results per page (default: 20, max: 100) |
status |
string | Filter: active, inactive |
sentiment |
string | Filter: happy, neutral, frustrated, angry |
search |
string | Search by name or email |
tag |
string | Filter by tag (exact match) |
Example
curl "https://www.cstar.help/api/v1/teams/{teamId}/customers?status=active&search=jane" \
-H "Authorization: Bearer sk_live_xxx"
Response
{
"success": true,
"data": [
{
"id": "cus_x9Y8z7W6v5U4",
"object": "customer",
"name": "Jane Doe",
"email": "jane@example.com",
"sentiment": "happy",
"status": "active",
"tags": ["vip", "enterprise"],
"notes": "Enterprise customer since 2024",
"customFields": {
"company": "Acme Corp",
"plan": "enterprise"
},
"ticketCount": 5,
"metadata": {},
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2026-03-30T10:30:00Z"
}
],
"pagination": {
"total": 156,
"page": 1,
"pageSize": 20,
"hasMore": true
}
}
Create Customer
POST /api/v1/teams/{teamId}/customers
Requires a secret key.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Display name |
email |
string | Yes | Email address (must be unique per team) |
sentiment |
string | No | happy, neutral (default), frustrated, angry |
status |
string | No | active (default), inactive |
tags |
string[] | No | Array of tags |
notes |
string | No | Internal notes |
customFields |
object | No | Key-value pairs for custom data |
metadata |
object | No | Developer metadata (max 50 keys, string values) |
curl -X POST "https://www.cstar.help/api/v1/teams/{teamId}/customers" \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-john-smith-001" \
-d '{
"name": "John Smith",
"email": "john@example.com",
"tags": ["trial"]
}'
Returns 201 Created. If the email already exists on the team, returns 409 Conflict.
Get Customer
GET /api/v1/teams/{teamId}/customers/{customerId}
Returns the customer object plus a recentTickets array of their 10 most recent tickets.
Response
{
"success": true,
"data": {
"id": "cus_x9Y8z7W6v5U4",
"object": "customer",
"name": "Jane Doe",
"email": "jane@example.com",
"sentiment": "happy",
"status": "active",
"tags": ["vip"],
"ticketCount": 5,
"recentTickets": [
{
"id": "tkt_abc123",
"title": "Login issue",
"status": "resolved",
"createdAt": "2026-03-29T14:00:00Z"
}
],
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2026-03-30T10:30:00Z"
}
}
The field name is recentTickets (camelCase). Earlier docs showed it as tickets, that was wrong.
To skip the embedded tickets, pass any non-tickets value to expand, for example ?expand=customer. Without an expand query parameter, recent tickets are included by default.
Update Customer
PATCH /api/v1/teams/{teamId}/customers/{customerId}
Requires a secret key. Send only the fields you want to change.
| Field | Type | Description |
|---|---|---|
name |
string | Update display name |
email |
string | Update email (checked for uniqueness) |
sentiment |
string | happy, neutral, frustrated, angry |
status |
string | active, inactive |
tags |
string[] | Replace the tags array |
notes |
string | Update internal notes |
customFields |
object | Replace custom fields |
metadata |
object | Merged with existing metadata (set a key to null to remove it) |
Changing the email to one that already belongs to another customer on the team returns 409 Conflict.
Delete Customer
DELETE /api/v1/teams/{teamId}/customers/{customerId}
Requires a secret key with delete permissions.
Returns:
{
"success": true,
"data": { "deleted": true, "id": "cus_x9Y8z7W6v5U4" }
}
Metadata
The metadata field follows Stripe conventions:
- Max 50 keys per resource
- Keys: max 40 characters, strings only
- Values: max 500 characters, strings only
- Set a key to
nullto remove it - PATCH merges with existing metadata (doesn't replace the whole object)
Webhook Events
| Event | Fires When |
|---|---|
customer.created |
Customer is created |
customer.updated |
Customer fields change |
customer.deleted |
Customer is deleted |