Desarrolladores

API

Con la API utiliza sus bots desde su propio software, como una intranet, una aplicación o un CRM. El bot responde igual que en el chat de su sitio web: con su propio contenido, ubicaciones, horarios de apertura, reglas y flujos de opciones.

Autenticación

Cree una clave en el portal, en Conexiones → Claves API. Asígnele un nombre y elija si tiene acceso a todos los bots de su cuenta (también los que añada más adelante) o a uno o varios bots, y si puede crear bots. La clave solo se muestra una vez. Puede crear tantas como quiera y revocar cada una por separado.

Envíe la clave en la cabecera de cada solicitud:

Authorization: Bearer cop_…
Una clave API debe estar en su servidor. No la ponga nunca en código que llegue a sus visitantes a través de un navegador o una aplicación. Para su sitio web, utilice el widget normal.

Comprobar la clave

GET /key

Devuelve el nombre de la clave, la cuenta y los bots a los que tiene acceso. Útil para comprobar que todo funciona.

{
  "name": "Intranet",
  "account": "Kindercentrum",
  "all_bots": false,
  "bots": [{ "id": 24, "name": "Kindercentrum" }]
}

Consultar bots

GET /bots

Todos los bots a los que tiene acceso esta clave. Los bots en pausa no aparecen. Con el id elige el bot en las siguientes llamadas.

{
  "data": [
    {
      "id": 24,
      "name": "Kindercentrum",
      "site_url": "https://www.kindercentrum.be",
      "status": "active",
      "default_locale": "nl",
      "locales": ["nl", "fr"]
    }
  ]
}

Un bot

GET /bots/{id}

Los mismos datos, además del saludo, los botones de inicio y las ubicaciones del bot.

{
  "data": {
    "id": 24,
    "name": "Kindercentrum",
    …
    "greeting": "Hallo! Ik ben de assistent van Kindercentrum. Waarmee kan ik u helpen?",
    "start_choices": ["Inschrijven", "Openingsuren"],
    "offices": [{ "id": 7, "name": "Biezebos", "city": "Gent" }],
    "progress": { "percent": 100, "status": "Klaar", "ready": true, "pages": 84 }
  }
}

Crear bot

POST /bots

Crea un bot nuevo para un sitio web, como Nuevo bot en el portal: el sitio se lee enseguida, se adoptan los colores y el icono, y quien creó la clave recibe un correo en cuanto el bot está listo para probar. Siga el progreso con GET /bots/{id} (progress).

Solo con una clave que tenga activado Puede crear bots. Cada bot tiene su propio plan: si el nuevo bot cuesta más, primero recibe 402 con el precio y vuelve a enviar la solicitud con "confirm_price": true. Como máximo 10 bots nuevos por hora y clave.

CampoTipoSignificado
nametextoNombre del bot, máx. 100 caracteres. Obligatorio.
site_urltextoSitio web que lee el bot, por ejemplo www.suempresa.es. Obligatorio.
default_localetextoIdioma principal: nl, en, fr, de o es. Por defecto el idioma de su cuenta.
localeslistaIdiomas adicionales del bot. Los idiomas que el bot encuentra en el sitio se añaden solos.
plantextolite, plus, pro o business. Sin plan elegimos el plan más pequeño que se ajuste al sitio.
support_emailtextoDirección para las preguntas que el bot reenvía; por defecto la dirección de soporte de su cuenta.
auto_approvesí/noUsar enseguida las páginas nuevas y modificadas (sí por defecto) o aprobarlas primero en el portal.
for_customersí/noSolo para socios: un bot para un cliente, que no entra en su propia suscripción.
confirm_pricesí/noConfirma el precio si el bot cuesta más.
POST /bots
{ "name": "Webshop", "site_url": "www.webshop.be", "plan": "pro", "confirm_price": true }

201 Created
{
  "data": {
    "id": 31,
    "name": "Webshop",
    "site_url": "https://www.webshop.be/",
    "status": "active",
    …
    "progress": { "percent": 10, "status": "Website wordt gelezen", "ready": false, "pages": 0 },
    "embed_code": "<script src="https://chatonpage.com/widget.js" data-key="pk_…" async></script>",
    "portal_url": "https://chatonpage.com/app/…/bots/31/edit"
  }
}

Hacer una pregunta

POST /bots/{id}/messages

Envía un mensaje al bot y devuelve la respuesta. Sin conversation se inicia una nueva conversación. Envíe la conversation de la respuesta en la siguiente pregunta y el bot recordará de qué trata la conversación.

