Documentación de la API de Be CRM

Bienvenido a la referencia de la API REST de Be CRM. Con ella puedes crear y actualizar leads, enviar correos, dar de baja contactos y verificar tus integraciones — todo desde tus propios sistemas.

URL base

Todas las peticiones se hacen sobre HTTPS contra:

https://crm.begraffic.com

Formato

  • Las peticiones y respuestas usan JSON (Content-Type: application/json).
  • Cada token está asociado a un workspace (tu tableId), así que solo operas sobre los datos de tu propio workspace.
  • Las respuestas devuelven códigos HTTP estándar: 2xx éxito, 400 body inválido, 401/403 autenticación, 404 no encontrado, 405 método no permitido.

Recursos

RecursoPara qué sirve
LeadsCrear, buscar y actualizar contactos, sus listas y organización.
CorreosEnviar correos personalizados o por plantilla desde tu remitente verificado.
Baja (unsubscribe)Dar de baja a un contacto desde un enlace público.
Webhook de verificaciónConfirmar que una integración externa quedó conectada.

Autenticación

Todas las peticiones autenticadas requieren el encabezado:

Authorization: Bearer <token>

Tipos de token

  1. Token de API (recomendado para integraciones): se genera en la consola y tiene el formato bmkt_.... El servidor guarda solo un hash SHA‑256, por lo que el secreto nunca se almacena ni se expone. Cópialo en el momento de crearlo — es la única vez que lo verás completo.
  2. ID token de sesión: el token de un usuario autenticado.

En ambos casos se valida que el token esté activo y asociado a un workspace (tableId). Si el token no existe o está deshabilitado, recibirás 401 Unauthorized.

Cómo crear un token de API

  1. Entra a tu consola de Be CRM.
  2. Ve a Ajustes → API keys.
  3. Crea una nueva clave, asígnale un nombre y cópiala (solo se muestra una vez).
  4. Úsala en el encabezado Authorization: Bearer bmkt_....

Trata tus tokens como contraseñas: no los publiques en el frontend ni los subas a un repositorio. Si se filtra uno, elimínalo desde la consola y genera otro.

curl https://crm.begraffic.com/api/leads?email=lead@ejemplo.com \
  -H "Authorization: Bearer bmkt_tu_token"

Instrucciones para administrar leads

Endpoints

  • GET /api/leads?email=correo@example.com para buscar un lead por email.
  • POST /api/leads para crear un nuevo lead.
  • PATCH /api/leads/{id} o PUT /api/leads/{id} para actualizar un lead existente.

Autenticación

Incluye el header:

Authorization: Bearer <token>

El token puede ser un ID token de Firebase o un token pre-registrado en la colección tokens. En ambos casos se validará que esté activo y asociado a una tableId.

Asignación por defecto del token (API key)

Una API key puede tener configurada una asignación por defecto (en Ajustes › API keys: organización, etiquetas y/o listas). Al crear un lead nuevo con esa key:

  • La organización por defecto se asigna solo si el request NO envía organizationId ni organizationName (lo explícito del request siempre manda).
  • Las etiquetas y listas por defecto se suman a las que envíes en tags/groups.
  • Solo aplica al crear un lead nuevo; al actualizar un lead existente, los defaults del token no se aplican.

