Sincronizar contactos con tu CRM

Creá o actualizá contactos sin duplicarlos, guardá datos propios en campos personalizados, leé sus etiquetas y traé la base completa página por página.

Vas a mantener los contactos de Funnelchat alineados con tu CRM. Usa Crear o actualizar un contacto para escribir sin duplicar, Listar campos personalizados y Asignar un campo personalizado a contactos para tus datos propios, Listar las etiquetas de un contacto para su segmento y Listar contactos para recorrer la base.

Al terminar esta guía, cada alta o cambio en tu CRM se refleja en Funnelchat con un solo contacto por teléfono, con sus campos personalizados cargados, y tu CRM conoce las etiquetas de cada contacto y la base completa de la cuenta.

Antes de empezar

  • Una clave con conversations:write y conversations:read. Escribir, para crear contactos y cargar campos; leer, para listar. Si todavía no tenés una, seguí la guía rápida.
  • La clave en la variable FC_API_KEY, entre comillas si la guardás en un .env (lleva un |).
  • El teléfono de cada contacto con código de país. Es lo que identifica al contacto en esta sincronización.

1. Creá o actualizá el contacto

Crear o actualizar un contacto busca el contacto por phone: si existe lo actualiza, si no lo crea. Es la opción segura para reintentos: llamarla dos veces con el mismo teléfono no duplica. Acepta phone (obligatorio), name y email, y pide conversations:write.

curl -X POST "https://api.funnelchat.com/api/v1/contacts/update-or-create" \
  -H "Authorization: Bearer $FC_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "5491100000000",
    "name": "Ana Pérez",
    "email": "ana@example.com"
  }'

Respuesta de ejemplo (recortada a algunos campos):

{
  "id": 102578466,
  "name": "Ana Pérez",
  "email": "ana@example.com",
  "phone": "5491100000000",
  "whatsapp_id": "",
  "has_phone": true,
  "country_code": "",
  "status": 1,
  "created_at": "2026-10-04T21:23:45.000000Z",
  "active_conversation": 0,
  "unread_messages": 0
}
  • Tratá 200 y 201 como éxito. En las pruebas del 04-10 respondió 201 al crear el contacto y 200 cuando ya existía, con el mismo id; la especificación documenta solo 200 (conocido en v0).
  • El teléfono, con o sin +. Los dos encuentran el mismo contacto, y la API lo guarda sin + (detalle).
  • Guardá el id en tu CRM. Lo piden los pasos siguientes. Para otros datos de la ficha (notas, asesor, favorito), usá Actualizar un contacto con ese id.

2. Encontrá el campo personalizado

Los campos personalizados guardan datos propios de tu negocio, como plan, ciudad o fecha de compra. Para cargar un valor necesitás el id del campo: buscalo por nombre con Listar campos personalizados y quedate con el que coincida exacto.

curl -G "https://api.funnelchat.com/api/v1/custom-fields" \
  --data-urlencode "name=plan" \
  --data-urlencode "perPage=100" \
  -H "Authorization: Bearer $FC_API_KEY" \
  -H "Accept: application/json"

Respuesta de ejemplo (recortada, sin links ni meta):

{
  "data": [
    {
      "id": 3101,
      "name": "plan",
      "type": 0,
      "description": null,
      "created_at": "2026-09-01T10:00:00.000000Z"
    }
  ]
}

Si el campo no existe

Crealo con Crear un campo personalizado (name y type, obligatorios). type es un entero de 0 a 4 cuyo significado la especificación no documenta, y no se puede cambiar después. Para elegirlo, mirá el type de un campo existente del mismo tipo de dato en la respuesta de arriba.

3. Guardá el valor en el contacto

Asignar un campo personalizado a contactos recibe una lista: cada elemento lleva customFieldId, contactId y value (texto). Podés cargar varios campos y varios contactos en una sola llamada.

curl -X POST "https://api.funnelchat.com/api/v1/contacts/attach-custom-field" \
  -H "Authorization: Bearer $FC_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '[
    { "customFieldId": 3101, "contactId": 102578466, "value": "Premium" }
  ]'

Respuesta de ejemplo (según la especificación, un resultado por elemento):

[
  {
    "message": "Custom field value attached successfully.",
    "status": 201
  }
]

