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

StatusDescription
activeNormal, active customer
inactiveNo recent activity
blockedBlocked 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_enriched view
  • 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 null if 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:

  • active counts customers with contact in the last 30 days
  • newThisMonth counts 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

TypeDescription
ticketTicket creation events
conversationPublic replies on tickets
emailEmail-type conversations
noteInternal notes on tickets
orderShopify 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 null for phone or defaultAddress if not available
  • Requires Shopify integration to be configured

Permissions

OperationRequired Role
List customersAgent, Admin
Get customerAgent, Admin
Get by emailAny authenticated
Create customerAgent, Admin
Update customerAgent, Admin
Delete customerAdmin
Get statsAny authenticated
Merge customersAdmin
Get timelineAgent, Admin
Get Shopify ordersAny authenticated
Get Shopify detailsAny 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.