Campos del body (creación)

  • name (string, opcional)
  • secondName (string, opcional)
  • lastname (string, opcional)
  • secondLastname (string, opcional)
  • email (string, opcional): se normaliza a minúsculas y sin espacios. Debe tener formato válido si se envía.
  • phone (objeto, opcional): { phone_code, phone } para código de área y número fijo. Si envías phone, phone_code es requerido y el número solo acepta dígitos y símbolos + ( ) -.
  • movil (objeto, opcional): { movil_code, movil } para código y número móvil. Si envías movil, movil_code es requerido y el número solo acepta dígitos y símbolos + ( ) -.
  • address (objeto, opcional): { address1, address2, city, state, country, zip }.
  • gender (string, opcional): género del lead; usa Masculino o Femenino (no sensible a mayúsculas/minúsculas). Se guarda recortado.
  • birthdate (string, Date o null, opcional): fecha de nacimiento. Acepta formato YYYY-MM-DD o un objeto Date; se guarda como timestamp. Envía null o cadena vacía para borrar.
  • financialActivities (array<object>, opcional): movimientos financieros iniciales. Cada elemento acepta kind (requerido), amount (number), currency (string), description (string), status (string), date (string o Date; YYYY-MM-DD).
  • tags (string[], opcional): etiquetas del lead. Se eliminan duplicados y espacios.
  • groups (string[], opcional): IDs de listas a las que se vincula el lead. Se escriben en el campo groups del lead (es así como un lead "pertenece" a una lista). Se deduplican.
  • organizationId (string, opcional): ID de la organización (empresa) a la que se asigna el lead. Si el ID no existe, se responde 400. Al asignarla se setean organizationId y organizationName, y además se agregan las listas de la organización a groups y sus etiquetas a tags, para que el lead realmente aparezca bajo esa organización.
  • organizationName (string, opcional): nombre de la organización. Normalmente se infiere de organizationId; envíalo solo si quieres fijarlo manualmente, o para guardar el nombre sin un ID.
  • active (boolean, opcional): por defecto true.
  • image (string, opcional): URL o referencia a la foto del lead.

Campos del body (actualización)

Se aceptan los mismos campos que en creación. Solo se actualizan las propiedades enviadas. email, tags, phone, movil, address, gender y birthdate también se normalizan (se recortan y omiten cuando quedan vacíos); gender debe ser Masculino o Femenino o se rechazará con 400. Para borrar phone, movil, address, gender o birthdate, envía el campo como null o con sus valores vacíos. Además puedes enviar removeTags (string[]) para eliminar etiquetas existentes sin necesidad de enviar el arreglo completo.

Para la asignación a listas y organización en actualización:

  • groups (string[]): IDs de listas que se agregan a las existentes del lead (merge).
  • removeGroups (string[]): IDs de listas que se quitan del lead.
  • organizationId (string): asigna la organización (con el mismo merge de listas/etiquetas descrito en creación). Envía organizationId: null o "" para desasignar la organización (borra organizationId y organizationName).
  • organizationName (string): fija el nombre de la organización sin tocar el ID.

También puedes agregar una nota al lead con el campo note:

  • note.title (string, requerido)
  • note.content (string, opcional)
  • note.dueDate (string o Date, opcional): formato YYYY-MM-DD.
  • note.reminderAt (string o Date, opcional): formato YYYY-MM-DD.
  • note.tags (string[], opcional): etiquetas de la nota; se deduplican.
  • note.type (string, opcional): note o reminder (por defecto note).

Las notas se guardan en la subcolección tables/{tableId}/leads/{leadId}/notes con createdAt y createdBy automáticos.

Para agregar movimientos financieros, envía financialActivities (array de objetos):

  • kind (string, requerido)
  • amount (number, opcional)
  • currency (string, opcional; ej. USD, DOP, EUR)
  • description (string, opcional)
  • status (string, opcional)
  • date (string o Date, opcional; formato YYYY-MM-DD)

Se guardan en tables/{tableId}/leads/{leadId}/financialActivities con createdAt, updatedAt y createdBy automáticos. En la respuesta se incluye financialCreatedIds cuando se crean desde PATCH/PUT.

Respuestas

  • 200 (GET): retorna el lead encontrado.
  • 201 (POST): retorna el lead creado con su id.
  • 200 (PATCH/PUT): retorna el lead actualizado.
  • 400: body inválido o falta el id en la ruta.
  • 401/403: problemas con el token o falta de permisos.
  • 404: el lead no existe (en actualizaciones).
  • 405: método no permitido.

