Desarrolladores

API y webhooks de Anka Tickets

Conecta tu tienda online, tu ERP o herramientas como Zapier, Make y n8n con tus líneas de WhatsApp: crea contactos, envía mensajes y campañas, maneja pedidos, arranca flujos del bot y recibe un aviso firmado cada vez que pasa algo.

Inicio rápido

  1. En Anka, ve a Integraciones → API Keys y crea una clave con solo los permisos que vas a usar. Se muestra una vez: guárdala como un secreto.
  2. Envíala en cada petición como Authorization: Bearer cmnk_…. Todas las rutas empiezan con https://ankatickets.com/api/public/v1.
  3. Prueba que funciona:
curl https://ankatickets.com/api/public/v1/me \
  -H "Authorization: Bearer cmnk_TU_CLAVE"

La API está en los planes que incluyen API Keys; las campañas y los webhooks también necesitan que el plan los incluya. Todo está en español: los errores traen un message que puedes mostrar tal cual.

Permisos de las claves

Cada endpoint pide un permiso. Si la clave no lo tiene, responde 403.

PermisoQué permite
contacts:readLeer contactos
contacts:writeCrear, editar y borrar contactos
messages:sendEnviar mensajes y plantillas
messages:readVer mensajes y su estado
conversations:writePasar chats a una persona (bot)
catalog:readLeer productos, precios y sucursales
orders:readVer pedidos
orders:writeCrear pedidos y cambiar su estado
flows:startArrancar flujos (bots) para un contacto
campaigns:readVer campañas y su avance
campaigns:writeCrear, enviar y cancelar campañas (envío masivo)
webhooks:manageSuscribir y quitar webhooks (Zapier, Make, n8n)

Paginación, límites y errores

  • Paginación: las listas devuelven { data, has_more, next_cursor }. Para la página siguiente manda ?cursor= con el next_cursor.
  • Límite: 120 peticiones por minuto por clave. Cada respuesta trae X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset.
  • Reintentos seguros: en los envíos manda Idempotency-Key con un valor único. Si tu sistema repite la petición en las 24 h siguientes, recibe la misma respuesta (con Idempotent-Replayed: true) y nada se envía dos veces.
  • Fechas en ISO 8601 (UTC) y teléfonos con código de país.
{ "error": "insufficient_scope", "message": "Esta clave no tiene el permiso «Ver pedidos» (orders:read)." }
StatuserrorQué significa
400invalid_json, invalid_…Falta un dato o tiene un formato inválido. message dice cuál.
401unauthorized, key_expiredFalta la clave, no existe, fue revocada o venció.
403insufficient_scopeLa clave no tiene el permiso que pide ese endpoint.
403plan_feature_missingEl plan de la empresa no incluye esa función.
404…_not_foundNo existe o es de otra empresa.
409chat_assigned, channel_unavailable, …El estado actual no lo permite (por ejemplo una persona ya atiende el chat).
422empty_audienceLa audiencia de la campaña no tiene a nadie a quien enviar.
429rate_limitedMás de 120 peticiones por minuto con la misma clave. Espera Retry-After segundos.
502delivery_failedWhatsApp rechazó el mensaje (queda guardado como fallido).

Cuenta

Para comprobar que una clave funciona (Zapier, Make y n8n lo usan al conectar).

GET/me

Ver la empresa y los permisos de la clave

Permiso: cualquier clave

curl -X GET "https://ankatickets.com/api/public/v1/me" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": {
    "company": {
      "id": "c0ffee00-0000-4000-8000-000000000000",
      "name": "Mi Empresa"
    },
    "key": {
      "id": "a1b2c3d4-0000-4000-8000-000000000001",
      "name": "Zapier",
      "prefix": "cmnk_AbCd",
      "scopes": [
        "contacts:read",
        "webhooks:manage"
      ],
      "expires_at": null
    }
  }
}

Contactos

Las personas que te escriben. Un contacto se identifica por su teléfono (con o sin «+», se comparan solo los dígitos).

GET/contacts

Listar contactos

Con sort=updated_asc y updated_after puedes sincronizar solo lo que cambió desde la última vez.

Permiso: contacts:read

