Contacts
Contacts are social media users who have interacted with your connected accounts. VoxBurst syncs them automatically from inbound interactions. You can also create, update, and tag contacts manually.
Contacts are available on all plans — no feature flag required.
Base URL
https://api.voxburst.io/v1/contactsResponse shapes
There are two response shapes depending on the endpoint:
- List shape — returned by
GET /v1/contacts. Includes messaging-status fields. - Detail shape — returned by
GET /v1/contacts/:id,POST /v1/contacts/:id/messages, and all mutation endpoints (POST /v1/contacts,PATCH /v1/contacts/:id,POST .../tags,DELETE .../tags/:tag). A superset of the list shape — addsdeliveries,sequenceEnrollments,comments, andinboundMessagesarrays.
Contact fields (list shape)
| Field | Type | Description |
|---|---|---|
id | string | Contact ID |
workspaceId | string | Workspace this contact belongs to |
platform | string | Platform constant (e.g. INSTAGRAM) |
platformId | string | User ID on the platform |
displayName | string | null | Display name on the platform |
username | string | null | Handle or username |
avatarUrl | string | null | Profile avatar URL |
tags | string[] | Workspace-defined tags for segmentation |
customFields | object | Arbitrary key-value metadata |
socialAccount | object | null | The connected account this contact was last seen through. Fields: id, displayName, username, platform. null if the contact was created manually and has not been attributed to a connected account. |
lastSeenAt | string | null | ISO 8601 timestamp of last inbound interaction |
lastInboundAt | string | null | ISO 8601 timestamp of the most recent inbound message from this contact |
messengerEligibleUntil | string | null | Facebook only. ISO 8601 timestamp of when the 24-hour Messenger window closes. null when the window has closed or has never been opened. Always null once a window lapses — never returns a past timestamp. |
isSuppressed | boolean | true if this contact has opted out (STOP keyword or manual suppression). Suppressed contacts cannot be messaged. |
suppressedAt | string | null | ISO 8601 timestamp of when suppression was applied. null if not suppressed. |
suppressionReason | string | null | "STOP_KEYWORD" or "MANUAL". null if not suppressed. |
messagesSentCount | integer | Total number of messages sent to this contact (broadcasts + direct messages combined) |
sequenceEnrollmentCount | integer | Total number of sequence enrollments for this contact |
createdAt | string | ISO 8601 creation timestamp |
updatedAt | string | ISO 8601 last-updated timestamp |
Additional fields in the detail shape
| Field | Type | Description |
|---|---|---|
deliveries | object[] | Last 20 delivery records for this contact, ordered newest first. Each entry: id, status (PENDING | DELIVERING | DELIVERED | FAILED | READ), sentAt (ISO 8601 | null), readAt (ISO 8601 | null), errorCode (string | null), createdAt, broadcast ({ id, name } | null — null for direct messages). |
sequenceEnrollments | object[] | Last 10 sequence enrollments, ordered newest first. Each entry: id, status (ACTIVE | COMPLETED | EXITED | FAILED), currentStep (integer), enrolledAt, exitedAt (ISO 8601 | null), exitReason (string | null), sequence ({ id, name }). |
List Contacts
GET /v1/contacts
List contacts with optional search, platform, and tag filtering. Uses page/limit pagination. Returns the list shape.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
search | string | Search by display name or username |
platform | string | Filter by platform constant (e.g. INSTAGRAM) |
tags | string | Comma-separated list of tags to filter by |
page | integer | Page number (default: 1) |
limit | integer | Results per page (default: 20, max: 100) |
curl "https://api.voxburst.io/v1/contacts?platform=INSTAGRAM&tags=vip&limit=20" \
-H "Authorization: Bearer eyJexample..."Response
{
"contacts": [
{
"id": "contact_abc123",
"workspaceId": "wsp_example123",
"platform": "INSTAGRAM",
"platformId": "1234567890",
"displayName": "Alice Example",
"username": "alice",
"avatarUrl": "https://cdn.example.com/avatar.jpg",
"tags": ["vip"],
"customFields": {},
"socialAccount": {
"id": "acct_example123",
"displayName": "My Brand",
"username": "mybrand",
"platform": "INSTAGRAM"
},
"lastSeenAt": "2026-08-07T14:00:00Z",
"lastInboundAt": null,
"messengerEligibleUntil": null,
"isSuppressed": false,
"suppressedAt": null,
"suppressionReason": null,
"messagesSentCount": 3,
"sequenceEnrollmentCount": 1,
"createdAt": "2026-04-01T10:00:00Z",
"updatedAt": "2026-08-07T14:00:00Z"
}
],
"total": 1,
"page": 1,
"limit": 20,
"pages": 1
}Get Contact
GET /v1/contacts/:id
Retrieve a single contact by ID. Returns the detail shape — includes all list-shape fields plus deliveries and sequenceEnrollments.
curl https://api.voxburst.io/v1/contacts/contact_abc123 \
-H "Authorization: Bearer eyJexample..."Response
{
"contact": {
"id": "contact_abc123",
"workspaceId": "wsp_example123",
"platform": "FACEBOOK",
"platformId": "9876543210",
"displayName": "Bob Example",
"username": null,
"avatarUrl": null,
"tags": ["customer"],
"customFields": {},
"socialAccount": {
"id": "acct_example456",
"displayName": "My Page",
"username": "mypage",
"platform": "FACEBOOK"
},
"lastSeenAt": "2026-08-07T10:00:00Z",
"lastInboundAt": "2026-08-07T10:00:00Z",
"messengerEligibleUntil": "2026-08-08T10:00:00Z",
"isSuppressed": false,
"suppressedAt": null,
"suppressionReason": null,
"messagesSentCount": 2,
"sequenceEnrollmentCount": 0,
"createdAt": "2026-07-01T10:00:00Z",
"updatedAt": "2026-08-07T10:00:00Z",
"deliveries": [
{
"id": "bdlv_example789",
"status": "DELIVERED",
"sentAt": "2026-08-06T12:00:00Z",
"readAt": null,
"errorCode": null,
"createdAt": "2026-08-06T12:00:00Z",
"broadcast": { "id": "bcast_example123", "name": "August Campaign" }
}
],
"sequenceEnrollments": [],
"comments": [
{
"id": "cmt_example123",
"content": "Do you ship internationally?",
"platform": "FACEBOOK",
"platformPostId": "123456789_987654321",
"platformCreatedAt": "2026-08-05T09:12:00Z",
"replied": true
}
],
"inboundMessages": [
{
"id": "imsg_example123",
"platform": "FACEBOOK",
"messageText": "Hi, I had a question about my order.",
"receivedAt": "2026-08-07T10:00:00Z"
}
]
}
}Engagement history fields
The detail shape includes two engagement history arrays. Both are capped and ordered newest first — they are not paginated, and there is no parameter to request more.
| Field | Type | Cap | Description |
|---|---|---|---|
comments | object[] | 20 most recent | Comments this contact left on your content, newest first by platformCreatedAt |
inboundMessages | object[] | 30 most recent | Direct messages this contact sent to the connected account, newest first by receivedAt |
comments[] item fields
| Field | Type | Description |
|---|---|---|
id | string | VoxBurst comment ID — use with Inbox Comments for the full record |
content | string | Text of the comment |
platform | string | Platform constant (UPPERCASE) |
platformPostId | string | Platform ID of the post the comment was left on |
platformCreatedAt | string (ISO 8601) | When the comment was created on the platform |
replied | boolean | Whether the comment has been replied to |
inboundMessages[] item fields
| Field | Type | Description |
|---|---|---|
id | string | Inbound message ID |
platform | string | Platform constant (UPPERCASE) |
messageText | string | Text of the message the contact sent |
receivedAt | string (ISO 8601) | When the message was received |
inboundMessages contains only messages received from the contact. Messages
you sent are tracked separately in deliveries (for broadcasts) and via
messagesSentCount. To reconstruct a full conversation thread, merge the two by
timestamp.
Because both arrays are capped, a contact with a long history will have older comments and messages omitted with no indicator that truncation occurred. Do not treat these arrays as a complete record — use them for recent context only.
Create Contact
POST /v1/contacts
Manually create a contact. If a contact with the same platform + platformId already exists in the workspace, the record is upserted (updated in place, not duplicated). Returns the detail shape.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
platform | string | Yes | Platform constant (e.g. INSTAGRAM) |
platformId | string | Yes | The user’s ID on the platform |
displayName | string | No | Display name (max 255 chars) |
username | string | No | Handle or username (max 255 chars) |
avatarUrl | string | No | Valid HTTPS URL |
tags | string[] | No | Tags (each max 100 chars, default: []) |
customFields | object | No | Key-value metadata (default: {}) |
curl -X POST https://api.voxburst.io/v1/contacts \
-H "Authorization: Bearer eyJexample..." \
-H "Content-Type: application/json" \
-d '{
"platform": "INSTAGRAM",
"platformId": "1234567890",
"displayName": "Alice Example",
"username": "alice",
"tags": ["vip"]
}'Response (201)
Returns the detail shape — same as GET /v1/contacts/:id.
Update Contact
PATCH /v1/contacts/:id
Update a contact’s display name, tags, or custom fields. All fields are optional. Returns the detail shape.
Request Body
| Field | Type | Description |
|---|---|---|
displayName | string | null | New display name, or null to clear |
tags | string[] | Replacement tag list (replaces all existing tags) |
customFields | object | Replacement custom fields |
curl -X PATCH https://api.voxburst.io/v1/contacts/contact_abc123 \
-H "Authorization: Bearer eyJexample..." \
-H "Content-Type: application/json" \
-d '{
"tags": ["vip", "customer"],
"customFields": { "tier": "gold" }
}'Response
Returns the detail shape — same as GET /v1/contacts/:id.
Delete Contact
DELETE /v1/contacts/:id
Delete a contact from the workspace.
curl -X DELETE https://api.voxburst.io/v1/contacts/contact_abc123 \
-H "Authorization: Bearer eyJexample..."Response
{ "ok": true }Add Tag
POST /v1/contacts/:id/tags
Add a single tag to a contact without replacing the existing tag list. Returns the detail shape.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
tag | string | Yes | Tag to add (1–100 chars) |
curl -X POST https://api.voxburst.io/v1/contacts/contact_abc123/tags \
-H "Authorization: Bearer eyJexample..." \
-H "Content-Type: application/json" \
-d '{ "tag": "newsletter" }'Response
Returns the detail shape — same as GET /v1/contacts/:id.
Remove Tag
DELETE /v1/contacts/:id/tags/:tag
Remove a specific tag from a contact. Returns the detail shape.
curl -X DELETE https://api.voxburst.io/v1/contacts/contact_abc123/tags/newsletter \
-H "Authorization: Bearer eyJexample..."Response
Returns the detail shape — same as GET /v1/contacts/:id.
Send Direct Message
POST /v1/contacts/:id/messages
Send a 1:1 direct message to a contact via their linked social account. Supported platforms: Facebook (Messenger) and Instagram (Direct).
Eligibility is enforced server-side at send time. The isSuppressed and messengerEligibleUntil fields on a contact are correct at the time of the API call that returned them, but a tab can be stale. Every send is re-validated against live state — a prior successful send does not guarantee the next one will be accepted.
Eligibility — when the API refuses with 400
A send returns 400 VALIDATION_ERROR (not 200) if any of the following are true at the moment of the call:
- The contact has opted out (
isSuppressed: true). - Facebook only: The 24-hour Messenger window has closed. Meta requires an inbound message from the user before a Page can reply, and only permits replies within 24 hours of their most recent inbound. When
messengerEligibleUntilisnull, the window is not open. - The contact has no linked social account (
socialAccountisnull). - The contact’s platform is not Facebook or Instagram.
Transport failures — when the API returns 200 with FAILED status
If VoxBurst reached the platform API but Meta rejected the message (for example, an access-token error or a temporary platform refusal), the call returns 200 with delivery.status: "FAILED" and a delivery.errorCode describing the platform-level reason. The delivery record is created in either case — a failed send is still visible in the contact’s history.
Do not conflate these two outcomes. A 400 means the send was blocked before reaching Meta. A 200 with "FAILED" status means the send was attempted and Meta rejected it.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Message text (1–2,000 characters) |
curl -X POST https://api.voxburst.io/v1/contacts/contact_abc123/messages \
-H "Authorization: Bearer eyJexample..." \
-H "Content-Type: application/json" \
-d '{ "text": "Hi! Just following up on your question." }'Response (200)
{
"delivery": {
"deliveryId": "bdlv_example789",
"status": "DELIVERED",
"errorCode": null
},
"contact": {
"id": "contact_abc123",
"platform": "FACEBOOK",
"messengerEligibleUntil": "2026-08-08T10:00:00Z",
"isSuppressed": false,
"deliveries": [
{
"id": "bdlv_example789",
"status": "DELIVERED",
"sentAt": "2026-08-07T14:00:00Z",
"readAt": null,
"errorCode": null,
"createdAt": "2026-08-07T14:00:00Z",
"broadcast": null
}
]
}
}delivery.status values:
"DELIVERED"— sent successfully."FAILED"— the transport layer was reached but the platform rejected the message. Checkdelivery.errorCodefor the reason.
The contact field in the response is the full detail shape, updated to reflect the new delivery. The deliveries array includes the just-created record.
Error responses
| Status | Code | Cause |
|---|---|---|
| 400 | VALIDATION_ERROR | Contact is suppressed, Messenger window is closed, no linked social account, or platform is not Facebook or Instagram |
| 404 | NOT_FOUND | Contact not found in this workspace |
| 422 | VALIDATION_ERROR | text is empty or exceeds 2,000 characters |