Cloud Chat: Importación masiva de contactos vía API
Para importar o actualizar varios contactos a la vez vía API en Cloud Chat, envía una solicitud POST al endpoint a continuación.
¿Quieres importar solo un contacto? Usa el endpoint de Importar contacto individual. Los dos endpoints tienen formatos de cuerpo diferentes — no los mezcles.
Endpoint
POST https://{{CLOUDCHAT_DOMAIN}}/api/v1/accounts/{{ACCOUNT_ID}}/contacts/bulk_create
Encabezados
| Encabezado | Valor |
|---|---|
content-type |
application/json |
api_access_token |
Tu token de API |
Cuerpo de la Solicitud
El cuerpo usa un arreglo data que contiene los contactos a importar:
{
"upsert": true,
"data": [
{
"name": "Contacto 1",
"email": "[email protected]",
"phone_number": "+5511999998888",
"identifier": "EXT-001",
"additional_attributes": {
"company": "Acme",
"city": "São Paulo"
},
"custom_attributes": {
"plano": "premium",
"origen": "campaña-marzo"
}
},
{
"name": "Contacto 2",
"phone_number": "+5511888887777"
}
]
}
Campos por contacto
| Campo | Obligatorio | Descripción |
|---|---|---|
phone_number |
Al menos 1 de los 3* | Teléfono en formato internacional con + |
email |
Al menos 1 de los 3* | Correo electrónico del contacto |
identifier |
Al menos 1 de los 3* | Identificador externo único |
name |
No | Nombre del contacto |
additional_attributes |
No | Objeto con atributos adicionales (clave-valor). Sobrescrito por completo en cada update (salvo que se use merge_jsonb: "shallow", ver más abajo). |
custom_attributes |
No | Objeto con atributos personalizados creados por la cuenta (clave-valor). Sobrescrito por completo en cada update (salvo que se use merge_jsonb: "shallow", ver más abajo). |
tags_add |
No | Lista de etiquetas a agregar. Requiere apply_tags: true o api_version: "v2". |
tags_remove |
No | Lista de etiquetas a remover. Requiere apply_tags: true o api_version: "v2". |
*Cada contacto debe tener al menos uno entre phone_number, email o identifier.
Bandera upsert
| Valor | Comportamiento |
|---|---|
false o ausente |
Solo crea contactos nuevos. Si alguno de los campos clave (email, teléfono o identifier) ya existe en la base, la fila falla con error 422 (no se ignora silenciosamente). |
true |
Crea contactos nuevos y actualiza contactos existentes. |
Ejemplo de solicitud (curl)
curl -X POST \
'https://{{CLOUDCHAT_DOMAIN}}/api/v1/accounts/{{ACCOUNT_ID}}/contacts/bulk_create' \
--header 'content-type: application/json' \
--header 'api_access_token: TU_TOKEN' \
--data '{
"upsert": true,
"data": [
{
"name": "Contacto 1",
"email": "[email protected]",
"phone_number": "+5511999998888"
},
{
"name": "Contacto 2",
"phone_number": "+5511888887777"
}
]
}'
Respuesta
Éxito (HTTP 201)
{ "success": true }
Error de validación o conflicto (HTTP 422)
{
"success": false,
"errors": [
{
"index": 0,
"attributes": { "name": "Contacto 1", "email": "[email protected]", ... },
"errors": ["Email is invalid"]
},
{
"index": 1,
"attributes": { "name": "Contacto 2", ... },
"errors": "Duplicate: Contact with this phone number already exists"
}
]
}
indexindica la posición (0-based) de la fila en el arraydata.errorspor fila puede ser un array de mensajes de validación O un único string para conflictos de clave (duplicidad, divergencia identifier × email × phone — en este último caso el código devuelto esAMBIGUOUS_MATCH).
Cuerpo vacío (HTTP 400)
Devuelto cuando data es nulo o un array vacío.
Precauciones con upsert
Atención — riesgo de pérdida de datos con
upsert: true:Al actualizar un contacto existente vía
bulk_createconupsert: true, cualquier campo que no envíes será sobrescrito connull.Ejemplo: Si un contacto tiene
phone_numberregistrados, y envías una actualización solo conphone_number, el campoSi necesitas actualizar solo algunos campos sin borrar los demás, usa el endpoint de actualización individual:
PUT /api/v1/accounts/{{ACCOUNT_ID}}/contacts/{{CONTACT_ID}}Otra alternativa es usar
coalesce_nulls: true(ver sección "Comportamiento avanzado") para que losnullenviados se interpreten como "preservar el valor existente".
Comportamiento avanzado (opcional)
A partir del 30 de abril de 2026, el endpoint acepta parámetros opcionales en el cuerpo de la solicitud que permiten ajustar cómo se procesa el payload. Estos parámetros son opt-in: si no los envías, el comportamiento es el de antes del 30/04/2026.
| Parámetro | Tipo | Default | Efecto |
|---|---|---|---|
api_version |
string | "v1" |
"v1": paridad con el comportamiento anterior. "v2": habilita todas las features nuevas en bloque (tags granulares, __DELETE__, merge JSONB shallow, lookup por variantes). "v2-safe": subconjunto conservador. |
apply_tags |
boolean | false |
Habilita el procesamiento de tags_add / tags_remove por fila. |
respect_sentinel |
boolean | false |
Habilita la semántica del valor __DELETE__ para borrar claves de JSONB y campos escalares. |
merge_jsonb |
string | "overwrite" |
"overwrite": sobrescribe el objeto JSONB completo. "shallow": hace merge clave por clave (no sobrescribe lo que no envíes). |
coalesce_nulls |
boolean | false |
Trata null en el payload como "preservar valor existente" en lugar de "borrar". |
coerce_blank_jsonb |
boolean | false |
Trata cadena vacía ("") en valores de JSONB como __DELETE__ (borra la clave). |
normalize_lookup |
boolean | false |
Habilita la búsqueda por variantes de teléfono (especialmente útil para números BR con/sin el dígito 9). |
orphan_guard |
boolean | false |
Rechaza el payload cuando la actualización dejaría a un contacto existente sin ninguna clave fuerte (email, teléfono, identifier). |
Atajo: para habilitar todas las features nuevas en bloque sin tener que activar cada flag por separado, usa
api_version: "v2".
tags_add y tags_remove (granulares)
Permite agregar y remover etiquetas específicas sin sobrescribir las existentes en el contacto:
{
"api_version": "v2",
"data": [
{
"email": "[email protected]",
"tags_add": ["vip", "premium"],
"tags_remove": ["pending"]
}
]
}
⚠️ Atención:
tags_addytags_removeson silenciosamente ignorados si no envíasapply_tags: trueoapi_version: "v2". El default mantiene el comportamiento anterior. Si en la misma fila una etiqueta aparece entags_addy entags_remove, la remoción gana (el orden de la operación es agregar → remover).
__DELETE__ para borrar claves en JSONB
Con respect_sentinel: true o api_version: "v2", el valor literal "__DELETE__" borra la clave correspondiente:
{
"api_version": "v2",
"data": [
{
"email": "[email protected]",
"custom_attributes": {
"favorite_color": "__DELETE__"
}
}
]
}
El sentinel funciona en claves de primer nivel de additional_attributes y custom_attributes. También funciona en campos escalares (name, phone_number, email, identifier) para limpiar el valor (NULL).
merge_jsonb: shallow vs overwrite
Por defecto ("overwrite"), enviar un custom_attributes reemplaza el objeto completo. Con "shallow", solo se actualizan las claves presentes en el payload, preservando las demás:
{
"api_version": "v1",
"merge_jsonb": "shallow",
"respect_sentinel": true,
"data": [
{
"email": "[email protected]",
"custom_attributes": {
"plano": "gold"
}
}
]
}
⚠️ Atención — combinación arriesgada: usar
merge_jsonb: "shallow"sinrespect_sentinel: truesignifica que no hay forma de borrar una clave durante el merge. Si tu integración asume que enviar una cadena vacía o ausente debería eliminar la clave, esa expectativa no se cumplirá: el valor antiguo permanecerá intacto. Cuando usesshallow, activa tambiénrespect_sentinel: truey usa"__DELETE__"para borrar claves explícitamente.
coalesce_nulls
Por defecto, enviar "email": null borra el campo. Con coalesce_nulls: true, los null se ignoran y el valor existente se conserva:
{
"coalesce_nulls": true,
"data": [
{
"phone_number": "+5511999998888",
"email": null,
"name": "Nuevo nombre"
}
]
}
coerce_blank_jsonb
Trata cadenas vacías dentro de JSONB como instrucción de borrado (equivalente a __DELETE__):
{
"coerce_blank_jsonb": true,
"respect_sentinel": true,
"data": [
{
"email": "[email protected]",
"custom_attributes": {
"nota_interna": ""
}
}
]
}
⚠️ Atención — riesgo de borrado accidental: no actives
coerce_blank_jsonb: truesi tu integración envía cadenas vacías legítimas en JSONB (por ejemplo, un campo que el cliente dejó en blanco intencionalmente y debe persistirse como""). Con esta bandera activa, cualquier""será interpretado como "borra esta clave". Úsalo solo si controlas el origen del payload y sabes que las cadenas vacías representan eliminación.
normalize_lookup — búsqueda por variantes de teléfono
Con normalize_lookup: true o api_version: "v2", al buscar el contacto existente el sistema considera variantes del número de teléfono (especialmente para números brasileños con o sin el dígito 9):
{
"normalize_lookup": true,
"data": [
{
"phone_number": "+5511933334444",
"name": "María"
}
]
}
Si en la base ya existe un contacto con +551133334444 (sin el 9), el sistema lo encuentra y lo actualiza, en lugar de crear un duplicado. Útil para evitar duplicación cruzada entre formatos.
orphan_guard
Con orphan_guard: true, el sistema rechaza cualquier fila cuyo resultado dejaría al contacto sin ninguna clave fuerte (sin email, sin teléfono y sin identifier):
{
"orphan_guard": true,
"data": [
{
"identifier": "EXT-001",
"email": "__DELETE__",
"phone_number": "__DELETE__"
}
]
}
⚠️ Atención: las cuentas que tienen contactos creados con solo
identifier(sin email ni teléfono) deben evaluar antes de activar este flag. Esos contactos pueden ser rechazados al actualizarse si la lógica de tu integración asume queidentifiersolo no es suficiente. Usaorphan_guard: truesolo si sabes que todos tus contactos tienen al menos una clave fuerte (email o teléfono) además del identifier, o si ese es exactamente el comportamiento que deseas hacer cumplir.
Diferencia entre este endpoint y la Importación individual
Este endpoint (bulk_create) |
Importar individual (external_import) |
|
|---|---|---|
| Cuándo usar | Importar/actualizar varios contactos a la vez | Importar/actualizar 1 contacto por llamada |
| Formato del cuerpo | {"upsert": true, "data": [{...}, {...}]} |
Objeto JSON simple (plano) |
| Upsert | Necesita el banderín "upsert": true |
Siempre activo (automático) |
| Precaución con null | Campos omitidos se sobrescriben con null (salvo con coalesce_nulls: true) |
Campos omitidos se conservan |
| Búsqueda por variantes de teléfono | Disponible con normalize_lookup: true o api_version: "v2" |
No disponible (solo lookup exacto por phone_number) |
¡No mezcles los formatos! Si envías un objeto plano (sin
data: [...]) a este endpoint, no funcionará. Para contacto individual, usa el endpoint de importación individual.
Dónde encontrar los parámetros de configuración
-
CLOUDCHAT_DOMAIN: El dominio visible en la URL del navegador al acceder a Cloud Chat
-
ACCOUNT_ID: En configuraciones de la cuenta
-
API_TOKEN: En
https://{{CLOUDCHAT_DOMAIN}}/app/accounts/{{ACCOUNT_ID}}/profile/settings