ContactZapp API reference
The ContactZapp API reads and writes the contacts in a ContactZapp account, so that Zapier, Make, n8n and your own code can use them.
Create an API key in ContactZapp under Settings, Advanced. Send it with every request as Authorization: Bearer <key>. The first call to try is GET /api/v1/me.
GET /api/v1/openapi.json
This API's OpenAPI document.
No API key needed.
Returns this API's OpenAPI 3.1 document as JSON. It needs no API key, so tools such as Pipedream and Activepieces can import it from the live API.
Responses
| Status | What it means |
|---|---|
| 200 | The OpenAPI document. |
GET /api/v1/me
Who this API key acts for, on which plan, with which scopes.
Any API key can call this.
Checks an API key. Returns the user the key acts for, the user's plan and the key's scopes. Any live key may call it, so it is the call to make when you want to test a key.
Send the key in every request as Authorization: Bearer <key>. Each operation names the scope it needs; a key without that scope gets 403. There is a limit on requests; past it the answer is 429 with a Retry-After header. Every error body has a stable code.
Responses
| Status | What it means |
|---|---|
| 200 | The key's user, plan and scopes. |
| 401 | No API key (api_key_missing; a session cookie alone counts as none),
or one that is not a live key: unknown, revoked or malformed
(api_key_invalid). |
| 429 | The user's keys together made 60 requests in the last minute
(rate_limited). Retry-After says, in seconds, when the next request
would be served. |
| 500 | The server could not complete the request (internal_error). The cause is logged, never returned. |
| 503 | The key or the rate limit could not be checked (unavailable). Retry. |
GET /api/v1/contacts
Find the user's contacts by email, phone or text, or list them.
Needs the contacts:read scope.
Finds the user's contacts, or lists them. Give at most one of email, phone and query:
emailandphonefind the contacts that hold that address or number. An email is compared ignoring case, and a phone number ignoring formatting.queryis the same free-text search the Contacts screen has.- With none of them, every contact is listed, newest first.
Pages work with a cursor: pass nextCursor back as cursor, with the same filter, until a page comes back without one. Deleted and merged contacts are never returned.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
email | query | string | No | |
phone | query | string | No | |
query | query | string | No | |
cursor | query | string | No | The previous page's nextCursor. |
limit | query | integer | No | Page size; 50 when omitted. |
Responses
| Status | What it means |
|---|---|
| 200 | One page of contacts. |
| 400 | A query parameter this route cannot use (invalid_parameter): a
limit out of range, a cursor this route did not issue, or two
filters where one is allowed. |
| 401 | No API key (api_key_missing; a session cookie alone counts as none),
or one that is not a live key: unknown, revoked or malformed
(api_key_invalid). |
| 403 | The key lacks the scope this route needs (insufficient_scope). |
| 429 | The user's keys together made 60 requests in the last minute
(rate_limited). Retry-After says, in seconds, when the next request
would be served. |
| 500 | The server could not complete the request (internal_error). The cause is logged, never returned. |
| 503 | The key or the rate limit could not be checked (unavailable). Retry. |
POST /api/v1/contacts
Create a contact, treated like one arriving from a sync.
Needs the contacts:write scope.
Creates a contact. It is treated like a contact that arrives from a sync.
The contact is written to one of the user's two-way accounts, which ContactZapp chooses: the earliest-linked account that keeps an address book and is not paused. The sync then copies it to every other two-way account, and never to a read-only one. It is matched like any incoming contact:
- when the user's auto-merge level covers the match, the values are added to the contact the user already has (200,
merged); - otherwise the contact is created (201,
created), and a possible match waits in Review duplicates (201,needs_review).
A create is never refused for the plan's contact limit. Over the limit, the contact is created frozen (201), and contact.frozen in the answer is true. ContactZapp keeps a frozen contact but does not sync it until the user upgrades or frees a place.
Send an Idempotency-Key header to make a retry safe. A repeat with the same key and the same body within 24 hours answers with the first result and writes nothing. The same key with a different body is a 422.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
Idempotency-Key | header | string | No | Any string of 1 to 255 bytes that the caller chooses for this write, such as a UUID. A repeat with the same key and the same body within 24 hours answers with the first result and writes nothing; the same key with another body is a 422. |
Responses
| Status | What it means |
|---|---|
| 200 | The values were merged into a contact the user has (merged). |
| 201 | A new contact (created, or needs_review with its possible matches). |
| 400 | The body is not a contact (invalid_body), carries an
impossible date (invalid_date) or no value at all (empty_contact),
or the Idempotency-Key header is empty or too long
(idempotency_key_invalid), or the body carries a field a contact
write does not take (unknown_field). |
| 401 | No API key (api_key_missing; a session cookie alone counts as none),
or one that is not a live key: unknown, revoked or malformed
(api_key_invalid). |
| 403 | The key lacks the scope this route needs (insufficient_scope). |
| 409 | The contact waits in Review duplicates
(contact_pending_review), was merged away or deleted
(contact_gone), or changed while it was written (contact_changed,
retry); or a request with this Idempotency-Key is still being handled
(idempotency_key_in_progress). |
| 413 | The request body is larger than the API accepts (body_too_large). |
| 422 | The user has no two-way account that keeps an address
book and is not paused (no_account_to_write_to), or this
Idempotency-Key was used with another body in the last 24 hours
(idempotency_key_reused). |
| 429 | The user's keys together made 60 requests in the last minute
(rate_limited). Retry-After says, in seconds, when the next request
would be served. |
| 500 | The server could not complete the request (internal_error). The cause is logged, never returned. |
| 503 | The key or the rate limit could not be checked (unavailable). Retry. |
GET /api/v1/contacts/{id}
One of the user's contacts.
Needs the contacts:read scope.
Returns one contact. id is the contact's id or its publicId (CS-0042). A deleted or merged contact is a 404, and so is another user's contact.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
id | path | string | Yes |
Responses
| Status | What it means |
|---|---|
| 200 | The contact. |
| 401 | No API key (api_key_missing; a session cookie alone counts as none),
or one that is not a live key: unknown, revoked or malformed
(api_key_invalid). |
| 403 | The key lacks the scope this route needs (insufficient_scope). |
| 404 | No such contact or trigger for this key's user (not_found). |
| 429 | The user's keys together made 60 requests in the last minute
(rate_limited). Retry-After says, in seconds, when the next request
would be served. |
| 500 | The server could not complete the request (internal_error). The cause is logged, never returned. |
| 503 | The key or the rate limit could not be checked (unavailable). Retry. |
PATCH /api/v1/contacts/{id}
Change a contact's fields, as the app's edit does.
Needs the contacts:write scope.
Changes a contact's fields, as the app's edit does. Only the fields in the body change; a field the body leaves out keeps its value.
The change is recorded in History as the user's, naming the API key, and written to every two-way account the contact is in, never to a read-only one. A contact waiting in Review duplicates cannot be changed until the review is decided (409, contact_pending_review). Idempotency-Key works as it does when you create a contact.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
id | path | string | Yes | The contact's id or its publicId (CS-0042). |
Idempotency-Key | header | string | No | Any string of 1 to 255 bytes that the caller chooses for this write, such as a UUID. A repeat with the same key and the same body within 24 hours answers with the first result and writes nothing; the same key with another body is a 422. |
Responses
| Status | What it means |
|---|---|
| 200 | The contact as it is now (updated). |
| 400 | The body is not a contact (invalid_body), carries an
impossible date (invalid_date) or no value at all (empty_contact),
or the Idempotency-Key header is empty or too long
(idempotency_key_invalid), or the body carries a field a contact
write does not take (unknown_field). |
| 401 | No API key (api_key_missing; a session cookie alone counts as none),
or one that is not a live key: unknown, revoked or malformed
(api_key_invalid). |
| 403 | The key lacks the scope this route needs (insufficient_scope). |
| 404 | No such contact or trigger for this key's user (not_found). |
| 409 | The contact waits in Review duplicates
(contact_pending_review), was merged away or deleted
(contact_gone), or changed while it was written (contact_changed,
retry); or a request with this Idempotency-Key is still being handled
(idempotency_key_in_progress). |
| 413 | The request body is larger than the API accepts (body_too_large). |
| 422 | The user has no two-way account that keeps an address
book and is not paused (no_account_to_write_to), or this
Idempotency-Key was used with another body in the last 24 hours
(idempotency_key_reused). |
| 429 | The user's keys together made 60 requests in the last minute
(rate_limited). Retry-After says, in seconds, when the next request
would be served. |
| 500 | The server could not complete the request (internal_error). The cause is logged, never returned. |
| 503 | The key or the rate limit could not be checked (unavailable). Retry. |
GET /api/v1/accounts
The user's linked accounts.
Needs the contacts:read scope.
Lists the user's linked accounts, as the Settings screen does: provider, name, sync direction, connection state and when each account last synced.
Responses
| Status | What it means |
|---|---|
| 200 | The linked accounts. |
| 401 | No API key (api_key_missing; a session cookie alone counts as none),
or one that is not a live key: unknown, revoked or malformed
(api_key_invalid). |
| 403 | The key lacks the scope this route needs (insufficient_scope). |
| 429 | The user's keys together made 60 requests in the last minute
(rate_limited). Retry-After says, in seconds, when the next request
would be served. |
| 500 | The server could not complete the request (internal_error). The cause is logged, never returned. |
| 503 | The key or the rate limit could not be checked (unavailable). Retry. |
GET /api/v1/triggers/{event}
A polling trigger — the newest events of one type first.
Needs the contacts:read scope.
Polling trigger: returns the newest events of one type first. This is what Zapier, Make and n8n poll to start an automation. Each item has an id that never changes. Polling again with nothing new returns the same ids, and a new event appears at the top. Deduplicate by id.
contact.created, contact.updated, contacts.merged and account.needs_attention come from the change history ContactZapp records. One item is one recorded event, and it is kept as long as the History screen keeps it. match.needs_review comes from the duplicate flags the Contacts screen shows: one item for each contact flagged as a possible duplicate and not yet merged. Its id changes when the flag is raised again.
Each item carries what an automation needs without a second call: the contact's fields (null when the contact has since been deleted or merged), for a merge the ids of the contacts it absorbed, and for an account the account and why it needs attention.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
event | path | Yes | ||
cursor | query | string | No | The previous page's nextCursor, to read further back. |
limit | query | integer | No | Page size; 50 when omitted. |
Responses
| Status | What it means |
|---|---|
| 200 | One page of events, newest first. |
| 400 | A query parameter this route cannot use (invalid_parameter): a
limit out of range, a cursor this route did not issue, or two
filters where one is allowed. |
| 401 | No API key (api_key_missing; a session cookie alone counts as none),
or one that is not a live key: unknown, revoked or malformed
(api_key_invalid). |
| 403 | The key lacks the scope this route needs (insufficient_scope). |
| 404 | No such contact or trigger for this key's user (not_found). |
| 429 | The user's keys together made 60 requests in the last minute
(rate_limited). Retry-After says, in seconds, when the next request
would be served. |
| 500 | The server could not complete the request (internal_error). The cause is logged, never returned. |
| 503 | The key or the rate limit could not be checked (unavailable). Retry. |