Reglas y comportamiento

  • Debes enviar al menos un método de contacto: email, phone.phone o movil.movil.
  • Si envías phone.phone o movil.movil, debes incluir su código (phone_code o movil_code) y los números solo aceptan dígitos y + ( ) -.
  • Si no envías email pero sí teléfono/móvil, name se vuelve requerido para mostrar el contacto.
  • El email es opcional, pero si se envía debe tener formato válido; se guarda en minúsculas y recortado.
  • Al crear se incrementan los contadores de leads y se actualizan métricas relacionadas (etiquetas, grupos, organizaciones) cuando aplican.
  • Si no envías active, se marca como true.
  • tags se guardan sin duplicados, ignorando mayúsculas/minúsculas.
  • groups contiene IDs de listas (no nombres); son los que determinan la pertenencia del lead a una lista.
  • Asignar organizationId busca la empresa en el workspace; si no existe se responde 400. Al asignarla se guardan organizationId y organizationName, y se replican las señales de pertenencia de la organización: sus listas se agregan a groups y sus etiquetas a tags (igual que cuando un lead entra por formulario).
  • En actualización, groups y tags hacen merge con los valores existentes; usa removeGroups/removeTags para quitar elementos puntuales.
  • Se registra createdBy con el email del usuario autenticado cuando está disponible.

Ejemplo Node.js (crear lead)

const fetch = require("node-fetch");

const body = {
  name: "Juan",
  secondName: "Carlos",
  lastname: "Pérez",
  secondLastname: "Gómez",
  email: "juan.perez@example.com",
  phone: { phone_code: "+52", phone: "5551234567" },
  movil: { movil_code: "+52", movil: "5512345678" },
  address: {
    address1: "Av. Reforma 123",
    address2: "Piso 4",
    city: "Ciudad de México",
    state: "CDMX",
    country: "México",
    zip: "01000",
  },
  gender: "Masculino",
  birthdate: "1990-05-10",
  image: "https://cdn.example.com/avatars/juan.png",
  tags: ["Clientes", "Newsletter"],
  groups: ["listId_abc", "listId_def"], // IDs de listas
  organizationId: "companyId_123", // asigna la organización (agrega sus listas y tags)
  active: true,
  financialActivities: [
    {
      kind: "Compra",
      amount: 2500,
      currency: "MXN",
      description: "Primer pedido",
      status: "Pagado",
      date: "2025-03-12",
    },
  ],
};

fetch("https://crm.begraffic.com/api/leads", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TU_TOKEN",
  },
  body: JSON.stringify(body),
})
  .then((res) => res.json())
  .catch(console.error);

Ejemplo Node.js (actualizar lead)

const fetch = require("node-fetch");

const body = {
  tags: ["Clientes", "VIP"],
  active: false,
  birthdate: "1990-05-10",
  phone: { phone_code: "+1", phone: "3051234567" },
  address: {
    city: "Miami",
    country: "USA",
  },
  note: {
    title: "Llamada de seguimiento",
    content: "Preguntar por presupuesto y disponibilidad.",
    dueDate: "2025-03-15",
    tags: ["followup"],
  },
  financialActivities: [
    {
      kind: "Donación",
      amount: 100,
      currency: "USD",
      status: "Pendiente",
    },
  ],
};

fetch("https://crm.begraffic.com/api/leads/ID_DEL_LEAD", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TU_TOKEN",
  },
  body: JSON.stringify(body),
})
  .then((res) => res.json())
  .catch(console.error);

Ejemplo Node.js (quitar tags)

const fetch = require("node-fetch");

const body = {
  removeTags: ["Clientes"],
};

fetch("https://crm.begraffic.com/api/leads/ID_DEL_LEAD", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TU_TOKEN",
  },
  body: JSON.stringify(body),
})
  .then((res) => res.json())
  .catch(console.error);

Ejemplo Node.js (asignar organización y listas)

const fetch = require("node-fetch");

const body = {
  organizationId: "companyId_123", // agrega también las listas y tags de la organización
  groups: ["listId_abc"], // vincular a listas adicionales por su ID
};

fetch("https://crm.begraffic.com/api/leads/ID_DEL_LEAD", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TU_TOKEN",
  },
  body: JSON.stringify(body),
})
  .then((res) => res.json())
  .catch(console.error);

Ejemplo Node.js (quitar listas y desasignar organización)

const fetch = require("node-fetch");

const body = {
  removeGroups: ["listId_abc"], // quita estas listas del lead
  organizationId: null, // desasigna la organización (borra organizationId y organizationName)
};

fetch("https://crm.begraffic.com/api/leads/ID_DEL_LEAD", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TU_TOKEN",
  },
  body: JSON.stringify(body),
})
  .then((res) => res.json())
  .catch(console.error);

