Inbox
Keep one record per person across your messaging channels, with tags, notes and subscription state, and use it to target broadcasts and sequences.
A contact is a person you talk to. One contact can have several channels: their Instagram DMs, their WhatsApp number and their Telegram chat can all point at the same record. Contacts carry tags, notes, a subscription flag and custom fields, and they are what broadcasts and sequences target.
Contacts belong to a profile. Pozy creates them as people message your accounts; you can also create and import them yourself.
#Create a contact
Create the record and, optionally, its first channel in the same call by passing accountId, platform and platformIdentifier (the person's id on that platform; a phone number for WhatsApp and SMS).
curl -X POST https://api.pozy.ai/v1/contacts \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "6650c1e2a3b4c5d6e7f80911",
"name": "Marta Pons",
"email": "marta@example.com",
"tags": ["vip", "newsletter"],
"accountId": "6650c1e2a3b4c5d6e7f80a44",
"platform": "whatsapp",
"platformIdentifier": "+34600111222"
}'const res = await fetch("https://api.pozy.ai/v1/contacts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
profileId: "6650c1e2a3b4c5d6e7f80911",
name: "Marta Pons",
email: "marta@example.com",
tags: ["vip", "newsletter"],
accountId: "6650c1e2a3b4c5d6e7f80a44",
platform: "whatsapp",
platformIdentifier: "+34600111222",
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.contact.id, data.channel?.id);import os
import requests
resp = requests.post(
"https://api.pozy.ai/v1/contacts",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
json={
"profileId": "6650c1e2a3b4c5d6e7f80911",
"name": "Marta Pons",
"email": "marta@example.com",
"tags": ["vip", "newsletter"],
"accountId": "6650c1e2a3b4c5d6e7f80a44",
"platform": "whatsapp",
"platformIdentifier": "+34600111222",
},
timeout=30,
)
print(resp.json()["contact"]["id"])isSubscribed defaults to true. If something about the request was off but the contact was still created (for example a channel that could not be attached), the response carries a warning.
#Import in bulk
POST /v1/contacts/bulk imports up to 1,000 contacts per call into one profile. Duplicates are skipped and any new tags are merged onto the existing contact.
{
"profileId": "6650c1e2a3b4c5d6e7f80911",
"accountId": "6650c1e2a3b4c5d6e7f80a44",
"contacts": [
{ "name": "Marta Pons", "platformIdentifier": "+34600111222", "tags": ["spring-2026"] },
{ "name": "Leo Martin", "platformIdentifier": "+34600333444", "email": "leo@example.com" }
]
}{ "success": true, "created": 2, "skipped": 0, "errors": [], "total": 2 }accountIdis required whenever contacts carry aplatformIdentifier, and then every row needs one.- On WhatsApp and SMS, identifiers are normalised to digits. A value that is not a phone number is rejected for that row only.
- Bad rows are reported in
errorswith HTTP200; the rest of the import still goes through. Checkerrorson every call.
#Find contacts
GET /v1/contacts lists a profile's contacts. Filter with search (name, email and company), tag or tags, platform, accountId and isSubscribed, and page with limit (up to 200) and skip.
Each contact includes its tags, custom field values, notes, message counters (messagesSentCount, messagesReceivedCount) and the last time a message went each way. GET /v1/contacts/{id}/channels lists every channel of one contact, with the conversation id for each.
#Update and tag
PATCH /v1/contacts/{contactId} changes only the fields you send: name, email, company, avatarUrl, tags, isSubscribed, isBlocked and notes.
tags replaces the whole list. To add one tag, read the contact, append, and send the full list back. Workflows can tag contacts for you with their add_tag and remove_tag nodes; see Workflow nodes.
#Opt-in and blocking
| Field | Effect |
|---|---|
isSubscribed: false |
Broadcast segments that filter on isSubscribed: true leave the contact out, and sequences with exitOnUnsubscribe (on by default) end for them |
isBlocked: true |
Marks the person as blocked in your records |
Respect opt-outs on every channel. For WhatsApp, SMS and RCS, the platforms and carriers require prior consent before you send promotional messages. See WhatsApp and SMS.
#Delete
DELETE /v1/contacts/{contactId} permanently deletes the contact and all its channels. It cannot be undone.
#Contact webhooks
| Event | Fires when |
|---|---|
contact.updated |
A contact's details changed |
contact.deleted |
A contact was deleted |
contact.tag_added, contact.tag_removed |
A tag was added or removed, by the API, a workflow node or a tracked link click in a comment automation |
contact.field_changed |
A custom field changed value |
Tag and field events fire once per real change. Re-adding a tag the contact already has, or writing a field's current value, fires nothing. Creating a contact with tags fires nothing either; only changes to existing contacts do. The payload's source says who made the change: api (the API or the dashboard), workflow or automation.
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/contacts |
List contacts |
POST /v1/contacts |
Create contact |
GET /v1/contacts/{contactId} |
Get contact |
PATCH /v1/contacts/{contactId} |
Update contact |
DELETE /v1/contacts/{contactId} |
Delete contact |
GET /v1/contacts/{contactId}/channels |
List channels for a contact |
POST /v1/contacts/bulk |
Bulk create contacts |
#Webhook events
| Event | When it fires |
|---|---|
contact.updated |
Contact updated. |
contact.deleted |
Contact deleted. |
contact.tag_added |
Contact tag added. |
contact.tag_removed |
Contact tag removed. |
contact.field_changed |
Contact field changed. |