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.

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_…
Une clé API a sa place sur votre serveur. Ne la placez jamais dans du code qui aboutit chez vos visiteurs dans un navigateur ou une application. Pour votre site web, utilisez le widget habituel.

Vérifier la clé

GET /key

Renvoie 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

GET /bots

Tous 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

GET /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

POST /bots

Cré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é.

ChampTypeSignification
nametexteNom du bot, 100 caractères max. Obligatoire.
site_urltexteSite que le bot lit, par exemple www.votreentreprise.be. Obligatoire.
default_localetexteLangue principale : nl, en, fr, de ou es. Par défaut la langue de votre compte.
localeslisteLangues supplémentaires du bot. Les langues trouvées sur le site s'ajoutent automatiquement.
plantextelite, plus, pro ou business. Sans formule, nous choisissons la plus petite formule adaptée au site.
support_emailtexteAdresse pour les questions que le bot transmet ; par défaut l'adresse de support de votre compte.
auto_approveoui/nonUtiliser immédiatement les pages nouvelles et modifiées (oui par défaut) ou les faire d'abord approuver dans le portail.
for_customeroui/nonUniquement pour les partenaires : un bot pour un client, qui ne relève pas de votre propre abonnement.
confirm_priceoui/nonConfirme 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

POST /bots/{id}/messages

Envoie 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.

ChampTypeSignification
messagetexteLa question ou la réponse de l'utilisateur, 4 000 caractères max.
conversation facultatiftexteId d'une conversation démarrée avec cette clé.
locale facultatiftexteLangue 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 facultatifnombreAgence concernée par une nouvelle conversation.
user facultatiftexteVotre 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 facultatifbooléentrue 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
  }
}

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

GET /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énementQuand
lead.createdUn visiteur a demandé à être contacté.
lead.updatedLe suivi d'un lead a changé (new, contacted, closed), dans le portail ou via l'API. Avec previous_status.
report.createdLe 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"
    }
  }
}

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

GET /events

Les 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ètreSignification
after optionnelUniquement les événements avec un ID plus élevé.
type optionnelUn ou plusieurs événements, séparés par une virgule, par exemple lead.created.
bot optionnelUniquement les événements de ce bot.
limit optionnel1 à 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

GET /leads

Leads 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.

GET /leads/{id}

Un lead, avec les mêmes champs que dans lead.created, plus bot.

PATCH /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.

POST /webhooks
ChampTypeSignification
urltexteAdresse HTTPS publique. Obligatoire.
events optionnellistePar défaut, tous les événements.
bots optionnellisteID des bots. Par défaut, les bots associés à la clé.
name optionneltexteNom 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.

StatutSignification
401Clé absente, invalide ou révoquée.
402Le compte n'est pas actif (par exemple, l'essai est terminé) ou, pour POST /bots : le prix doit être confirmé ("code": "price_confirmation_required").
403Cette clé ne peut pas créer de bots ou n'a pas accès aux leads.
404Le bot, la conversation, le lead ou le webhook n'existe pas, ou cette clé n'y a pas accès.
422Un champ est manquant ou invalide.
429Trop 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