ParámetroDescripción
limitCuántos por página (1 a 200, 50 por defecto).
cursorEl next_cursor de la página anterior.
qBusca en nombre, teléfono y correo.
phoneTeléfono exacto.
emailCorreo exacto.
tagNombre de una etiqueta.
stageId o nombre de la etapa.
created_afterFecha ISO 8601.
updated_afterFecha ISO 8601.
sortcreated_desc (por defecto) o updated_asc.
curl -X GET "https://ankatickets.com/api/public/v1/contacts" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
      "name": "Ana Pérez",
      "phone": "+18095550123",
      "email": "ana@ejemplo.com",
      "company_name": null,
      "country": "DO",
      "instagram_username": null,
      "opted_out": false,
      "blocked": false,
      "tags": [
        "VIP"
      ],
      "custom_fields": {
        "zona": "Piantini"
      },
      "stage": {
        "id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
        "name": "Cliente"
      },
      "created_at": "2026-10-20T13:00:00.000Z",
      "updated_at": "2026-10-28T15:04:05.000Z",
      "last_interaction_at": "2026-10-28T15:04:05.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
POST/contacts

Crear o actualizar un contacto

Busca por teléfono (o por correo). Si existe lo actualiza (200); si no, lo crea (201).

Permiso: contacts:write

  • phoneobligatorio — Teléfono con código de país.
  • name — Nombre.
  • email — Correo.
  • company_name / country — Empresa y país (código ISO).
  • custom_fields — Objeto con tus campos personalizados (ver /custom-fields).
  • stage — Id o nombre de la etapa del embudo.
  • tags — Lista de etiquetas para agregar (se crean si no existen).
  • opted_out / blocked — true o false.
  • note + source — Deja una nota interna en el contacto.
curl -X POST "https://ankatickets.com/api/public/v1/contacts" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "+18095550123",
  "name": "Ana Pérez",
  "email": "ana@ejemplo.com",
  "tags": [
    "VIP"
  ],
  "custom_fields": {
    "zona": "Piantini"
  }
}'
Respuesta 201
{
  "data": {
    "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
    "name": "Ana Pérez",
    "phone": "+18095550123",
    "email": "ana@ejemplo.com",
    "company_name": null,
    "country": "DO",
    "instagram_username": null,
    "opted_out": false,
    "blocked": false,
    "tags": [
      "VIP"
    ],
    "custom_fields": {
      "zona": "Piantini"
    },
    "stage": {
      "id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
      "name": "Cliente"
    },
    "created_at": "2026-10-20T13:00:00.000Z",
    "updated_at": "2026-10-28T15:04:05.000Z",
    "last_interaction_at": "2026-10-28T15:04:05.000Z"
  },
  "created": true
}
GET/contacts/{id}

Ver un contacto

Permiso: contacts:read

ParámetroDescripción
id(ruta)Id del contacto.
curl -X GET "https://ankatickets.com/api/public/v1/contacts/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": {
    "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
    "name": "Ana Pérez",
    "phone": "+18095550123",
    "email": "ana@ejemplo.com",
    "company_name": null,
    "country": "DO",
    "instagram_username": null,
    "opted_out": false,
    "blocked": false,
    "tags": [
      "VIP"
    ],
    "custom_fields": {
      "zona": "Piantini"
    },
    "stage": {
      "id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
      "name": "Cliente"
    },
    "created_at": "2026-10-20T13:00:00.000Z",
    "updated_at": "2026-10-28T15:04:05.000Z",
    "last_interaction_at": "2026-10-28T15:04:05.000Z"
  }
}
PATCH/contacts/{id}

Editar un contacto

Cambia solo lo que mandes; null borra el dato. custom_fields se une a los actuales y tags agrega.

Permiso: contacts:write

ParámetroDescripción
id(ruta)Id del contacto.
curl -X PATCH "https://ankatickets.com/api/public/v1/contacts/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Ana P.",
  "custom_fields": {
    "zona": "Naco"
  }
}'
Respuesta 200
{
  "data": {
    "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
    "name": "Ana Pérez",
    "phone": "+18095550123",
    "email": "ana@ejemplo.com",
    "company_name": null,
    "country": "DO",
    "instagram_username": null,
    "opted_out": false,
    "blocked": false,
    "tags": [
      "VIP"
    ],
    "custom_fields": {
      "zona": "Piantini"
    },
    "stage": {
      "id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
      "name": "Cliente"
    },
    "created_at": "2026-10-20T13:00:00.000Z",
    "updated_at": "2026-10-28T15:04:05.000Z",
    "last_interaction_at": "2026-10-28T15:04:05.000Z"
  }
}
DELETE/contacts/{id}

Borrar un contacto

Como en la pantalla: no se puede borrar si tiene conversaciones (409).

Permiso: contacts:write

ParámetroDescripción
id(ruta)Id del contacto.
curl -X DELETE "https://ankatickets.com/api/public/v1/contacts/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "deleted": true,
  "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01"
}
POST/contacts/{id}/tags