Un elemento con status: 403 trae en message si el campo o el contacto no son de tu cuenta. Para borrar un valor, usá Quitar un campo personalizado de contactos.

4. Leé las etiquetas del contacto

Para reflejar en tu CRM el segmento de cada contacto, Listar las etiquetas de un contacto devuelve sus etiquetas, paginadas con perPage.

curl -G "https://api.funnelchat.com/api/v1/contacts/102578466/tags" \
  --data-urlencode "perPage=100" \
  -H "Authorization: Bearer $FC_API_KEY" \
  -H "Accept: application/json"

Respuesta de ejemplo (recortada, sin links ni meta; … marca valores cuyo formato la especificación no documenta):

{
  "data": [
    {
      "id": 4001,
      "name": "Cliente",
      "description": "",
      "color": "…",
      "assigned_at": "…"
    }
  ]
}

Asignar etiquetas no está abierto a claves

Con una clave podés crear etiquetas (Crear una etiqueta) y leerlas, pero no agregárselas ni quitárselas a un contacto: contacts/add-tags y contacts/remove-tags responden 403 route_not_available con cualquier permiso, porque una etiqueta puede disparar un flujo que manda mensajes (Qué está abierto a claves). Hoy las etiquetas viajan de Funnelchat a tu CRM, no al revés.

5. Traé todos los contactos

Para la carga inicial, o para reconciliar, recorré Listar contactos página por página:

  • Mandá siempre perPage (hasta 100) y un orden estable, como orderBy=id con orderByMethod=asc.
  • Para la página siguiente, seguí links.next: conserva perPage y los filtros de tu consulta.
  • Pará cuando links.next sea null (o cuando meta.current_page llegue a meta.last_page).

Para traer solo una parte, sumá filtros como created_from (fecha de alta) o tag_ids[] con tag_mode (all, any o exclude).

curl -G "https://api.funnelchat.com/api/v1/contacts" \
  --data-urlencode "perPage=100" \
  --data-urlencode "page=1" \
  --data-urlencode "orderBy=id" \
  --data-urlencode "orderByMethod=asc" \
  -H "Authorization: Bearer $FC_API_KEY" \
  -H "Accept: application/json"

Respuesta de ejemplo (recortada a un contacto y sin links):

{
  "data": [
    {
      "id": 102578466,
      "name": "Ana Pérez",
      "email": "ana@example.com",
      "phone": "5491100000000",
      "has_phone": true,
      "status": 1,
      "created_at": "2026-10-04T21:23:45.000000Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "path": "https://api.funnelchat.com/api/v1/contacts",
    "per_page": 100,
    "to": 100,
    "total": 419
  }
}

Enterate de los cambios sin consultar en bucle

Con el receptor de Recibir los mensajes de WhatsApp en tu sistema, suscribite en conversations a contact.created (un contacto nuevo), contact.tag_added y contact.tag_removed (cambió una etiqueta). contact.created llega por cualquier canal, también por los contactos que crea tu propia integración: si tu CRM escribe y escucha a la vez, evitá el eco. Los payloads están en el catálogo de eventos.

Si falla

Qué vesQué pasóQué hacer
401Falta la clave, no existe o fue revocada.Revisá la cabecera Authorization: Bearer con la clave completa.
403 con error: "permission_denied"A la clave le falta el permiso: conversations:write para crear y cargar campos, conversations:read para listar.Creá una clave con ese permiso. message dice cuál falta.
403 con error: "route_not_available"La ruta no está abierta a claves en el piloto, como contacts/add-tags.No hay permiso que pedir: ninguna clave la habilita.
422La validación falló: falta phone, email no es un email, perPage está fuera de 1 a 100 o el orden no es uno de los admitidos.errors dice qué campo o parámetro corregir.
404 al crear o actualizarLa especificación lo documenta con message «Contacto no encontrado.», sin detallar el caso.Revisá el phone que mandaste.
200 con elementos en status: 403Al asignar campos, el campo o el contacto de ese elemento no es de tu cuenta.Revisá los id de ese elemento. Cada elemento trae su propio resultado: los que vienen con 201 se guardaron.
503El portero no pudo evaluar la llamada (gate_unavailable).Esperá lo que indica Retry-After y reintentá. Crear o actualizar se puede reintentar sin duplicar.

El detalle de los códigos está en Errores.

Relacionado

En esta página