CRM API
Manage CRM records programmatically. Create, read, update, delete, and search your customer records.
Authentication
This endpoint supports API Token authentication. Include your API token in the Authorization header:
Authorization: Bearer YOUR_API_TOKENSee Authentication for how to generate an API token.
Rate Limiting
API requests are rate limited per account. Default limits:
| Window | Limit |
|---|---|
| Per minute | 120 requests |
| Per hour | 3,000 requests |
Successful responses include rate limit headers:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1704067260When rate limited, you'll receive a 403 Forbidden response. Wait and retry with exponential backoff.
TIP
Contact your administrator to adjust rate limits for your account.
List Kanbans
Retrieve all Kanban boards in your account with their statuses and custom field definitions.
Request
GET /kanbansQuery Parameters
| Parameter | Type | Description |
|---|---|---|
limit | number | Number of kanbans to return (optional) |
nextToken | string | Pagination token from previous response |
accessible | boolean | If true, returns only kanbans the user has access to (for regular users) |
Example Request
curl -X GET "https://api.maiacompany.io/kanbans" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"items": [
{
"id": "kanban_1704067200000_abc123def",
"name": "Sales Pipeline",
"slug": "sales-pipeline",
"description": "Main sales pipeline for tracking leads",
"isDefault": true,
"order": 0,
"createdAt": 1704067200000,
"updatedAt": 1704153600000,
"statuses": [
{
"value": "new",
"label": "New",
"color": "#1890ff"
},
{
"value": "contacted",
"label": "Contacted",
"color": "#52c41a"
},
{
"value": "qualified",
"label": "Qualified",
"color": "#722ed1"
},
{
"value": "closed",
"label": "Closed Won",
"color": "#13c2c2"
}
],
"fields": [
{
"id": "field_1704067200001_xyz789",
"name": "Email",
"type": "email",
"description": "Contact email address",
"order": 0,
"isSystem": false
},
{
"id": "field_1704067200002_abc456",
"name": "Phone",
"type": "phone",
"description": "Contact phone number",
"order": 1,
"isSystem": false
},
{
"id": "field_1704067200003_def789",
"name": "Deal Value",
"type": "currency",
"description": "Expected deal value",
"order": 2,
"isSystem": false
},
{
"id": "field_1704067200004_ghi012",
"name": "Lead Source",
"type": "select",
"description": "Where the lead came from",
"options": ["Website", "Referral", "Social Media", "Cold Call"],
"order": 3,
"isSystem": false
}
]
}
],
"nextToken": null
}Kanban Object Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the kanban board |
name | string | Display name of the kanban |
slug | string | URL-friendly version of the name |
description | string | Optional description |
isDefault | boolean | Whether this is the default kanban |
order | number | Display order in the sidebar |
createdAt | number | Unix timestamp (milliseconds) |
updatedAt | number | Unix timestamp (milliseconds) |
statuses | array | List of status configurations |
fields | array | List of custom field definitions |
List Records
Retrieve CRM records with optional filtering and pagination.
Request
GET /crm/recordsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
kanbanId | string | Filter by kanban board ID |
status | string | Filter by status value |
assignedUserId | string | Filter by assigned user ID |
limit | number | Number of records to return (default: 20, max: 100) |
nextToken | string | Pagination token from previous response |
Example Request
curl -X GET "https://api.maiacompany.io/crm/records?kanbanId=kanban_123&limit=10" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"items": [
{
"recordId": "record_1704067200000_abc123",
"accountId": "account_xyz",
"kanbanId": "kanban_123",
"title": "John Doe - New Lead",
"status": "new",
"customFields": {
"field_email_123": "john@example.com",
"field_phone_456": "+1234567890"
},
"assignedUserIds": ["user_abc"],
"createdAt": 1704067200000,
"updatedAt": 1704153600000
}
],
"nextToken": "eyJ0aXRsZSI6..."
}Get Record
Retrieve a single CRM record by ID.
Request
GET /crm/records/{recordId}Example Request
curl -X GET "https://api.maiacompany.io/crm/records/record_1704067200000_abc123" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"recordId": "record_1704067200000_abc123",
"accountId": "account_xyz",
"kanbanId": "kanban_123",
"title": "John Doe - New Lead",
"status": "new",
"customFields": {
"field_email_123": "john@example.com",
"field_phone_456": "+1234567890",
"field_value_789": 5000
},
"assignedUserIds": ["user_abc"],
"createdAt": 1704067200000,
"updatedAt": 1704153600000
}Error Responses
| Status | Description |
|---|---|
| 404 | Record not found |
| 401 | Unauthorized |
Create Record
Create a new CRM record.
Request
POST /crm/recordsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
kanbanId | string | Kanban board ID (uses default if omitted) |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Record title |
status | string | No | Initial status (uses kanban default) |
customFields | object | No | Custom field values keyed by field ID |
assignedUserIds | array | No | User IDs to assign to the record |
channelId | string | No | Channel ID for duplicate prevention |
Example Request
curl -X POST "https://api.maiacompany.io/crm/records?kanbanId=kanban_123" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Jane Smith - Website Inquiry",
"status": "new",
"customFields": {
"field_email_123": "jane@example.com",
"field_phone_456": "+1987654321",
"field_source_789": "Website"
}
}'Response
Returns 201 Created with the created record.
{
"recordId": "record_1704067200001_def456",
"accountId": "account_xyz",
"kanbanId": "kanban_123",
"title": "Jane Smith - Website Inquiry",
"status": "new",
"customFields": {
"field_email_123": "jane@example.com",
"field_phone_456": "+1987654321",
"field_source_789": "Website"
},
"assignedUserIds": [],
"createdAt": 1704067200001,
"updatedAt": 1704067200001
}Update Record
Update an existing CRM record.
Request
PUT /crm/records/{recordId}Request Body
| Field | Type | Description |
|---|---|---|
title | string | Record title |
status | string | Status value |
customFields | object | Custom field values keyed by field ID |
assignedUserIds | array | User IDs assigned to the record |
All fields are optional. Only provided fields are updated.
Example Request
curl -X PUT "https://api.maiacompany.io/crm/records/record_1704067200001_def456" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "contacted",
"customFields": {
"field_notes_abc": "Followed up via email on 2024-01-15"
}
}'Response
Returns 200 OK with the updated record.
{
"recordId": "record_1704067200001_def456",
"accountId": "account_xyz",
"kanbanId": "kanban_123",
"title": "Jane Smith - Website Inquiry",
"status": "contacted",
"customFields": {
"field_email_123": "jane@example.com",
"field_phone_456": "+1987654321",
"field_source_789": "Website",
"field_notes_abc": "Followed up via email on 2024-01-15"
},
"assignedUserIds": [],
"createdAt": 1704067200001,
"updatedAt": 1704153600002
}Delete Record
Delete a CRM record.
Request
DELETE /crm/records/{recordId}Example Request
curl -X DELETE "https://api.maiacompany.io/crm/records/record_1704067200001_def456" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
Returns 200 OK with a success message.
{
"message": "CRM record deleted successfully"
}Search Records
Search CRM records using full-text search across title and indexed custom fields.
Request
GET /crm/searchQuery Parameters
| Parameter | Type | Description |
|---|---|---|
q | string | Search query (full-text search on indexed fields) |
kanbanId | string | Filter by kanban board ID |
status | string | Filter by status value |
page | number | Page number (default: 1) |
pageSize | number | Number of records per page (default: 20, max: 100) |
customFieldId | string | Custom field ID to search by exact value (bypasses full-text search) |
customFieldValue | string | Exact value to match for the given custom field (required with customFieldId) |
Custom Field Lookup
Use customFieldId + customFieldValue together to find records by an exact match on a specific custom field value. This is useful for looking up records by phone number, email, or any other indexed field.
This search uses the DynamoDB field value index directly (not full-text search), so it is fast and precise. The match is case-insensitive.
# Find all records where the "phone" field equals "+1234567890"
GET /crm/search?customFieldId=field_phone_456&customFieldValue=+1234567890When these parameters are provided, the q, kanbanId, and status parameters are ignored.
Indexed Fields
Search matches against the record title and the following custom field types:
| Field Type | Indexed |
|---|---|
text | Yes |
email | Yes |
phone | Yes |
url | Yes |
select | Yes |
multiselect | Yes |
number | No |
currency | No |
date | No |
datetime | No |
boolean | No |
textarea | No |
Search Behavior
Full-Text Search
Search is powered by Typesense, providing powerful full-text search capabilities:
- Substring Matching - Search for text anywhere within field values (beginning, middle, or end)
- Typo Tolerance - Finds matches even with up to 2 typos in your query
- Case-Insensitive - All searches are case-insensitive
Examples:
| Field Value | Query | Match? |
|---|---|---|
| "John Doe - New Lead" | john | Yes |
| "John Doe - New Lead" | doe | Yes |
| "John Doe - New Lead" | lead | Yes |
| "john@example.com" | john | Yes |
| "john@example.com" | example | Yes |
| "+1234567890" | +123 | Yes |
| "+1234567890" | 7890 | Yes |
| "John Doe" | jonh | Yes (typo tolerance) |
Example Requests
Full-text search:
curl -X GET "https://api.maiacompany.io/crm/search?q=john&kanbanId=kanban_123" \
-H "Authorization: Bearer YOUR_API_TOKEN"Custom field exact-match lookup:
curl -X GET "https://api.maiacompany.io/crm/search?customFieldId=field_phone_456&customFieldValue=%2B1234567890" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"items": [
{
"accountId": "account_xyz",
"recordId": "record_1704067200000_abc123",
"title": "John Doe - New Lead",
"status": "new",
"customFields": {
"field_email_123": "john@example.com"
},
"assignedUserIds": ["user_abc"],
"channelId": "channel_123",
"createdAt": 1704067200000,
"updatedAt": 1704153600000,
"createdBy": "user_abc",
"updatedBy": "user_abc"
}
],
"total": 1,
"page": 1,
"pageSize": 20,
"totalPages": 1
}Get Aggregations
Get status counts for all records in a kanban.
Request
GET /crm/search/aggregationsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
kanbanId | string | Kanban board ID (optional) |
Example Request
curl -X GET "https://api.maiacompany.io/crm/search/aggregations?kanbanId=kanban_123" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"status": [
{ "key": "new", "count": 25 },
{ "key": "contacted", "count": 15 },
{ "key": "qualified", "count": 8 },
{ "key": "closed", "count": 42 }
]
}Get Field Definitions
Retrieve custom field definitions for your kanbans.
Request
GET /crm/fieldsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
kanbanId | string | Filter fields to a specific kanban |
all | boolean | If true, returns all fields across all kanbans |
Example Request
curl -X GET "https://api.maiacompany.io/crm/fields?kanbanId=kanban_123" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"fields": [
{
"fieldId": "field_email_123",
"name": "Email",
"type": "email",
"description": "Contact email address",
"order": 0,
"isSystem": false,
"boundToChannels": ["WhatsApp Business"]
},
{
"fieldId": "field_phone_456",
"name": "Phone",
"type": "phone",
"description": "Contact phone number",
"order": 1,
"isSystem": false,
"boundToChannels": []
},
{
"fieldId": "field_source_789",
"name": "Lead Source",
"type": "select",
"description": "How the lead found us",
"options": ["Website", "Referral", "Social Media", "Cold Call"],
"order": 2,
"isSystem": false,
"boundToChannels": []
}
]
}Field Types
| Type | Description |
|---|---|
text | Single-line text |
textarea | Multi-line text |
number | Numeric value |
currency | Currency value |
date | Date only |
datetime | Date and time |
select | Single selection dropdown |
multiselect | Multiple selection |
boolean | True/false toggle |
email | Email address |
phone | Phone number |
url | URL/link |
Get Statuses
Retrieve status definitions for a kanban.
Request
GET /crm/statusesQuery Parameters
| Parameter | Type | Description |
|---|---|---|
kanbanId | string | Kanban board ID (optional) |
Example Request
curl -X GET "https://api.maiacompany.io/crm/statuses?kanbanId=kanban_123" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"statuses": [
{ "value": "new", "label": "New", "color": "#1890ff" },
{ "value": "contacted", "label": "Contacted", "color": "#52c41a" },
{ "value": "qualified", "label": "Qualified", "color": "#722ed1" },
{ "value": "closed", "label": "Closed Won", "color": "#13c2c2" }
]
}Error Responses
| Status | Description |
|---|---|
400 | Bad request - invalid parameters |
401 | Unauthorized - invalid or missing token |
403 | Forbidden - rate limited or no access |
404 | Resource not found |
500 | Server error |
Example Error
{
"error": "Bad Request",
"message": "title is required"
}