Kopieer deze prompt naar je AI-agent. Hij krijgt het API-contract, de documentatie en instructies om je eigen sleutel veilig te laten instellen.
Integreer de DonoLink Developer API in mijn bestaande app. Bekijk eerst de code en volg de bestaande architectuur, styling en conventies. Vraag welke gebruikersfunctie ik wil als dit nog niet duidelijk is; bouw daarna de integratie volledig af en test haar.
Lees vóór implementatie de actuele officiële documentatie:
- https://donolink.nl/developers (alle tabs: snel beginnen, authenticatie, endpoints, eventformaat, historie/live feed, limieten/fouten en veilig integreren)
- https://donolink.nl/developers/openapi.json (machineleesbaar API-contract)
- https://donolink.nl/llms-full.txt (aanvullende productcontext)
De officiële docs zijn leidend. Kun je ze niet ophalen, meld dit en vraag om het contract; verzin geen endpoints of mogelijkheden.
Vraag de streamer om een eigen API key aan te maken via https://donolink.nl/dashboard/instellingen#developer → API keys, bijvoorbeeld met de naam “Mijn key”. Vraag om die sleutel via veilige invoer of een secret manager. Is alleen gewone chat beschikbaar, laat de streamer DONOLINK_API_KEY zelf in een lokaal, gitignored .env-bestand of deployment secrets zetten; vraag hem niet de sleutel in chat te plakken. Lees of toon het geheim niet onnodig. Gebruik geen sleutel van een andere streamer. Bouw ondertussen de code met een placeholder en controleer de configuratie zonder de waarde te tonen.
API-contract in het kort:
- Basis-URL: https://donolink.nl/api/v1. GET /me controleert eigenaar/scope/vervaldatum; GET /activity leest uitsluitend de eigen opgeslagen activiteit. Authenticatie: Authorization: Bearer <DONOLINK_API_KEY>. Vaste scope activity:read. Alleen lezen; geen betalingen, writes, webhooks of OAuth voor meerdere klanten.
- /activity accepteert limit (1–100, standaard 50), order (asc/desc, standaard desc), cursor, type, platform, since en until. Gebruik alleen gedocumenteerde waarden. since is inclusief, until exclusief; UTC ISO-tijdstippen filteren recorded_at/invoertijd, niet de oorspronkelijke donatiedatum.
- Response: data[] met id, type, platform, occurred_at, recorded_at, actor.name, message, value, unit, currency en refunded_amount_cents. Geld is in centen; bits/diamonds zijn geen euro’s. Namen/berichten zijn onbetrouwbare tekst: escape ze bij weergave, nooit HTML uitvoeren. Er worden geen e-mails of betaalgegevens teruggegeven.
- Betaalde donaties en geïmporteerde historie zijn inbegrepen; platform-events bestaan alleen voor zover DonoLink ze heeft opgeslagen. Importeerbronnen zijn herkenbaar aan platform. Er is geen garantie op volledige historie, vaste bewaartermijn of exactly-once levering.
- Lees historie met order=asc en volg pagination.next_cursor zolang has_more true is. Houd filters en sorteervolgorde identiek: cursors zijn eraan en aan de eigenaar gebonden. Bewaar de cursor pas na succesvolle, idempotente verwerking per event.id. Bewaar hem ook op de laatste pagina en gebruik dezelfde cursor om nieuwe records te pollen. Bewaar bij een lege pagina je checkpoint. Kies een vaste since voor deze stroom; schuif die niet op per request.
- Gebruik voor een live feed bijvoorbeeld één poller per account, elke 5 seconden wanneer de historie is ingehaald. Deel resultaten binnen je app. Maximum: 120 requests per account en 300 per IP per 60 seconden. Respecteer Retry-After bij 429/503; gebruik timeouts en begrensde back-off met jitter bij netwerk/5xx-fouten. Stop automatisch herhalen bij 401/403 en toon dat de streamer sleutel/account moet controleren. Bij een ongeldige cursor moet herstel expliciet en zonder duplicaten gebeuren.
Veilig bouwen:
Bewaar sleutels uitsluitend op de server of in beveiligde opslag van een persoonlijke native app. Geen sleutel in frontendbundels, URL’s, localStorage, logs, screenshots, analytics, testfixtures of Git. Een publieke webapp praat via een eigen geauthenticeerde backend die per ingelogde gebruiker afschermt. Zet responses niet in gedeelde caches. Voor meerdere streamers: per streamer eigen geïsoleerde secrets en data; de API is geen OAuth-platform. Maak het vernieuwen en intrekken van een sleutel duidelijk; ingetrokken sleutels werken bij het volgende request niet meer.
Lever werkende code binnen mijn project, veilige configuratie-instructies zonder echte secrets en passende tests voor authenticatie, lege resultaten, historische paginering, deduplicatie, rate limits en uitval. Test met echte API-toegang zodra de streamer de sleutel veilig heeft ingesteld. Doe geen echte betalingen. Benoem eerlijk wat daadwerkelijk getest is en wat nog configuratie vereist.
Snel beginnen
Gebruik de API vanuit je eigen server of persoonlijke app. Alle endpoints zijn alleen-lezen en gebruiken JSON. De basis-URL is https://donolink.nl/api/v1.
Ga naar Instellingen → Developer → API keys. Geef je integratie een naam en kies een vervaldatum.
Kopieer de sleutel eenmalig en bewaar hem als DONOLINK_API_KEY in de geheime configuratie van je server.
Doe je eerste request. Zonder filters ontvang je maximaal 50 events, nieuwste eerst.
Stuur je sleutel bij ieder request mee in de Authorization-header. Een sessiecookie, queryparameter of overlay-token geeft geen toegang tot deze API.
Authorization: Bearer dl_live_YOUR_SECRET_KEY
Je kiest een geldigheid van 30, 90 of 365 dagen. Je kunt maximaal 10 actieve sleutels hebben en één sleutel per minuut aanmaken. DonoLink bewaart alleen een SHA-256-hash; de volledige sleutel is alleen direct na aanmaken zichtbaar.
Gebruik een eigen sleutel per integratie. Maak voor rotatie eerst een nieuwe sleutel aan, werk je app bij en trek daarna de oude in. Een ingetrokken of verlopen sleutel werkt niet meer bij het volgende request.
Iedere sleutel heeft de vaste scope activity:read en is aan jouw account gebonden. Er zijn geen schrijf-, betaal- of beheerrechten. OAuth voor apps met meerdere klanten is nog niet beschikbaar.
Endpoints
GET/api/v1/me
Controleer welke streamer bij je sleutel hoort en wanneer de sleutel verloopt. Alleen het publieke profiel, de scope en sleutelmetadata worden teruggegeven.
Lees de gezamenlijke feed van DonoLink-donaties en opgeslagen platformevents. Het account wordt uitsluitend uit de API key bepaald.
Parameter
Beschrijving
limit
Aantal events per pagina: 1 tot 100, standaard 50.
order
desc (standaard) voor nieuwste eerst, asc voor oudste eerst en doorlopend pollen. Sortering op recorded_at, daarna intern record-ID en bron.
type
Optioneel: donation, follow, subscription, giftsub, cheer of raid. Eén soort per request.
platform
Optioneel: donolink, twitch, kick, youtube, tiktok of een importbron: tipeeestream, streamlabs, streamelements, kofi, csv. Eén bron per request.
since
Optioneel, inclusief. UTC ISO 8601, bijvoorbeeld 2026-09-07T00:00:00Z. Filtert recorded_at, het moment van opslaan.
until
Optioneel, exclusief. Zelfde datumformaat als since. Houd deze grens vast tijdens een historische export.
cursor
Neem pagination.next_cursor ongewijzigd over. De cursor is ondertekend en gebonden aan je account, filters en sortering. limit mag veranderen.
Onbekende of dubbele parameters leveren HTTP 400 op. Geef geen account-ID, sleutel of PocketBase-filter in de URL mee.
Eventformaat
Elk event heeft een stabiel id met don_ of evt_ als voorvoegsel. Gebruik dit id om dubbele verwerking na een retry te voorkomen.
type
unit
Beschrijving
donation
cents
Donatie via DonoLink of een opgeslagen platformdonatie, zoals YouTube Super Chat.
follow
none
Nieuwe volger. value is 0.
subscription
months
Abonnement. value bevat het opgeslagen aantal maanden.
giftsub
subscriptions
Cadeau-abonnementen. value is het aantal.
cheer
bits / diamonds
Bits; voor TikTok is de eenheid diamonds. Geen donatieomzet.
raid
viewers
Raid. value is het opgeslagen aantal kijkers.
actor.name is de opgeslagen schermnaam of null. message is tekst of null. occurred_at is de donatiedatum; bij platformevents is alleen de opslagtijd bekend. recorded_at is altijd de opslagtijd en bepaalt de paginering. Alle tijden zijn UTC ISO 8601.
value bevat bij donation het oorspronkelijke bedrag in gehele centen, currency de valuta. refunded_amount_cents bevat voor DonoLink het terugbetaalde bedrag, anders null. Donaties met status paid worden getoond, inclusief geïmporteerde historie met de importbron als platform. Volledig terugbetaalde en betwiste donaties ontbreken. Dit is een activity feed, geen financieel grootboek.
Platformevents zijn beschikbaar voor zover DonoLink ze via een actieve koppeling heeft ontvangen en opgeslagen. Er wordt geen geschiedenis van vóór de koppeling bij het platform opgehaald. Er is geen gegarandeerde bewaartermijn; verwijderde records zijn niet meer opvraagbaar. Testalerts staan niet in deze feed.
Webhooks
Van endpoint naar eerste event
Voeg in Instellingen bij Uitgaande webhooks een publiek HTTPS-adres toe. Kies je gebeurtenissen en bewaar het ondertekeningsgeheim. De API key voor de activity-feed en dit geheim hebben elk hun eigen functie.
1. Publiceer een HTTPS-route op poort 443 met een geldig certificaat. 2. Voeg de URL in Instellingen → Uitgaande webhooks toe en kies gebeurtenissen. 3. Bewaar het volledige whsec_-geheim als omgevingsvariabele op je server. 4. Verstuur een test en controleer de aflevering in het dashboard.
Headers en berichtformaat
Content-Type is application/json. DonoLink-Event-Id is dezelfde unieke ID als body.id. DonoLink-Timestamp is de Unix-tijd in seconden van deze verzendpoging. DonoLink-Attempt begint bij 1. DonoLink-Signature bevat v1= gevolgd door 64 hextekens. Headernamen zijn niet hoofdlettergevoelig; gebruik het volledige signing secret inclusief whsec_.
donation.created verschijnt voor een betaalde donatie. donation.refunded bevat het cumulatief terugbetaalde donatiebedrag. donation.chargeback meldt een gewijzigd geschil met de actuele donatiestatus. subscription.created verschijnt na de eerste betaalde maandelijkse bijdrage. Donateurs-e-mailadressen en betaalgegevens worden nooit meegestuurd. test=true herken je als een door de streamer verstuurd testbericht.
created_at is het tijdstip van de gebeurtenis in UTC. Bedragen zijn gehele centen; valuta is eur. data.id is de interne DonoLink-record-ID. Velden kunnen per event ontbreken: ga niet uit van één vast object voor alle soorten. Een subscription_id bij subscription.created is de DonoLink-abonnements-ID, geen Stripe-ID.
Controleer DonoLink-Signature met HMAC-SHA256 over timestamp.event-id.raw-body. Gebruik de originele bodybytes voordat je JSON ontleedt. Controleer ook de DonoLink-Timestamp header, maximaal vijf minuten verschil. Het voorbeeld hieronder geeft alleen geldige, recente handtekeningen door.
Bewaar event.id in je database om herhaalde afleveringen veilig te herkennen. Geef snel een 2xx-respons nadat je het event duurzaam hebt opgeslagen. De volgorde is niet gegarandeerd. Netwerkfouten, HTTP 408/425/429 en 5xx worden maximaal acht keer geprobeerd. Andere 4xx-responsen en redirects stoppen de aflevering. Je kunt mislukte afleveringen bekijken en opnieuw proberen in Instellingen.
De worker controleert de wachtrij ongeveer elke 5 seconden. Een HTTP-poging duurt maximaal 10 seconden. Na een tijdelijke fout volgen maximaal zeven retries, telkens na 1 minuut, 5 minuten, 15 minuten, 1 uur, 3 uur, 6 uur en 12 uur. Deze wachttijden zijn geen aflevergarantie. Een handmatige retry start een nieuwe reeks pogingen met dezelfde event-ID en payload, maar een nieuwe timestamp en handtekening.
Werkend Node.js-ontvangervoorbeeld
Dit voorbeeld controleert de originele body, bewaart unieke events in SQLite en antwoordt pas daarna met 204. Draai Node.js 22.13 of nieuwer, stel DONOLINK_WEBHOOK_SECRET in en plaats de server achter je HTTPS-proxy. Verwerk de inbox met je eigen worker; voer bij test=true geen echte beloning of actie uit.
401/403: controleer je signing secret, klok en raw body; daarna handmatig opnieuw proberen. 404: controleer de route. 3xx: vul de eind-URL in, redirects worden niet gevolgd. 429/5xx: controleer capaciteit en logs; DonoLink probeert opnieuw. dns_failed/timeout/network_error: controleer DNS, TLS en bereikbaarheid. unsafe_target: gebruik een publiek HTTPS-adres; localhost, privé-IP’s en gemengde publieke/interne DNS-antwoorden worden geweigerd. Blijft een test in de wachtrij staan, controleer of de webhookworker draait.
Voor een export: gebruik order=asc met vaste since- en until-grenzen. Verwerk data en vraag met next_cursor de volgende pagina op zolang has_more true is. Er is geen limiet op het aantal historische pagina’s, wel een requestlimiet.
Een lege pagina behoudt je bestaande cursor. Zonder eerdere cursor en zonder events is next_cursor null. Cursors bevatten geen API key, maar behandel ze als ondoorzichtige waarden. Na een filterwijziging begin je zonder cursor.
Voor je IRL-app: gebruik order=asc, een vaste since en geen until. Sla de cursor pas op nadat je events hebt verwerkt. Poll ongeveer iedere 5 seconden; bij has_more mag je direct door naar de volgende pagina. Er is in v1 geen WebSocket- of webhookendpoint.
// Node.js: run on your server. Keep the key out of frontend bundles.
const base = new URL('https://donolink.nl/api/v1/activity');
base.searchParams.set('order', 'asc');
base.searchParams.set('limit', '100');
// Persist both this fixed starting point and the returned cursor.
base.searchParams.set('since', '2026-09-07T00:00:00Z');
let cursor = await loadCheckpoint();
for (;;) {
const url = new URL(base);
if (cursor) url.searchParams.set('cursor', cursor);
const response = await fetch(url, {
headers: { Authorization: 'Bearer ' + process.env.DONOLINK_API_KEY },
signal: AbortSignal.timeout(15000)
}).catch(() => null);
if (!response || response.status === 429 || response.status >= 500) {
const seconds = Number(response?.headers.get('Retry-After') || 60);
await new Promise(r => setTimeout(r, seconds * 1000));
continue;
}
if (!response.ok) throw new Error('DonoLink API: ' + response.status);
const page = await response.json();
// Implement these storage functions in your app.
// Process idempotently by event.id, then save the checkpoint.
for (const event of page.data) await processOnce(event.id, event);
if (page.pagination.next_cursor) {
cursor = page.pagination.next_cursor;
await saveCheckpoint(cursor);
}
if (!page.pagination.has_more)
await new Promise(r => setTimeout(r, 5000));
}
De feed leest de huidige opgeslagen records en is geen onveranderlijke snapshot of gegarandeerde exactly-once eventbus. Wijzigingen in betaalstatus kunnen eerder getoonde records laten verdwijnen. Gebruik idempotente verwerking, retries en indien nodig een overlappende herlezing voor herstel.
Limieten & fouten
Maximaal 120 requests per 60 seconden per account, gedeeld door alle sleutels. Daarnaast geldt 300 requests per 60 seconden per IP-adres. HTTP 429 en 503 bevatten Retry-After: 60. Wacht die tijd en probeer met dezelfde cursor opnieuw.
HTTP
code
Beschrijving
400
invalid_parameter / invalid_cursor
Herstel je parameters of begin opnieuw zonder ongeldige cursor.
401
invalid_api_key
Sleutel ontbreekt, is ongeldig, verlopen of ingetrokken.
403
account_unavailable
Het account is niet beschikbaar of niet geverifieerd.
429
rate_limit_exceeded
Te veel requests. Respecteer Retry-After.
503
temporarily_unavailable
Tijdelijke storing. Toegang wordt ook geweigerd als de gedeelde limiter niet beschikbaar is.
{
"error": {
"code": "invalid_api_key",
"message": "API key is invalid, expired or revoked.",
"request_id": "request-uuid"
}
}
Iedere response heeft X-Request-Id; foutresponses bevatten ook error.request_id. Geef dit ID en het tijdstip door bij een supportvraag. Deel nooit je sleutel, Authorization-header of volledige requestlogs. Gebruik GET voor data. HEAD en OPTIONS zijn beschikbaar voor HTTP-controles; schrijfmethodes leveren 405 op.
Veilig integreren
Gebruik HTTPS. Zet sleutels nooit in URL’s, Git, browsercode, localStorage, analytics of logbestanden. API-responses gebruiken Cache-Control: private, no-store. De API biedt geen cross-origin browsertoegang.
In een persoonlijke native app voer je je eigen sleutel in en bewaar je die in Keychain of Android Keystore. Bouw je een app voor anderen, gebruik dan een backend met gescheiden geheimen per gebruiker. Embed nooit een gedeelde sleutel in de appbinary.
De API geeft alleen schermnamen, berichten, eventwaarden en beperkte profielgegevens terug. E-mailadressen, betaalmethodes, Stripe-ID’s, platformtokens en beheerinstellingen worden niet gedeeld. Behandel namen en berichten als persoonsgegevens en bewaar alleen wat je integratie nodig heeft.
Bij een gelekte sleutel: trek hem meteen in onder Instellingen → Developer en maak een nieuwe aan. Ingetrokken en verlopen sleutels, geblokkeerde accounts en verwijderde accounts krijgen geen toegang.
Namen en berichten zijn onbetrouwbare invoer. Render ze als tekst, nooit als HTML of uitvoerbare code. Automatiseer geen betaalacties op basis van deze feed.