Qué se puede y no se puede hacer

  • ✔️ Crear leads con tags, imagen y estado personalizado.
  • ✔️ Asignar el lead a listas (groups) y a una organización (organizationId), igual que desde un formulario.
  • ✔️ Actualizar solo los campos necesarios usando PATCH o PUT.
  • ✔️ Quitar elementos puntuales con removeTags / removeGroups, o desasignar la organización con organizationId: null.
  • ✔️ Usar tokens de Firebase o tokens gestionados en la colección tokens.
  • ❌ Omitir el header Authorization.
  • ❌ Enviar bodies que no sean JSON válido.
  • ❌ Asignar una organizationId que no exista en el workspace (responde 400).
  • ❌ Usar métodos distintos de POST, PATCH o PUT.

Instrucciones para enviar correos (sencillos y templates)

Endpoint

POST /api/emails/sendEmail

Autenticación

Incluye el header:

Authorization: Bearer <token>

Campos del body

Envío sencillo (custom)

  • to (string, obligatorio): destinatario principal (solo uno)
  • subject (string, obligatorio)
  • html o text (string, al menos uno obligatorio)
  • from (string, opcional): correo remitente específico (ver "Remitente")
  • fromName (string, opcional)
  • replyTo, cc, bcc (string o array, opcional)
  • idCustomer (string, opcional)

Envío con template

  • to (string, obligatorio): destinatario principal (solo uno)
  • template (objeto, obligatorio):
    • name (string, obligatorio): nombre del template en tu proveedor de correo
    • data (objeto, opcional): datos para el template
  • from (string, opcional): correo remitente específico (ver "Remitente")
  • fromName (string, opcional)
  • replyTo, cc, bcc, idCustomer: igual que en el envío sencillo

Remitente

  • Sin from, el correo sale desde el primer remitente verificado del workspace.
  • Con from, el correo sale desde esa dirección solo si está registrada como remitente del workspace (o pertenece a un dominio verificado). También puedes usar senderId con el id de la identidad registrada.
  • Si el remitente solicitado no está configurado, la API responde 404 con {"error": "El remitente <correo> no está configurado en este workspace. ..."} y no envía el correo (nunca se sustituye por otro remitente).
  • Si está registrado pero aún sin verificar, responde 409.

Restricciones

  • Solo se permite un destinatario principal en to
  • Máximo 50 destinatarios en cc, bcc, replyTo
  • Los correos deben ser válidos
  • Si usas template, no incluyas subject, html ni text
  • Si usas envío sencillo, no incluyas template
  • El remitente debe estar verificado en el proveedor de correo configurado
  • En envíos sencillos (custom), se agrega automáticamente un pie con el nombre de la empresa, un enlace a Be CRM y un link de baja

Ejemplo Node.js (fetch)

Sencillo

const fetch = require("node-fetch");

const body = {
  to: "destinatario@ejemplo.com",
  subject: "Asunto de prueba",
  html: "<h1>Hola mundo</h1>",
  fromName: "Mi Empresa",
};

fetch("https://crm.begraffic.com/api/emails/sendEmail", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TU_TOKEN",
  },
  body: JSON.stringify(body),
})
  .then((res) => res.json())
  .catch(console.error);

Template

const fetch = require("node-fetch");

const body = {
  to: "destinatario@ejemplo.com",
  template: {
    name: "MiTemplateCorreo",
    data: {
      nombre: "Juan",
      producto: "CRM",
    },
  },
  // Opcional: remitente específico (debe estar registrado en el workspace,
  // si no la API responde 404 sin enviar).
  from: "ventas@miempresa.com",
  fromName: "Mi Empresa",
};

fetch("https://crm.begraffic.com/api/emails/sendEmail", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TU_TOKEN",
  },
  body: JSON.stringify(body),
})
  .then((res) => res.json())
  .catch(console.error);

