CRM API Reference
Complete API reference for all CRM endpoints — contacts, companies, activities, tags, lists, properties, and CQL queries.
CRM API Reference
All CRM endpoints require a Server API key with CRM scope.
Authorization: Bearer YOUR_SERVER_API_KEYWrite operations use the Kafka-first pattern and return 202 Accepted with a provisional record and X-Correlation-Id header. The actual DB write happens asynchronously.
Base URL: https://api.usetransactional.com/crm
Contacts
List contacts
GET /crm/contacts
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
limit | number | 20 | Results per page (max 100) |
search | string | — | Search by name, email, phone |
status | string | — | Filter: ACTIVE, ARCHIVED, DELETED |
lifecycleStage | string | — | Filter: SUBSCRIBER, LEAD, MARKETING_QUALIFIED, SALES_QUALIFIED, OPPORTUNITY, CUSTOMER, EVANGELIST |
module | string | — | Filter by active module |
isIdentified | boolean | — | Filter identified contacts |
from | string | — | Created after (ISO date) |
to | string | — | Created before (ISO date) |
sortBy | string | — | createdAt, lastActivityAt, email, name |
sortOrder | string | desc | asc or desc |
Response:
{
"data": [{ "id": 1, "email": "alice@example.com", "name": "Alice", ... }],
"meta": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }
}Search contacts
GET /crm/contacts/search?q=alice&limit=20
Typeahead search across name, email, and phone.
Filter contacts (CQL)
POST /crm/contacts/filter
{
"filter": {
"logic": "AND",
"conditions": [
{ "type": "field", "field": "status", "operator": "eq", "value": "ACTIVE" },
{ "type": "field", "field": "lifecycleStage", "operator": "in", "value": ["LEAD", "CUSTOMER"] }
]
},
"page": 1,
"limit": 20,
"sortBy": "createdAt",
"sortOrder": "desc"
}Get contact
GET /crm/contacts/:id
Accepts numeric ID, external ID (ctc_*), or email address.
Returns the full contact with companies, tags, custom properties, and activity count.
Create contact
POST /crm/contacts
{
"email": "alice@example.com",
"name": "Alice Johnson",
"givenName": "Alice",
"familyName": "Johnson",
"phone": "+1234567890",
"lifecycleStage": "LEAD",
"properties": {
"mrr": 4999,
"plan": "Enterprise"
}
}Response: 202 Accepted
{
"data": {
"id": null,
"externalId": "ctc_abc123...",
"email": "alice@example.com",
"status": "ACTIVE",
"pending": true
}
}Headers: X-Correlation-Id, Location: /crm/contacts/ctc_abc123...
Dedup: Returns 409 if a contact with the same email already exists.
Update contact
PATCH /crm/contacts/:id
{
"name": "Alice Smith",
"lifecycleStage": "CUSTOMER",
"properties": { "mrr": 9999 }
}Response: 202 Accepted with merged provisional record.
Delete contact
DELETE /crm/contacts/:id
Soft-delete (sets status to DELETED). Response: 202 Accepted.
Merge contacts
POST /crm/contacts/:winnerId/merge
{ "loserId": 2 }The winner absorbs the loser's companies, tags, activities. Response: 202 Accepted.
Assign tag
POST /crm/contacts/:id/tags
{ "tagId": 5 }Remove tag
DELETE /crm/contacts/:id/tags/:tagId
List activities
GET /crm/contacts/:id/activities
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
limit | number | 20 | Results per page (max 100) |
type | string | — | Filter by type: NOTE, CALL, MEETING, EMAIL, TASK, PAGE_VIEW, CUSTOM_EVENT |
module | string | — | Filter by module: CRM, SUPPORT, INTEGRATION |
from | string | — | After (ISO date) |
to | string | — | Before (ISO date) |
Log activity
POST /crm/contacts/:id/activities
{
"type": "CALL",
"content": "Discussed pricing and timeline",
"properties": { "duration": "30m", "outcome": "interested" }
}Activity types: NOTE, CALL, MEETING, EMAIL, TASK
Response: 202 Accepted. Activities are pushed to connected integrations (HubSpot as native engagements, Mailchimp as notes + events).
List associated companies
GET /crm/contacts/:id/companies
Returns companies linked to this contact with role and primary flag.
Associate company
POST /crm/contacts/:id/companies
{ "companyId": 5, "role": "Decision Maker", "isPrimary": true }Remove company association
DELETE /crm/contacts/:id/companies/:companyId
Sync status
GET /crm/contacts/:id/sync-status
Returns integration sync state for this contact: external IDs, provider names, last synced timestamps.
{
"data": {
"contactId": 1,
"externalId": "ctc_abc123...",
"integrations": [
{
"integrationId": 2,
"integrationName": "HubSpot - Production",
"provider": "HUBSPOT",
"externalId": "469194699512",
"lastSyncedAt": "2026-04-10T10:30:36Z",
"syncHash": "a1b2c3..."
}
]
}
}Contact summary
GET /crm/contacts/:id/summary
One-view summary designed for AI agents and quick lookups.
{
"data": {
"id": 1,
"name": "Alice Johnson",
"email": "alice@example.com",
"status": "ACTIVE",
"lifecycleStage": "CUSTOMER",
"companies": [{ "name": "Acme Corp", "domain": "acme.com", "role": "CTO" }],
"tags": ["vip", "enterprise"],
"activityCount": 26,
"lastActivityAt": "2026-04-10T10:30:00Z"
}
}Companies
List companies
GET /crm/companies
Same query parameters as contacts: page, limit, search, status, size, industry, from, to, sortBy, sortOrder.
Search companies
GET /crm/companies/search?q=acme&limit=20
Filter companies (CQL)
POST /crm/companies/filter
Same format as contacts filter.
Get company
GET /crm/companies/:id
Accepts numeric ID or external ID (cmp_*).
Create company
POST /crm/companies
{
"name": "Acme Corp",
"domain": "acme.com",
"industry": "SaaS",
"size": "50-200",
"plan": "Enterprise",
"website": "https://acme.com",
"phone": "+1234567890",
"properties": { "annual_revenue": "5000000" }
}Dedup: Returns 409 if a company with the same domain already exists.
Update company
PATCH /crm/companies/:id
Delete company
DELETE /crm/companies/:id
List contacts for company
GET /crm/companies/:id/contacts
Returns all contacts linked to this company with roles.
Tags
List tags
GET /crm/tags
Create tag
POST /crm/tags
{
"key": "vip",
"name": "VIP Customer",
"description": "High-value customers",
"color": "#FFD700"
}Key must be lowercase alphanumeric with hyphens or underscores. Dedup: Returns 409 if key already exists.
Update tag
PATCH /crm/tags/:id
{ "name": "Enterprise VIP", "color": "#FF6600" }Delete tag
DELETE /crm/tags/:id
Lists / Segments
List all lists
GET /crm/lists?entityType=CONTACT
Get list
GET /crm/lists/:id
Create list
POST /crm/lists
{
"name": "Active Customers",
"entityType": "CONTACT",
"isDynamic": true,
"filterCriteria": {
"logic": "AND",
"conditions": [
{ "type": "field", "field": "lifecycleStage", "operator": "eq", "value": "CUSTOMER" }
]
}
}Update list
PATCH /crm/lists/:id
Delete list
DELETE /crm/lists/:id
Evaluate dynamic list
POST /crm/lists/:id/evaluate
Re-runs the CQL filter and updates membership. Returns the updated member count.
List members
GET /crm/lists/:id/members?page=1&limit=50
Add members
POST /crm/lists/:id/members
{ "entityIds": [1, 2, 3] }Static lists only. Max 1000 per call.
Remove members
DELETE /crm/lists/:id/members
{ "entityIds": [4, 5] }Properties
List property definitions
GET /crm/properties?entity=CONTACT&group=CUSTOM
| Parameter | Type | Description |
|---|---|---|
entity | string | CONTACT, COMPANY, CONTACT_COMPANY |
group | string | BASIC, CONTACT_INFO, SOCIAL, COMPANY_INFO, CUSTOM |
isSystem | boolean | Filter system vs custom properties |
Create property
POST /crm/properties
{
"key": "mrr",
"entity": "CONTACT",
"label": "Monthly Revenue",
"type": "NUMBER",
"group": "CUSTOM",
"description": "Monthly recurring revenue in USD",
"isRequired": false
}Property types: TEXT, NUMBER, BOOLEAN, DATE, DATETIME, EMAIL, PHONE, URL, SELECT, MULTI_SELECT, CURRENCY, TEXTAREA
Dedup: Returns 409 if (entity, key) already exists.
Update property
PATCH /crm/properties/:id
Archive property
DELETE /crm/properties/:id
Soft-archive — values are preserved, field is hidden from the UI.
CQL Query
Execute query
POST /crm/query
{
"entity": "contact",
"where": "status = ACTIVE AND email contains '@example.com'",
"sort": "createdAt:desc",
"page": 1,
"limit": 20
}Alternatively use the JSON filter format:
{
"entity": "contact",
"filter": {
"logic": "AND",
"conditions": [
{ "type": "field", "field": "status", "operator": "eq", "value": "ACTIVE" }
]
}
}Response:
{
"data": [...],
"meta": {
"total": 142,
"limit": 20,
"page": 1,
"totalPages": 8,
"queryTimeMs": 12,
"estimatedCost": 5
}
}List queryable fields
GET /crm/fields?entity=contact
Returns all system fields and custom properties with their types, operators, and access levels.
Legacy Endpoints
These endpoints still work for backward compatibility but the RESTful versions above are preferred:
| Legacy | RESTful |
|---|---|
GET /crm/activities/contacts/:id | GET /crm/contacts/:id/activities |
POST /crm/activities/contacts/:id | POST /crm/contacts/:id/activities |
Error Codes
| Code | HTTP | Description |
|---|---|---|
NOT_FOUND | 404 | Contact, company, tag, list, or property not found |
INVALID_ID | 400 | ID parameter is not a valid number or external ID |
CONTACT_EMAIL_EXISTS | 409 | A contact with this email already exists |
COMPANY_DOMAIN_EXISTS | 409 | A company with this domain already exists |
TAG_KEY_EXISTS | 409 | A tag with this key already exists |
PROPERTY_KEY_EXISTS | 409 | A property with this key+entity already exists |
ALREADY_ASSOCIATED | 409 | Contact is already associated with the company |
INVALID_REQUEST | 400 | Cannot merge with self, cannot add to dynamic list, etc. |
PENDING | 202 | Resource exists but write hasn't committed yet (Kafka-first) |
Rate Limits
CRM endpoints share the server API key's rate limit tier. Write operations are processed asynchronously via Kafka and don't block the HTTP response.
Integration sync to external providers (HubSpot, Mailchimp) is separately rate-limited:
- HubSpot: 80 requests / 10 seconds
- Mailchimp: 8 requests / second
On This Page
- Contacts
- List contacts
- Search contacts
- Filter contacts (CQL)
- Get contact
- Create contact
- Update contact
- Delete contact
- Merge contacts
- Assign tag
- Remove tag
- List activities
- Log activity
- List associated companies
- Associate company
- Remove company association
- Sync status
- Contact summary
- Companies
- List companies
- Search companies
- Filter companies (CQL)
- Get company
- Create company
- Update company
- Delete company
- List contacts for company
- Tags
- List tags
- Create tag
- Update tag
- Delete tag
- Lists / Segments
- List all lists
- Get list
- Create list
- Update list
- Delete list
- Evaluate dynamic list
- List members
- Add members
- Remove members
- Properties
- List property definitions
- Create property
- Update property
- Archive property
- CQL Query
- Execute query
- List queryable fields
- Legacy Endpoints
- Error Codes
- Rate Limits