RaabtaHQ
Create a contact

Create a contact

POST/api/v1/contacts

scope contacts:writeIdempotent

Creates a contact. At least one of phone, name or email is required. A phone number that already belongs to a contact in this account returns 409 conflict with the existing contact’s id in details — dedupe is by normalised digits, so +971 50 123 4567 and 971501234567 are the same number. Counts toward the account’s contact limit.

Body

application/json
NameTypeDescription
phoneoptional
string | null
nameoptional
string | null
emailoptional
string (email) | null
companyoptional
string | null
tag_idsoptional
string (uuid)[]Tags to apply; must belong to this account
custom_fieldsoptional
map<string, string | null>Custom-field values keyed by field id
Request
curl -X POST "https://your-crm.example.com/api/v1/contacts" \
  -H "Authorization: Bearer $RAABTA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-3b4d-4c5e-8f7a-9b0c1d2e3f4a" \
  -d '{
    "phone": "+971501234567",
    "name": "Ayesha Khan",
    "email": "ayesha@example.com",
    "company": "Khan Trading LLC",
    "tag_ids": [
      "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
    ],
    "custom_fields": {
      "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d": "Instagram ad"
    }
  }'
Response · 201 Created
{
  "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"
  }
}

Response

Wrapped in { data: … }
NameTypeDescription
idrequired
string (uuid)UUID
namerequired
string | nullDisplay name
phonerequired
string | nullE.164 phone number, or null for a contact reached only on Messenger/Instagram/web chat
emailrequired
string | nullEmail address, when one is known
companyrequired
string | nullCompany or organisation the contact belongs to
avatar_urlrequired
string | nullProfile picture URL, when a channel supplied one
tagsrequired
object[]Tags applied to this contact
tags[].idrequired
string (uuid)UUID
tags[].namerequired
stringTag name
tags[].colorrequired
stringHex 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
stringField name as shown in the dashboard
custom_fields[].valuerequired
string | nullThe value for this contact
created_atrequired
string (date-time)ISO 8601 timestamp
updated_atrequired
string (date-time)ISO 8601 timestamp

Errors

StatusCodeWhen
400bad_requestThe request could not be parsed: malformed JSON, an invalid cursor, or a query parameter of the wrong shape.
400validation_errorThe body or query failed validation. `details` lists each failing field with a `path` and a `message`.
401unauthorizedNo usable API key: the Authorization header is missing or malformed, or the key is unknown, revoked or expired. The three are deliberately indistinguishable.
402plan_limit_reachedThe account has reached a plan limit for this resource. `details[0].limit` is the cap; ask the account owner to raise it.
403forbiddenThe 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.
403account_suspendedThe account this key belongs to is suspended. Rotating the key will not help; contact support.
409conflictThe request conflicts with current state: a duplicate phone number on create, an invalid state transition, or an idempotent request that is still in flight.
422unprocessableThe 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`).
429rate_limitedThe per-key budget, or the per-IP budget for failed authentication, is exhausted. Honour `Retry-After` before retrying.
500internalSomething failed on our side. Safe to retry with the same Idempotency-Key; quote `request_id` if it persists.