Entwickler
API
Mit der API verwenden Sie Ihre Bots aus Ihrer eigenen Software, etwa einem Intranet, einer App oder einem CRM. Der Bot antwortet genau wie im Chat auf Ihrer Website: mit Ihren eigenen Inhalten, Standorten, Öffnungszeiten, Regeln und Auswahlabläufen.
- Basis-URL:
https://chatonpage.com/api/v1 - Anfragen und Antworten in JSON (UTF-8). Senden Sie
Accept: application/jsonmit. - Arbeiten Sie mit einem KI-Assistenten wie ChatGPT oder Claude? Dann verwenden Sie den MCP-Server.
- Neue Leads sofort in Ihr CRM, Zapier oder Make? Verwenden Sie Webhooks.
Authentifizierung
Erstellen Sie einen Schlüssel im Portal unter Verbindungen → API-Schlüssel. Geben Sie ihm einen Namen und wählen Sie, ob er Zugriff auf alle Bots Ihres Kontos (auch Bots, die Sie später hinzufügen) oder auf einen oder mehrere Bots hat und ob er Bots erstellen darf. Sie sehen den Schlüssel nur ein einziges Mal. Sie können beliebig viele erstellen und jeden einzeln widerrufen.
Senden Sie den Schlüssel im Header jeder Anfrage mit:
Authorization: Bearer cop_…
Schlüssel prüfen
/keyGibt den Namen des Schlüssels, das Konto und die Bots zurück, auf die er zugreifen kann. Praktisch, um zu testen, ob alles funktioniert.
{
"name": "Intranet",
"account": "Kindercentrum",
"all_bots": false,
"bots": [{ "id": 24, "name": "Kindercentrum" }]
}
Bots abrufen
/botsAlle Bots, auf die dieser Schlüssel Zugriff hat. Pausierte Bots sind nicht aufgeführt. Mit der id wählen Sie den Bot in den folgenden Calls.
{
"data": [
{
"id": 24,
"name": "Kindercentrum",
"site_url": "https://www.kindercentrum.be",
"status": "active",
"default_locale": "nl",
"locales": ["nl", "fr"]
}
]
}
Ein Bot
/bots/{id}Dieselben Daten, plus die Begrüßung, die Start-Schaltflächen und die Standorte des Bots.
{
"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 }
}
}
Bot erstellen
/botsErstellt einen neuen Bot für eine Website, wie Neuer Bot im Portal: Die Website wird sofort gelesen, Farben und Symbol werden übernommen, und wer den Schlüssel erstellt hat, erhält eine E-Mail, sobald der Bot zum Testen bereit ist. Verfolgen Sie den Fortschritt mit GET /bots/{id} (progress).
Nur mit einem Schlüssel, bei dem Darf Bots erstellen aktiviert ist. Jeder Bot hat einen eigenen Tarif: Kostet der neue Bot extra, erhalten Sie zuerst 402 mit dem Preis und senden die Anfrage erneut mit "confirm_price": true. Höchstens 10 neue Bots pro Stunde und Schlüssel.
| Feld | Typ | Bedeutung |
|---|---|---|
name | Text | Name des Bots, max. 100 Zeichen. Pflichtfeld. |
site_url | Text | Website, die der Bot liest, zum Beispiel www.ihrefirma.de. Pflichtfeld. |
default_locale | Text | Hauptsprache: nl, en, fr, de oder es. Standardmäßig die Sprache Ihres Kontos. |
locales | Liste | Zusätzliche Sprachen des Bots. Sprachen, die der Bot auf der Website findet, kommen automatisch hinzu. |
plan | Text | lite, plus, pro oder business. Ohne Tarif wählen wir den kleinsten Tarif, der zur Website passt. |
support_email | Text | Adresse für Fragen, die der Bot weiterleitet; standardmäßig die Support-Adresse Ihres Kontos. |
auto_approve | ja/nein | Neue und geänderte Seiten sofort verwenden (standardmäßig ja) oder zuerst im Portal freigeben lassen. |
for_customer | ja/nein | Nur für Partner: ein Bot für einen Kunden, der nicht unter Ihr eigenes Abonnement fällt. |
confirm_price | ja/nein | Bestätigt den Preis, wenn der Bot extra kostet. |
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"
}
}
Frage stellen
/bots/{id}/messagesSendet eine Nachricht an den Bot und gibt die Antwort zurück. Ohne conversation beginnt ein neues Gespräch. Senden Sie die conversation aus der Antwort bei der nächsten Frage mit, dann merkt sich der Bot, worum es im Gespräch geht.
| Feld | Typ | Bedeutung |
|---|---|---|
message | Text | Die Frage oder die Antwort des Benutzers, max. 4.000 Zeichen. |
conversation optional | Text | ID eines Gesprächs, das mit diesem Schlüssel gestartet wurde. |
locale optional | Text | Sprache eines neuen Gesprächs (nl, fr, en, …). Standardmäßig die Sprache des Bots. Antwortet der Benutzer in einer anderen Sprache, folgt der Bot dieser. |
office_id optional | Zahl | Standort, um den es in einem neuen Gespräch geht. |
user optional | Text | Ihre eigene Kennung für den Benutzer, max. 40 Zeichen. So finden Sie seine Gespräche im Portal wieder. Keine Namen oder E-Mail-Adressen. |
stream optional | boolean | true, um die Antwort Wort für Wort zu erhalten. Siehe 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: die Antwort in Markdown (fett, Listen, Links).sources: die Seiten, auf denen die Antwort basiert.choices: Der Bot bittet den Benutzer, eine Auswahl zu treffen. Siehe Auswahl.office_options: Der Bot fragt, welcher Standort gemeint ist (id,name,city).escalated: Die Frage wurde an einen Mitarbeiter weitergeleitet, wie beim Bot eingestellt.
Streaming
Mit "stream": true erhalten Sie einen text/event-stream (Server-Sent Events), sodass Sie die Antwort schon anzeigen, während der Bot schreibt:
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… }}
Geht unterwegs etwas schief, folgt event: error mit einer message.
Gespräch abrufen
/bots/{id}/conversations/{conversation}Alle Nachrichten eines Gesprächs, das mit diesem Schlüssel gestartet wurde.
{
"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": "…" }
]
}
}
Leads weiterleiten
Fragt ein Besucher danach, kontaktiert zu werden, dann erstellt der Bot einen Lead mit Name, E-Mail-Adresse, Telefonnummer, Frage und Zusammenfassung. Mit Webhooks wird jeder neue Lead sofort an Ihr CRM, Zapier, Make oder Ihre eigene Software gesendet. Sie können Leads und Events auch selbst abrufen.
Leads enthalten Kontaktdaten von Besuchern. Daher funktionieren die folgenden Aufrufe nur mit einem Schlüssel, bei dem Zugriff auf Leads aktiviert ist, und nur für die Bots dieses Schlüssels. Ohne diesen Zugriff erhalten Sie 403.
Webhooks
Erstellen Sie einen Webhook im Portal unter Verbindungen → Webhooks oder über die API. Wählen Sie die URL, die Events und die Bots aus. Bei jedem Event senden wir einen POST mit JSON an diese URL.
| Event | Wann |
|---|---|
lead.created | Ein Besucher hat um Kontaktaufnahme gebeten. |
lead.updated | Die Nachverfolgung eines Leads hat sich geändert (new, contacted, closed), im Portal oder über die API. Mit previous_status. |
report.created | Der Bot hat eine Beschwerde oder Fehlermeldung erfasst. Fünf Minuten später, damit Ergänzungen des Besuchers enthalten sind. |
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"
}
}
}
- Antworte mit einem
2xx-Status innerhalb von 10 Sekunden. Verarbeite das Event am besten im Hintergrund. - Falls es nicht klappt, versuchen wir es erneut nach 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 6 Stunden und 12 Stunden. Verwenden Sie
id, um ein Event, das zweimal eintrifft, nur einmal zu verarbeiten. - Antwortest du mit
410 Gone, deaktivieren wir den Webhook. Falls es bei 5 Events nacheinander nicht klappt, ebenfalls; die Administratoren erhalten dann eine E-Mail. Danach kannst du ihn wieder aktivieren und die fehlgeschlagenen Events im Portal erneut senden. - Wir folgen keinen Weiterleitungen und senden nur an öffentliche
https-Adressen. - Mit Testen im Portal senden Sie ein Ereignis webhook.test, um Ihre Verbindung auszuprobieren.
Signatur prüfen
Jeder Webhook hat ein Geheimnis (whsec_…), im Portal unter Geheimnis einsehbar. Der Header X-ChatOnPage-Signature enthält t (Zeitstempel, Unix-Sekunden) und v1: den HMAC-SHA256 von t, einem Punkt und dem Roh-Body, mit dem Geheimnis als Schlüssel. Vergleichen Sie diese und lehnen Sie Nachrichten ab, bei denen t mehr als 5 Minuten abweicht.
// 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;
Events abrufen
/eventsDieselbe Events wie bei Webhooks, beginnend mit den ältesten, für Nutzer, die lieber selbst abrufen (z. B. alle 5 Minuten). Speichere next_after und sende es als after mit: Dann erhältst du nur die neuen Events. Events werden 30 Tage lang gespeichert.
| Parameter | Bedeutung |
|---|---|
after optional | Nur Events mit einer höheren ID. |
type optional | Ein oder mehrere Events, durch Kommas getrennt, z. B. lead.created. |
bot optional | Nur Events von diesem Bot. |
limit optional | 1 bis 100, Standard 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 der Bots des Schlüssels, neueste zuerst. Filter mit status (new, contacted, closed), bot und since (Datum oder Zeitstempel, ISO 8601). Gibt es eine nächste Seite, senden Sie next_before als before mit.
/leads/{id}Ein Lead mit denselben Feldern wie bei lead.created, zusätzlich bot.
/leads/{id}Passt die Nachverfolgung an, z. B. {"status": "contacted"}, sobald der Lead in Ihrem CRM ist. Das sehen Sie auch im Portal, und es löst ein Event lead.updated aus.
Webhooks über die API
So verbinden Automatisierungsplattformen wie Zapier und Make sich an und wieder ab. Ein Schlüssel sieht nur die Webhooks, die er selbst erstellt hat. Wird der Schlüssel widerrufen, verschwinden auch seine Webhooks.
/webhooks| Feld | Typ | Bedeutung |
|---|---|---|
url | Text | Öffentliche HTTPS-Adresse. Pflichtfeld. |
events optional | Liste | Standardmäßig alle Events. |
bots optional | Liste | IDs der Bots. Standardmäßig die Bots des Schlüssels. |
name optional | Text | Name im Portal. Standardmäßig der Name des Schlüssels. |
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_…"
}
}
Das Geheimnis steht nur in dieser Antwort. Außerdem: GET /webhooks (Liste), DELETE /webhooks/{id} (abmelden) und POST /webhooks/{id}/test (Testereignis). Höchstens 25 Webhooks pro Konto.
Auswahl
Bei einem Auswahlablauf (zum Beispiel Anmeldung) gibt der Bot choices mit. Zeigen Sie diese als Schaltflächen an und senden Sie den gewählten Text wörtlich als nächste message zurück, mit derselben conversation. Der Benutzer darf auch etwas anderes eingeben: Der Bot erkennt dann selbst, ob es eine Auswahl oder eine neue Frage ist.
Fehler und Limits
Bei einem Fehler erhalten Sie einen HTTP-Status mit {"error": "…"}. Bei ungültigen Feldern (422) stehen die Details in errors.
| Status | Bedeutung |
|---|---|
| 401 | Kein, ungültiger oder widerrufener Schlüssel. |
| 402 | Das Konto ist nicht aktiv (zum Beispiel Testphase abgelaufen) oder bei POST /bots: Der Preis muss bestätigt werden ("code": "price_confirmation_required"). |
| 403 | Dieser Schlüssel darf keine Bots erstellen oder hat keinen Zugriff auf Leads. |
| 404 | Der Bot, das Gespräch, der Lead oder der Webhook existiert nicht, oder dieser Schlüssel hat keinen Zugriff darauf. |
| 422 | Ein Feld fehlt oder ist ungültig. |
| 429 | Zu viele Fragen: maximal 60 pro Minute pro Schlüssel, plus das Tageslimit pro Bot. |
Jede Antwort zählt zu den Antworten in Ihrem Abonnement, genau wie im Chat auf Ihrer Website. Gespräche über die API finden Sie im Portal unter Gespräche.
Beispiele
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