curl --request POST \
--url https://api.vambe.me/api/public/customer/upsert/info \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"channel": "whatsapp"
}
'Upsert a contact
Creates or finds an existing contact for whatsapp, web-whatsapp, or webchat channels. For whatsapp/web-whatsapp, the contact is identified by phone number. For webchat, the contact is identified by external_user_id. Optionally assigns an agent, updates metadata, sets a stage, and upserts custom field values on the associated customer.
curl --request POST \
--url https://api.vambe.me/api/public/customer/upsert/info \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"channel": "whatsapp"
}
'Overview
Create or update a customer contact with intelligent AI-powered data processing. This endpoint automatically creates the contact if it doesnβt exist, or updates it if it does, along with optional metadata, custom field values, stage assignment, and agent assignment - all in a single request. Supports WhatsApp, Web-WhatsApp, and Webchat channels. For WhatsApp-based channels the contact is identified by phone number; for webchat, the contact is identified by an external user ID you provide.Use Cases
- CRM Data Import: Bulk import customer data from external CRM systems
- Form Submissions: Create customers from web form submissions with flexible data
- E-commerce Integration: Sync customer data from your e-commerce platform
- Lead Capture: Capture leads with any structure of data
- Customer Onboarding: Create customer profiles during signup flows
- Data Migration: Migrate customer data from legacy systems
- Webchat User Tracking: Create contacts for authenticated webchat users with your own external IDs
Authentication
This endpoint requires authentication using an API key. Include your API key in the request header:x-api-key: your_api_key_here
Request Body
Common Fields
| Field | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Channel type: "whatsapp", "web-whatsapp", or "webchat" |
contact_name | string | No | Customerβs display name |
contact_email | string | No | Customerβs email address |
stageId | string | No | Pipeline stage UUID to assign the customer to |
agentId | string | No | Team member UUID to assign as responsible agent |
meta_data | object | No | Any additional customer data (AI will structure it) |
custom_field_values | array | No | Custom field values to set on the customer (see below) |
WhatsApp / Web-WhatsApp Fields
Required whenchannel is "whatsapp" or "web-whatsapp":
| Field | Type | Required | Description |
|---|---|---|---|
channel_phone_number | string | Yes | Your WhatsApp channel phone number (the sender) |
contact_phone_number | string | Yes | Customerβs phone number |
Webchat Fields
Required whenchannel is "webchat":
| Field | Type | Required | Description |
|---|---|---|---|
channel_id | string | Yes | UUID of your webchat channel |
external_user_id | string | Yes | Your unique identifier for the user (used to find or create the contact) |
Custom Field Values
Thecustom_field_values array lets you set values on the associated customer using custom field definition keys. Each entry has:
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | The custom field definition key |
value | string | Yes | The value to set |
"custom_field_values": [
{ "key": "rut", "value": "12345678-9" },
{ "key": "company_name", "value": "Acme Corp" }
]
Response Structure
Returns the created or updated AI contact object:| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier for the contact |
name | string | Contactβs name |
phone | string | Contactβs phone number |
email | string | Contactβs email |
platform | string | Channel platform (whatsapp, web-whatsapp, webchat) |
client_id | string (UUID) | Your organization ID |
ai_customer_id | string (UUID) | Associated customer profile ID |
created_at | string (ISO) | Contact creation timestamp |
default_stage_id | string (UUID) | Current pipeline stage (if assigned) |
Example Requests
WhatsApp Contact
curl --request POST \
'https://api.vambe.me/api/public/customer/upsert/info' \
--header 'Content-Type: application/json' \
--header 'x-api-key: your_api_key_here' \
--data-raw '{
"channel_phone_number": "+56987654321",
"channel": "whatsapp",
"contact_phone_number": "+56912345678",
"contact_name": "Maria Rodriguez",
"contact_email": "maria.rodriguez@example.com",
"stageId": "550e8400-e29b-41d4-a716-446655440000",
"agentId": "228d7a0d-9072-4ca8-939b-959b75cc606a",
"meta_data": {
"company": "Acme Corp",
"position": "CEO",
"industry": "Technology",
"source": "LinkedIn Campaign"
},
"custom_field_values": [
{ "key": "company_name", "value": "Acme Corp" },
{ "key": "rut", "value": "12345678-9" }
]
}'
Webchat Contact
curl --request POST \
'https://api.vambe.me/api/public/customer/upsert/info' \
--header 'Content-Type: application/json' \
--header 'x-api-key: your_api_key_here' \
--data-raw '{
"channel": "webchat",
"channel_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_user_id": "user-abc-123",
"contact_name": "Jane Doe",
"contact_email": "jane@example.com",
"custom_field_values": [
{ "key": "plan", "value": "premium" },
{ "key": "account_id", "value": "ACC-9876" }
]
}'
Example Response
{
"id": "df980fc8-b6db-4820-bf22-2969482d106d",
"name": "Maria Rodriguez",
"phone": "+56912345678",
"email": "maria.rodriguez@example.com",
"platform": "whatsapp",
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"ai_customer_id": "660e8400-e29b-41d4-a716-446655440001",
"created_at": "2024-09-30T10:00:00.000Z",
"default_stage_id": "550e8400-e29b-41d4-a716-446655440000",
"blocked": false,
"chat_status": "UNATTENDED",
"is_chat_read": false
}
Common Use Cases
1. Import CRM Data with Custom Fields
const importCRMCustomer = async (crmData) => {
const response = await fetch(
'https://api.vambe.me/api/public/customer/upsert/info',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your_api_key_here',
},
body: JSON.stringify({
channel_phone_number: '+56987654321',
channel: 'whatsapp',
contact_name: crmData.full_name,
contact_phone_number: crmData.phone,
contact_email: crmData.email,
stageId: 'your_initial_stage_id',
custom_field_values: [
{ key: 'company_name', value: crmData.company_name },
{ key: 'industry', value: crmData.industry_sector },
{ key: 'lead_score', value: String(crmData.qualification_score) },
],
meta_data: {
source: crmData.lead_source,
notes: crmData.internal_notes,
},
}),
},
);
const contact = await response.json();
console.log(`Imported customer: ${contact.name} (${contact.id})`);
return contact;
};
2. Create Webchat Contact from Authenticated User
const createWebchatContact = async (authenticatedUser) => {
const response = await fetch(
'https://api.vambe.me/api/public/customer/upsert/info',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your_api_key_here',
},
body: JSON.stringify({
channel: 'webchat',
channel_id: 'your_webchat_channel_uuid',
external_user_id: authenticatedUser.id,
contact_name: authenticatedUser.displayName,
contact_email: authenticatedUser.email,
custom_field_values: [
{ key: 'plan', value: authenticatedUser.plan },
{ key: 'account_id', value: authenticatedUser.accountId },
],
}),
},
);
return await response.json();
};
3. Process Form Submission
const processContactForm = async (formData) => {
const response = await fetch(
'https://api.vambe.me/api/public/customer/upsert/info',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your_api_key_here',
},
body: JSON.stringify({
channel_phone_number: process.env.WHATSAPP_PHONE,
channel: 'whatsapp',
contact_name: formData.name,
contact_phone_number: formData.phone,
contact_email: formData.email,
stageId: 'your_leads_stage_id',
agentId: formData.assignToAgent || null,
meta_data: {
formType: 'Contact Form',
submittedAt: new Date().toISOString(),
subject: formData.subject,
message: formData.message,
source: formData.utm_source,
campaign: formData.utm_campaign,
},
}),
},
);
return await response.json();
};
4. E-commerce Customer Sync
const syncEcommerceCustomer = async (order) => {
const response = await fetch(
'https://api.vambe.me/api/public/customer/upsert/info',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your_api_key_here',
},
body: JSON.stringify({
channel_phone_number: '+56987654321',
channel: 'whatsapp',
contact_name: `${order.customer.first_name} ${order.customer.last_name}`,
contact_phone_number: order.customer.phone,
contact_email: order.customer.email,
stageId: 'your_customers_stage_id',
meta_data: {
orderId: order.id,
orderNumber: order.order_number,
orderTotal: order.total_price,
currency: order.currency,
products: order.line_items.map((item) => item.title).join(', '),
},
}),
},
);
return await response.json();
};
5. Bulk Customer Import
const bulkImportCustomers = async (customers) => {
const results = [];
for (const customer of customers) {
try {
const response = await fetch(
'https://api.vambe.me/api/public/customer/upsert/info',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your_api_key_here',
},
body: JSON.stringify({
channel_phone_number: process.env.WHATSAPP_PHONE,
channel: 'whatsapp',
contact_name: customer.name,
contact_phone_number: customer.phone,
contact_email: customer.email,
custom_field_values: customer.customFields || [],
meta_data: customer.additionalData,
}),
},
);
const result = await response.json();
results.push({ phone: customer.phone, success: true, id: result.id });
} catch (error) {
results.push({
phone: customer.phone,
success: false,
error: error.message,
});
}
// Add delay to avoid rate limiting
await new Promise((resolve) => setTimeout(resolve, 200));
}
console.log(
`Imported ${results.filter((r) => r.success).length} of ${customers.length} customers`,
);
return results;
};
Key Features
Upsert Behavior
The contact lookup depends on the channel:| Channel | Identified by |
|---|---|
whatsapp | contact_phone_number on the given channel |
web-whatsapp | contact_phone_number on the given channel |
webchat | external_user_id on the given channel_id |
- If contact exists: Returns the existing contact and applies any updates (name, agent, stage, metadata, custom fields)
- If contact doesnβt exist: Creates a new contact and customer
- Idempotent: Safe to call multiple times with the same data
Custom Field Values
- Values are set on the customer (not the contact), so they are shared across all contacts for that customer
- Fields are identified by their definition key (e.g.
"rut","company_name") - If a field definition is marked as an identifier, the value participates in automatic customer deduplication
- All custom field definition keys must exist beforehand; the endpoint returns a
400error listing any missing keys - Supported field types:
text,number,date,boolean,options
AI-Powered Data Processing
Themeta_data field uses AI to:
- Extract structured data from unstructured metadata
- Match fields to your custom field definitions
- Convert data types automatically
- Handle flexible formats for phone numbers, dates, etc.
Automatic Processing
All in one request:- Create/update contact
- Assign to pipeline stage
- Assign to agent
- Process and save metadata
- Upsert custom field values
Channel Types
| Channel | Description | Required Fields |
|---|---|---|
whatsapp | WhatsApp Business API | channel_phone_number, contact_phone_number |
web-whatsapp | WhatsApp Web (QR Code) | channel_phone_number, contact_phone_number |
webchat | Webchat widget | channel_id, external_user_id |
Error Responses
| Status Code | Description |
|---|---|
| 400 | Bad Request - Missing required fields for the channel, or invalid custom field keys |
| 401 | Unauthorized - Invalid or missing API key |
| 500 | Internal Server Error - Something went wrong |
Important Notes
- Channel Phone Number: Must be a WhatsApp number/channel you own and have configured
- Channel ID: Must be a webchat channel UUID you own
- External User ID: Your own unique identifier for the webchat user; used to deduplicate contacts
- Phone Format: Automatically cleaned (spaces, dashes, etc. are removed)
- Custom Field Keys: Must match existing custom field definitions; create definitions first in the Vambe dashboard
- AI Processing: Metadata is processed asynchronously with AI
- Immediate Response: Returns contact immediately, metadata processing continues in background
Best Practices
1. Use Custom Fields for Structured Data
Prefercustom_field_values over meta_data when you have well-defined fields. Custom fields are typed, searchable, and can be marked as identifiers for deduplication.
// Preferred: structured custom fields
{
"custom_field_values": [
{ "key": "company_name", "value": "Acme Corp" },
{ "key": "rut", "value": "12345678-9" },
{ "key": "plan", "value": "enterprise" }
]
}
// Use meta_data for unstructured/dynamic data
{
"meta_data": {
"notes": "Met at conference, interested in premium plan",
"source": "Trade Show 2025"
}
}
2. Include Source Information
meta_data: {
source: 'LinkedIn Campaign',
importedAt: new Date().toISOString(),
importedBy: 'CRM Sync Script',
}
3. Validate Data Before Sending
const validateAndUpsert = async (customerData) => {
if (!customerData.phone || !customerData.name) {
throw new Error('Phone and name are required');
}
const cleanPhone = customerData.phone.replace(/[^\d+]/g, '');
return await fetch('https://api.vambe.me/api/public/customer/upsert/info', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your_api_key_here',
},
body: JSON.stringify({
channel_phone_number: process.env.WHATSAPP_PHONE,
channel: 'whatsapp',
contact_phone_number: cleanPhone,
contact_name: customerData.name,
contact_email: customerData.email,
custom_field_values: customerData.customFields || [],
meta_data: customerData.additionalData,
}),
}).then((r) => r.json());
};
Related Endpoints
- POST /api/public/contact//update-metadata - Update metadata for existing contact
- POST /api/public/ticket/open/web-whatsapp/ - Create ticket for contact
- GET /api/public/contact//info - Get contact details
Migration from Deprecated Endpoint
If youβre migrating from the deprecatedPOST /api/public/whatsapp/message/note/send endpoint:
Old:
POST /api/public/whatsapp/message/note/send
{
"to_phone": "+56912345678",
"from_phone": "your_phone",
"note": "Internal note",
"contact_name": "John Doe",
"meta_data": { ... }
}
POST /api/public/customer/upsert/info
{
"channel_phone_number": "your_phone",
"channel": "whatsapp",
"contact_phone_number": "+56912345678",
"contact_name": "John Doe",
"meta_data": {
"internal_note": "Internal note",
...
}
}
Headers
API key needed to authorize the request
Body
Channel type for the contact.
whatsapp, web-whatsapp, webchat Phone number or ID of the channel. Required for whatsapp/web-whatsapp.
1UUID of the webchat channel. Required for webchat.
^([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)$Stage ID to assign to the contact.
^([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)$Agent ID to assign to the contact.
^([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)$Phone number of the contact. Required for whatsapp/web-whatsapp.
External user identifier used to find or create the webchat contact. Required for webchat.
1Email of the contact.
Display name of the contact.
Arbitrary metadata to set on the contact.
Custom field values to upsert on the customer, identified by definition key.
Show child attributes
Show child attributes
Response
Contact created or found successfully.
Was this page helpful?