Agregar etiquetas

Permiso: contacts:write

ParámetroDescripción
id(ruta)Id del contacto.
curl -X POST "https://ankatickets.com/api/public/v1/contacts/ID/tags" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
  "tags": [
    "VIP",
    "Mayorista"
  ]
}'
Respuesta 200
{
  "data": {
    "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
    "name": "Ana Pérez",
    "phone": "+18095550123",
    "email": "ana@ejemplo.com",
    "company_name": null,
    "country": "DO",
    "instagram_username": null,
    "opted_out": false,
    "blocked": false,
    "tags": [
      "VIP"
    ],
    "custom_fields": {
      "zona": "Piantini"
    },
    "stage": {
      "id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
      "name": "Cliente"
    },
    "created_at": "2026-10-20T13:00:00.000Z",
    "updated_at": "2026-10-28T15:04:05.000Z",
    "last_interaction_at": "2026-10-28T15:04:05.000Z"
  }
}
DELETE/contacts/{id}/tags

Quitar etiquetas

Manda { "tags": [...] } en el cuerpo o ?tags=VIP,Mayorista.

Permiso: contacts:write

ParámetroDescripción
id(ruta)Id del contacto.
curl -X DELETE "https://ankatickets.com/api/public/v1/contacts/ID/tags" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
  "tags": [
    "Mayorista"
  ]
}'
Respuesta 200
{
  "data": {
    "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
    "name": "Ana Pérez",
    "phone": "+18095550123",
    "email": "ana@ejemplo.com",
    "company_name": null,
    "country": "DO",
    "instagram_username": null,
    "opted_out": false,
    "blocked": false,
    "tags": [
      "VIP"
    ],
    "custom_fields": {
      "zona": "Piantini"
    },
    "stage": {
      "id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
      "name": "Cliente"
    },
    "created_at": "2026-10-20T13:00:00.000Z",
    "updated_at": "2026-10-28T15:04:05.000Z",
    "last_interaction_at": "2026-10-28T15:04:05.000Z"
  }
}
GET/tags

Listar etiquetas

Permiso: contacts:read o contacts:write

curl -X GET "https://ankatickets.com/api/public/v1/tags" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
      "name": "VIP",
      "color": "#00a99d"
    }
  ]
}
GET/custom-fields

Listar campos personalizados

Permiso: contacts:read o contacts:write

curl -X GET "https://ankatickets.com/api/public/v1/custom-fields" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "key": "zona",
      "label": "Zona",
      "type": "text",
      "options": null
    }
  ]
}
GET/lifecycle-stages

Listar etapas del embudo

Permiso: contacts:read o contacts:write

curl -X GET "https://ankatickets.com/api/public/v1/lifecycle-stages" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
      "name": "Cliente",
      "kind": "won",
      "position": 2
    }
  ]
}

Mensajes

Enviar por WhatsApp o Instagram. En las líneas de la API oficial de Meta, fuera de la ventana de 24 h solo se puede mandar una plantilla aprobada.

POST/messages

Enviar un mensaje

Responde 200 si salió y 502 si WhatsApp lo rechazó (queda guardado como fallido). Si el contacto no existe, se crea.

Permiso: messages:send · acepta Idempotency-Key

  • to o contact_idobligatorio — Teléfono o id del contacto.
  • typeobligatorio — text, image, video, audio, document o template.
  • text — Texto (o pie de foto).
  • media_url / file_name — Archivo público para fotos, videos, audios y documentos.
  • template — { name, language, variables } para plantillas de Meta.
  • channel_id — Línea por la que sale (ver /channels). Si falta, la del último chat.
curl -X POST "https://ankatickets.com/api/public/v1/messages" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: un-valor-unico" \
  -d '{
  "to": "+18095550123",
  "type": "text",
  "text": "¡Tu pedido va en camino! 🛵"
}'
Respuesta 200
{
  "data": {
    "id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
    "conversation_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "contact": {
      "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
      "name": "Ana Pérez",
      "phone": "+18095550123"
    },
    "direction": "outbound",
    "sender_type": "system",
    "sender": null,
    "automated": false,
    "source": "api",
    "type": "text",
    "text": "Hola, ¿tienen delivery hoy?",
    "media": null,
    "status": "sent",
    "error": null,
    "created_at": "2026-10-28T15:04:05.000Z"
  },
  "ok": true,
  "message_id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
  "conversation_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
  "contact_id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
  "status": "sent"
}
GET/messages/{id}