Qué se puede y no se puede hacer

  • ✔️ Puedes enviar correos personalizados o usando templates de tu proveedor de correo.
  • ✔️ Puedes incluir cc, bcc, replyTo, idCustomer si lo necesitas.
  • ✔️ Puedes elegir el remitente con from (o senderId) entre los remitentes registrados del workspace.
  • ❌ No puedes enviar a más de un destinatario principal.
  • ❌ No puedes enviar sin asunto y cuerpo (en modo sencillo).
  • ❌ No puedes enviar sin template.name (en modo template).
  • ❌ No puedes enviar si el remitente no está verificado en tu proveedor de correo.
  • ❌ No puedes enviar desde un from que no esté configurado en el workspace (la API responde 404 y no sustituye el remitente).

API de baja de correos (unsubscribe)

Endpoint

POST /api/unsubscribe

Uso

Permite dar de baja a un lead desde un enlace público. Debes enviar el tableId y al menos leadId o email.

Campos del body

  • tableId (string, obligatorio): ID del workspace.
  • leadId (string, opcional): ID del lead.
  • email (string, opcional): email del lead (normalizado a minúsculas).
  • reason (string, opcional): motivo de baja (se guarda como nota del lead).

Respuesta exitosa

{
  "status": "unsubscribed",
  "leadId": "abc123",
  "alreadyInactive": false,
  "noteAdded": true
}

Ejemplo

const body = {
  tableId: "workspace-123",
  email: "lead@ejemplo.com",
  reason: "Ya no deseo recibir mensajes",
};

fetch("https://crm.begraffic.com/api/unsubscribe", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(body),
});

Webhook de verificación de integraciones

Este directorio contiene los endpoints que se exponen públicamente como webhooks dentro de la API de Pages de Next.js. Actualmente incluye el webhook de verificación de conexión para que plataformas externas puedan validar que su token está correctamente configurado y asignado al workspace indicado.

POST /api/webhooks/connection (también disponible como GET)

Propósito

Permite realizar un "ping" desde otra plataforma para verificar en tiempo real que la integración funciona. La respuesta incluye:

  • Estado general de la verificación (status, message, timestamp).
  • Información del workspace (ID, nombre y plan cuando están disponibles).
  • El tipo de autenticación que se usó: token de API o sesión de usuario, con la misma metadata que ves en la consola (nombre, descripción, estado, contador de usos y fechas).

Autenticación

Debes enviar el encabezado Authorization: Bearer <token> donde <token> puede ser:

  1. Un ID token de Firebase (sesión de usuario autenticada).
  2. Un token API generado en la consola (formato bmkt_...). El servidor calcula internamente el hash SHA‑256 y lo compara con la colección de tokens, por lo que jamás se expone el secreto.

Si el token está inactivo o no existe, recibirás 401 Unauthorized.

Solicitud de ejemplo

curl -X POST https://tu-dominio/api/webhooks/connection \
  -H "Authorization: Bearer bmkt_ejemplo123"

Respuesta exitosa

{
  "status": "ok",
  "message": "Integración verificada correctamente.",
  "timestamp": "2024-02-18T23:09:11.038Z",
  "workspace": {
    "id": "workspaceId",
    "name": "Marketing Team",
    "plan": "Pro"
  },
  "auth": {
    "type": "token",
    "tokenId": "f3d2b7...",
    "name": "Webhook CRM",
    "description": "Integración externa",
    "active": true,
    "createdBy": "admin@example.com",
    "uses": 42,
    "dateCreated": "2024-02-15T21:01:12.000Z",
    "lastUsed": "2024-02-18T23:07:55.311Z"
  }
}

Cuando la autenticación proviene de un usuario, la sección auth cambia a:

{
  "type": "user",
  "email": "usuario@example.com"
}

Errores comunes

CódigoCausaObservaciones
401Falta el encabezado AuthorizationIncluye siempre Bearer <token>
401Token inválido o deshabilitadoRevisa que el token exista y siga activo
403Usuario autenticado sin tableIdLa cuenta debe estar asociada a un workspace
405Método no permitidoSolo se aceptan GET o POST

Implementaciones recomendadas

  • Usa este webhook como parte del flujo de alta de integraciones: después de guardar un token en otra plataforma, llama al endpoint y valida que status === "ok".
  • Registra el tokenId (hash) que devuelve la respuesta para facilitar auditorías sin almacenar el secreto completo.

Cualquier webhook adicional debe documentarse en este mismo archivo para mantener una referencia centralizada de los endpoints públicos.