Zo zet je een betaal-QR op je facturen
Elke factuur krijgt zijn eigen unieke QR-code met het juiste bedrag en betalingskenmerk. Kies de route die bij je past:
🧰
Handig, geen programmeur
Je maakt je facturen in Word, Excel of een boekhoudpakket en durft een stapje verder te gaan dan alleen klikken. Geen programmeerkennis nodig.
👩💻
Ontwikkelaars
Je bouwt of beheert de software die facturen genereert en wilt QR-codes volautomatisch aanmaken via de REST API.
Spoor 1
Handig, maar geen programmeur
Drie manieren, van makkelijk naar iets avontuurlijker. Voor allemaal geldt: maak eerst een gratis account aan en vul bij Instellingen je standaard-IBAN in.
Optie A — Klikken in het dashboard (geen enkele code)
Maak de QR-code aan
Ga naar Dashboard → QR-codes, vul het factuurbedrag en het betalingskenmerk in (bijv. je factuurnummer) en klik op QR-code genereren.
Download de afbeelding
Klik in de lijst op PNG (voor Word/e-mail) of SVG (voor drukwerk) en sla de afbeelding op.
Plak hem op je factuur
Voeg de afbeelding in je factuursjabloon in — onderaan bij de betaalinstructies werkt het best, minimaal 2×2 cm groot. Zet er een regel bij als: “Scan de QR-code met je telefooncamera en betaal direct.”
Stuur je digitaal? Zet de betaallink erbij
Klik in de lijst op Kopieer achter de betaallink en plak hem in je e-mail of factuur. Zie “De betaallink meesturen” hieronder.
De betaallink meesturen (voor digitale facturen)
Een QR-code is ideaal op een uitgeprinte factuur, maar onhandig als je klant de factuur op zijn telefoon opent — hij kan zijn eigen scherm niet scannen. Daarom heeft elke QR-code ook een betaallink naar precies dezelfde betaalpagina:
https://zwoep.nl/b/K7M4X2PQJe vindt hem in het dashboard bij QR-codes, in de kolom Betaallink, met een kopieerknop ernaast. Direct na het aanmaken staat hij er ook al, inclusief een kant-en-klare regel om in je mail te plakken:
Betaal € 149,95 voor Factuur 2026-0042 direct online:
https://zwoep.nl/b/K7M4X2PQ- Zet beide op je factuur. De QR-code voor wie hem afdrukt of op een tweede apparaat leest, de link voor wie hem op zijn telefoon opent. Het blijft één betaalverzoek met één status.
- Je betaalt niet dubbel. Alleen de eerste keer dat een mens het betaalverzoek opent telt als verwerkte QR — of dat nu via de QR-code of via de link gebeurt.
- Automatisch verkeer telt niet mee. Linkcontroles van mailbeveiliging, previews in WhatsApp of Teams en prefetch door de browser laten we bewust buiten de telling.
Optie B — Afbeeldings-URL in een digitale factuur of e-mail
Verstuur je facturen als HTML-e-mail of online pagina? Dan hoef je niets te downloaden: elke QR-code heeft een vaste afbeeldings-URL die je direct in je sjabloon zet.
<img src="https://zwoep.nl/qr/JOUW-QR-ID/image?format=png&size=300"
alt="Betaal deze factuur via QR" width="150" height="150">Het QR-ID vind je in het dashboard bij de aangemaakte code. Let op: elke factuur = één eigen QR-code, dus dit werkt vooral handig in combinatie met optie C of D.
Optie C — Google Sheets: QR-codes uit je factuurlijst
Houd je je facturen bij in een spreadsheet? Met dit kant-en-klare script maakt Google Sheets voor elke rij automatisch een QR-code aan. Zet in kolom A het factuurnummer en in kolom B het bedrag (bijv. 149.95).
Wil je liever één keer een CSV uploaden dan een script rij voor rij te laten draaien? Zie “Batch-upload” verderop op deze pagina.
Maak een API-key aan
In het dashboard onder API-keys. Kopieer de key — je ziet hem maar één keer.
Open de scripteditor
In Google Sheets: Extensies → Apps Script. Plak het script hieronder en vul je API-key in.
Voer het script uit
Klik op Uitvoeren. In kolom C verschijnt de QR-code als afbeelding, in kolom D de afbeeldings-URL en in kolom E de betaallink om mee te sturen in een digitale factuur. Sleep de afbeelding zo je factuur in, of gebruik de kolommen in een mailmerge.
const API_KEY = "zw_live_JOUW_KEY_HIER";
function maakQrCodes() {
const sheet = SpreadsheetApp.getActiveSheet();
const rijen = sheet.getDataRange().getValues();
for (let i = 1; i < rijen.length; i++) {
const [kenmerk, bedrag, bestaandeQr] = rijen[i];
if (!kenmerk || !bedrag || bestaandeQr) continue;
const antwoord = UrlFetchApp.fetch("https://zwoep.nl/api/v1/qr-codes", {
method: "post",
contentType: "application/json",
headers: { Authorization: "Bearer " + API_KEY },
payload: JSON.stringify({
amount: { value: String(bedrag), currency: "EUR" },
reference: String(kenmerk),
}),
});
const qr = JSON.parse(antwoord.getContentText());
sheet.getRange(i + 1, 3).setFormula('=IMAGE("' + qr.links.imagePng + '")');
sheet.getRange(i + 1, 4).setValue(qr.links.imagePng + "&size=1024");
sheet.getRange(i + 1, 5).setValue(qr.links.paymentLink);
}
}Gebruik je een boekhoudpakket (Moneybird, e-Boekhouden, Exact)?
Voor Moneybird staat een kant-en-klare koppeling in de ontwikkelaarssectie hieronder — die is ook door een handige niet-programmeur te draaien. Voor andere pakketten: vraag je leverancier naar een “webhook” of “koppeling per factuur”, of stuur ons een bericht, dan kijken we mee.
Heeft je pakket géén API (zoals MoneyMonk, Rompslomp of Tellow)? Dan kan het via PDF-nabewerking: een klein script leest het factuurnummer en bedrag uit de gedownloade factuur-PDF, maakt de QR aan en stempelt hem op de factuur. Dat werkt met elk pakket dat facturen als PDF exporteert (ook direct als kant-en-klaar script te downloaden).
Spoor 2
Ontwikkelaars
REST API v1: JSON in en uit, authenticatie met een Bearer-token, cursor-paginering. Eén call per factuur bij het genereren — meer is het niet.
Kant-en-klare voorbeelden per boekhoudpakket
Moneybird heeft een publieke REST API, dus de hele keten kan volautomatisch: open facturen ophalen, per factuur een QR aanmaken, de factuur-PDF downloaden en de QR erop stempelen.
e-Boekhouden.nl heeft een publieke REST API met sessie-gebaseerde authenticatie: elke factuur bevat een kant-en-klare PDF-downloadlink, dus ook hier kan de hele keten automatisch.
Jortt heeft een publieke REST API met OAuth2 Client Credentials-authenticatie — geen eindgebruiker-login nodig voor je eigen koppeling, en de hele keten kan automatisch.
Exact Online heeft het grootste marktaandeel in het Nederlandse mkb en een publieke REST API — maar authenticeert alleen via OAuth2 Authorization Code, dus dit voorbeeld vergt één eenmalige handmatige inlogstap.
WeFact heeft een publieke API met een simpele API-sleutel — geen OAuth, geen tokens die verlopen. Van alle koppelingen hieronder is dit de kortste: één sleutel, twee calls per factuur.
Heeft jouw pakket geen publieke API — of wil je er simpelweg geen koppeling voor bouwen? Dan werkt dit voorbeeld altijd: je downloadt de factuur-PDF uit je pakket, en het script leest factuurnummer en bedrag eruit, maakt de QR aan en stempelt hem erop.
- MoneyMonk
- Rompslomp
- Tellow
- InformerOnline
- Asperion
- Reeleezee
- Minox
- Silvasoft
- … en elk ander pakket dat PDF's exporteert
Jouw boekhoudpakket er niet bij?
Geen probleem: de kaart Werkt met elk pakket hierboven werkt zolang je pakket facturen als PDF exporteert. Wil je liever een directe koppeling, stuur ons dan een bericht via support@zwoep.nl — heeft het pakket een API, dan bouwen we er graag een voorbeeld bij.
Authenticatie
Maak een API-key aan in het dashboard (API-keys) en stuur hem mee in elke request:
Authorization: Bearer zw_live_...QR-code aanmaken
POST /api/v1/qr-codes bij het genereren van de factuur. Elke QR-code gebruikt altijd het IBAN en de begunstigde die bij Instellingen op je account staan — dat is niet per aanroep te overschrijven, zodat een gelekte API-key nooit betalingen naar een ander rekeningnummer kan laten sturen.
# curl
curl -X POST https://zwoep.nl/api/v1/qr-codes \
-H "Authorization: Bearer $ZWOEP_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": { "value": "149.95", "currency": "EUR" },
"reference": "Factuur 2026-0042",
"description": "Websiteonderhoud juni",
"expiresAt": "2026-10-01T00:00:00Z"
}'// Node.js / TypeScript
const response = await fetch("https://zwoep.nl/api/v1/qr-codes", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ZWOEP_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: { value: "149.95", currency: "EUR" },
reference: "Factuur 2026-0042",
}),
});
const qr = await response.json();
// qr.links.imagePng → embed in je factuur-PDF
// qr.links.paymentLink → als klikbare link in de factuurmail
// qr.id → bewaar bij de factuur voor statuschecks# Python
import requests
qr = requests.post(
"https://zwoep.nl/api/v1/qr-codes",
headers={"Authorization": f"Bearer {ZWOEP_KEY}"},
json={
"amount": {"value": "149.95", "currency": "EUR"},
"reference": "Factuur 2026-0042",
},
).json()
print(qr["links"]["imagePng"])Batch-upload (meerdere QR-codes in één call)
Moet je in één keer veel QR-codes aanmaken? POST /api/v1/qr-codes/batch accepteert een JSON-array of een CSV-bestand (max. 100 rijen per call) en meldt per rij terug of het gelukt is.
POST /api/v1/qr-codes/batch
Content-Type: application/json
Authorization: Bearer zw_live_...
[
{ "amount": { "value": "12.50", "currency": "EUR" }, "reference": "Factuur 2026-001" },
{ "amount": { "value": "8.00", "currency": "EUR" }, "reference": "Factuur 2026-002" }
]Of als CSV — handig als je facturen al in een spreadsheet staan:
POST /api/v1/qr-codes/batch
Content-Type: text/csv
Authorization: Bearer zw_live_...
amount,reference,description
12.50,Factuur 2026-001,
8.00,Factuur 2026-002,Tweede termijnDe response meldt per rij of het gelukt is, met de index uit je array of CSV:
{
"object": "qr_code_batch",
"created": 2,
"failed": 0,
"results": [
{ "index": 0, "status": "created", "qrCode": { "...": "..." } },
{ "index": 1, "status": "created", "qrCode": { "...": "..." } }
]
}CSV-kolomnamen: amount, reference (verplicht), description, expiresAt (optioneel). Kolomvolgorde is vrij.
Gebruik in CSV een punt als decimaalteken, bijv. 12.50 — een komma wordt als kolomscheiding gelezen.
De betaallink in de factuurmail
Verstuur je facturen per e-mail, zet dan naast de afbeelding ook links.paymentLink in de mail of het HTML-sjabloon. Die wijst naar dezelfde betaalpagina, maar is klikbaar — je klant leest de factuur immers vaak op de telefoon waarmee hij zou moeten scannen.
<img src="${qr.links.imagePng}&size=300" width="150" height="150"
alt="Betaal deze factuur via QR">
<p>Liever niet scannen? Betaal direct via
<a href="${qr.links.paymentLink}">${qr.links.paymentLink}</a></p>Beide routes leiden naar hetzelfde betaalverzoek en dezelfde status; alleen het eerste bezoek van een mens is de verwerkte (factureerbare) scan. Linkcontroles van mailbeveiliging, previews in chat-apps en browser-prefetch tellen niet mee, dus de link meesturen kost je niets extra.
De afbeelding op de factuur
links.imagePngaccepteert&size=128…2048— gebruik ≥1024 voor print (2×2 cm op 300 dpi).links.imageSvgis vector en dus altijd scherp — de beste keuze voor PDF-generatoren die SVG aankunnen.- Download de afbeelding tijdens het genereren en embed hem in de PDF; hotlinken kan, maar een factuur hoort zelfstandig leesbaar te zijn.
- Idempotentie: maak per factuurnummer één QR aan en bewaar het
id— niet bij elke PDF-regeneratie een nieuwe.
Status volgen
Pol de status om te zien of je klant de factuur heeft opgepakt — bijvoorbeeld in je aanmaanproces:
curl https://zwoep.nl/api/v1/qr-codes/{id} \
-H "Authorization: Bearer $ZWOEP_KEY"
# of alles wat nog open staat:
curl "https://zwoep.nl/api/v1/qr-codes?status=ACTIVE&limit=100" \
-H "Authorization: Bearer $ZWOEP_KEY"| Status | Betekenis |
|---|---|
| ACTIVE | Aangemaakt, nog niet gescand |
| SCANNED | Gescand door de ontvanger (het factureerbare moment) |
| PAID | Betaald via de iDEAL-knop (vereist een gekoppelde betaalprovider) |
| EXPIRED | Verlopen (expiresAt verstreken) |
| REVOKED | Ingetrokken via API of dashboard |
Intrekken & fouten
curl -X DELETE https://zwoep.nl/api/v1/qr-codes/{id} \
-H "Authorization: Bearer $ZWOEP_KEY"Foutresponses zijn JSON met een error-object: 401 ongeldige key, 404 onbekend id, 422 validatiefout (met details per veld). Lijsten pagineren met nextCursor.
Webhooks
In plaats van te pollen kun je in het dashboard onder Webhooks één of meer endpoints registreren die automatisch een POST krijgen bij een scan of statuswijziging. Twee event-typen:
qr_code.scanned— alleen bij de eerste (factureerbare) scan.qr_code.status_changed— bij elke statusovergang (SCANNED,PAID,EXPIRED,REVOKED); bevat altijdpreviousStatus/newStatus.
{
"id": "clxyz-delivery-id",
"type": "qr_code.status_changed",
"createdAt": "2026-07-20T10:15:00.000Z",
"data": {
"qrCodeId": "clxyz...",
"reference": "Factuur 2026-0042",
"amount": { "value": "149.95", "currency": "EUR" },
"previousStatus": "ACTIVE",
"newStatus": "SCANNED"
}
}Elke aanroep heeft een Zwoep-Signature-header in het formaat t=<unix>,v1=<hex>, waarbij hex een HMAC-SHA256 is (met je webhook-secret als sleutel) over de string ${t}.${rawBody}:
const crypto = require("crypto");
function isValidSignature(secret, rawBody, header) {
const [tPart, v1Part] = header.split(",");
const timestamp = tPart.split("=")[1];
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
return v1Part === `v1=${expected}`;
}Mislukte afleveringen krijgen automatisch retries met backoff; een endpoint dat lang genoeg onbereikbaar blijft, wordt automatisch gepauzeerd en moet je handmatig heractiveren in het dashboard. Een tijdelijke storing aan jouw kant is dus geen gemiste event.
Klaar om te beginnen?
Account aanmaken is gratis, QR-codes genereren ook. Je betaalt alleen als je klant scant.
Gratis account aanmaken