CampoTipoSignificado
messagetextoLa pregunta o la respuesta del usuario, máx. 4.000 caracteres.
conversation opcionaltextoId de una conversación iniciada con esta clave.
locale opcionaltextoIdioma de una nueva conversación (nl, fr, en, …). Por defecto, el idioma del bot. Si el usuario responde en otro idioma, el bot lo sigue.
office_id opcionalnúmeroUbicación a la que se refiere una nueva conversación.
user opcionaltextoSu propio identificador para el usuario, máx. 40 caracteres. Así encontrará sus conversaciones en el portal. Sin nombres ni direcciones de correo electrónico.
stream opcionalbooleantrue para recibir la respuesta palabra por palabra. Consulte Streaming.
{
  "conversation": "9f0c2a4e-6b1d-4c3e-9a57-2f1e8d7c6b5a",
  "reply": {
    "message_id": 18342,
    "content": "Voor welk type opvang wil je inschrijven?",
    "sources": [{ "title": "Inschrijven", "url": "https://www.kindercentrum.be/inschrijven" }],
    "choices": ["Kinderdagverblijf", "Buitenschoolse opvang (BKO)", "Gezinsopvang"],
    "office_options": [],
    "escalated": false
  }
}

Streaming

Con "stream": true recibe un text/event-stream (Server-Sent Events), para mostrar la respuesta mientras el bot la escribe:

event: start
data: {"conversation":"9f0c2a4e-…"}

event: delta
data: {"text":"Voor welk type "}

event: delta
data: {"text":"opvang wil je inschrijven?"}

event: done
data: {"conversation":"9f0c2a4e-…","reply":{ …zoals hierboven… }}

Si algo falla por el camino, se envía event: error con un message.

Obtener conversación

GET /bots/{id}/conversations/{conversation}

Todos los mensajes de una conversación iniciada con esta clave.

{
  "data": {
    "id": "9f0c2a4e-…",
    "bot_id": 24,
    "locale": "nl",
    "office_id": null,
    "started_at": "2026-10-17T10:12:03+02:00",
    "messages": [
      { "id": 18341, "role": "user", "content": "Ik wil mijn kind inschrijven", "choices": [], "at": "…" },
      { "id": 18342, "role": "assistant", "content": "Voor welk type opvang …", "choices": ["Kinderdagverblijf", …], "at": "…" }
    ]
  }
}

Reenviar leads

Si un visitante solicita ser contactado, el bot creará un lead con nombre, dirección de correo electrónico, número de teléfono, pregunta y resumen. Con webhooks, cada nuevo lead se enviará directamente a su CRM, Zapier, Make o software propio. También puede recuperar leads y eventos usted mismo.

Los leads contienen datos de contacto de los visitantes. Por lo tanto, las llamadas a continuación solo funcionan con una clave en la que Acceso a leads esté activado y solo para los bots de esa clave. Sin ese acceso, recibirá un 403.

Webhooks

Cree un webhook en el portal en Conexiones → Webhooks o a través de la API. Elija la URL, los eventos y los bots. Con cada evento, enviamos un POST con JSON a esa URL.

EventoCuándo
lead.createdUn visitante solicitó ser contactado.
lead.updatedEl seguimiento de un lead cambió (new, contacted, closed), en el portal o mediante la API. Con previous_status.
report.createdEl bot registró una queja o un reporte de error. Cinco minutos después, para incluir las adiciones del visitante.
POST https://jouw-crm.example.com/hooks/chatonpage
Content-Type: application/json
X-ChatOnPage-Event: lead.created
X-ChatOnPage-Event-Id: 5821
X-ChatOnPage-Delivery: 9310
X-ChatOnPage-Signature: t=1792923600,v1=5f2b…

{
  "id": 5821,
  "type": "lead.created",
  "created_at": "2026-10-25T14:02:11+02:00",
  "account": { "id": 3, "name": "Kindercentrum" },
  "bot": { "id": 24, "name": "Kindercentrum" },
  "data": {
    "lead": {
      "id": 1288,
      "status": "new",
      "name": "An Peeters",
      "email": "an@example.com",
      "phone": "+32 470 12 34 56",
      "question": "Is er nog plaats in de BKO vanaf januari?",
      "summary": "Ouder zoekt opvang voor twee kinderen vanaf januari.",
      "language": "nl",
      "office": { "id": 7, "name": "Biezebos" },
      "conversation_id": "9f0c2a4e-6b1d-4c3e-9a57-2f1e8d7c6b5a",
      "portal_url": "https://chatonpage.com/app/kindercentrum/leads",
      "created_at": "2026-10-25T14:02:09+02:00",
      "updated_at": "2026-10-25T14:02:09+02:00"
    }
  }
}

Verificar la firma

Cada webhook tiene un secreto (whsec_…), visible en el portal bajo Secreto. El encabezado X-ChatOnPage-Signature contiene t (marca de tiempo, segundos Unix) y v1: el HMAC-SHA256 de t, un punto y el cuerpo en bruto, usando el secreto como clave. Compárelos y rechace los mensajes si t difiere más de 5 minutos.

