API Integratie
De Denkchat API stelt je in staat om chatbots programmatisch aan te sturen, gesprekken te beheren en data op te halen.
Base URL
https://denkchat.nl/apiAuthenticatie
Dashboard API-endpoints vereisen een Bearer token (Supabase access token) in de header:
Authorization: Bearer YOUR_ACCESS_TOKENDe embed chat API (/api/embed/chat) vereist geen authenticatie — deze is publiek toegankelijk voor widget-integraties.
Chat API
Bericht versturen
Stuur berichten naar een agent en ontvang een streaming text response.
curl -X POST https://denkchat.nl/api/embed/chat \
-H "Content-Type: application/json" \
-d '{
"agentId": "JOUW_AGENT_ID",
"messages": [
{ "role": "user", "content": "Wat zijn jullie openingstijden?" }
],
"sessionId": "optioneel-session-id",
"visitorEmail": "optioneel@email.nl"
}'Request body:
| Veld | Type | Beschrijving | Verplicht |
|---|---|---|---|
agentId | string | Je agent ID | Ja |
messages | array | Array van { role, content } objecten | Ja |
sessionId | string | Bestaand sessie-ID om gesprek voort te zetten | Nee |
visitorEmail | string | E-mailadres van de bezoeker | Nee |
Response:
De response is een plain text stream. De volledige tekst van het antwoord wordt chunk-voor-chunk gestreamd:
Onze openingstijden zijn maandag t/m vrijdag van 9:00 tot 17:00.Response headers:
| Header | Beschrijving |
|---|---|
X-Session-Id | Het sessie-ID (nieuw aangemaakt of bestaand) |
JavaScript voorbeeld
const response = await fetch('https://denkchat.nl/api/embed/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
agentId: 'JOUW_AGENT_ID',
messages: [
{ role: 'user', content: 'Hoe kan ik een bestelling retourneren?' }
],
}),
});
// Sessie-ID ophalen voor vervolgberichten
const sessionId = response.headers.get('X-Session-Id');
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(decoder.decode(value, { stream: true }));
}Python voorbeeld
import requests
response = requests.post(
'https://denkchat.nl/api/embed/chat',
json={
'agentId': 'JOUW_AGENT_ID',
'messages': [
{'role': 'user', 'content': 'Wat kosten jullie producten?'}
],
},
stream=True,
)
session_id = response.headers.get('X-Session-Id')
for chunk in response.iter_content(decode_unicode=True):
if chunk:
print(chunk, end='')Gesprek samenvatting
Genereer een AI-samenvatting van een chatsessie.
curl "https://denkchat.nl/api/chat/summary?sessionId=SESSION_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response:
{
"summary": "De bezoeker vroeg naar openingstijden en retourbeleid. De agent heeft beide vragen correct beantwoord. Sentiment: positief."
}Agent configuratie ophalen
curl "https://denkchat.nl/api/embed/config?agentId=JOUW_AGENT_ID"Response:
{
"name": "Klantenservice Bot",
"welcomeMessage": "Hoi! Hoe kan ik je helpen?",
"primaryColor": "#E54D2E",
"position": "bottom-right"
}Kennisbank API
Externe systemen (zoals de WordPress plugin) beheren kennisbank-content via /api/embed/knowledge. Deze endpoints vereisen een API-key (dk_live_...) als Bearer token — zie Authenticatie.
Kennisbank items synchroniseren
Stuur pagina's of documenten naar de kennisbank van een agent. Items worden geüpsert op basis van hun bron-URL (url) en daarna verwerkt: chunken, samenvatten, embedden.
curl -X POST https://denkchat.nl/api/embed/knowledge \
-H "Authorization: Bearer dk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"title": "Over ons",
"content": "Wij zijn een familiebedrijf sinds 1998...",
"url": "https://voorbeeld.nl/over-ons",
"type": "page"
}
]
}'Request body:
| Veld | Type | Beschrijving | Verplicht |
|---|---|---|---|
items | array | Array van items (max. 50 per request) | Ja |
title | string | Titel van het item | Ja |
content | string | Platte tekst (HTML wordt niet gestript) | Ja |
url | string | Bron-URL; dit is de sleutel waarop geüpsert wordt | Ja |
type | string | Vrij label, belandt in metadata.wp_type | Ja |
Ongewijzigde items worden overgeslagen
Een item wordt overgeslagen als de aangeleverde tekst identiek is aan wat er al staat én de bestaande rij gezond is (status ready met chunks). Dat scheelt bij elke sync het opnieuw chunken, samenvatten en embedden van content die niet veranderd is — de duurste stap van dit endpoint. Een overgeslagen item krijgt wél zijn metadata ververst.
Wil je bewust opnieuw laten verwerken — bijvoorbeeld nadat de chunking of de samenvattingen zijn gewijzigd — gebruik dan ?force=true:
curl -X POST "https://denkchat.nl/api/embed/knowledge?force=true" \
-H "Authorization: Bearer dk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{ "items": [ ... ] }'Response:
{
"success": true,
"synced": 3,
"skipped": 1,
"errors": 1,
"skipped_unchanged": 1,
"results": [
{ "url": "https://voorbeeld.nl/over-ons", "status": "ready", "knowledgeId": "..." },
{ "url": "https://voorbeeld.nl/contact", "status": "ready", "knowledgeId": "...", "unchanged": true },
{ "url": "https://voorbeeld.nl/kapot", "status": "error" }
],
"remaining": { "currentMb": 2.4, "limitMb": 20 }
}| Veld | Beschrijving |
|---|---|
synced | Items die geslaagd zijn — nieuw, bijgewerkt, of ongewijzigd |
skipped | Alleen de mislukte items (zelfde getal als errors, zie de opmerking hieronder) |
errors | Alleen de mislukte items |
skipped_unchanged | Items die ongewijzigd waren en dus niet opnieuw verwerkt zijn |
results | Per item de bron-URL en de status: ready of error. Bij een ongewijzigd item staat er ook unchanged: true bij |
remaining | Opslaggebruik van de kennisbank na deze request |
skipped en errors dragen op dit moment hetzelfde getal. skipped
bestond al vóór de "ongewijzigde items overslaan"-optimalisatie hierboven en
betekende altijd "mislukte items" — bestaande integraties (zoals de
WordPress-plugin) tonen bij skipped > 0 een storage-limit-waarschuwing.
Om die niet te breken telt skipped nog steeds alleen fouten; de nieuwe
informatie over ongewijzigde items vind je in skipped_unchanged en aan
results[].unchanged.
Bij het overschrijden van de opslaglimiet van je plan geeft dit endpoint 402 met error: "plan_limit_reached" en wordt er niets verwerkt.
Kennisbank items verwijderen
Verwijder eerder gesyncte items op basis van hun bron-URL. Bijbehorende chunks en embeddings worden automatisch mee verwijderd.
Alleen items die via deze API (of de WordPress plugin) zijn gesynct kunnen verwijderd worden — kennisbank-content die in het dashboard is toegevoegd blijft buiten bereik van de API-key. Dit endpoint is gelimiteerd tot 30 requests per minuut per agent.
curl -X DELETE https://denkchat.nl/api/embed/knowledge \
-H "Authorization: Bearer dk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://voorbeeld.nl/over-ons",
"https://voorbeeld.nl/contact"
]
}'Request body:
| Veld | Type | Beschrijving | Verplicht |
|---|---|---|---|
urls | array | Bron-URL's van de te verwijderen items (1 t/m 100) | Ja |
Response:
{
"success": true,
"deleted": 1,
"notFound": ["https://voorbeeld.nl/contact"]
}| Veld | Beschrijving |
|---|---|
deleted | Aantal daadwerkelijk verwijderde items |
notFound | URL's die niet als API-gesynct item in de kennisbank van deze agent gevonden zijn |
Product Sync API
Externe systemen kunnen productcatalogusgegevens in agenten synchroniseren via /api/embed/products. Deze endpoints vereisen een API-sleutel (dk_live_...) als Bearer token — zie Authenticatie. API-toegang hoort bij het Standard-plan en hoger.
Deze data vormt de grondslag voor productkaarten en eigenschappenfiltering in de chatwidget.
Draait de winkel op WooCommerce? Gebruik dan de WooCommerce-koppeling in plaats van zelf te pushen: die leest de catalogus uit de publieke Store API, houdt hem automatisch bij en heeft geen API-sleutel nodig. Dit endpoint blijft bestaan voor eigen webshops en andere systemen.
Producten synchroniseren
Synchroniseer of update producten in de productcatalogus van een agent. Producten worden geüpsert op basis van (agent_id, external_id) en hun beschrijvingen worden automatisch ingebed voor eigenschap-gebaseerd filteren.
curl -X POST https://denkchat.nl/api/embed/products \
-H "Authorization: Bearer dk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"products": [
{
"external_id": "123",
"name": "SPF 50 Zonnecrème",
"sku": "ZC-SPF50-200",
"price": 24.99,
"regular_price": 29.99,
"on_sale": true,
"in_stock": true,
"product_url": "https://voorbeeld.nl/products/spf50-crème",
"image_url": "https://voorbeeld.nl/images/spf50.jpg",
"add_to_cart_url": "https://voorbeeld.nl/products/spf50-crème?add-to-cart=123",
"brand": "Zonneexperts",
"categories": ["Zonnecrèmes", "Face"],
"attributes": ["SPF 50", "Waterproof", "Gevoelige huid"],
"attributes_source": {
"SPF 50": "shop",
"Waterproof": "shop",
"Gevoelige huid": "llm"
},
"description": "Zeer effectieve dagcreme met SPF 50+ UV-filter..."
}
]
}'Request body:
| Veld | Type | Beschrijving | Verplicht |
|---|---|---|---|
products | array | Array van product-objecten (max. 500) | Ja |
external_id | string | Unieke product-ID uit je winkelssysteem | Ja |
name | string | Productnaam | Ja |
sku | string | Artikelnummer uit je winkelsysteem | Nee |
price | number | Huidige verkoopprijs | Ja |
price_from | boolean | Is price een vanaf-prijs? Zet dit op true bij een product met meerdere uitvoeringen, waarvan je de laagste prijs meestuurt — de chatbot toont dan "vanaf €24,99" in plaats van dat bedrag als dé prijs (standaard false) | Nee |
regular_price | number | Normale prijs (voor aanbiedingen) | Nee |
on_sale | boolean | Geldt er een aanbieding? | Nee |
in_stock | boolean | Is het product beschikbaar? | Nee |
product_url | string | Link naar productdetailpagina | Ja |
image_url | string | URL van productafbeelding | Nee |
add_to_cart_url | string | Link om product direct in winkelwagen te doen | Nee |
brand | string | Merknaam | Nee |
categories | array | Productcategorieën (string array) | Nee |
attributes | array | Producteigenschappen/tags (string array) | Nee |
attributes_source | object | Voor elke attribuut: "shop" of "llm" (bron-markering) | Nee |
description | string | Productbeschrijving (embedded voor filtering) | Nee |
Vul description zo volledig mogelijk: de chatbot gebruikt deze tekst om zijn
advies te onderbouwen (wat het product doet, voor wie het bedoeld is,
ingrediënten). Zonder beschrijving kan hij alleen op naam, prijs en
attributes adviseren. De eerste 700 tekens worden meegegeven aan het model;
de beschrijving zelf verschijnt niet op de productkaart in de widget.
Response:
{
"success": true,
"synced": 1
}| Veld | Beschrijving |
|---|---|
success | Of het synchronisatieproces succesvol was |
synced | Aantal daadwerkelijk gesyncte of bijgewerkte producten |
Productlimiet per plan
Het aantal producten per agent is begrensd door je plan:
| Plan | Max. producten |
|---|---|
| Free | 0 |
| Hobby | 500 |
| Standard | 2.000 |
| Pro | 10.000 |
| Enterprise | onbeperkt |
Alleen nieuwe external_id's tellen mee: een request dat uitsluitend bestaande producten bijwerkt (een prijs- of voorraadwijziging) gaat altijd door, ook als je al op je limiet zit. Zou het request de limiet overschrijden, dan wordt er niets geschreven en volgt een 402:
{
"error": "plan_limit_reached",
"current": 500,
"max": 500
}Producten verwijderen
Verwijder eerder gesyncte producten op basis van hun externe IDs.
curl -X DELETE https://denkchat.nl/api/embed/products \
-H "Authorization: Bearer dk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"external_ids": ["123", "456"]
}'Request body:
| Veld | Type | Beschrijving | Verplicht |
|---|---|---|---|
external_ids | array | Externe product-IDs om te verwijderen (1 t/m 500) | Ja |
Response:
{
"success": true,
"deleted": 2,
"notFound": []
}| Veld | Beschrijving |
|---|---|
deleted | Aantal daadwerkelijk verwijderde producten |
notFound | Externe IDs die niet in deze agent gevonden zijn |
Rate limits
| Plan | Berichten/dag |
|---|---|
| Free | 50 |
| Hobby | 500 |
| Standard | 4.000 |
| Pro | 15.000 |
| Enterprise | 100.000 |
Bij overschrijding ontvang je een 429 Too Many Requests response.