Customers API
The Customers API manages customer profiles, tracks their communication history, and provides insights into customer behavior.
Customer Object
{
id: string; // UUID
organization_id: string; // UUID of the organization
user_id?: string; // UUID of linked user account (if exists)
email: string; // Customer's email address
name?: string; // Customer's display name
phone?: string; // Phone number
company?: string; // Company name
status: string; // active, inactive, blocked
tags: string[]; // Array of tags
notes?: string; // Internal notes about the customer
metadata?: object; // Custom metadata
// Computed fields
total_tickets: number; // Total number of tickets
open_tickets: number; // Number of open tickets
total_orders: number; // Total orders (if Shopify connected)
lifetime_value: number; // Total order value
first_contact_at?: string; // ISO timestamp of first contact
last_contact_at?: string; // ISO timestamp of last contact
// Shopify integration
shopify_customer_id?: number; // Linked Shopify customer ID
created_at: string; // ISO timestamp
updated_at: string; // ISO timestamp
deleted_at?: string; // ISO timestamp (soft delete)
}
Status Values
| Status | Description |
|---|---|
active | Normal, active customer |
inactive | No recent activity |
blocked | Blocked from creating tickets |
List Customers
Retrieve customers with filters, search, and pagination.
Procedure: customers.list
Authentication: Required (agent or admin)
Input:
{
status?: 'all' | 'active' | 'inactive' | 'blocked'; // default: 'all'
search?: string; // Search name, email, company
sortBy?: 'name' | 'email' | 'last_contact' | 'total_tickets' | 'created_at'; // default: 'last_contact'
sortOrder?: 'asc' | 'desc'; // default: 'desc'
minTickets?: number; // Minimum ticket count filter
maxTickets?: number; // Maximum ticket count filter
hasOpenTickets?: boolean; // Filter by open ticket status
tags?: string[]; // Filter by tags (any match)
cursor?: number; // Pagination cursor (offset)
limit?: number; // 1-100, default: 50
}
Example:
curl -X GET "https://your-domain.com/api/trpc/customers.list?input=%7B%22search%22:%22acme%22,%22hasOpenTickets%22:true,%22limit%22:25%7D" \
-H "Cookie: your-session-cookie"
Response:
{
"result": {
"data": {
"json": {
"customers": [
{
"id": "uuid",
"email": "customer@acme.com",
"name": "Jane Doe",
"company": "Acme Corp",
"status": "active",
"tags": ["enterprise", "priority"],
"total_tickets": 12,
"open_tickets": 2,
"last_contact_at": "2024-01-15T10:30:00.000Z",
"created_at": "2023-06-01T09:00:00.000Z"
}
],
"total": 150,
"nextCursor": 25
}
}
}
}
Notes:
- Search matches against name, email, and company fields (case-insensitive)
- Tags filter uses
overlaps- returns customers with any matching tag - Results are paginated using offset-based cursors
Get Customer
Retrieve a single customer by ID with enriched data and recent tickets.
Procedure: customers.get
Authentication: Required (agent or admin)
Input:
{
id: string; // Customer UUID
}
Example:
curl -X GET "https://your-domain.com/api/trpc/customers.get?input=%7B%22id%22:%22customer-uuid%22%7D" \
-H "Cookie: your-session-cookie"
Response:
{
"result": {
"data": {
"json": {
"id": "uuid",
"email": "customer@example.com",
"name": "Jane Doe",
"company": "Acme Corp",
"phone": "+1-555-123-4567",
"status": "active",
"tags": ["enterprise"],
"notes": "VIP customer, prefers email communication",
"metadata": {
"industry": "Technology",
"size": "Enterprise"
},
"total_tickets": 12,
"open_tickets": 2,
"total_orders": 8,
"lifetime_value": 4500.00,
"first_contact_at": "2023-06-01T09:00:00.000Z",
"last_contact_at": "2024-01-15T10:30:00.000Z",
"recent_tickets": [
{
"id": "ticket-uuid",
"number": 1234,
"subject": "Integration question",
"status": "open",
"priority": "normal",
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T11:00:00.000Z"
}
],
"created_at": "2023-06-01T09:00:00.000Z"
}
}
}
}
Notes:
- Returns enriched customer data from the
customers_enrichedview - Includes up to 10 most recent tickets
Get Customer by Email
Retrieve a customer by their email address. Useful for integration widgets.
Procedure: customers.getByEmail
Authentication: Required
Input:
{
email: string; // Email address
}
Example:
curl -X GET "https://your-domain.com/api/trpc/customers.getByEmail?input=%7B%22email%22:%22customer@example.com%22%7D" \
-H "Cookie: your-session-cookie"
Response:
{
"result": {
"data": {
"json": {
"id": "uuid",
"email": "customer@example.com",
"name": "Jane Doe",
"status": "active",
// ... other customer fields
}
}
}
}
Notes:
- Returns
nullif no customer found with the email - Only searches within the user's organization
Create Customer
Create a new customer profile.
Procedure: customers.create
Authentication: Required (agent or admin)
Input:
{
email: string; // Required, must be valid email
name?: string; // Display name
phone?: string; // Phone number
company?: string; // Company name
tags?: string[]; // Tags to apply
notes?: string; // Internal notes
metadata?: Record<string, any>; // Custom metadata
}
Example:
curl -X POST "https://your-domain.com/api/trpc/customers.create" \
-H "Content-Type: application/json" \
-H "Cookie: your-session-cookie" \
-d '{
"json": {
"email": "newcustomer@example.com",
"name": "John Smith",
"company": "Smith Enterprises",
"tags": ["new", "enterprise"],
"metadata": {
"source": "trade_show",
"industry": "Manufacturing"
}
}
}'
Response:
{
"result": {
"data": {
"json": {
"id": "uuid",
"email": "newcustomer@example.com",
"name": "John Smith",
"company": "Smith Enterprises",
"status": "active",
"tags": ["new", "enterprise"],
"metadata": {
"source": "trade_show",
"industry": "Manufacturing"
},
"created_at": "2024-01-15T10:30:00.000Z"
}
}
}
}
Errors:
CONFLICT: A customer with this email already exists
Notes:
- If a user account with the same email exists, the customer is automatically linked
- Email must be unique within the organization
Update Customer
Update an existing customer's information.
Procedure: customers.update
Authentication: Required (agent or admin)
Input:
{
id: string; // Required, customer UUID
email?: string; // New email address
name?: string; // Display name
phone?: string; // Phone number
company?: string; // Company name
tags?: string[]; // Replace tags
notes?: string; // Internal notes
status?: 'active' | 'inactive' | 'blocked';
metadata?: Record<string, any>; // Replace metadata
}
Example:
curl -X POST "https://your-domain.com/api/trpc/customers.update" \
-H "Content-Type: application/json" \
-H "Cookie: your-session-cookie" \
-d '{
"json": {
"id": "customer-uuid",
"tags": ["enterprise", "priority"],
"status": "active",
"notes": "Upgraded to enterprise plan"
}
}'
Response:
{
"result": {
"data": {
"json": {
"id": "uuid",
"email": "customer@example.com",
"tags": ["enterprise", "priority"],
"status": "active",
"notes": "Upgraded to enterprise plan",
"updated_at": "2024-01-15T11:00:00.000Z"
}
}
}
}
Delete Customer
Soft delete a customer. Only admins can delete customers.
Procedure: customers.delete
Authentication: Required (admin only)
Input:
{
id: string; // Customer UUID
}
Example:
curl -X POST "https://your-domain.com/api/trpc/customers.delete" \
-H "Content-Type: application/json" \
-H "Cookie: your-session-cookie" \
-d '{"json":{"id":"customer-uuid"}}'
Response:
{
"result": {
"data": {
"json": {
"success": true
}
}
}
}
Notes:
- Soft delete - data is retained but excluded from queries
- Only admins can delete customers
- Associated tickets are not deleted
Get Customer Statistics
Get aggregate statistics about customers for the organization.
Procedure: customers.stats
Authentication: Required
Input: None
Example:
curl -X GET "https://your-domain.com/api/trpc/customers.stats" \
-H "Cookie: your-session-cookie"
Response:
{
"result": {
"data": {
"json": {
"total": 1500,
"active": 342,
"withOpenTickets": 89,
"newThisMonth": 45
}
}
}
}
Notes:
activecounts customers with contact in the last 30 daysnewThisMonthcounts customers created since the first of the current month
Merge Customers
Merge two customer profiles into one. Useful for handling duplicates.
Procedure: customers.merge
Authentication: Required (admin only)
Input:
{
primaryId: string; // UUID of customer to keep
secondaryId: string; // UUID of customer to merge and delete
}
Example:
curl -X POST "https://your-domain.com/api/trpc/customers.merge" \
-H "Content-Type: application/json" \
-H "Cookie: your-session-cookie" \
-d '{
"json": {
"primaryId": "customer-uuid-1",
"secondaryId": "customer-uuid-2"
}
}'
Response:
{
"result": {
"data": {
"json": {
"success": true
}
}
}
}
Merge Behavior:
- Primary customer's data takes precedence (if not null)
- Tags are merged (union of both)
- Metadata is merged (primary overwrites conflicts)
- Notes are concatenated with a separator
- Ticket counts and lifetime value are summed
- First/last contact dates use earliest/latest values
- All tickets from secondary customer are reassigned to primary
- Secondary customer is soft deleted
Get Customer Timeline
Get a unified, chronological view of all customer interactions including tickets, conversations, emails, orders, and notes.
Procedure: customers.timeline
Authentication: Required (agent or admin)
Input:
{
customerId: string; // Required
limit?: number; // 1-100, default: 50
cursor?: string; // ISO timestamp for pagination
types?: ('ticket' | 'conversation' | 'email' | 'order' | 'note')[]; // Filter event types
}
Example:
curl -X GET "https://your-domain.com/api/trpc/customers.timeline?input=%7B%22customerId%22:%22uuid%22,%22limit%22:20,%22types%22:[%22ticket%22,%22order%22]%7D" \
-H "Cookie: your-session-cookie"
Response:
{
"result": {
"data": {
"json": {
"events": [
{
"id": "order-uuid",
"type": "order",
"timestamp": "2024-01-15T10:30:00.000Z",
"title": "Order #1234",
"description": "USD 150.00 - Payment: paid, Fulfillment: fulfilled",
"metadata": {
"orderNumber": "1234",
"orderStatus": "fulfilled",
"financialStatus": "paid",
"totalPrice": "150.00",
"currency": "USD"
},
"channel": "shopify"
},
{
"id": "ticket-uuid",
"type": "ticket",
"timestamp": "2024-01-14T09:00:00.000Z",
"title": "Ticket #5678: Shipping question",
"description": "Status: resolved, Priority: normal",
"metadata": {
"status": "resolved",
"priority": "normal",
"source": "email"
},
"channel": "email",
"ticketId": "uuid",
"ticketNumber": 5678
},
{
"id": "conv-uuid",
"type": "conversation",
"timestamp": "2024-01-14T10:00:00.000Z",
"title": "Reply on Ticket #5678",
"description": "Support Agent: Thank you for reaching out...",
"metadata": {
"conversationType": "comment",
"internal": false,
"authorName": "Support Agent"
},
"channel": "comment",
"ticketId": "uuid",
"ticketNumber": 5678
}
],
"total": 45,
"nextCursor": "2024-01-10T09:00:00.000Z"
}
}
}
}
Timeline Event Types
| Type | Description |
|---|---|
ticket | Ticket creation events |
conversation | Public replies on tickets |
email | Email-type conversations |
note | Internal notes on tickets |
order | Shopify orders |
Notes:
- Events are sorted chronologically (newest first)
- Pagination uses timestamp cursors
- Shopify orders only appear if customer has a linked
shopify_customer_id
Get Shopify Orders
Retrieve Shopify orders for a customer.
Procedure: customers.getShopifyOrders
Authentication: Required
Input:
{
shopifyCustomerId: number; // Shopify customer ID
limit?: number; // 1-50, default: 5
}
Example:
curl -X GET "https://your-domain.com/api/trpc/customers.getShopifyOrders?input=%7B%22shopifyCustomerId%22:123456789,%22limit%22:10%7D" \
-H "Cookie: your-session-cookie"
Response:
{
"result": {
"data": {
"json": [
{
"id": "uuid",
"shopify_order_id": "5678901234",
"shopify_order_number": "1234",
"shopify_shop_domain": "mystore.myshopify.com",
"order_status": "fulfilled",
"financial_status": "paid",
"fulfillment_status": "fulfilled",
"total_price": "150.00",
"currency": "USD",
"created_at": "2024-01-15T10:30:00.000Z",
"order_data": { /* Full Shopify order data */ }
}
]
}
}
}
Get Shopify Customer Details
Get additional details about a linked Shopify customer, including their default address.
Procedure: customers.getShopifyCustomerDetails
Authentication: Required
Input:
{
shopifyCustomerId: number; // Shopify customer ID
}
Example:
curl -X GET "https://your-domain.com/api/trpc/customers.getShopifyCustomerDetails?input=%7B%22shopifyCustomerId%22:123456789%7D" \
-H "Cookie: your-session-cookie"
Response:
{
"result": {
"data": {
"json": {
"phone": "+1-555-123-4567",
"defaultAddress": {
"address1": "123 Main St",
"address2": "Suite 100",
"city": "San Francisco",
"province": "California",
"province_code": "CA",
"country": "United States",
"country_code": "US",
"zip": "94102",
"phone": "+1-555-123-4567"
}
}
}
}
}
Notes:
- Returns
nullfor phone or defaultAddress if not available - Requires Shopify integration to be configured
Permissions
| Operation | Required Role |
|---|---|
| List customers | Agent, Admin |
| Get customer | Agent, Admin |
| Get by email | Any authenticated |
| Create customer | Agent, Admin |
| Update customer | Agent, Admin |
| Delete customer | Admin |
| Get stats | Any authenticated |
| Merge customers | Admin |
| Get timeline | Agent, Admin |
| Get Shopify orders | Any authenticated |
| Get Shopify details | Any authenticated |
Webhooks
Customer-related webhook events are not currently available. Customer data changes are typically surfaced through ticket-related webhook events.
See Webhooks for available events.