Skip to content

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:

bash
Authorization: Bearer YOUR_API_TOKEN

See Authentication for how to generate an API token.

Rate Limiting

API requests are rate limited per account. Default limits:

WindowLimit
Per minute120 requests
Per hour3,000 requests

Successful responses include rate limit headers:

http
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1704067260

When 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

http
GET /kanbans

Query Parameters

ParameterTypeDescription
limitnumberNumber of kanbans to return (optional)
nextTokenstringPagination token from previous response
accessiblebooleanIf true, returns only kanbans the user has access to (for regular users)

Example Request

bash
curl -X GET "https://api.maiacompany.io/kanbans" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "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

FieldTypeDescription
idstringUnique identifier for the kanban board
namestringDisplay name of the kanban
slugstringURL-friendly version of the name
descriptionstringOptional description
isDefaultbooleanWhether this is the default kanban
ordernumberDisplay order in the sidebar
createdAtnumberUnix timestamp (milliseconds)
updatedAtnumberUnix timestamp (milliseconds)
statusesarrayList of status configurations
fieldsarrayList of custom field definitions

List Records

Retrieve CRM records with optional filtering and pagination.

Request

http
GET /crm/records

Query Parameters

ParameterTypeDescription
kanbanIdstringFilter by kanban board ID
statusstringFilter by status value
assignedUserIdstringFilter by assigned user ID
limitnumberNumber of records to return (default: 20, max: 100)
nextTokenstringPagination token from previous response

Example Request

bash
curl -X GET "https://api.maiacompany.io/crm/records?kanbanId=kanban_123&limit=10" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "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

http
GET /crm/records/{recordId}

Example Request

bash
curl -X GET "https://api.maiacompany.io/crm/records/record_1704067200000_abc123" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "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

StatusDescription
404Record not found
401Unauthorized

Create Record

Create a new CRM record.

Request

http
POST /crm/records

Query Parameters

ParameterTypeDescription
kanbanIdstringKanban board ID (uses default if omitted)

Request Body

FieldTypeRequiredDescription
titlestringYesRecord title
statusstringNoInitial status (uses kanban default)
customFieldsobjectNoCustom field values keyed by field ID
assignedUserIdsarrayNoUser IDs to assign to the record
channelIdstringNoChannel ID for duplicate prevention

Example Request

bash
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.

json
{
  "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

http
PUT /crm/records/{recordId}

Request Body

FieldTypeDescription
titlestringRecord title
statusstringStatus value
customFieldsobjectCustom field values keyed by field ID
assignedUserIdsarrayUser IDs assigned to the record

All fields are optional. Only provided fields are updated.

Example Request

bash
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.

json
{
  "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

http
DELETE /crm/records/{recordId}

Example Request

bash
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.

json
{
  "message": "CRM record deleted successfully"
}

Search Records

Search CRM records using full-text search across title and indexed custom fields.

Request

http
GET /crm/search

Query Parameters

ParameterTypeDescription
qstringSearch query (full-text search on indexed fields)
kanbanIdstringFilter by kanban board ID
statusstringFilter by status value
pagenumberPage number (default: 1)
pageSizenumberNumber of records per page (default: 20, max: 100)
customFieldIdstringCustom field ID to search by exact value (bypasses full-text search)
customFieldValuestringExact 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.

bash
# Find all records where the "phone" field equals "+1234567890"
GET /crm/search?customFieldId=field_phone_456&customFieldValue=+1234567890

When 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 TypeIndexed
textYes
emailYes
phoneYes
urlYes
selectYes
multiselectYes
numberNo
currencyNo
dateNo
datetimeNo
booleanNo
textareaNo

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 ValueQueryMatch?
"John Doe - New Lead"johnYes
"John Doe - New Lead"doeYes
"John Doe - New Lead"leadYes
"john@example.com"johnYes
"john@example.com"exampleYes
"+1234567890"+123Yes
"+1234567890"7890Yes
"John Doe"jonhYes (typo tolerance)

Example Requests

Full-text search:

bash
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:

bash
curl -X GET "https://api.maiacompany.io/crm/search?customFieldId=field_phone_456&customFieldValue=%2B1234567890" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "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

http
GET /crm/search/aggregations

Query Parameters

ParameterTypeDescription
kanbanIdstringKanban board ID (optional)

Example Request

bash
curl -X GET "https://api.maiacompany.io/crm/search/aggregations?kanbanId=kanban_123" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "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

http
GET /crm/fields

Query Parameters

ParameterTypeDescription
kanbanIdstringFilter fields to a specific kanban
allbooleanIf true, returns all fields across all kanbans

Example Request

bash
curl -X GET "https://api.maiacompany.io/crm/fields?kanbanId=kanban_123" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "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

TypeDescription
textSingle-line text
textareaMulti-line text
numberNumeric value
currencyCurrency value
dateDate only
datetimeDate and time
selectSingle selection dropdown
multiselectMultiple selection
booleanTrue/false toggle
emailEmail address
phonePhone number
urlURL/link

Get Statuses

Retrieve status definitions for a kanban.

Request

http
GET /crm/statuses

Query Parameters

ParameterTypeDescription
kanbanIdstringKanban board ID (optional)

Example Request

bash
curl -X GET "https://api.maiacompany.io/crm/statuses?kanbanId=kanban_123" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "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

StatusDescription
400Bad request - invalid parameters
401Unauthorized - invalid or missing token
403Forbidden - rate limited or no access
404Resource not found
500Server error

Example Error

json
{
  "error": "Bad Request",
  "message": "title is required"
}

MAIA Platform Documentation