Desarrolladores

La API de CompanyFlowHQ

Envía clientes potenciales desde cualquier web o app, publica en redes sociales desde tus propias herramientas y entérate al momento de lo que pasa con los webhooks. Incluida en los planes Growth, Pro y Social Agency.

Primeros pasos

  1. En CompanyFlowHQ, ve a Ajustes → Desarrolladores y crea una clave de API. Marca solo los permisos que necesite.
  2. Copia la clave enseguida: empieza por cfhq_live_ y solo se muestra una vez.
  3. Llama a la API desde tu servidor (nunca desde una página web o una app que pueda ver un cliente).

Dirección base: https://companyflowhq.com/api/v1. Las peticiones y respuestas son JSON (UTF-8). Las horas están en ISO 8601 y UTC. Los importes van en la unidad más pequeña (peniques para GBP).

Autenticación

Envía la clave en la cabecera Authorization:

curl https://companyflowhq.com/api/v1/leads -H "Authorization: Bearer cfhq_live_YOUR_KEY"

Permisos (ámbitos):

ÁmbitoPermite a la clave…
leads:readLeer clientes potenciales
leads:writeAñadir y actualizar clientes potenciales y añadir notas
social:readLeer cuentas y publicaciones de redes sociales
social:writeCrear publicaciones en redes sociales y subir imágenes
bookings:readLeer reservas
invoices:readLeer facturas
ads:readLeer campañas y resultados de anuncios de Facebook e Instagram
webhooks:manageGestionar webhooks
hooks:manageDisparadores instantáneos de Zapier / Make
deals:readLeer negocios
deals:writeAñadir negocios
invoices:writeCrear borradores de facturas
messages:sendEnviar emails, SMS y mensajes de WhatsApp
tickets:writeAbrir solicitudes de soporte

Las claves se pueden revocar en cualquier momento y se les puede poner fecha de caducidad. Solo guardamos una huella (SHA-256) de cada clave.

Endpoints

MétodoRutaÁmbitoQué hace
GET/api/v1/leadsleads:readLista de clientes potenciales, del más reciente al más antiguo. Filtros: stage, email, updated_since.
POST/api/v1/leadsleads:writeAñade un cliente potencial (con la misma comprobación de duplicados y respuestas automáticas que tu formulario web).
GET/api/v1/leads/{id}leads:readUn cliente potencial.
PATCH/api/v1/leads/{id}leads:writeCambia los datos, la etapa o la prioridad.
POST/api/v1/leads/{id}/notesleads:writeAñade una nota a la cronología del cliente potencial.
GET/api/v1/social/accountssocial:readTus cuentas de redes sociales conectadas.
POST/api/v1/social/postssocial:writeCrea una publicación para una o varias cuentas: ahora, a una hora concreta, en el siguiente hueco de la cola o como borrador.
GET/api/v1/social/posts/{id}social:readEl estado de una publicación y el resultado en cada red.
POST/api/v1/mediasocial:writeSube una imagen (JPEG, PNG, WebP o GIF, hasta 20 MB). Devuelve su id.
GET/api/v1/bookingsbookings:readReservas. Filtros: from, to, status.
GET/api/v1/invoicesinvoices:readFacturas, presupuestos y facturas rectificativas enviados. Filtros: status, kind.
POST/api/v1/leads/upsertleads:writeBusca un cliente potencial por email o teléfono y actualízalo, o añádelo si es nuevo.
GET/api/v1/leads/searchleads:readBusca un cliente potencial por ?email= o ?phone=.
POST/api/v1/leads/{id}/tasksleads:writeAñade una tarea (title, due_at, assign_to_email).
POST/api/v1/leads/{id}/sequencesleads:writeInicia una secuencia de seguimiento automática ({ sequence_id }).
GET/api/v1/sequencesleads:readTus secuencias de seguimiento.
POST/api/v1/messagesmessages:sendEnvía un email, un SMS o un WhatsApp a un cliente potencial. Se respetan el consentimiento, las bajas y las horas de silencio.
POST/api/v1/invoicesinvoices:writeCrea un borrador de factura o presupuesto (nunca se envía automáticamente).
GET/api/v1/invoices/searchinvoices:readBusca una factura por ?number=.
GET / POST/api/v1/dealsdeals:read / deals:writeLista o añade negocios.
GET/api/v1/deals/pipelinesdeals:readEmbudos de negocios y sus etapas.
POST/api/v1/ticketstickets:writeAbre una solicitud de soporte.
GET/api/v1/meanyTu espacio de trabajo y el propietario de la clave (prueba de conexión).
POST / DELETE/api/v1/hooks, /api/v1/hooks/{id}hooks:manageHooks REST: suscribe una URL a un evento o cancela la suscripción (lo usan Zapier y Make).
GET/api/v1/triggers/{event}hooks:manageLos últimos elementos de un evento, con la misma forma que un envío (?limit=3).
GET / POST/api/v1/webhookswebhooks:manageLista o añade webhooks.
PATCH / DELETE/api/v1/webhooks/{id}webhooks:manageCambia, desactiva o elimina un webhook.