// Node.js (Express): app.post('/hooks/chatonpage', express.raw({ type: 'application/json' }), …)
const crypto = require('crypto');
const { t, v1 } = Object.fromEntries(req.get('X-ChatOnPage-Signature').split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', process.env.CHATONPAGE_WEBHOOK_SECRET).update(`${t}.${req.body}`).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now() / 1000 - t) < 300;
# PHP
[$t, $v1] = sscanf($_SERVER['HTTP_X_CHATONPAGE_SIGNATURE'], 't=%d,v1=%s');
$body = file_get_contents('php://input');
$valid = hash_equals(hash_hmac('sha256', $t.'.'.$body, getenv('CHATONPAGE_WEBHOOK_SECRET')), $v1) && abs(time() - $t) < 300;

Obtener eventos

GET /events

Los mismos eventos que en los webhooks, primero los más antiguos, para quienes prefieren recopilarlos ellos mismos (por ejemplo, cada 5 minutos). Guarde next_after y envíelo como after: así solo recibirá lo nuevo. Los eventos se conservan durante 30 días.

ParámetroSignificado
after opcionalSolo eventos con un ID superior.
type opcionalUno o más eventos, separados por comas, por ejemplo lead.created.
bot opcionalSolo eventos de este bot.
limit opcionalDe 1 a 100, por defecto 50.
GET /events?type=lead.created&after=5820

{
  "data": [{ "id": 5821, "type": "lead.created", "created_at": "…", "account": { … }, "bot": { … }, "data": { "lead": { … } } }],
  "next_after": 5821
}

Leads

GET /leads

Leads de los bots de la clave, primero los más recientes. Filtre con status (new, contacted, closed), bot y since (fecha u hora, ISO 8601). Si hay una página siguiente, envíe next_before como before.

GET /leads/{id}

Un lead, con los mismos campos que en lead.created, más bot.

PATCH /leads/{id}

Actualiza el seguimiento, por ejemplo, {"status": "contacted"} cuando el lead esté en su CRM. Esto también se mostrará en el portal y generará un evento lead.updated.

Webhooks a través de la API

Así es como las plataformas de automatización como Zapier y Make se conectan y desconectan. Una clave solo ve los webhooks que ha creado. Si la clave se revoca, sus webhooks también desaparecerán.

POST /webhooks
CampoTipoSignificado
urltextoDirección https pública. Obligatorio.
events opcionallistaPor defecto, todos los eventos.
bots opcionallistaIDs de los bots. Por defecto, los bots asociados a la clave.
name opcionaltextoNombre en el portal. Por defecto, el nombre de la clave.
POST /webhooks
{ "url": "https://hooks.zapier.com/hooks/standard/123/abc", "events": ["lead.created"] }

201 Created
{
  "data": {
    "id": 12,
    "name": "Zapier",
    "url": "https://hooks.zapier.com/hooks/standard/123/abc",
    "events": ["lead.created"],
    "bots": "all",
    "active": true,
    "disabled_reason": null,
    "created_at": "…",
    "secret": "whsec_…"
  }
}

El secreto solo aparece en esta respuesta. Además: GET /webhooks (lista), DELETE /webhooks/{id} (darse de baja) y POST /webhooks/{id}/test (evento de prueba). Como máximo 25 webhooks por cuenta.

Opciones

En un flujo de opciones (por ejemplo, una inscripción), el bot devuelve choices. Muéstrelas como botones y envíe el texto elegido tal cual como siguiente message, con la misma conversation. El usuario también puede escribir otra cosa: el bot entenderá por sí mismo si es una opción o una nueva pregunta.

Errores y límites

En caso de error recibirá un estado HTTP con {"error": "…"}. Si hay campos no válidos (422), los detalles figuran en errors.

EstadoSignificado
401Clave ausente, no válida o revocada.
402La cuenta no está activa (por ejemplo, terminó la prueba) o, en POST /bots: hay que confirmar el precio ("code": "price_confirmation_required").
403Esta clave no puede crear bots o no tiene acceso a leads.
404El bot, la conversación, el lead o el webhook no existen, o esta clave no tiene acceso a ellos.
422Falta un campo o no es válido.
429Demasiadas preguntas: máximo 60 por minuto por clave, más el límite diario por bot.

Cada respuesta cuenta para las respuestas de su suscripción, igual que en el chat de su sitio web. Las conversaciones a través de la API aparecen en el portal en Conversaciones.

Ejemplos

curl

curl https://chatonpage.com/api/v1/bots \
  -H "Authorization: Bearer $CHATONPAGE_KEY"

curl https://chatonpage.com/api/v1/bots/24/messages \
  -H "Authorization: Bearer $CHATONPAGE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Wat zijn de openingsuren in Gent?"}'

JavaScript (Node.js 18+)

const res = await fetch('https://chatonpage.com/api/v1/bots/24/messages', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATONPAGE_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ message: 'Wat zijn de openingsuren in Gent?', conversation }),
});
const { conversation: id, reply } = await res.json();
console.log(reply.content);

Python

import os, requests

r = requests.post(
    "https://chatonpage.com/api/v1/bots/24/messages",
    headers={"Authorization": f"Bearer {os.environ['CHATONPAGE_KEY']}"},
    json={"message": "Wat zijn de openingsuren in Gent?"},
    timeout=60,
)
data = r.json()
print(data["reply"]["content"])
conversation = data["conversation"]  # meesturen bij de volgende vraag