Ver un mensaje y su estado

status: sent, delivered, read, failed (o received si lo mandó el cliente).

Permiso: messages:read

ParámetroDescripción
id(ruta)Id del mensaje.
curl -X GET "https://ankatickets.com/api/public/v1/messages/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": {
    "id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
    "conversation_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "contact": {
      "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
      "name": "Ana Pérez",
      "phone": "+18095550123"
    },
    "direction": "inbound",
    "sender_type": "contact",
    "sender": null,
    "automated": false,
    "source": null,
    "type": "text",
    "text": "Hola, ¿tienen delivery hoy?",
    "media": null,
    "status": "received",
    "error": null,
    "created_at": "2026-10-28T15:04:05.000Z"
  }
}
GET/channels

Listar líneas

Permiso: messages:send o messages:read

curl -X GET "https://ankatickets.com/api/public/v1/channels" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "type": "qr",
      "name": "Ventas",
      "phone": "+18095550000",
      "status": "connected"
    }
  ]
}
GET/templates

Listar plantillas aprobadas por Meta

Permiso: messages:send o messages:read

ParámetroDescripción
channel_idSolo las de una línea.
curl -X GET "https://ankatickets.com/api/public/v1/templates" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "name": "pedido_listo",
      "language": "es",
      "category": "UTILITY",
      "body_text": "Hola {{1}}, tu pedido {{2}} está listo.",
      "variables": [
        "1",
        "2"
      ],
      "variable_count": 2
    }
  ]
}

Campañas

Envíos masivos. Salen por lotes con pausas (como desde la pantalla) para cuidar tu número. Los contactos que respondieron BAJA nunca la reciben. El plan de la empresa tiene que incluir Campañas.

GET/campaigns

Listar campañas

Permiso: campaigns:read

ParámetroDescripción
limit1 a 100 (25 por defecto).
cursorEl next_cursor de la página anterior.
statusdraft, scheduled, sending o completed.
curl -X GET "https://ankatickets.com/api/public/v1/campaigns" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
      "name": "Promo viernes",
      "status": "sending",
      "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "template": null,
      "message": "Hola {{nombre}}, este viernes el delivery es gratis 🎉",
      "audience": {
        "type": "tag",
        "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
      },
      "created_via": "api",
      "recipients": {
        "total": 120,
        "pending": 100,
        "sent": 19,
        "failed": 1
      },
      "batch_size": 20,
      "batch_delay_seconds": 60,
      "scheduled_at": "2026-10-28T15:04:05.000Z",
      "completed_at": null,
      "created_at": "2026-10-28T15:04:05.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
POST/campaigns

Crear una campaña

Queda en borrador, programada (scheduled_at) o saliendo ya (send_now: true, responde 202). Por QR se manda message, que acepta {{nombre}}, {{telefono}}, {{empresa}} y tus campos personalizados. Por la API oficial se manda template_id y variables.

Permiso: campaigns:write · acepta Idempotency-Key

  • nameobligatorio — Nombre interno.
  • channel_id — Línea. Con plantilla puede faltar (se usa la de la plantilla).
  • message — Texto (líneas por QR).
  • template_id + variables — Plantilla aprobada (líneas de la API oficial).
  • audienceobligatorio — { type: "all" }, { type: "tag" | "department" | "segment", id } o { type: "contacts", contact_ids, phones } (hasta 1000; los teléfonos nuevos se crean como contacto).
  • scheduled_at — Fecha ISO 8601 futura.
  • send_now — true para enviarla ya.
  • batch_size / batch_delay_seconds — Tamaño del lote (1 a 500, 20) y espera entre lotes (0 a 3600 s, 60).
curl -X POST "https://ankatickets.com/api/public/v1/campaigns" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: un-valor-unico" \
  -d '{
  "name": "Promo viernes",
  "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
  "message": "Hola {{nombre}}, este viernes el delivery es gratis 🎉",
  "audience": {
    "type": "contacts",
    "phones": [
      "+18095550123",
      "+18095550124"
    ]
  },
  "send_now": true
}'
Respuesta 202
{
  "data": {
    "id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "Promo viernes",
    "status": "sending",
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "template": null,
    "message": "Hola {{nombre}}, este viernes el delivery es gratis 🎉",
    "audience": {
      "type": "tag",
      "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
    },
    "created_via": "api",
    "recipients": {
      "total": 120,
      "pending": 100,
      "sent": 19,
      "failed": 1
    },
    "batch_size": 20,
    "batch_delay_seconds": 60,
    "scheduled_at": "2026-10-28T15:04:05.000Z",
    "completed_at": null,
    "created_at": "2026-10-28T15:04:05.000Z"
  }
}
GET/campaigns/{id}