Ejemplo de clientes potenciales

Añadir un cliente potencial (curl)

curl -X POST https://companyflowhq.com/api/v1/leads \
  -H "Authorization: Bearer cfhq_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: form-2026-09-27-000123" \
  -d '{
    "first_name": "Amara",
    "last_name": "Perera",
    "email": "amara@example.com",
    "phone": "+44 7700 900123",
    "requirement": "MSc Data Science, January intake",
    "consent_marketing": true
  }'

Devuelve 201 con { "data": { …lead } }. Si la persona ya está en tu espacio de trabajo (mismo email o teléfono), recibes 200 con "duplicate": true y el cliente potencial existente. Enviar dos veces la misma Idempotency-Key nunca crea dos clientes potenciales.

Avanzar un cliente potencial (JavaScript)

const res = await fetch("https://companyflowhq.com/api/v1/leads/LEAD_ID", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.CFHQ_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ stage: "qualified", priority: "hot" }),
});
if (!res.ok) {
  const { error } = await res.json();
  throw new Error(`${error.code}: ${error.message}`);
}
const { data: lead } = await res.json();

Ejemplo de publicaciones

Sube primero una imagen (opcional) y después crea la publicación. Cada cuenta puede tener su propio texto.

# 1. Upload a picture
curl -X POST https://companyflowhq.com/api/v1/media \
  -H "Authorization: Bearer cfhq_live_YOUR_KEY" \
  -F "file=@open-day.jpg"
# → { "data": { "id": "MEDIA_ID", ... } }

# 2. Schedule the post
curl -X POST https://companyflowhq.com/api/v1/social/posts \
  -H "Authorization: Bearer cfhq_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Open day this Saturday, 10am to 2pm. Everyone welcome!",
    "media_ids": ["MEDIA_ID"],
    "accounts": [
      { "account_id": "FACEBOOK_ACCOUNT_ID" },
      { "account_id": "LINKEDIN_ACCOUNT_ID", "text": "Join our open day this Saturday (10:00–14:00)." }
    ],
    "schedule": { "mode": "at", "at": "2026-10-03T08:00:00Z" }
  }'

schedule.mode puede ser now, at, queue (siguiente hueco libre) o draft. Añade "require_approval": true para enviarla a aprobación en CompanyFlowHQ. Las publicaciones siguen las mismas reglas que en la app, incluida la aprobación del cliente en las agencias. Consulta el progreso con GET /api/v1/social/posts/{id}: results muestra el estado, el enlace y cualquier error de cada red.

Paginación

Las listas devuelven hasta limit elementos (25 por defecto, 100 como máximo), del más reciente al más antiguo:

{ "data": [ … ], "has_more": true, "next_cursor": "WyIyMDI2LTA5…" }

Pasa ?cursor= con el valor de next_cursor para obtener la página siguiente. Para cuando has_more sea false.

Webhooks

Añade un webhook en Ajustes → Desarrolladores (o con la API). Cuando pasa algo, enviamos un POST con JSON a tu dirección HTTPS:

EventoCuándo
lead.createdLlega un nuevo cliente potencial
lead.updatedCambian los datos o la etapa de un cliente potencial
booking.createdSe hace una reserva
invoice.paidSe paga una factura por completo
social.post.publishedSale una publicación en redes sociales
social.post.failedFalla una publicación en redes sociales
social.post.milestoneUna publicación en redes sociales supera un hito de visualizaciones (1.000, 2.500, 5.000…)
chat.lead_createdUn chat de la web se convierte en cliente potencial
lead.stage_changedUn cliente potencial pasa a otra etapa
lead.wonUn cliente potencial se marca como ganado
booking.cancelledSe cancela una reserva
quote.acceptedUn cliente acepta un presupuesto
deal.wonSe gana un negocio
deal.stage_changedUn negocio pasa a otra etapa
form.responseAlguien rellena un formulario o encuesta
ticket.createdLlega una nueva solicitud de soporte
review.feedbackUn cliente deja una opinión privada
message.receivedLlega un mensaje nuevo a la bandeja de entrada
POST /your-webhook
Content-Type: application/json
X-CompanyFlow-Event: lead.created
X-CompanyFlow-Delivery: 5f0c…
X-CompanyFlow-Signature: t=1790000000,v1=6b1f…

