API y webhooks: conecta Craft a tu CRM o tu web
Cómo controlar Craft con peticiones con clave y recibir en tu propio servidor eventos de nuevos contactos, pulsaciones de botones, pagos y publicaciones.
Puedes conectar Craft a tu CRM, a la web de un cliente o a cualquier servicio propio. Todo lo que puede hacer el conector MCP está disponible como una petición HTTPS normal con una clave: embudos, contactos, difusiones, publicaciones, carruseles, imágenes. Los webhooks funcionan al revés: Craft envía un evento a tu servidor cuando aparece un nuevo contacto, alguien pulsa un botón, alcanza el objetivo de un embudo, paga o sale una publicación.
Cómo funciona#
- En Craft creas una clave y eliges qué puede hacer: solo leer, o trabajar con embudos, contactos, difusiones, publicaciones, etc.
- Tu servidor envía una petición a
https://craftopen.space/api/v1/tools/<method>con la clave en una cabecera. Los parámetros del método van en el cuerpo JSON de la petición. - Craft ejecuta el método en tu cuenta, exactamente como si lo hicieras en la web o se lo pidieras a tu IA a través del conector MCP, con las mismas comprobaciones y límites.
- Para los eventos añades un webhook: la dirección de tu servidor. Craft envía ahí una petición firmada y reintenta si tu servidor no responde.
Qué necesitas#
- Cualquier plan: las claves y los webhooks se pueden crear en todos los planes, incluido el gratuito.
- Un servidor o servicio que pueda enviar y recibir peticiones HTTPS: tu propio código, Make, n8n, Zapier o un CRM con webhooks entrantes.
Paso 1. Crea una clave#
Menú del perfil → API y webhooks (o Ajustes → API y webhooks) → Nueva clave.
- Ponle a la clave un nombre que reconozcas después: «HubSpot», «Web del cliente».
- Elige qué puede hacer la clave. Por defecto, una clave nueva solo puede leer. Todo lo demás se activa con casillas: embudos, contactos, difusiones, publicaciones, contenido, Fábrica de contenido, cuentas. «Todo» abre todas las capacidades, incluidas las que se añadan después.
- Elige cómo publica la clave las publicaciones y difusiones: - Solo borradores: todo se guarda como borrador y lo envías tú en Craft; - Programado: no antes de 10 minutos, para que haya tiempo de cancelar; - Al instante: publica y envía en el momento de la petición.
- Define el límite diario de gasto en imágenes y cuántas difusiones al día puede enviar la clave desde un bot.
- Pulsa Crear clave y cópiala enseguida.
🔴 La clave se muestra una sola vez. Craft solo guarda su huella y no puede recuperarla. ¿La perdiste? Revócala y crea una nueva. Nunca pongas la clave en el código de una página web ni en una app móvil: la vería cualquier visitante. La clave solo debe estar en un servidor.
Puedes cambiar los permisos y ajustes de una clave en cualquier momento con «Ajustes»: los cambios se aplican en un minuto. «Revocar» desactiva la clave al instante.
Paso 2. Tu primera petición#
curl -X POST https://craftopen.space/api/v1/tools/list_funnels \
-H "Authorization: Bearer craft_your_key" \
-H "Content-Type: application/json" \
-d '{}'
Una respuesta correcta siempre tiene este aspecto; los propios datos están en data:
{"ok": true, "data": {"funnels": [ ... ]}}
Si algo va mal, recibes "ok": false y un motivo claro:
{"ok": false, "error": {"code": "funnel_not_found", "message": "Воронка не найдена."}}
⚠️ Por ahora los mensajes de error están escritos en ruso. Basa tu lógica en error.code: es
estable y no depende del idioma.
Métodos disponibles#
Todos los métodos y sus parámetros con descripción:
- en Craft:
GET https://craftopen.space/api/v1/toolscon tu clave; la lista indica qué permiso necesita cada método y si esta clave puede llamarlo; - una descripción OpenAPI: craftopen.space/api/v1/openapi.json: Postman, Insomnia y los generadores de clientes la entienden.
Los métodos son los mismos que los del conector MCP. Los más habituales:
| Tarea | Método | Permiso de la clave |
|---|---|---|
| Lista de embudos y sus estadísticas | list_funnels, get_funnel_stats |
lectura |
| Buscar suscriptores del bot | find_contacts |
lectura |
| Ficha del suscriptor | get_contact |
lectura |
| Añadir o quitar una etiqueta | tag_contacts |
contactos |
| Ejecutar un embudo para una persona | run_funnel_for_contact |
contactos, modo «Al instante» |
| Publicación programada | list_publishing_accounts → schedule_post |
publicaciones |
| Difusión de Telegram | save_broadcast_draft → send_broadcast_test → send_broadcast |
difusiones |
| Carrusel a partir de tu texto | create_carousel, render_carousel |
contenido |
| Imagen por créditos | generate_image |
contenido |
GET https://craftopen.space/api/v1/me muestra qué clave estás usando, sus permisos y cuántas
peticiones quedan hoy.
Reintentar sin duplicados#
Si una petición se cortó y no sabes si llegó, envíala de nuevo con la
misma cabecera Idempotency-Key. La publicación no saldrá dos veces y una imagen no se cobrará dos veces.
curl -X POST https://craftopen.space/api/v1/tools/schedule_post \
-H "Authorization: Bearer craft_your_key" \
-H "Idempotency-Key: post-2026-09-27-lime-1" \
-H "Content-Type: application/json" \
-d '{"account_ids": ["..."], "confirm_usernames": ["@lime_agency"],
"carousel_id": "...", "caption": "Post text",
"scheduled_at": "2026-09-27T19:00:00+03:00"}'
⚠️ Las confirmaciones forman parte de la petición. Donde la web preguntaría «¿estás seguro?»,
la API espera una confirmación en los parámetros: los @usuarios de las cuentas para una publicación
(confirm_usernames), el número exacto de destinatarios para una difusión (confirm_recipients),
el nombre de un embudo activo que estás editando, el precio de un bloque de pago. Si la
confirmación no coincide con lo que ve Craft, no se envía nada: la respuesta te indica el
valor correcto.
Errores y qué hacer#
| Estado | Significado |
|---|---|
| 400 | Parámetros no válidos: message dice cuáles |
| 401 | Sin clave, clave incorrecta o clave revocada |
| 403 | Plan sin API (plan_required), a la clave le falta un permiso (scope_required) o el modo de publicación no permite la acción |
| 404 | No encontrado: embudo, contacto, publicación o método |
| 409 | Los datos cambiaron desde que los leíste, o una confirmación no coincidió: vuelve a leerlos |
| 429 | Límite de frecuencia: espera tantos segundos como indique la cabecera Retry-After |
| 500, 503 | Fallo temporal: reintenta en un minuto con la misma Idempotency-Key |
Límites#
- 60 peticiones por minuto y 10 000 al día por cuenta, sumando todas las claves. El día se cuenta en hora de Moscú (UTC+3). ¿Necesitas más? Escribe a soporte; el límite se amplía sin cambiar de plan.
- Los webhooks no cuentan para el límite.
- Las imágenes gastan créditos a los mismos precios que en la web, y nunca por encima del límite diario que definiste para la clave.
- Difusiones: no más del número elegido al día por bot. Antes de enviar es obligatoria una prueba para ti con
send_broadcast_testdel mismo texto exacto.
Webhooks#
API y webhooks → Añadir webhook: introduce la dirección de tu servidor (solo https://) y elige los
eventos. Justo después de crearlo, Craft muestra el secreto de firma: guárdalo, no se vuelve a mostrar
(puedes emitir uno nuevo con «Nuevo secreto»).
Probar envía un evento ping y muestra qué respondió tu servidor. Registro lista las
entregas recientes: qué se envió, si llegó, la respuesta y un botón «Reintentar».
Eventos#
| Evento | Cuándo llega |
|---|---|
contact.created |
Alguien se suscribió a un bot o escribió por primera vez a tu Direct de Instagram |
funnel.button_clicked |
Alguien pulsó un botón en un embudo |
funnel.goal_reached |
Alguien alcanzó el objetivo del embudo |
payment.succeeded |
Se completó un pago en un bloque de pago de un embudo |
post.published |
Una publicación salió en la red social |
agent.message_received |
Un mensaje de un cliente llegó al agente de IA, en Instagram o Telegram |
agent.replied |
El agente de IA respondió al cliente y la respuesta se envió. Los datos incluyen el texto de la respuesta |
agent.handed_over |
El agente de IA pasó el cliente al equipo. Los datos incluyen el resumen y si se entregó y a quién |
Los eventos del agente incluyen platform (instagram o telegram), account_id (la cuenta de Instagram o el bot), contact_id y una marca de tiempo, para que puedas guardar el historial de conversaciones del agente en tu propio CRM o servicio.
Ejemplo de cuerpo de la petición:
{
"id": "6f1c…",
"type": "contact.created",
"created_at": "2026-09-26T09:04:11+00:00",
"data": {
"platform": "telegram",
"contact_id": "…",
"bot_username": "shop_bot",
"first_name": "Anna",
"username": "anna",
"source": {"type": "link", "ref": "promo"}
}
}
El id del evento es el mismo en todos los reintentos: úsalo para descartar duplicados.
Comprobar que una petición viene de Craft#
Cada petición tiene una cabecera Craft-Signature: t=<time>,v1=<signature>. La firma es
HMAC-SHA256 de la cadena <time>.<request body> con tu secreto. Calcúlala y compara:
import hmac, hashlib, time
def craft_signature_ok(header: str, body: bytes, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300: # older than 5 minutes — reject
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
const crypto = require("crypto");
function craftSignatureOk(header, rawBody, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
⚠️ La firma se calcula sobre el cuerpo en bruto de la petición: exactamente los bytes que llegaron. Si tu framework primero interpreta el JSON y lo vuelve a serializar, la firma no coincidirá.
Reintentos#
Craft espera una respuesta hasta 10 segundos. Cualquier respuesta 2xx cuenta como entregada.
Si no, Craft reintenta al cabo de 1 minuto, 5 minutos, 30 minutos, 2, 6, 12 y 24 horas: 8
intentos en total, tras los cuales el evento se marca como «fallido» (puedes reenviarlo desde el registro).
🔴 Tras 50 entregas fallidas seguidas el webhook se desactiva para que no siga llamando a una dirección muerta. Arregla tu servidor y pulsa «Activar»: el contador se reinicia. Los eventos que ocurrieron mientras el webhook estaba desactivado no se envían.
Responde rápido y haz el trabajo lento después de responder: si tu servidor tarda más de 10 segundos, Craft cuenta el intento como fallido y vuelve a enviar el evento.
Tu propio canal: respuestas del agente de IA#
Si los clientes te escriben donde Craft no está conectado (tu propia mensajería, el chat de tu web, tu CRM), el agente de IA también puede responder ahí. Tu sistema envía el mensaje del cliente y Craft devuelve la respuesta del agente. Tu sistema se la entrega al cliente.
curl -X POST https://craftopen.space/api/v1/agent/reply \
-H "Authorization: Bearer craft_…" -H "Content-Type: application/json" \
-d '{"ig_user_id": "1784…", "contact_id": "client-42", "text": "How much is JET+?", "name": "Ivan"}'
- Qué agente responde:
ig_user_idde una cuenta de Instagram otg_bot_idde un bot de Telegram con un agente configurado. Se usan sus reglas, base de conocimiento, modelo y ajustes de traspaso. contact_id: tu identificador estable del cliente; el agente recuerda la conversación por él.name: opcional, para el traspaso al equipo.- Respuesta:
reply: texto para el cliente;handed_over: el agente pasó el cliente al equipo (el contacto va adonde van los contactos de Instagram);paused: el agente se calla hasta que el equipo lo devuelva;credits_charged: créditos gastados. - Las respuestas cuestan créditos, igual que en Instagram. Sin créditos, recibes un error
no_credits. - Los eventos
agent.message_received,agent.repliedyagent.handed_overtambién se lanzan para este canal.
⚠️ Por este canal el agente solo envía texto: ni vídeos ni archivos de los embudos.
Todavía no disponible#
- Bibliotecas de cliente listas: usa la descripción OpenAPI.
- Crear claves y webhooks a través de la propia API: solo en la página API y webhooks.
- «Iniciar sesión con Craft» para apps de terceros: una clave solo da acceso a tu propia cuenta.