Ver una campaña y su avance

Permiso: campaigns:read

ParámetroDescripción
id(ruta)Id de la campaña.
curl -X GET "https://ankatickets.com/api/public/v1/campaigns/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": {
    "id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "Promo viernes",
    "status": "sending",
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "template": null,
    "message": "Hola {{nombre}}, este viernes el delivery es gratis 🎉",
    "audience": {
      "type": "tag",
      "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
    },
    "created_via": "api",
    "recipients": {
      "total": 120,
      "pending": 100,
      "sent": 19,
      "failed": 1
    },
    "batch_size": 20,
    "batch_delay_seconds": 60,
    "scheduled_at": "2026-10-28T15:04:05.000Z",
    "completed_at": null,
    "created_at": "2026-10-28T15:04:05.000Z"
  }
}
POST/campaigns/{id}/send

Enviar ya un borrador o una programada

202. Si la línea está desconectada responde 409 channel_unavailable y no envía nada.

Permiso: campaigns:write

ParámetroDescripción
id(ruta)Id de la campaña.
curl -X POST "https://ankatickets.com/api/public/v1/campaigns/ID/send" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 202
{
  "data": {
    "id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "Promo viernes",
    "status": "sending",
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "template": null,
    "message": "Hola {{nombre}}, este viernes el delivery es gratis 🎉",
    "audience": {
      "type": "tag",
      "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
    },
    "created_via": "api",
    "recipients": {
      "total": 120,
      "pending": 100,
      "sent": 19,
      "failed": 1
    },
    "batch_size": 20,
    "batch_delay_seconds": 60,
    "scheduled_at": "2026-10-28T15:04:05.000Z",
    "completed_at": null,
    "created_at": "2026-10-28T15:04:05.000Z"
  }
}
POST/campaigns/{id}/cancel

Cancelar una programada

Vuelve a borrador. Una campaña que ya está saliendo no se puede cancelar (409).

Permiso: campaigns:write

ParámetroDescripción
id(ruta)Id de la campaña.
curl -X POST "https://ankatickets.com/api/public/v1/campaigns/ID/cancel" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": {
    "id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "Promo viernes",
    "status": "draft",
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "template": null,
    "message": "Hola {{nombre}}, este viernes el delivery es gratis 🎉",
    "audience": {
      "type": "tag",
      "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
    },
    "created_via": "api",
    "recipients": {
      "total": 120,
      "pending": 100,
      "sent": 19,
      "failed": 1
    },
    "batch_size": 20,
    "batch_delay_seconds": 60,
    "scheduled_at": null,
    "completed_at": null,
    "created_at": "2026-10-28T15:04:05.000Z"
  }
}

Pedidos

Los pedidos que toma el asistente o tu equipo.

GET/orders

Listar pedidos

Permiso: orders:read

ParámetroDescripción
limitCuántos por página (1 a 200, 50 por defecto).
cursorEl next_cursor de la página anterior.
statuspending, preparing, shipped, delivered o cancelled.
branch_idId de la sucursal.
created_afterFecha ISO 8601.
updated_afterFecha ISO 8601.
curl -X GET "https://ankatickets.com/api/public/v1/orders" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
      "number": "ANK-10234",
      "status": "pending",
      "items": "2x Empanada de pollo\n1x Jugo de chinola",
      "notes": null,
      "customer": {
        "name": "Ana Pérez",
        "phone": "18095550123"
      },
      "delivery": {
        "type": "delivery",
        "address": "Calle Ficticia 12, Piantini",
        "reference": "Edificio azul",
        "fee": 50
      },
      "payment_method": "Efectivo",
      "total": 410,
      "branch": {
        "id": "6e5d4c3b-2a19-4087-b6a5-948372615049",
        "name": "Sucursal Centro"
      },
      "driver": null,
      "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "created_at": "2026-10-28T15:04:05.000Z",
      "updated_at": "2026-10-28T15:04:05.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/orders/{id}

Ver un pedido

Permiso: orders:read