{ "id": "evt_…", "type": "lead.created", "created": "2026-09-27T10:00:00.000Z",
  "data": { "lead": { "id": "…", "first_name": "Amara", … }, "source": "Website" } }

Responde con cualquier estado 2xx en menos de 10 segundos. Cualquier otra cosa cuenta como fallo y lo reintentamos al cabo de 1 minuto, 5 minutos, 30 minutos, 2 horas y 12 horas. Tras 20 fallos seguidos, el webhook se desactiva y se avisa por email al propietario. No se siguen las redirecciones, y las direcciones deben ser públicas (nada de redes privadas o locales). El mismo evento puede llegar dos veces de vez en cuando: usa id para ignorar las repeticiones.

Zapier y Make

Conecta CompanyFlowHQ con más de 7.000 apps —Xero, QuickBooks, Slack, Google Sheets y muchas más— sin programar.

  1. En Ajustes → Desarrolladores, pulsa Crear clave de Zapier (o Crear clave de Make) y copia la clave.
  2. En Zapier, elige la app de CompanyFlowHQ y pega la clave cuando te la pida. En Make, añade la app de CompanyFlowHQ (o usa su módulo HTTP con la cabecera Authorization: Bearer cfhq_live_…).
  3. Elige un disparador como Nuevo cliente potencial, Factura pagada o Negocio ganado y luego una acción en la otra app.

Los disparadores son instantáneos: cuando se activa un Zap, Zapier se suscribe con POST /api/v1/hooks { "event": "lead.created", "target_url": "https://hooks.zapier.com/…" } y cancela la suscripción con DELETE /api/v1/hooks/{id} cuando se desactiva. Los envíos usan el mismo formato, firma y reintentos que los webhooks; responder 410 Gone elimina la suscripción. Puedes ver y quitar los disparadores activos en Ajustes → Desarrolladores.

Comprobar firmas

Cada mensaje va firmado con el secreto de tu webhook (empieza por whsec_). La firma es un HMAC-SHA256 de <t>.<raw body>. Compruébala siempre y rechaza los mensajes de hace más de 5 minutos.

Node.js (Express)

import crypto from "node:crypto";
import express from "express";

const app = express();
app.post("/companyflow-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-CompanyFlow-Signature") ?? "";
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", process.env.CFHQ_WEBHOOK_SECRET)
    .update(`${t}.${req.body}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  const ok =
    fresh && v1 && v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
  if (!ok) return res.status(400).send("Bad signature");
  const event = JSON.parse(req.body);
  // … handle event.type / event.data …
  res.sendStatus(200);
});

Cloudflare Workers / Deno / navegadores (Web Crypto)

async function verify(secret, header, rawBody) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const key = await crypto.subtle.importKey(
    "raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
  const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${t}.${rawBody}`));
  const hex = [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");
  return hex === v1; // use a constant-time compare in production
}

Límites de frecuencia

Cada clave puede hacer 120 peticiones por minuto. Cada respuesta incluye X-RateLimit-Limit y X-Request-Id. Si superas el límite, recibes 429 con una cabecera Retry-After: espera esos segundos y vuelve a intentarlo.

Errores

Los errores siempre tienen la misma forma:

{ "error": { "code": "insufficient_scope", "message": "This API key doesn't have the “leads:write” permission." } }
EstadoCódigoSignificado
400invalid_request / invalid_json / invalid_cursorHay que corregir algo de la petición; el mensaje dice qué.
401unauthorized / revoked / expiredFalta la clave, es incorrecta, está revocada o ha caducado.
402plan_requiredEl plan del espacio de trabajo no incluye la API.
403insufficient_scope / key_owner_inactiveLa clave no tiene el permiso, o la persona que la creó ya no está.
404not_foundNo hay nada con ese id en tu espacio de trabajo.
409conflictNo se puede cambiar ahora mismo (por ejemplo, una publicación que ya ha salido).
413payload_too_largeLos cuerpos JSON deben ocupar menos de 256 KB y las imágenes menos de 20 MB.
415unsupported_media_typeSube un JPEG, PNG, WebP o GIF.
429rate_limitedMás de 120 peticiones por minuto. Espera los segundos de Retry-After.
500server_errorFallo nuestro. Vuelve a intentarlo; si se repite, indica el X-Request-Id.

¿Dudas? Escribe a support@companyflowhq.com.