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.
- URL base:
https://chatonpage.com/api/v1 - Solicitudes y respuestas en JSON (UTF-8). Envíe
Accept: application/json. - ¿Trabaja con un asistente de IA como ChatGPT o Claude? Utilice entonces el servidor MCP.
- ¿Nuevos leads directamente en su CRM, Zapier o Make? Utilice webhooks.
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_…
Comprobar la clave
/keyDevuelve 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
/botsTodos 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
/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
/botsCrea 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.
| Campo | Tipo | Significado |
|---|---|---|
name | texto | Nombre del bot, máx. 100 caracteres. Obligatorio. |
site_url | texto | Sitio web que lee el bot, por ejemplo www.suempresa.es. Obligatorio. |
default_locale | texto | Idioma principal: nl, en, fr, de o es. Por defecto el idioma de su cuenta. |
locales | lista | Idiomas adicionales del bot. Los idiomas que el bot encuentra en el sitio se añaden solos. |
plan | texto | lite, plus, pro o business. Sin plan elegimos el plan más pequeño que se ajuste al sitio. |
support_email | texto | Dirección para las preguntas que el bot reenvía; por defecto la dirección de soporte de su cuenta. |
auto_approve | sí/no | Usar enseguida las páginas nuevas y modificadas (sí por defecto) o aprobarlas primero en el portal. |
for_customer | sí/no | Solo para socios: un bot para un cliente, que no entra en su propia suscripción. |
confirm_price | sí/no | Confirma 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
/bots/{id}/messagesEnví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.
| Campo | Tipo | Significado |
|---|---|---|
message | texto | La pregunta o la respuesta del usuario, máx. 4.000 caracteres. |
conversation opcional | texto | Id de una conversación iniciada con esta clave. |
locale opcional | texto | Idioma 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 opcional | número | Ubicación a la que se refiere una nueva conversación. |
user opcional | texto | Su 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 opcional | boolean | true 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
}
}
content: la respuesta en Markdown (negrita, listas, enlaces).sources: las páginas en las que se basa la respuesta.choices: el bot pide al usuario que elija una opción. Consulte Opciones.office_options: el bot pregunta a qué ubicación se refiere (id,name,city).escalated: la pregunta se ha traspasado a un empleado, según la configuración del bot.
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
/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.
| Evento | Cuándo |
|---|---|
lead.created | Un visitante solicitó ser contactado. |
lead.updated | El seguimiento de un lead cambió (new, contacted, closed), en el portal o mediante la API. Con previous_status. |
report.created | El 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"
}
}
}
- Responda con un código de estado
2xxen menos de 10 segundos. Procesa el evento preferiblemente en segundo plano. - Si no tiene éxito, lo intentamos de nuevo después de 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas y 12 horas. Use
idpara procesar solo una vez un evento que llegue dos veces. - Si responde con
410 Gone, desactivaremos el webhook. Si no funciona con 5 eventos consecutivos, también lo haremos; los administradores recibirán un correo. Después, vuelva a activarlo y reenvíe los eventos fallidos en el portal. - No seguimos redirecciones y solo enviamos a direcciones públicas
https. - Con Probar en el portal envía un evento webhook.test para probar su integración.
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
/eventsLos 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ámetro | Significado |
|---|---|
after opcional | Solo eventos con un ID superior. |
type opcional | Uno o más eventos, separados por comas, por ejemplo lead.created. |
bot opcional | Solo eventos de este bot. |
limit opcional | De 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
/leadsLeads 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.
/leads/{id}Un lead, con los mismos campos que en lead.created, más bot.
/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.
/webhooks| Campo | Tipo | Significado |
|---|---|---|
url | texto | Dirección https pública. Obligatorio. |
events opcional | lista | Por defecto, todos los eventos. |
bots opcional | lista | IDs de los bots. Por defecto, los bots asociados a la clave. |
name opcional | texto | Nombre 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.
| Estado | Significado |
|---|---|
| 401 | Clave ausente, no válida o revocada. |
| 402 | La cuenta no está activa (por ejemplo, terminó la prueba) o, en POST /bots: hay que confirmar el precio ("code": "price_confirmation_required"). |
| 403 | Esta clave no puede crear bots o no tiene acceso a leads. |
| 404 | El bot, la conversación, el lead o el webhook no existen, o esta clave no tiene acceso a ellos. |
| 422 | Falta un campo o no es válido. |
| 429 | Demasiadas 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