ParámetroDescripción
id(ruta)Id del pedido.
curl -X GET "https://ankatickets.com/api/public/v1/orders/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": {
    "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "number": "ANK-10234",
    "status": "pending",
    "items": "2x Empanada de pollo\n1x Jugo de chinola",
    "notes": null,
    "customer": {
      "name": "Ana Pérez",
      "phone": "18095550123"
    },
    "delivery": {
      "type": "delivery",
      "address": "Calle Ficticia 12, Piantini",
      "reference": "Edificio azul",
      "fee": 50
    },
    "payment_method": "Efectivo",
    "total": 410,
    "branch": {
      "id": "6e5d4c3b-2a19-4087-b6a5-948372615049",
      "name": "Sucursal Centro"
    },
    "driver": null,
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "created_at": "2026-10-28T15:04:05.000Z",
    "updated_at": "2026-10-28T15:04:05.000Z"
  }
}
PATCH/orders/{id}

Cambiar el estado o el motorista

Cambiar el estado le avisa al cliente por WhatsApp, igual que desde la pantalla Pedidos.

Permiso: orders:write

ParámetroDescripción
id(ruta)Id del pedido.
curl -X PATCH "https://ankatickets.com/api/public/v1/orders/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "shipped",
  "driver_id": null
}'
Respuesta 200
{
  "data": {
    "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "number": "ANK-10234",
    "status": "shipped",
    "items": "2x Empanada de pollo\n1x Jugo de chinola",
    "notes": null,
    "customer": {
      "name": "Ana Pérez",
      "phone": "18095550123"
    },
    "delivery": {
      "type": "delivery",
      "address": "Calle Ficticia 12, Piantini",
      "reference": "Edificio azul",
      "fee": 50
    },
    "payment_method": "Efectivo",
    "total": 410,
    "branch": {
      "id": "6e5d4c3b-2a19-4087-b6a5-948372615049",
      "name": "Sucursal Centro"
    },
    "driver": null,
    "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "created_at": "2026-10-28T15:04:05.000Z",
    "updated_at": "2026-10-28T15:04:05.000Z"
  }
}
POST/orders

Crear o actualizar un pedido

Idempotente por numeroOrden: reenviar el mismo número actualiza el pedido en vez de duplicarlo.

Permiso: orders:write

  • numeroOrdenobligatorio — Tu número de pedido.
  • pedidoobligatorio — Detalle de lo pedido.
  • nombre / telefonoobligatorio — Cliente.
  • direccionEnvio / referenciaOcomentario — Entrega.
  • efectivoTarjeta — Forma de pago.
  • sucursal — Nombre de la sucursal.
  • status — Estado inicial (pending por defecto).
curl -X POST "https://ankatickets.com/api/public/v1/orders" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
  "numeroOrden": "WEB-5531",
  "pedido": "2x Empanada de pollo",
  "nombre": "Ana Pérez",
  "telefono": "18095550123",
  "direccionEnvio": "Calle Ficticia 12",
  "efectivoTarjeta": "Tarjeta",
  "sucursal": "Sucursal Centro"
}'
Respuesta 200
{
  "ok": true,
  "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
  "numero_orden": "WEB-5531"
}

Flujos (bot)

Arranca un flujo de Automatizaciones → Flujos para un contacto: por ejemplo «se pagó el pedido» y el bot le escribe.

GET/flows

Listar flujos

Permiso: flows:start

curl -X GET "https://ankatickets.com/api/public/v1/flows" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "f1e2d3c4-b5a6-4978-8a6b-5c4d3e2f1a0b",
      "name": "Aviso de pago",
      "active": true,
      "trigger": "webhook"
    }
  ]
}
POST/flows/{id}/start

Arrancar un flujo para un contacto

fields guarda campos personalizados antes de arrancar, para usarlos en los mensajes. Si una persona del equipo ya atiende ese chat responde 409.

Permiso: flows:start · acepta Idempotency-Key

ParámetroDescripción
id(ruta)Id del flujo.
curl -X POST "https://ankatickets.com/api/public/v1/flows/ID/start" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: un-valor-unico" \
  -d '{
  "phone": "+18095550123",
  "name": "Ana",
  "fields": {
    "pedido": "#77"
  }
}'
Respuesta 200
{
  "ok": true,
  "conversation_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
  "contact_id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
  "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
}

Webhooks (suscripciones)

Para que otro sistema se suscriba solo a los avisos (REST hooks). Es lo que usan Zapier, Make y n8n en sus disparadores instantáneos. Los webhooks también se crean a mano en Integraciones → Webhooks.

POST/webhooks

Suscribir una URL

Responde 201 con el secret para verificar la firma (solo se muestra esta vez). Máximo 50 webhooks por empresa.

Permiso: webhooks:manage

  • url (o target_url)obligatorio — Dirección pública https que recibirá los avisos.
  • events (o event)obligatorio — Lista de eventos; ["*"] = todos.
  • label — Nombre para reconocerlo en el panel.
