De API van je website.
Lees de gegevens van je eigen site met een persoonlijke sleutel, bijvoorbeeld om bestellingen in je boekhouding te zetten of je omzet in een dashboard te tonen. Alleen lezen, van server naar server.
Zo begin je.
- 1. Kijk of je pakket past. API-sleutels zijn er vanaf het pakket Plus. Staat je site op een kleiner pakket, dan zie je in je portaal hoe je overstapt.
- 2. Maak een sleutel. Log in, ga naar API-sleutels en geef de sleutel een naam. Kies wat hij mag lezen en eventueel wanneer hij vervalt. We vragen je wachtwoord opnieuw en sturen je een mail. De sleutel zie je één keer: kopieer hem meteen.
- 3. Roep de API aan. Stuur de sleutel mee in de header Authorization. Hieronder haal je de bestellingen op.
curl https://morgenklaar.nl/api/v1/bestellingen \
-H "Authorization: Bearer mk_jouw_sleutel"
Het antwoord ziet er zo uit. Bedragen zijn gehele centen inclusief btw, tijden zijn in Nederlandse tijd.
{
"data": [
{
"nummer": 42,
"status": "betaald",
"betaald_op": "2026-10-26T14:03:11+01:00",
"gewijzigd_op": "2026-10-26T14:03:12+01:00",
"totaal_centen": 4850,
"koper": { "naam": "Sanne de Vries", "email": "sanne@voorbeeld.nl" }
}
],
"per_pagina": 25,
"volgende_cursor": null
}
Wat je kunt lezen.
Alle adressen beginnen met https://morgenklaar.nl/api/v1 en gebruiken GET. Elk eindpunt hoort bij één recht van je sleutel.
GET /site
De naam en het adres van de site waar deze sleutel bij hoort, wat de sleutel mag en de limieten. Handig om te controleren of je sleutel werkt.
Geen apart recht nodig.
GET /bestellingen
De bestellingen van je webshop, nieuwste eerst: betaalde bestellingen, ook als er later iets van terugging. Proefbestellingen staan er niet bij. Met de gegevens van de koper. Met gewijzigd_sinds loopt de lijst op wijzigingstijd, oudste eerst, om te synchroniseren.
Recht: lezen:bestellingen
GET /bestellingen/{nummer}
Eén bestelling op zijn nummer (het nummer van je winkel, zoals in je portaal).
Recht: lezen:bestellingen
GET /producten
De producten die op je site te zien zijn, op slug (a-z). In elk product staat de volgorde van je winkel.
Recht: lezen:producten
GET /producten/{slug}
Eén product op zijn slug.
Recht: lezen:producten
GET /afspraken
De afspraken van je site, op begintijd, oudste eerst. Zonder de gegevens van de bezoeker. Met gewijzigd_sinds loopt de lijst op wijzigingstijd, oudste eerst, om te synchroniseren.
Recht: lezen:afspraken
GET /afspraken/{id}
Eén afspraak op zijn id.
Recht: lezen:afspraken
GET /artikelen
De artikelen op je site, op slug (a-z), met de samenvatting. In elk artikel staan de datum en de volgorde van je site (nieuwste eerst). De hele tekst staat bij één artikel.
Recht: lezen:artikelen
GET /artikelen/{slug}
Eén artikel met zijn hele tekst.
Recht: lezen:artikelen
GET /paginas
De pagina's van je site zoals de site ze nu laat zien (de voorpagina heet _voorpagina), op pad (a-z). Pagina's met een wachtwoord of voor leden staan er niet bij. Zwaarder dan de rest: hier geldt een lagere limiet.
Recht: lezen:paginas
GET /paginas/{pad}
De teksten van één pagina, per blok, zoals de site ze nu laat zien. Is de pagina erg lang, dan staat er afgekapt: true. Zwaarder dan de rest: hier geldt een lagere limiet.
Recht: lezen:paginas
GET /omzet
De omzet van je webshop per dag of per maand, op de dag van betalen. Zonder van en tot: de laatste 30 dagen. Hooguit 366 dagen. Het antwoord kan tot een minuut oud zijn. Zwaarder dan de rest: hier geldt een lagere limiet.
Recht: lezen:omzet
De rechten van een sleutel.
Geef een sleutel alleen wat het systeem nodig heeft. De gegevens van kopers (naam, e-mailadres, telefoonnummer, adres) staan alleen bij de bestellingen, dus alleen met het recht lezen:bestellingen. Afspraken, omzet en de rest laten ze weg.
lezen:bestellingen
De bestellingen uit je webshop, met de gegevens van de kopers: naam, e-mailadres, telefoonnummer en bezorgadres.
lezen:producten
De producten uit je webshop met prijs, voorraad en opties.
lezen:afspraken
De afspraken die bezoekers bij je maakten: dienst, tijd en status, zonder de gegevens van de bezoeker.
lezen:artikelen
De artikelen op je site, zoals je nieuws of blog.
lezen:paginas
De teksten van de pagina's van je site.
lezen:omzet
De omzet van je webshop per dag of per maand, zonder gegevens van kopers.
schrijven:concept
Teksten, kleuren, letters en vormen van je site aanpassen, en blokken tijdelijk verbergen, in het concept van je editor. Blokken toevoegen, verschuiven of weghalen en foto's kiezen, als voorstel. Er gaat niets online: dat doe je zelf met Online zetten of Goedkeuren in je portaal.
Lijsten, pagina's en limieten.
- Een lijst komt in stukken. Met per_pagina kies je hoeveel (1 tot en met 100). Is er meer, dan staat volgende_cursor in het antwoord. Geef die onveranderd mee als cursor voor het volgende stuk. Is hij null, dan ben je klaar.
- Producten, artikelen en pagina's komen op slug (pad bij pagina's), van a tot z. Een cursor onthoudt de laatste slug, dus er valt niets weg of dubbel als er tussen twee verzoeken iets wordt toegevoegd of weggehaald. De volgorde van je site staat in het veld volgorde.
- Bestellingen, afspraken, artikelen en de omzet kun je filteren met van en tot, als datum zoals 2026-10-01. Het filter werkt op een ander veld per eindpunt: bij bestellingen en de omzet op de dag van betalen (betaald_op), bij afspraken op het begin van de afspraak (begin) en bij artikelen op de datum van het artikel.
- Wil je je administratie bijhouden? Vraag bestellingen of afspraken met gewijzigd_sinds, zoals ?gewijzigd_sinds=2026-10-26T14:03:11%2B01:00. Je krijgt dan alles wat sinds dat tijdstip veranderde, oudste wijziging eerst, ook een terugbetaling of een afspraak die is afgezegd. Bewaar na elke ronde het gewijzigd_op van de laatste die je verwerkte en geef dat de volgende keer mee. Het tijdstip is inclusief, dus je krijgt de laatste nog een keer: verwerk een bestelling die je al had zonder hem dubbel te boeken.
- Per sleutel mag je 60 verzoeken per minuut en 2000 per dag doen. De omzet en de pagina's kosten ons meer en hebben een lagere limiet van 20 per minuut. Alle sleutels van één site samen mogen 120 per minuut en 5000 per dag. Daarboven krijg je een 429 met de header Retry-After: het aantal seconden dat je moet wachten.
- Elk antwoord op een verzoek met een geldige sleutel heeft de headers X-RateLimit-Limit, X-RateLimit-Remaining en X-RateLimit-Reset: de strengste minuutlimiet die voor dit verzoek geldt, hoeveel je nog over hebt en het tijdstip waarop het weer ruimte geeft (seconden sinds 1 januari 1970). Voor de daglimiet zijn er dezelfde drie met Day erin, zoals X-RateLimit-Day-Remaining.
- Je kunt 10 sleutels tegelijk open hebben per site.
Als er iets misgaat.
Elke fout komt in dezelfde vorm. De code is voor je programma en verandert niet, het bericht is voor jou en staat in het Nederlands.
{"fout": {"code": "scope_ontbreekt", "bericht": "..."}}
401 ongeldig_token
De sleutel ontbreekt of werkt niet. Dat zeggen we voor elke reden hetzelfde: ook een ingetrokken of verlopen sleutel krijgt dit.
403 pakket_te_laag
Je site staat op een pakket onder Plus. De sleutel blijft bestaan en werkt weer zodra het pakket weer hoog genoeg is.
403 site_niet_actief
Je site staat nu niet online (gestopt of gepauzeerd). De sleutel blijft bestaan en werkt weer zodra de site weer online is.
403 scope_ontbreekt
De sleutel heeft het recht niet dat voor dit eindpunt nodig is.
403 herkomst_geweigerd
Je riep /mcp aan vanaf een webpagina in een browser. Gebruik MCP vanuit een programma op je computer of server.
404 niet_gevonden
Dit bestaat niet, of hoort niet bij jouw sleutel. Je kunt nooit bij gegevens van een andere site komen.
400 ongeldige_parameter
Een waarde in je verzoek klopt niet, bijvoorbeeld een datum of de cursor. Het bericht zegt welke.
405 alleen_lezen
Je gebruikte een andere methode dan GET. Deze versie van de API kan alleen lezen.
429 te_veel_verzoeken
Je zit aan een limiet. Wacht zoveel seconden als in de header Retry-After staat.
500 serverfout
Er ging iets mis aan onze kant. Probeer het later opnieuw.
503 storing
We zijn even niet bereikbaar. Wacht zoveel seconden als in de header Retry-After staat en probeer het dan opnieuw.
Veilig omgaan met je sleutel.
- Zet de sleutel alleen op een server. Niet in een app, niet in de code van een webpagina en niet op een openbare plek zoals GitHub. Daarom werkt de API ook niet vanuit een browser.
- We bewaren je sleutel zelf niet, alleen een afdruk. Raak je hem kwijt, dan maak je een nieuwe.
- Lekt een sleutel of gebruik je hem niet meer? Trek hem in via je portaal. Dat werkt meteen.
- Hoort een sleutel bij een site die je overdraagt, dan werkt hij niet meer. Zakt je site onder Plus, of staat hij even niet online (gestopt of gepauzeerd), dan krijg je een 403 en blijft de sleutel bestaan. Zodra het weer kan, werkt hij weer.
- We houden per sleutel bij wanneer hij werd gebruikt en welke adressen van de API hij opvroeg (met de uitkomst), zonder de inhoud of persoonsgegevens, en bewaren dat 90 dagen. Je ziet de laatste 20 aanroepen in je portaal. We zien ook of een sleutel vanaf een ander adres kwam dan de vorige keer, zonder dat adres te bewaren, en tonen je dat in je portaal.
Met je eigen AI (MCP).
Werk je met een AI-assistent zoals Claude of Cursor? Dan laat je hem met een sleutel je eigen gegevens lezen, via MCP (Model Context Protocol). Je vraagt bijvoorbeeld "Hoeveel omzet had ik vorige maand?" of "Welke afspraken staan er morgen?" en je assistent zoekt het zelf op. Hij kan alleen lezen, met de rechten en limieten van de sleutel.
Claude en ChatGPT, zonder sleutel
In Claude (op claude.ai en in de apps) en in ChatGPT koppel je je site zonder sleutel. Voeg daar een eigen connector toe met het adres https://morgenklaar.nl/mcp. De app stuurt je naar morgenklaar.nl: je logt in, kiest de site en de rechten, en klikt op Toestaan. Daarna zie je de koppeling in je portaal onder API-sleutels, bij Gekoppelde apps, en daar trek je hem ook weer in.
- Een gekoppelde app werkt alleen voor de site die je koos, met de rechten die je aanvinkte. Hij kan lezen, en alleen als je "Je site aanpassen in het concept" aanvinkt ook je site aanpassen, net als een sleutel met schrijven:concept (zie hieronder). Er gaat nooit iets vanzelf online. Hij heeft dezelfde limieten als een sleutel.
- Alleen de eigenaar van een site kan een app koppelen, vanaf het pakket Plus, voor een site die online staat.
- De app krijgt een toegangscode die een uur werkt en vernieuwt die zelf. Gebruikt de app de koppeling 60 dagen niet, dan stopt hij vanzelf.
Met een sleutel
Het adres is https://morgenklaar.nl/mcp. Maak in je portaal een aparte sleutel voor je assistent en zet hem hieronder op de plek van mk_jouw_sleutel.
Claude Code
Typ dit in je terminal:
claude mcp add --transport http morgenklaar https://morgenklaar.nl/mcp \
--header "Authorization: Bearer mk_jouw_sleutel"
Cursor
Zet dit in .cursor/mcp.json in je thuismap (of in je project):
{
"mcpServers": {
"morgenklaar": {
"url": "https://morgenklaar.nl/mcp",
"headers": { "Authorization": "Bearer mk_jouw_sleutel" }
}
}
}
Claude Desktop
Claude Desktop stuurt zelf nog geen sleutel mee. Het hulpprogramma mcp-remote doet dat voor hem; daarvoor heb je Node.js nodig. Zet dit in claude_desktop_config.json (in Claude Desktop onder Settings, Developer, Edit Config) en start Claude Desktop opnieuw:
{
"mcpServers": {
"morgenklaar": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://morgenklaar.nl/mcp", "--header", "Authorization:${MK_SLEUTEL}"],
"env": { "MK_SLEUTEL": "Bearer mk_jouw_sleutel" }
}
}
}
- Je assistent ziet deze tools, één per adres hierboven: site, bestellingen, bestelling, producten, product, afspraken, afspraak, artikelen, artikel, paginas, pagina, omzet. Elke tool vraagt hetzelfde recht als zijn adres. Mist de sleutel dat recht, dan zegt de tool dat.
- Elke vraag van je assistent aan een tool telt mee voor de limieten van je sleutel, net als een gewone aanroep. In je portaal zie je ze terug bij de laatste aanroepen, als /mcp/ met de naam van de tool.
- Wat je assistent leest, gaat naar de aanbieder van die AI. Geef een sleutel voor je assistent dus alleen het recht lezen:bestellingen als je het goed vindt dat namen, e-mailadressen en adressen van je kopers daar terechtkomen.
- De sleutel staat nu in een bestand op je eigen computer. Doe dit dus alleen op een computer die van jou is, en trek de sleutel in als je die computer niet meer gebruikt.
- Wat bezoekers schrijven, zoals een opmerking bij een bestelling, geven we door als gegevens en niet als opdracht. Heeft je assistent ook andere koppelingen, zoals je mail, kijk dan zelf na wat hij doet.
Je site laten aanpassen, in het concept
Geef je de sleutel ook het recht schrijven:concept, dan kan je assistent je site voor je aanpassen. Je vraagt bijvoorbeeld "Maak de kop op mijn voorpagina korter" of "Zet de letters wat zakelijker". Alles wat hij doet komt in het concept van je editor, net als wat je zelf typt voordat je op Online zetten drukt. Er gaat nooit iets vanzelf online.
- De tools heten concept_lezen, tekst_wijzigen, blok_verbergen, stijl_kiezen, concept_bekijken. Je assistent leest eerst het concept, en past dan teksten, knoppen en links aan, verbergt een blok of een regel, of kiest kleuren, letters en vormen uit de vaste keuzes van de editor.
- Open daarna je editor in het portaal. Bovenaan staat wat je assistent klaarzette, met per onderdeel wat er anders is. Met Eerst bekijken zie je het in het voorbeeld, met Ga verder waar je was zet je het in je velden, en met Online zetten staat het op je site. Wil je het niet, kies dan Niet nodig.
- Wat je assistent niet kan: iets online zetten, pagina's toevoegen of weghalen, eigen CSS of code, je logo, je contactgegevens, je bedrijfsgegevens, prijzen van afspraken, de reviews van je klanten, je webshop, je mail, je domein of je abonnement. Dat doe je zelf in je portaal.
- Je assistent kan hooguit 10 keer per minuut iets aanpassen, met hooguit 50 wijzigingen per keer. Elke keer staat in je portaal bij de laatste aanroepen van de sleutel, zonder wat hij veranderde.
- Heb je de editor zelf open en typ je verder, dan bewaart de editor jouw velden als concept. Wat je assistent intussen klaarzette op dezelfde pagina, kan dan wegvallen. Laat hem dus niet tegelijk met jou aan dezelfde pagina werken.
Blokken en foto's, als voorstel
Met hetzelfde recht kan je assistent ook blokken toevoegen, verschuiven of weghalen en foto's kiezen. Je vraagt bijvoorbeeld "Zet een blok met mijn tarieven onder de diensten" of "Zet een foto van brood bovenaan". In de editor staat zoiets meteen online. Via je assistent nooit: hij maakt een voorstel, net als Leo, en jij beslist.
- De tools heten voorstel_bekijken, blok_toevoegen, blok_verplaatsen, blok_weghalen, fotos_lezen, foto_zetten. Je assistent kiest uit de blokken die de editor aanbiedt, met dezelfde beginregels, en foto's uit je eigen foto's of uit de fotobank van de editor. Een foto van een ander adres kan hij niet laten ophalen.
- Open daarna je editor in het portaal. Onderin staat Voorstel van je AI, met de naam van de sleutel of de app, en wat er verandert. Met Bekijken zie je het in het voorbeeld, met Goedkeuren staat het op je site, en met Afwijzen gebeurt er niets. Na Goedkeuren staat je site van ervoor onder Eerdere versies.
- Er staat per site één voorstel klaar. Je assistent vult zijn eigen voorstel aan, maar niet een voorstel van Leo of van een andere sleutel of app. Dan zegt hij dat je eerst moet goedkeuren of afwijzen.
- Blokken met afspraken, producten, een nieuwsbrief of insluitcode voegt hij niet toe, en blokken met afspraken, producten, een nieuwsbrief of reviews haalt hij niet weg. Het logo en het icoontje kiest hij niet. Hooguit 8 foto's per keer.
Webhooks.
Wil je niet steeds zelf vragen of er iets nieuws is? Stel dan een webhook in. Wij sturen een bericht naar jouw adres zodra er iets gebeurt, bijvoorbeeld naar Zapier, Make, n8n of je eigen server. Dat doe je in je portaal onder API-sleutels, vanaf het pakket Plus, met hooguit 5 webhooks per site.
bestelling.betaald
Zodra een koper een bestelling in je webshop heeft betaald. Met de gegevens van de koper: naam, e-mailadres, telefoonnummer en bezorgadres.
bestelling.status_gewijzigd
Als een betaalde bestelling helemaal is terugbetaald of door de koper via de bank is teruggeboekt. Ook met de gegevens van de koper.
afspraak.geboekt
Zodra een bezoeker een afspraak heeft gemaakt, en betaald als je vooraf laat betalen. Zonder de gegevens van de bezoeker.
afspraak.geannuleerd
Als een afspraak is afgezegd. Zonder de gegevens van de bezoeker.
Elk bericht is een POST met JSON. In data staan dezelfde velden als bij de API, zoals het er op het moment van versturen voor staat. Bij bestelling.status_gewijzigd staat de oude status in vorige_status. Met de knop Testbericht in je portaal stuur je een bericht met de gebeurtenis test.
{
"id": "lev_01k8gq2v7x3m9d4c5b6a7z8y9w",
"gebeurtenis": "bestelling.betaald",
"aangemaakt_op": "2026-10-26T14:03:11+01:00",
"site": { "naam": "Bakkerij de Vries", "domein": "bakkerijdevries.nl" },
"data": {
"nummer": 42,
"status": "betaald",
"betaald_op": "2026-10-26T14:03:11+01:00",
"totaal_centen": 4850,
"koper": { "naam": "Sanne de Vries", "email": "sanne@voorbeeld.nl" }
}
}
Controleer de handtekening
Bij het instellen krijg je één keer een geheim, dat begint met mkwh_. Elk bericht heeft deze headers:
- X-Morgenklaar-Handtekening: t=1761487391,v1=5f2b.... Hier is t het tijdstip van versturen in seconden sinds 1970, en v1 de HMAC-SHA256 (hex) over t, een punt en de ruwe body, met je geheim als sleutel.
- X-Morgenklaar-Gebeurtenis: wat er gebeurde, zoals bestelling.betaald.
- X-Morgenklaar-Levering: het id van dit bericht, hetzelfde als id in de body. Bij een nieuwe poging blijft het gelijk. Zag je het al, sla het bericht dan over.
- X-Morgenklaar-Poging: de hoeveelste poging dit is.
Reken de handtekening zelf na over de ruwe body, dus voordat je hem als JSON inleest. Vergelijk in constante tijd en weiger een bericht waarvan t meer dan vijf minuten afwijkt van je eigen klok. Zo kan een onderschept bericht later niet opnieuw worden gebruikt.
PHP
<?php
// body: de ruwe tekst van het verzoek, kop: de header X-Morgenklaar-Handtekening.
function klopt(string $body, string $kop, string $geheim): bool
{
parse_str(str_replace(',', '&', $kop), $delen);
$t = (int) ($delen['t'] ?? 0);
$v1 = $delen['v1'] ?? null;
$verwacht = hash_hmac('sha256', $t . '.' . $body, $geheim);
return is_string($v1) && abs(time() - $t) <= 300 && hash_equals($verwacht, $v1);
}
$body = file_get_contents('php://input');
if (! klopt($body, $_SERVER['HTTP_X_MORGENKLAAR_HANDTEKENING'] ?? '', getenv('MK_WEBHOOK_GEHEIM'))) {
http_response_code(400);
exit;
}
$bericht = json_decode($body, true);
// Zag je $bericht['id'] al eerder? Sla het bericht dan over.
// Verwerk $bericht['gebeurtenis'] en $bericht['data'], en antwoord snel.
http_response_code(204);
Node.js
const crypto = require('node:crypto');
// body: de ruwe tekst van het verzoek, kop: de header X-Morgenklaar-Handtekening.
function klopt(body, kop, geheim) {
const delen = Object.fromEntries(String(kop || '').split(',').map((d) => d.split('=', 2)));
const t = Number(delen.t);
const verwacht = crypto.createHmac('sha256', geheim).update(`${t}.${body}`).digest('hex');
const v1 = String(delen.v1 || '');
return Number.isInteger(t) && Math.abs(Date.now() / 1000 - t) <= 300
&& v1.length === verwacht.length
&& crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(verwacht));
}
// Met Express: lees de body ruw in, niet eerst als JSON.
app.post('/morgenklaar', express.raw({ type: 'application/json' }), (req, res) => {
const body = req.body.toString('utf8');
if (!klopt(body, req.get('X-Morgenklaar-Handtekening'), process.env.MK_WEBHOOK_GEHEIM)) {
return res.sendStatus(400);
}
const bericht = JSON.parse(body);
// Zag je bericht.id al eerder? Sla het bericht dan over.
// Verwerk bericht.gebeurtenis en bericht.data, en antwoord snel.
res.sendStatus(204);
});
- Antwoord binnen 10 seconden met een code in de 200. Doe zwaar werk liever daarna. Wat je terugstuurt lezen we niet.
- Lukt het niet (een andere code, geen antwoord op tijd, of een doorverwijzing, want die volgen we niet), dan proberen we het opnieuw na 1 minuut, 5 minuten, 30 minuten, 2 uur en 6 uur. Lukt er drie dagen lang geen enkele levering, dan zetten we de webhook uit en krijg je een mail.
- We sturen alleen naar een adres met https:// op de gewone poort (443), en nooit naar een adres in een intern netwerk. Draai je zelf n8n op een andere poort, zet er dan een gewone https-server voor.
- Het kan gebeuren dat een bericht twee keer komt, of later dan een nieuwer bericht. Gebruik het id om dubbele over te slaan, en gewijzigd_op in data om te zien wat het nieuwst is.
- Mist je server een tijd berichten? Haal wat je mist op met de API en gewijzigd_sinds. Berichten die niet aankwamen sturen we niet alsnog.
- In je portaal zie je per webhook de laatste 50 leveringen: wanneer, welke gebeurtenis, welke poging, de uitkomst en hoe lang het duurde. De inhoud van de berichten bewaren we niet. Dat overzicht bewaren we 90 dagen.
- Bij bestellingen staan de gegevens van de koper in het bericht. Stuur ze dus alleen naar een dienst die je vertrouwt. Staat je site niet online (gestopt of gepauzeerd), dan sturen we niets.
Versies.
Alle adressen staan onder /api/v1. In deze versie komen er alleen velden bij. Iets veranderen of weghalen gebeurt alleen in een nieuwe versie (/v2), en dat kondigen we van tevoren aan. Schrijf je programma dus zo dat het velden negeert die het niet kent.
Kom je er niet uit?
Vraag het ons gerust. Wil je een systeem koppelen dat hier nog niet bij past, of mis je iets? Laat het weten.