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
- En CompanyFlowHQ, ve a Ajustes → Desarrolladores y crea una clave de API. Marca solo los permisos que necesite.
- Copia la clave enseguida: empieza por
cfhq_live_y solo se muestra una vez. - 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):
| Ámbito | Permite a la clave… |
|---|---|
leads:read | Leer clientes potenciales |
leads:write | Añadir y actualizar clientes potenciales y añadir notas |
social:read | Leer cuentas y publicaciones de redes sociales |
social:write | Crear publicaciones en redes sociales y subir imágenes |
bookings:read | Leer reservas |
invoices:read | Leer facturas |
ads:read | Leer campañas y resultados de anuncios de Facebook e Instagram |
webhooks:manage | Gestionar webhooks |
hooks:manage | Disparadores instantáneos de Zapier / Make |
deals:read | Leer negocios |
deals:write | Añadir negocios |
invoices:write | Crear borradores de facturas |
messages:send | Enviar emails, SMS y mensajes de WhatsApp |
tickets:write | Abrir 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étodo | Ruta | Ámbito | Qué hace |
|---|---|---|---|
| GET | /api/v1/leads | leads:read | Lista de clientes potenciales, del más reciente al más antiguo. Filtros: stage, email, updated_since. |
| POST | /api/v1/leads | leads:write | Añ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:read | Un cliente potencial. |
| PATCH | /api/v1/leads/{id} | leads:write | Cambia los datos, la etapa o la prioridad. |
| POST | /api/v1/leads/{id}/notes | leads:write | Añade una nota a la cronología del cliente potencial. |
| GET | /api/v1/social/accounts | social:read | Tus cuentas de redes sociales conectadas. |
| POST | /api/v1/social/posts | social:write | Crea 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:read | El estado de una publicación y el resultado en cada red. |
| POST | /api/v1/media | social:write | Sube una imagen (JPEG, PNG, WebP o GIF, hasta 20 MB). Devuelve su id. |
| GET | /api/v1/bookings | bookings:read | Reservas. Filtros: from, to, status. |
| GET | /api/v1/invoices | invoices:read | Facturas, presupuestos y facturas rectificativas enviados. Filtros: status, kind. |
| POST | /api/v1/leads/upsert | leads:write | Busca un cliente potencial por email o teléfono y actualízalo, o añádelo si es nuevo. |
| GET | /api/v1/leads/search | leads:read | Busca un cliente potencial por ?email= o ?phone=. |
| POST | /api/v1/leads/{id}/tasks | leads:write | Añade una tarea (title, due_at, assign_to_email). |
| POST | /api/v1/leads/{id}/sequences | leads:write | Inicia una secuencia de seguimiento automática ({ sequence_id }). |
| GET | /api/v1/sequences | leads:read | Tus secuencias de seguimiento. |
| POST | /api/v1/messages | messages:send | Enví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/invoices | invoices:write | Crea un borrador de factura o presupuesto (nunca se envía automáticamente). |
| GET | /api/v1/invoices/search | invoices:read | Busca una factura por ?number=. |
| GET / POST | /api/v1/deals | deals:read / deals:write | Lista o añade negocios. |
| GET | /api/v1/deals/pipelines | deals:read | Embudos de negocios y sus etapas. |
| POST | /api/v1/tickets | tickets:write | Abre una solicitud de soporte. |
| GET | /api/v1/me | any | Tu espacio de trabajo y el propietario de la clave (prueba de conexión). |
| POST / DELETE | /api/v1/hooks, /api/v1/hooks/{id} | hooks:manage | Hooks REST: suscribe una URL a un evento o cancela la suscripción (lo usan Zapier y Make). |
| GET | /api/v1/triggers/{event} | hooks:manage | Los últimos elementos de un evento, con la misma forma que un envío (?limit=3). |
| GET / POST | /api/v1/webhooks | webhooks:manage | Lista o añade webhooks. |
| PATCH / DELETE | /api/v1/webhooks/{id} | webhooks:manage | Cambia, 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:
| Evento | Cuándo |
|---|---|
lead.created | Llega un nuevo cliente potencial |
lead.updated | Cambian los datos o la etapa de un cliente potencial |
booking.created | Se hace una reserva |
invoice.paid | Se paga una factura por completo |
social.post.published | Sale una publicación en redes sociales |
social.post.failed | Falla una publicación en redes sociales |
social.post.milestone | Una publicación en redes sociales supera un hito de visualizaciones (1.000, 2.500, 5.000…) |
chat.lead_created | Un chat de la web se convierte en cliente potencial |
lead.stage_changed | Un cliente potencial pasa a otra etapa |
lead.won | Un cliente potencial se marca como ganado |
booking.cancelled | Se cancela una reserva |
quote.accepted | Un cliente acepta un presupuesto |
deal.won | Se gana un negocio |
deal.stage_changed | Un negocio pasa a otra etapa |
form.response | Alguien rellena un formulario o encuesta |
ticket.created | Llega una nueva solicitud de soporte |
review.feedback | Un cliente deja una opinión privada |
message.received | Llega 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.
- En Ajustes → Desarrolladores, pulsa Crear clave de Zapier (o Crear clave de Make) y copia la clave.
- 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_…). - 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." } }
| Estado | Código | Significado |
|---|---|---|
| 400 | invalid_request / invalid_json / invalid_cursor | Hay que corregir algo de la petición; el mensaje dice qué. |
| 401 | unauthorized / revoked / expired | Falta la clave, es incorrecta, está revocada o ha caducado. |
| 402 | plan_required | El plan del espacio de trabajo no incluye la API. |
| 403 | insufficient_scope / key_owner_inactive | La clave no tiene el permiso, o la persona que la creó ya no está. |
| 404 | not_found | No hay nada con ese id en tu espacio de trabajo. |
| 409 | conflict | No se puede cambiar ahora mismo (por ejemplo, una publicación que ya ha salido). |
| 413 | payload_too_large | Los cuerpos JSON deben ocupar menos de 256 KB y las imágenes menos de 20 MB. |
| 415 | unsupported_media_type | Sube un JPEG, PNG, WebP o GIF. |
| 429 | rate_limited | Más de 120 peticiones por minuto. Espera los segundos de Retry-After. |
| 500 | server_error | Fallo nuestro. Vuelve a intentarlo; si se repite, indica el X-Request-Id. |
¿Dudas? Escribe a support@companyflowhq.com.