curl -X POST "https://ankatickets.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer cmnk_TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://hooks.zapier.com/hooks/standard/123/abc",
  "events": [
    "order.created"
  ],
  "label": "Zap: pedidos a Sheets"
}'
Respuesta 201
{
  "data": {
    "id": "d4c3b2a1-0f9e-4d8c-b7a6-9f8e7d6c5b4a",
    "url": "https://hooks.zapier.com/hooks/standard/123/abc",
    "events": [
      "order.created"
    ],
    "active": true,
    "label": "Zap: pedidos a Sheets",
    "created_via": "api",
    "created_at": "2026-10-28T15:04:05.000Z",
    "secret": "9f2c…(64 caracteres)"
  }
}
GET/webhooks

Listar webhooks

Permiso: webhooks:manage

curl -X GET "https://ankatickets.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "d4c3b2a1-0f9e-4d8c-b7a6-9f8e7d6c5b4a",
      "url": "https://hooks.zapier.com/hooks/standard/123/abc",
      "events": [
        "order.created"
      ],
      "active": true,
      "label": "Zap: pedidos a Sheets",
      "created_via": "api",
      "created_at": "2026-10-28T15:04:05.000Z"
    }
  ]
}
GET/webhooks/{id}

Ver un webhook

Permiso: webhooks:manage

ParámetroDescripción
id(ruta)Id del webhook.
curl -X GET "https://ankatickets.com/api/public/v1/webhooks/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
DELETE/webhooks/{id}

Quitar una suscripción

204. Solo los creados por la API; los del panel se borran en el panel (403).

Permiso: webhooks:manage

ParámetroDescripción
id(ruta)Id del webhook.
curl -X DELETE "https://ankatickets.com/api/public/v1/webhooks/ID" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
GET/webhooks/samples

Aviso de ejemplo de un evento

Datos ficticios con la misma forma que los reales, para mapear campos antes del primer aviso.

Permiso: webhooks:manage

ParámetroDescripción
eventPor ejemplo order.created.
curl -X GET "https://ankatickets.com/api/public/v1/webhooks/samples" \
  -H "Authorization: Bearer cmnk_TU_CLAVE"
Respuesta 200
{
  "data": [
    {
      "id": "e0e0e0e0-0000-4000-8000-000000000015",
      "event": "order.created",
      "created_at": "2026-10-28T15:04:05.000Z",
      "company_id": "c0ffee00-0000-4000-8000-000000000000",
      "data": {
        "order": {
          "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
          "number": "ANK-10234",
          "status": "pending",
          "items": "2x Empanada de pollo\n1x Jugo de chinola",
          "notes": null,
          "customer": {
            "name": "Ana Pérez",
            "phone": "18095550123"
          },
          "delivery": {
            "type": "delivery",
            "address": "Calle Ficticia 12, Piantini",
            "reference": "Edificio azul",
            "fee": 50
          },
          "payment_method": "Efectivo",
          "total": 410,
          "branch": {
            "id": "6e5d4c3b-2a19-4087-b6a5-948372615049",
            "name": "Sucursal Centro"
          },
          "driver": null,
          "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
          "created_at": "2026-10-28T15:04:05.000Z",
          "updated_at": "2026-10-28T15:04:05.000Z"
        }
      }
    }
  ]
}

Webhooks: eventos y firma

Cuando pasa algo, Anka hace un POST con JSON a tu URL. Los creas en Integraciones → Webhooks o por la API (POST /webhooks). Responde con un 2xx en menos de 10 segundos; si no, se reintenta a los 1 min, 5 min, 30 min, 2 h, 6 h, 12 h y después queda como fallido (lo puedes reenviar a mano desde el panel). El id se repite en los reintentos: úsalo para no procesar dos veces el mismo aviso.

