Développeurs
API
L'API vous permet d'utiliser vos bots depuis votre propre logiciel, comme un intranet, une application ou un CRM. Le bot répond comme dans le chat de votre site web : avec votre propre contenu, vos agences, heures d'ouverture, règles et parcours à choix.
- URL de base :
https://chatonpage.com/api/v1 - Requêtes et réponses en JSON (UTF-8). Envoyez
Accept: application/json. - Vous travaillez avec un assistant IA comme ChatGPT ou Claude ? Utilisez alors le serveur MCP.
- Des nouveaux leads directement dans votre CRM, Zapier ou Make ? Utilisez des webhooks.
Authentification
Créez une clé dans le portail sous Connexions → Clés API. Donnez-lui un nom et choisissez si elle a accès à tous les bots de votre compte (y compris ceux que vous ajouterez plus tard) ou à un ou plusieurs bots, et si elle peut créer des bots. La clé ne s'affiche qu'une seule fois. Vous pouvez en créer autant que vous le souhaitez et les révoquer séparément.
Envoyez la clé dans l'en-tête de chaque requête :
Authorization: Bearer cop_…
Vérifier la clé
/keyRenvoie le nom de la clé, le compte et les bots auxquels elle a accès. Pratique pour tester si tout fonctionne.
{
"name": "Intranet",
"account": "Kindercentrum",
"all_bots": false,
"bots": [{ "id": 24, "name": "Kindercentrum" }]
}
Récupérer les bots
/botsTous les bots auxquels cette clé a accès. Les bots en pause n'y figurent pas. L'id vous permet de choisir le bot dans les appels suivants.
{
"data": [
{
"id": 24,
"name": "Kindercentrum",
"site_url": "https://www.kindercentrum.be",
"status": "active",
"default_locale": "nl",
"locales": ["nl", "fr"]
}
]
}
Un bot
/bots/{id}Les mêmes données, plus le message d'accueil, les boutons de démarrage et les agences du 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 }
}
}
Créer un bot
/botsCrée un nouveau bot pour un site, comme Nouveau bot dans le portail : le site est lu immédiatement, les couleurs et l'icône sont reprises, et la personne qui a créé la clé reçoit un e-mail dès que le bot est prêt à être testé. Suivez la progression avec GET /bots/{id} (progress).
Uniquement avec une clé pour laquelle Peut créer des bots est activé. Chaque bot a sa propre formule : si le nouveau bot coûte en plus, vous recevez d'abord 402 avec le prix, puis vous renvoyez la requête avec "confirm_price": true. Au maximum 10 nouveaux bots par heure et par clé.
| Champ | Type | Signification |
|---|---|---|
name | texte | Nom du bot, 100 caractères max. Obligatoire. |
site_url | texte | Site que le bot lit, par exemple www.votreentreprise.be. Obligatoire. |
default_locale | texte | Langue principale : nl, en, fr, de ou es. Par défaut la langue de votre compte. |
locales | liste | Langues supplémentaires du bot. Les langues trouvées sur le site s'ajoutent automatiquement. |
plan | texte | lite, plus, pro ou business. Sans formule, nous choisissons la plus petite formule adaptée au site. |
support_email | texte | Adresse pour les questions que le bot transmet ; par défaut l'adresse de support de votre compte. |
auto_approve | oui/non | Utiliser immédiatement les pages nouvelles et modifiées (oui par défaut) ou les faire d'abord approuver dans le portail. |
for_customer | oui/non | Uniquement pour les partenaires : un bot pour un client, qui ne relève pas de votre propre abonnement. |
confirm_price | oui/non | Confirme le prix si le bot coûte en plus. |
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"
}
}
Poser une question
/bots/{id}/messagesEnvoie un message au bot et renvoie la réponse. Sans conversation, une nouvelle conversation démarre. Envoyez la conversation de la réponse avec la question suivante, et le bot se souvient du sujet de la conversation.
| Champ | Type | Signification |
|---|---|---|
message | texte | La question ou la réponse de l'utilisateur, 4 000 caractères max. |
conversation facultatif | texte | Id d'une conversation démarrée avec cette clé. |
locale facultatif | texte | Langue d'une nouvelle conversation (nl, fr, en, …). Par défaut, la langue du bot. Si l'utilisateur répond dans une autre langue, le bot la suit. |
office_id facultatif | nombre | Agence concernée par une nouvelle conversation. |
user facultatif | texte | Votre propre identifiant pour l'utilisateur, 40 caractères max. Vous retrouvez ainsi ses conversations dans le portail. Pas de noms ni d'adresses e-mail. |
stream facultatif | booléen | true pour recevoir la réponse mot par mot. Voir 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 réponse en Markdown (gras, listes, liens).sources: les pages sur lesquelles la réponse est basée.choices: le bot demande à l'utilisateur de faire un choix. Voir Choix.office_options: le bot demande de quelle agence il s'agit (id,name,city).escalated: la question a été transmise à un collaborateur, selon la configuration du bot.
Streaming
Avec "stream": true, vous recevez un text/event-stream (Server-Sent Events), ce qui vous permet d'afficher la réponse pendant que le bot l'écrit :
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 un problème survient en cours de route, un event: error suit avec un message.
Récupérer une conversation
/bots/{id}/conversations/{conversation}Tous les messages d'une conversation démarrée avec cette clé.
{
"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": "…" }
]
}
}
Transmettre les leads
Si un visiteur demande à être contacté, le bot crée un lead avec le nom, l'adresse e-mail, le numéro de téléphone, la question et un résumé. Grâce aux webhooks, chaque nouveau lead est immédiatement envoyé vers votre CRM, Zapier, Make ou votre propre logiciel. Vous pouvez également récupérer vous-même les leads et les événements.
Les leads contiennent les coordonnées des visiteurs. Par conséquent, les appels ci-dessous ne fonctionnent qu'avec une clé pour laquelle Accès aux leads est activé, et uniquement pour les bots associés à cette clé. Sans cet accès, vous obtiendrez une erreur 403.
Webhooks
Créez un webhook dans le portail sous Connexions → Webhooks, ou via l'API. Choisissez l'URL, les événements et les bots. Pour chaque événement, nous envoyons une requête POST avec JSON à cette URL.
| Événement | Quand |
|---|---|
lead.created | Un visiteur a demandé à être contacté. |
lead.updated | Le suivi d'un lead a changé (new, contacted, closed), dans le portail ou via l'API. Avec previous_status. |
report.created | Le bot a enregistré une plainte ou un signalement de bug. Cinq minutes plus tard, afin d'inclure les compléments du visiteur. |
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"
}
}
}
- Répondez avec un code de statut
2xxdans les 10 secondes. Traitez l'événement de préférence en arrière-plan. - Si cela échoue, nous réessayons après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures et 12 heures. Utilisez
idpour traiter une seule fois un événement reçu deux fois. - Si vous répondez avec
410 Gone, nous désactivons le webhook. Si 5 événements consécutifs échouent également, nous faisons de même ; les administrateurs reçoivent alors un e-mail. Ensuite, vous le réactivez et renvoyez les événements échoués dans le portail. - Nous ne suivons pas les redirections et n'envoyons que vers des adresses
httpspubliques. - Avec le bouton Tester dans le portail, vous envoyez un événement webhook.test pour tester votre connexion.
Vérifier la signature
Chaque webhook possède un secret (whsec_…), visible dans le portail sous Secret. L'en-tête X-ChatOnPage-Signature contient t (horodatage, secondes Unix) et v1 : le HMAC-SHA256 de t, un point et le corps brut, avec le secret comme clé. Comparez-les et rejetez les messages dont t s'écarte de plus de 5 minutes.
// 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;
Récupérer les événements
/eventsLes mêmes événements que pour les webhooks, du plus ancien au plus récent, pour ceux qui préfèrent les récupérer eux-mêmes (par exemple toutes les 5 minutes). Conservez next_after et envoyez-le comme after : vous ne recevrez ainsi que les nouveaux. Les événements sont conservés pendant 30 jours.
| Paramètre | Signification |
|---|---|
after optionnel | Uniquement les événements avec un ID plus élevé. |
type optionnel | Un ou plusieurs événements, séparés par une virgule, par exemple lead.created. |
bot optionnel | Uniquement les événements de ce bot. |
limit optionnel | 1 à 100, par défaut 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 des bots de la clé, du plus récent au plus ancien. Filtres : status (new, contacted, closed), bot et since (date ou heure, ISO 8601). S’il y a une page suivante, envoyez next_before comme before.
/leads/{id}Un lead, avec les mêmes champs que dans lead.created, plus bot.
/leads/{id}Met à jour le suivi, par exemple {"status": "contacted"} une fois que le lead est dans votre CRM. Cela est également visible dans le portail et génère un événement lead.updated.
Webhooks via l'API
C'est ainsi que les plateformes d'automatisation comme Zapier et Make se connectent et se déconnectent. Une clé ne voit que les webhooks qu'elle a créés elle-même. Si la clé est révoquée, ses webhooks disparaissent également.
/webhooks| Champ | Type | Signification |
|---|---|---|
url | texte | Adresse HTTPS publique. Obligatoire. |
events optionnel | liste | Par défaut, tous les événements. |
bots optionnel | liste | ID des bots. Par défaut, les bots associés à la clé. |
name optionnel | texte | Nom dans le portail. Par défaut, le nom de la clé. |
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_…"
}
}
Le secret ne figure que dans cette réponse. Par ailleurs : GET /webhooks (liste), DELETE /webhooks/{id} (désinscription) et POST /webhooks/{id}/test (événement de test). 25 webhooks au maximum par compte.
Choix
Dans un parcours à choix (par exemple une inscription), le bot renvoie choices. Affichez-les sous forme de boutons et renvoyez le texte choisi tel quel comme message suivant, avec la même conversation. L'utilisateur peut aussi taper autre chose : le bot comprend alors de lui-même s'il s'agit d'un choix ou d'une nouvelle question.
Erreurs et limites
En cas d'erreur, vous recevez un statut HTTP avec {"error": "…"}. Pour les champs invalides (422), les détails figurent dans errors.
| Statut | Signification |
|---|---|
| 401 | Clé absente, invalide ou révoquée. |
| 402 | Le compte n'est pas actif (par exemple, l'essai est terminé) ou, pour POST /bots : le prix doit être confirmé ("code": "price_confirmation_required"). |
| 403 | Cette clé ne peut pas créer de bots ou n'a pas accès aux leads. |
| 404 | Le bot, la conversation, le lead ou le webhook n'existe pas, ou cette clé n'y a pas accès. |
| 422 | Un champ est manquant ou invalide. |
| 429 | Trop de questions : 60 par minute et par clé au maximum, plus la limite journalière par bot. |
Chaque réponse est décomptée des réponses de votre abonnement, comme dans le chat de votre site web. Les conversations via l'API figurent dans le portail sous Conversations.
Exemples
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