Inicio API e integraciones | Claves, endpoints y webhooks API: Importación masiva de contactos vía API

API: Importación masiva de contactos vía API

Última actualización el Sep 22, 2026

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"
    }
  ]
}
  • index indica la posición (0-based) de la fila en el array data.
  • errors por 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 es AMBIGUOUS_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_create con upsert: true, cualquier campo que no envíes será sobrescrito con null.

Ejemplo: Si un contacto tiene email y phone_number registrados, y envías una actualización solo con phone_number, el campo email será eliminado.

Si 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 los null enviados 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_add y tags_remove son silenciosamente ignorados si no envías apply_tags: true o api_version: "v2". El default mantiene el comportamiento anterior. Si en la misma fila una etiqueta aparece en tags_add y en tags_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" sin respect_sentinel: true significa 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 uses shallow, activa también respect_sentinel: true y 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: true si 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 que identifier solo no es suficiente. Usa orphan_guard: true solo 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