{
  "id": "e0e0e0e0-0000-4000-8000-000000000001",
  "event": "message.received",
  "created_at": "2026-10-28T15:04:05.000Z",
  "company_id": "c0ffee00-0000-4000-8000-000000000000",
  "data": {
    "message": {
      "id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
      "conversation_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
      "channel_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "contact": {
        "id": "8c1f6a52-3b7e-4d0a-9f21-5a6b7c8d9e01",
        "name": "Ana Pérez",
        "phone": "+18095550123"
      },
      "direction": "inbound",
      "sender_type": "contact",
      "sender": null,
      "automated": false,
      "source": null,
      "type": "text",
      "text": "Hola, ¿tienen delivery hoy?",
      "media": null,
      "status": "received",
      "error": null,
      "created_at": "2026-10-28T15:04:05.000Z"
    }
  }
}
EventoCuándo llega
message.receivedMensaje recibido
message.sentMensaje enviado (agente, bot o API)
message.statusCambió el estado de un mensaje (entregado, leído, fallido)
conversation.createdConversación nueva
conversation.assignedConversación asignada
conversation.transferredConversación transferida de departamento
conversation.resolvedConversación terminada
conversation.reopenedConversación reabierta
contact.createdContacto nuevo
contact.updatedContacto actualizado
contact.deletedContacto eliminado
contact.tag_addedEtiqueta agregada
contact.tag_removedEtiqueta quitada
contact.stage_changedContacto cambió de etapa
order.createdPedido nuevo
order.status_changedCambió el estado de un pedido
payment.paidSe pagó un link de pago (Cobros)
csat.respondedRespuesta de la encuesta de satisfacción

Encabezados

  • X-Anka-Event, X-Anka-Event-Id y X-Anka-Delivery-Id.
  • X-Anka-Signature: t=1761663845,v1=5f8a…: v1 es el HMAC-SHA256, con el secreto del webhook, de <t>.<cuerpo>. Rechaza los avisos con más de 5 minutos para que nadie pueda repetir uno viejo.

Node.js

import crypto from "crypto";

// rawBody: el cuerpo tal cual llegó (string), sin volver a convertirlo a JSON.
export function avisoValido(rawBody, header, secret) {
  const partes = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(partes.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // más de 5 min: se descarta
  const esperado = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(partes.v1 ?? ""));
}

PHP

<?php
function aviso_valido(string $rawBody, string $header, string $secret): bool {
  parse_str(str_replace(',', '&', $header), $p);
  $t = intval($p['t'] ?? 0);
  if (!$t || abs(time() - $t) > 300) return false;
  $esperado = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
  return hash_equals($esperado, $p['v1'] ?? '');
}

Python

import hashlib, hmac, time

def aviso_valido(raw_body: bytes, header: str, secret: str) -> bool:
    p = dict(x.split("=", 1) for x in header.split(","))
    t = int(p.get("t", 0))
    if not t or abs(time.time() - t) > 300:
        return False
    esperado = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperado, p.get("v1", ""))

URL de un flujo (sin clave)

En Automatizaciones → Flujos, un flujo con inicio «Desde otro sistema (URL o API)» tiene su propia URL secreta (botón «Copiar URL para tu otro sistema»). Llamarla arranca el flujo para ese teléfono; la URL es la credencial, así que no lleva clave de API. Admite 60 llamadas por minuto.

curl -X POST https://ankatickets.com/api/public/hooks/flows/TOKEN_DEL_FLUJO \
  -H "Content-Type: application/json" \
  -d '{ "phone": "+18095550123", "name": "Ana", "fields": { "pedido": "#77" } }'

Zapier, Make y n8n

Con los webhooks y la API puedes conectar Anka con más de 6.000 aplicaciones sin programar. Los tres funcionan igual: un disparador que recibe los avisos de Anka y acciones que llaman a la API.

Zapier

  1. Disparador: elige Webhooks by Zapier → Catch Hook y copia la URL que te da.
  2. En Anka, Integraciones → Webhooks → Nuevo: pega la URL y marca los eventos (por ejemplo «Pedido nuevo»). Toca «Probar» para que Zapier vea un ejemplo.
  3. Acción hacia Anka: Webhooks by Zapier → Custom Request, con la URL del endpoint (por ejemplo https://ankatickets.com/api/public/v1/messages), el encabezado Authorization: Bearer cmnk_… y el cuerpo JSON.

Make

  1. Disparador: módulo Webhooks → Custom webhook; copia la dirección y regístrala en Anka como arriba. Con «Probar» Make aprende la estructura.
  2. Acción: módulo HTTP → Make a request, método y URL del endpoint, el encabezado Authorization y el cuerpo en JSON.

n8n

  1. Disparador: nodo Webhook (método POST). Registra en Anka la «Production URL». Si quieres verificar la firma, agrega un nodo Code con el ejemplo de Node.js de arriba.
  2. Acción: nodo HTTP Request con una credencial Header Auth (Authorization = Bearer cmnk_…). También puedes pegar cualquier ejemplo curl de esta página con «Import cURL».

¿Tu herramienta se suscribe sola a los avisos (REST hooks)? Usa POST /webhooks al activar y DELETE /webhooks/{id} al apagar, y GET /webhooks/samples para mostrar un ejemplo antes del primer aviso real.