Skip to content

Contacts

Base path: /api/v1/contacts

POST /contacts

Body:

FieldTypeRequiredDefaultDescription
display_namestringYesAuto-generated from first_name/last_name if not provided
first_namestringNonull
last_namestringNonull
emailsobject[]No[][{address, label, primary}] — label: work, personal, other
phonesobject[]No[][{number, label, primary}] — label: mobile, work, home, other
companystringNonull
job_titlestringNonull
addressesobject[]No[][{street, city, state, postal_code, country, label}]
notesstringNonullFree-text notes about the contact
avatar_pathstringNonullPath to image in the local file system
tagsstring[]No[]
custom_fieldsobjectNonullArbitrary key-value data

Response: 201 Created with the full contact object.

WebSocket event: contact.created

{
"display_name": "Ana García",
"first_name": "Ana",
"last_name": "García",
"emails": [{ "address": "[email protected]", "label": "work", "primary": true }],
"phones": [{ "number": "+34612345678", "label": "mobile", "primary": true }],
"company": "Acme Labs",
"job_title": "Head of Partnerships",
"tags": ["partnership"]
}

GET /contacts

Query parameters:

ParameterTypeDefaultDescription
limitinteger20
offsetinteger0
sortstringdisplay_nameValid: display_name, created_at, updated_at, company
orderstringascDefaults to asc (alphabetical)
searchstringFull-text search on name, emails, phones, company, notes
tagstringFilter by tag. Repeatable: ?tag=a&tag=b (AND)
companystringFilter by company (partial match, case-insensitive)
sourcestringFilter by origin
include_deletedbooleanfalseInclude soft-deleted contacts
only_deletedbooleanfalseOnly soft-deleted contacts (trash view)

Response: 200 OK with list envelope.

Supports ?format=compact for token-optimized output.

GET /contacts/:id

Response: 200 OK with single record envelope.

Returns 404 NOT_FOUND if the contact doesn’t exist or is soft-deleted.

PATCH /contacts/:id

Send only the fields to update.

Updatable fields: display_name, first_name, last_name, emails, phones, company, job_title, addresses, notes, avatar_path, tags, custom_fields

Response: 200 OK with the full updated contact.

WebSocket event: contact.updated

PATCH /contacts/:id/custom

Incremental update via shallow merge. Same behavior as Notes custom fields.

Response: 200 OK with the full contact.

WebSocket event: contact.updated

DELETE /contacts/:id — soft delete (sets deleted_at)

DELETE /contacts/:id?permanent=true — permanent delete (irreversible)

See Soft Deletes for details.

WebSocket event: contact.deleted

POST /contacts/:id/restore — clears deleted_at, contact reappears in listings.

WebSocket event: contact.restored