List contacts
GET/api/v1/contacts
scope
contacts:readPaginatedLists the account’s shared contacts, newest first, with cursor pagination. Filter by a search string, an exact phone number, a tag, or a creation/update time. Contacts on a member’s private personal-WhatsApp number are never returned.
Query parameters
| Name | Type | Description |
|---|---|---|
limitoptional | integer | Page size, 1–100Default: 20 |
cursoroptional | string | The `next_cursor` from the previous page |
qoptional | string | Case-insensitive match on name, email, phone or company |
phoneoptional | string | Exact phone match (E.164). Returns at most one contact |
tag_idoptional | string (uuid) | Only contacts carrying this tag |
created_sinceoptional | string (date-time) | Only contacts created at or after this time |
updated_sinceoptional | string (date-time) | Only contacts updated at or after this time |
Request
curl "https://your-crm.example.com/api/v1/contacts?limit=20&q=ayesha" \
-H "Authorization: Bearer $RAABTA_API_KEY"Response · 200 OK
{
"data": [
{
"id": "9d7e4c11-5a2b-4f7c-8e3d-2a1b0c9d8e7f",
"name": "Ayesha Khan",
"phone": "+971501234567",
"email": "ayesha@example.com",
"company": "Khan Trading LLC",
"avatar_url": null,
"tags": [
{
"id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"name": "VIP",
"color": "#10b981"
}
],
"custom_fields": [
{
"field_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
"name": "Source",
"value": "Instagram ad"
}
],
"created_at": "2026-09-01T08:15:00.000Z",
"updated_at": "2026-09-02T10:00:00.000Z"
}
],
"meta": {
"next_cursor": null,
"has_more": false,
"limit": 20
}
}Response
Wrapped in { data: [...], meta }| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | UUID |
namerequired | string | null | Display name |
phonerequired | string | null | E.164 phone number, or null for a contact reached only on Messenger/Instagram/web chat |
emailrequired | string | null | Email address, when one is known |
companyrequired | string | null | Company or organisation the contact belongs to |
avatar_urlrequired | string | null | Profile picture URL, when a channel supplied one |
tagsrequired | object[] | Tags applied to this contact |
tags[].idrequired | string (uuid) | UUID |
tags[].namerequired | string | Tag name |
tags[].colorrequired | string | Hex colour chosen in the dashboard |
custom_fieldsrequired | object[] | Custom-field values set on this contact |
custom_fields[].field_idrequired | string (uuid) | The custom field definition |
custom_fields[].namerequired | string | Field name as shown in the dashboard |
custom_fields[].valuerequired | string | null | The value for this contact |
created_atrequired | string (date-time) | ISO 8601 timestamp |
updated_atrequired | string (date-time) | ISO 8601 timestamp |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | The request could not be parsed: malformed JSON, an invalid cursor, or a query parameter of the wrong shape. |
| 400 | validation_error | The body or query failed validation. `details` lists each failing field with a `path` and a `message`. |
| 401 | unauthorized | No usable API key: the Authorization header is missing or malformed, or the key is unknown, revoked or expired. The three are deliberately indistinguishable. |
| 403 | forbidden | The key is valid but lacks the scope this endpoint requires, or the request came from an address outside the key’s IP allowlist. The message says which. |
| 403 | account_suspended | The account this key belongs to is suspended. Rotating the key will not help; contact support. |
| 422 | unprocessable | The request was well-formed but cannot be carried out. `reason` is a stable string saying why (for example `outside_window` or `stage_not_in_pipeline`). |
| 429 | rate_limited | The per-key budget, or the per-IP budget for failed authentication, is exhausted. Honour `Retry-After` before retrying. |
| 500 | internal | Something failed on our side. Safe to retry with the same Idempotency-Key; quote `request_id` if it persists. |