curl --request GET \
--url https://api.vambe.me/api/public/contacts \
--header 'x-api-key: <x-api-key>'const options = {method: 'GET', headers: {'x-api-key': '<x-api-key>'}};
fetch('https://api.vambe.me/api/public/contacts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.vambe.me/api/public/contacts"
headers = {"x-api-key": "<x-api-key>"}
response = requests.get(url, headers=headers)
print(response.text)[
{
"id": "df980fc8-b6db-4820-bf22-2969482d106d",
"name": "John Doe",
"phone": "+56912345678",
"email": "john@example.com",
"platform": "whatsapp",
"last_message_at": "2024-09-30T10:00:00.000Z",
"chat_status": "ATTENDED",
"created_at": "2024-01-15T10:00:00.000Z",
"updated_at": "2024-01-16T09:30:00.000Z",
"client_id": "123e4567-e89b-12d3-a456-426614174000",
"ai_customer_id": "123e4567-e89b-12d3-a456-426614174000",
"default_stage_id": "123e4567-e89b-12d3-a456-426614174000",
"blocked": false,
"is_chat_read": true,
"active_ticket_v2": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"current_stage_id": "123e4567-e89b-12d3-a456-426614174000"
},
"utm_events": [
{
"id": "<string>",
"created_at": "2024-01-15T10:00:00.000Z",
"customer_id": "<string>",
"integration_type": "facebook",
"ad_id": "<string>",
"referrer_url": "https://example.com/landing",
"search_keyword": "<string>"
}
]
}
]Get contacts by time filters
Retrieve contacts filtered by activity (last message within N days), creation time, or update time. Works across all channels (WhatsApp, Instagram, etc.). At least one of days, created_after, or updated_after is required. For incremental sync, poll with updated_after set to the latest updated_at you have stored: the response includes both created_at and updated_at so you can tell new contacts (created_at > your cursor) apart from updated ones. Results are ordered ascending by updated_at (or created_at) when those filters are used, so you can page forward with limit/offset and advance your cursor safely. Optionally filter by custom field values.
curl --request GET \
--url https://api.vambe.me/api/public/contacts \
--header 'x-api-key: <x-api-key>'const options = {method: 'GET', headers: {'x-api-key': '<x-api-key>'}};
fetch('https://api.vambe.me/api/public/contacts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.vambe.me/api/public/contacts"
headers = {"x-api-key": "<x-api-key>"}
response = requests.get(url, headers=headers)
print(response.text)[
{
"id": "df980fc8-b6db-4820-bf22-2969482d106d",
"name": "John Doe",
"phone": "+56912345678",
"email": "john@example.com",
"platform": "whatsapp",
"last_message_at": "2024-09-30T10:00:00.000Z",
"chat_status": "ATTENDED",
"created_at": "2024-01-15T10:00:00.000Z",
"updated_at": "2024-01-16T09:30:00.000Z",
"client_id": "123e4567-e89b-12d3-a456-426614174000",
"ai_customer_id": "123e4567-e89b-12d3-a456-426614174000",
"default_stage_id": "123e4567-e89b-12d3-a456-426614174000",
"blocked": false,
"is_chat_read": true,
"active_ticket_v2": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"current_stage_id": "123e4567-e89b-12d3-a456-426614174000"
},
"utm_events": [
{
"id": "<string>",
"created_at": "2024-01-15T10:00:00.000Z",
"customer_id": "<string>",
"integration_type": "facebook",
"ad_id": "<string>",
"referrer_url": "https://example.com/landing",
"search_keyword": "<string>"
}
]
}
]Overview
Retrieve contacts that had their last message within a specified number of days. This endpoint works across all communication channels (WhatsApp, Instagram, Web Chat, SMS, etc.) providing a unified view of recent contact activity. Perfect for identifying active contacts, tracking engagement, and filtering contacts by recency across your entire customer base.Use Cases
- Active Contacts Dashboard: Show contacts who messaged in the last 7, 30, or 90 days
- Engagement Metrics: Track how many contacts are actively engaging
- Re-engagement Campaigns: Find contacts who havenβt messaged recently
- Activity Reports: Generate reports of contact activity over time
- Cross-Channel Analytics: See all active contacts regardless of channel
- CRM Sync: Export recently active contacts to external systems
Authentication
This endpoint requires authentication using an API key. Include your API key in the request header:x-api-key: your_api_key_here
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
days | number | No | Filter contacts whose last message was within this many days (1-365) |
created_after | string (ISO 8601) | No | Return only contacts created strictly after this timestamp. Use for syncing newly created contacts. |
updated_after | string (ISO 8601) | No | Return only contacts updated strictly after this timestamp. Use as a sync cursor to pull both new and modified contacts since the last poll. |
limit | number | No | Maximum number of contacts to return (1-1000) |
offset | number | No | Number of contacts to skip, for pagination |
stage_id | string (UUID) | No | Stage ID to filter contacts by stage |
pipeline_id | string (UUID) | No | Pipeline ID to filter contacts by their active ticketβs pipeline |
custom_field_key | string | No | The key of the custom field to filter by. Must be provided together with custom_field_value. |
custom_field_value | string | No | The value to match for the custom field. Must be provided together with custom_field_key. |
entity_type | string | No | "ticket" or "customer" β scopes the custom field filter to that entity type. Only applies with custom_field_key/custom_field_value. |
with_utm_events | boolean | No | Set to true to include the per-contact utm_events array (marketing attribution). Off by default β the key is omitted entirely unless requested. |
days,
created_after, or updated_after.Response Structure
Returns an array of contact objects:| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier for the contact |
name | string | Contactβs full name |
phone | string | null | Contactβs phone number |
email | string | null | Contactβs email address |
platform | string | Communication channel (whatsapp, instagram, etc.) |
last_message_at | string (ISO) | Timestamp of the last message sent/received |
chat_status | string | Current chat status (UNATTENDED, ATTENDED, etc.) |
created_at | string (ISO) | Contact creation timestamp |
client_id | string (UUID) | Your organization ID |
ai_customer_id | string (UUID) | Associated customer profile ID |
default_stage_id | string (UUID) | Contactβs default pipeline stage |
blocked | boolean | Whether the contact is blocked |
is_chat_read | boolean | Whether the chat has been read by an agent |
active_ticket_v2 | object | null | Active ticket information (see below) |
utm_events | array | Marketing attribution events for this contact (see below) |
Active Ticket Object (active_ticket_v2)
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier for the ticket |
current_stage_id | string | null | Current pipeline stage ID for the ticket |
UTM Event Object (utm_events[])
Marketing attribution events recorded for the contact, ordered oldest first. Empty array when the contact has no attribution events.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier for the UTM event |
created_at | string (ISO) | null | When the attribution event was recorded |
customer_id | string (UUID) | null | Associated customer profile ID |
integration_type | string | null | Ad platform (facebook, google, tiktok, etc.) |
ad_id | string | null | Ad identifier |
referrer_url | string | null | Landing / referrer URL that captured the contact |
search_keyword | string | null | Search keyword that triggered the ad |
Example Request
Basic Request (All Contacts)
curl --request GET \
'https://api.vambe.me/api/public/contacts?days=30' \
--header 'x-api-key: your_api_key_here'
Request with Stage Filter
curl --request GET \
'https://api.vambe.me/api/public/contacts?days=30&stage_id=123e4567-e89b-12d3-a456-426614174000' \
--header 'x-api-key: your_api_key_here'
Request with Pipeline Filter
curl --request GET \
'https://api.vambe.me/api/public/contacts?days=30&pipeline_id=123e4567-e89b-12d3-a456-426614174000' \
--header 'x-api-key: your_api_key_here'
Request with UTM Events
curl --request GET \
'https://api.vambe.me/api/public/contacts?days=30&with_utm_events=true' \
--header 'x-api-key: your_api_key_here'
Example Response
The response below assumeswith_utm_events=true was passed β without it, the utm_events key is omitted from every contact entirely.
[
{
"id": "df980fc8-b6db-4820-bf22-2969482d106d",
"name": "Maria Rodriguez",
"phone": "+56912345678",
"email": "maria.rodriguez@example.com",
"platform": "whatsapp",
"last_message_at": "2024-09-30T14:25:33.000Z",
"chat_status": "ATTENDED",
"created_at": "2024-01-15T10:00:00.000Z",
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"ai_customer_id": "660e8400-e29b-41d4-a716-446655440001",
"default_stage_id": "770e8400-e29b-41d4-a716-446655440002",
"blocked": false,
"is_chat_read": true,
"active_ticket_v2": {
"id": "bb0e8400-e29b-41d4-a716-446655440006",
"current_stage_id": "cc0e8400-e29b-41d4-a716-446655440007"
},
"utm_events": [
{
"id": "ee0e8400-e29b-41d4-a716-446655440009",
"created_at": "2024-01-14T22:05:11.000Z",
"customer_id": "660e8400-e29b-41d4-a716-446655440001",
"integration_type": "facebook",
"ad_id": "23854715623450123",
"referrer_url": "https://example.com/landing?utm_campaign=spring",
"search_keyword": null
}
]
},
{
"id": "880e8400-e29b-41d4-a716-446655440003",
"name": "Carlos Silva",
"phone": "+56987654321",
"email": null,
"platform": "instagram",
"last_message_at": "2024-09-29T18:10:22.000Z",
"chat_status": "UNATTENDED",
"created_at": "2024-03-20T09:30:00.000Z",
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"ai_customer_id": "990e8400-e29b-41d4-a716-446655440004",
"default_stage_id": "aa0e8400-e29b-41d4-a716-446655440005",
"blocked": false,
"is_chat_read": false,
"active_ticket_v2": {
"id": "dd0e8400-e29b-41d4-a716-446655440008",
"current_stage_id": null
},
"utm_events": []
}
]
Common Use Cases
1. Get Contacts from Last 7 Days
const getRecentContacts = async () => {
const response = await fetch(
'https://api.vambe.me/api/public/contacts?days=7',
{
headers: {
'x-api-key': 'your_api_key_here',
},
},
);
const contacts = await response.json();
console.log(`Found ${contacts.length} contacts active in the last 7 days`);
return contacts;
};
2. Track Engagement by Channel
const getContactsByChannel = async (days) => {
const response = await fetch(
`https://api.vambe.me/api/public/contacts?days=${days}`,
{
headers: {
'x-api-key': 'your_api_key_here',
},
},
);
const contacts = await response.json();
// Group by platform
const byChannel = contacts.reduce((acc, contact) => {
const channel = contact.platform || 'unknown';
if (!acc[channel]) acc[channel] = [];
acc[channel].push(contact);
return acc;
}, {});
console.log('Contacts by channel:', {
whatsapp: byChannel.whatsapp?.length || 0,
instagram: byChannel.instagram?.length || 0,
webchat: byChannel.webchat?.length || 0,
});
return byChannel;
};
3. Find Unattended Contacts
const getUnattendedContacts = async (days) => {
const response = await fetch(
`https://api.vambe.me/api/public/contacts?days=${days}`,
{
headers: {
'x-api-key': 'your_api_key_here',
},
},
);
const contacts = await response.json();
// Filter unattended
const unattended = contacts.filter(
(c) => c.chat_status === 'UNATTENDED' && !c.is_chat_read,
);
console.log(`${unattended.length} unattended contacts need attention`);
return unattended;
};
4. Re-engagement Campaign Data
const getContactsForReengagement = async () => {
// Get contacts from 30-60 days ago (active before but not recently)
const recent30 = await fetch(
'https://api.vambe.me/api/public/contacts?days=30',
{
headers: { 'x-api-key': 'your_api_key_here' },
},
).then((r) => r.json());
const recent60 = await fetch(
'https://api.vambe.me/api/public/contacts?days=60',
{
headers: { 'x-api-key': 'your_api_key_here' },
},
).then((r) => r.json());
// Contacts in 60 days but not in 30 days
const recent30Ids = new Set(recent30.map((c) => c.id));
const reengageTargets = recent60.filter((c) => !recent30Ids.has(c.id));
console.log(`${reengageTargets.length} contacts for re-engagement campaign`);
return reengageTargets;
};
5. Export to External CRM
const exportRecentContactsToCRM = async (days) => {
const response = await fetch(
`https://api.vambe.me/api/public/contacts?days=${days}`,
{
headers: {
'x-api-key': 'your_api_key_here',
},
},
);
const contacts = await response.json();
// Export to external CRM
for (const contact of contacts) {
await fetch('https://external-crm.com/api/contacts/sync', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer crm_token',
},
body: JSON.stringify({
external_id: contact.id,
name: contact.name,
phone: contact.phone,
email: contact.email,
platform: contact.platform,
last_activity: contact.last_message_at,
status: contact.chat_status,
}),
});
}
console.log(`Exported ${contacts.length} contacts to CRM`);
};
6. Filter Contacts by Pipeline Stage
const getContactsByStage = async (days, stageId) => {
const response = await fetch(
`https://api.vambe.me/api/public/contacts?days=${days}&stage_id=${stageId}`,
{
headers: {
'x-api-key': 'your_api_key_here',
},
},
);
const contacts = await response.json();
console.log(`Found ${contacts.length} contacts in stage ${stageId}`);
return contacts;
};
// Example: Get all leads from the last 30 days
const leadStageId = '123e4567-e89b-12d3-a456-426614174000';
const leads = await getContactsByStage(30, leadStageId);
7. Activity Dashboard
const buildActivityDashboard = async () => {
const periods = [
{ label: 'Today', days: 1 },
{ label: 'Last 7 days', days: 7 },
{ label: 'Last 30 days', days: 30 },
{ label: 'Last 90 days', days: 90 },
];
const dashboard = {};
for (const period of periods) {
const response = await fetch(
`https://api.vambe.me/api/public/contacts?days=${period.days}`,
{
headers: {
'x-api-key': 'your_api_key_here',
},
},
);
const contacts = await response.json();
dashboard[period.label] = {
total: contacts.length,
unattended: contacts.filter((c) => c.chat_status === 'UNATTENDED').length,
attended: contacts.filter((c) => c.chat_status === 'ATTENDED').length,
byChannel: contacts.reduce((acc, c) => {
acc[c.platform] = (acc[c.platform] || 0) + 1;
return acc;
}, {}),
};
}
console.log('Activity Dashboard:', dashboard);
return dashboard;
};
Query Parameters Details
Days Parameter
Thedays parameter filters contacts where last_message_at is within the specified number of days from now:
| Days Value | Description | Use Case |
|---|---|---|
1 | Last 24 hours | Todayβs activity |
7 | Last week | Weekly engagement report |
30 | Last month | Monthly active contacts |
90 | Last quarter | Quarterly analysis |
180 | Last 6 months | Semi-annual review |
365 | Last year (maximum allowed) | Annual customer base analysis |
Stage ID Parameter
The optionalstage_id parameter filters contacts by their current pipeline stage. Only contacts with an active ticket in the specified stage will be returned.
| Parameter | Type | Required | Description |
|---|---|---|---|
stage_id | string | No | UUID of the pipeline stage to filter by |
stage_id is provided, only contacts with an active
ticket (active_ticket_v2) in that specific stage are returned. Contacts
without an active ticket or in a different stage are excluded.Pipeline ID Parameter
The optionalpipeline_id parameter filters contacts by the pipeline of their active ticketβs current stage.
| Parameter | Type | Required | Description |
|---|---|---|---|
pipeline_id | string | No | UUID of the pipeline to filter by |
pipeline_id is provided, only contacts with an active
ticket whose current stage belongs to that pipeline are returned. You can combine
pipeline_id with stage_id to filter by both.Response Ordering
Contacts are returned ordered bylast_message_at in descending order (most recent first).
Cross-Channel Support
This endpoint returns contacts from all channels:| Platform | Description |
|---|---|
whatsapp | WhatsApp Business API |
web-whatsapp | WhatsApp Web (QR) |
instagram | Instagram Direct Messages |
webchat | Web chat widget |
sms | SMS messages |
messenger | Facebook Messenger |
tiktok | TikTok Direct Messages |
voice | Voice calls |
playground | Test/playground channel |
Error Responses
| Status Code | Description |
|---|---|
| 400 | Bad Request - Invalid days parameter (must be 1-365) |
| 401 | Unauthorized - Invalid or missing API key |
| 500 | Internal Server Error - Something went wrong |
Important Notes
- Days Range: Must be between 1 and 365 days
- Null Values: Some fields like
emailorphonemay be null - Performance: Response time increases with larger
daysvalues - All Channels: Unlike the deprecated WhatsApp-specific endpoint, this returns contacts from ALL channels
- Contact Count: No pagination - returns all contacts matching the criteria
- Stage Filtering: When
stage_idis provided, only contacts with an active ticket in that stage are returned - Pipeline Filtering: When
pipeline_idis provided, only contacts with an active ticket in a stage belonging to that pipeline are returned - Combined Filters:
days,stage_id, andpipeline_idcan be used together to narrow down results - Active Ticket: The
active_ticket_v2object contains the current ticketβsidandcurrent_stage_id
Performance Considerations
- Cache Results: Consider caching responses for frequently used
daysvalues - Large Datasets: For
daysvalues > 90, expect larger response sizes - Rate Limiting: Be mindful of rate limits when making frequent requests
Comparison with Deprecated Endpoint
| Feature | New Endpoint (This) | Deprecated Endpoint |
|---|---|---|
| Path | /api/public/contacts | /api/public/whatsapp/contact/contacts |
| Channels | All channels | WhatsApp only |
| Response | AI Contact objects | WhatsApp Contact objects |
| Ordering | By last_message_at DESC | Not specified |
| Future Support | β Active development | β Deprecated |
Related Endpoints
- GET /api/public/contact//info - Get detailed contact info
- POST /api/public/customer/upsert/info - Create or update contact
- POST /api/public/contact//update-metadata - Update contact metadata
Best Practices
1. Choose Appropriate Days Range
// Good: Specific use case
const getWeeklyActive = () => fetch('...?days=7');
const getMonthlyActive = () => fetch('...?days=30');
// Avoid: Unnecessarily large range
const getAll = () => fetch('...?days=365'); // Only if you need a full year
2. Handle Empty Results
const contacts = await fetch('...?days=7').then((r) => r.json());
if (contacts.length === 0) {
console.log('No contacts in the last 7 days');
} else {
console.log(`Found ${contacts.length} active contacts`);
}
3. Filter on Client Side
const contacts = await fetch('...?days=30').then((r) => r.json());
// Filter by platform
const whatsappContacts = contacts.filter((c) => c.platform === 'whatsapp');
// Filter by status
const unread = contacts.filter((c) => !c.is_chat_read);
// Filter by date range
const thisWeek = contacts.filter((c) => {
const msgDate = new Date(c.last_message_at);
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
return msgDate > sevenDaysAgo;
});
Headers
API key needed to authorize the request
Query Parameters
Filter contacts whose last message was within this many days (1-365)
1 <= x <= 36530
Return only contacts created strictly after this ISO 8601 timestamp. Use for syncing newly created contacts.
^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$"2024-01-15T10:00:00.000Z"
Return only contacts updated strictly after this ISO 8601 timestamp. Use as a sync cursor to pull both new and modified contacts since the last poll.
^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$"2024-01-15T10:00:00.000Z"
Maximum number of contacts to return (1-1000)
1 <= x <= 1000100
Number of contacts to skip, for pagination
0 <= x <= 90071992547409910
Stage ID to filter contacts
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"123e4567-e89b-12d3-a456-426614174000"
Pipeline ID to filter contacts by their active ticket stage
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"123e4567-e89b-12d3-a456-426614174000"
The key of the custom field to filter by. Must be provided together with custom_field_value.
1"status"
The value to match for the custom field. Must be provided together with custom_field_key.
1"Active"
Specify "ticket" or "customer" to filter only by custom fields of that entity type. Only applies when custom_field_key and custom_field_value are provided.
ticket, customer Set to true to include the per-contact utm_events array (marketing attribution). Off by default β the key is omitted entirely unless requested, since it costs an extra lookup. Opt in only if you consume UTM attribution.
true
Response
Contacts retrieved successfully.
"df980fc8-b6db-4820-bf22-2969482d106d"
"John Doe"
"+56912345678"
"john@example.com"
"whatsapp"
"2024-09-30T10:00:00.000Z"
"ATTENDED"
"2024-01-15T10:00:00.000Z"
"2024-01-16T09:30:00.000Z"
"123e4567-e89b-12d3-a456-426614174000"
"123e4567-e89b-12d3-a456-426614174000"
"123e4567-e89b-12d3-a456-426614174000"
false
true
Show child attributes
Show child attributes
Marketing attribution events recorded for this contact, oldest first. Only present when with_utm_events=true; otherwise the key is omitted.
Show child attributes
Show child attributes
Was this page helpful?
