LazyBill API
Koppel je eigen systemen aan LazyBill: maak relaties en facturen aan, verstuur ze, boek uren en kosten, en krijg een seintje zodra een factuur betaald is. De API is beschikbaar op elk plan, ook tijdens de gratis proef.
Introductie
De LazyBill API is een REST-API die JSON praat. Alles wat je koppelt werkt op het niveau van één organisatie: je API-token hoort bij een organisatie en ziet alleen de relaties, facturen en uren van die organisatie.
- Basis-URL -
https://lazybill.nl/api/v1 - Formaat - JSON in en uit; stuur
Content-Type: application/jsonenAccept: application/jsonmee. - Versie - v1 zit in het pad. Binnen v1 breken we niets: er komen hooguit velden bij.
Typische toepassingen: na een geleverde dienst automatisch een concept-factuur klaarzetten, gewerkte uren doorsturen vanuit je eigen systeem, of je administratie bijwerken zodra een factuur betaald is.
Snelstart
In drie stappen van niets naar een verstuurde factuur. Maak eerst een token aan via
Instellingen > API in LazyBill en vul dat hieronder in voor {token}.
1. Maak een relatie aan
curl -X POST https://lazybill.nl/api/v1/relations \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"type": "company",
"name": "Acme B.V.",
"email": "administratie@acme.nl"
}'
2. Maak een concept-factuur
Gebruik het id uit stap 1 als relation_id.
curl -X POST https://lazybill.nl/api/v1/invoices \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"relation_id": 12,
"invoice_date": "2026-07-23",
"lines": [
{ "description": "Consultancy juli", "quantity": 8, "unit_price": "95.00", "vat_rate": 21 }
]
}'
3. Verstuur de factuur
De factuur krijgt nu een definitief nummer en wordt gemaild naar het adres van de relatie. Liever eerst controleren? Sla deze stap over: het concept staat gewoon in LazyBill klaar.
curl -X POST https://lazybill.nl/api/v1/invoices/88/send \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{}'
Authenticatie
Alle endpoints vereisen een API-token. De eigenaar van een organisatie maakt tokens aan via Instellingen > API. Het token wordt daar één keer getoond; bewaar het als een wachtwoord (bv. in een secrets-manager, nooit in je frontend of repository).
Stuur het token mee als Bearer-token in de Authorization-header:
Authorization: Bearer {token}
- Zonder (geldig) token krijg je
401 Unauthorized. - Een token geeft volledige toegang tot de data van zijn organisatie - en tot niets anders. Records van andere organisaties bestaan voor jouw token niet (
404). - Een token intrekken kan op dezelfde instellingenpagina; koppelingen die het gebruiken stoppen dan per direct.
- Gebruik per koppelend systeem een eigen token, dan kun je er later één intrekken zonder de rest te raken.
Conventies
Datums en tijdstippen
- Datums stuur en ontvang je als ISO 8601:
2026-07-23. - Tijdstippen (zoals
sent_atenpaid_at) komen terug als RFC 3339:2026-07-23T10:00:00+02:00.
Bedragen en btw
"95.00". Dit geldt ongeacht hoe de organisatie in LazyBill zelf prijzen invoert
(incl. of excl.) - de API is daar bewust onafhankelijk van. In responses is
subtotal exclusief en total inclusief btw.
Btw geef je per regel op als kaal percentage in vat_rate:
21, 9 of 0 - de drie Nederlandse tarieven. Een ander
percentage wordt afgekeurd met een 422, zodat een typefout nooit stilletjes een
verkeerde factuur oplevert. Responses geven hetzelfde kale percentage terug.
Daarnaast zijn er drie bijzondere regimes die alle drie 0% rekenen maar elk
een eigen, verplichte vermelding op de factuur hebben - een kaal percentage kan dat verschil
niet uitdrukken. Stuur daarvoor vat_scheme mee op de regel
(vat_rate mag dan weg); LazyBill zet de juiste vermelding automatisch op het
factuurdocument en de PDF. In responses zie je vat_scheme op elke regel terug
(null bij een gewoon percentage).
| vat_scheme | Betekenis | Vermelding op de factuur |
|---|---|---|
reverse_charged | Btw verlegd (bouw/onderaanneming, EU B2B) | "Btw verlegd." |
exempt | Vrijgesteld van btw (bv. KOR, zorg, onderwijs) | "Vrijgesteld van btw." |
margin | Margeregeling (handel in gebruikte goederen) | "Bijzondere regeling - gebruikte goederen (margeregeling)." |
Paginering
Lijst-endpoints zijn gepagineerd. Stuur page en per_page
(standaard 25, maximaal 100) mee; de response bevat naast data ook
links en meta met onder andere meta.current_page,
meta.last_page en meta.total.
GET /api/v1/invoices?status=sent&per_page=50&page=2
Rate limiting
Maximaal 60 requests per minuut per organisatie. Daarboven krijg je
429 Too Many Requests; de Retry-After-header vertelt hoeveel
seconden je moet wachten. Synchroniseer je veel data, bouw dan een kleine pauze in.
Fouten
Fouten komen altijd als JSON terug met een message. De statuscodes:
| Status | Betekenis |
|---|---|
401 | Geen of ongeldig API-token. |
402 | De proefgrens van het account is bereikt: versturen kan pas weer na het activeren van een abonnement. Concepten aanmaken blijft gewoon werken. |
403 | Deze actie is niet toegestaan. |
404 | Niet gevonden - ook wanneer het record bij een andere organisatie hoort. |
409 | Conflict met de huidige status, bv. een al verzonden factuur nogmaals versturen. |
422 | Validatiefout; zie het formaat hieronder. |
429 | Rate limit bereikt; probeer het na de Retry-After opnieuw. |
Bij een validatiefout (422) staat per veld wat er mis is:
{
"message": "Vul de naam van de relatie in.",
"errors": {
"name": ["Vul de naam van de relatie in."],
"lines.0.unit_price": ["Vul een geldig bedrag in."]
}
}
Relaties
Relaties zijn je klanten: bedrijven of particulieren. Elke factuur en elke geboekte post hangt aan een relatie.
Relaties ophalen
/api/v1/relations| Parameter | Uitleg |
|---|---|
search | Zoekt in de naam, bv. ?search=acme. |
per_page / page | Paginering (zie Conventies). |
{
"data": [
{
"id": 12,
"type": "company",
"name": "Acme B.V.",
"email": "administratie@acme.nl",
"phone": null,
"website": null,
"address_street": "Dorpsstraat 1",
"address_postal": "1234 AB",
"address_city": "Amsterdam",
"address_country": "nl",
"kvk_number": "12345678",
"vat_number": null,
"hourly_rate": null,
"send_reminders": true,
"notes": null,
"created_at": "2026-07-01T09:12:33+02:00",
"updated_at": "2026-07-01T09:12:33+02:00"
}
],
"links": { "...": "..." },
"meta": { "current_page": 1, "last_page": 1, "total": 1 }
}
Relatie aanmaken
/api/v1/relations| Veld | Type | Uitleg |
|---|---|---|
typeverplicht | string | company of person. |
nameverplicht | string | Naam van het bedrijf of de persoon. |
email | string | Facturen en herinneringen gaan naar dit adres. |
phone / website | string | Optionele contactgegevens. |
address_street / address_postal / address_city / address_country | string | Adres; land als tweeletterige code (bv. nl). |
kvk_number | string | 8 cijfers. |
vat_number | string | Btw-nummer. |
hourly_rate | decimal | Uurtarief-override voor deze relatie (excl. btw); leeg = het standaardtarief van de organisatie. |
send_reminders | boolean | Betaalherinneringen voor deze relatie (standaard aan). |
notes | string | Interne notitie. |
{
"type": "company",
"name": "Acme B.V.",
"email": "administratie@acme.nl",
"kvk_number": "12345678"
}
{
"data": {
"id": 12,
"type": "company",
"name": "Acme B.V.",
"email": "administratie@acme.nl",
"kvk_number": "12345678",
"...": "..."
}
}
Eén relatie ophalen
/api/v1/relations/{id}Geeft dezelfde velden terug als de lijst.
Producten
Producten en diensten beheer je in LazyBill zelf; via de API kun je ze opzoeken om een
product_id mee te geven op een factuurregel. Dat koppelt de regel aan het
product (handig voor rapportage), de omschrijving en prijs geef je per regel gewoon zelf op.
/api/v1/products{
"data": [
{
"id": 4,
"name": "Consultancy",
"description": "Advies en implementatie",
"price": "95.0000",
"vat_rate": 21,
"vat_scheme": null,
"unit": "hour",
"created_at": "2026-06-12T14:20:11+02:00",
"updated_at": "2026-06-12T14:20:11+02:00"
}
],
"links": { "...": "..." },
"meta": { "...": "..." }
}
price is exclusief btw. unit is een van piece, hour, day, week, month of year.
Facturen
Een factuur begint als concept: zonder nummer, nog volledig te bewerken in LazyBill. Pas bij het versturen krijgt hij een definitief, opvolgend nummer en wordt hij bevroren. Zo blijft de nummerreeks altijd sluitend.
Facturen ophalen
/api/v1/invoices| Parameter | Uitleg |
|---|---|
status | Filter: concept, sent, overdue, reminder_1, reminder_2, reminder_final of paid. |
relation_id | Alleen facturen van deze relatie. |
per_page / page | Paginering; gesorteerd nieuwste eerst. |
Eén factuur ophalen
/api/v1/invoices/{id}{
"data": {
"id": 88,
"number": "2026-0012",
"status": "sent",
"is_credit": false,
"credit_of_invoice_id": null,
"relation": { "id": 12, "name": "Acme B.V.", "...": "..." },
"invoice_date": "2026-07-23",
"due_date": "2026-08-22",
"reference": "Order 554",
"notes": null,
"subtotal": "760.00",
"vat_total": "159.60",
"total": "919.60",
"lines": [
{
"id": 301,
"product_id": 4,
"description": "Consultancy juli",
"quantity": "8.00",
"unit_price": "95.0000",
"vat_rate": 21,
"vat_scheme": null,
"line_total": "760.00",
"position": 0
}
],
"pay_url": "https://lazybill.nl/pay/abc123...",
"sent_at": "2026-07-23T10:00:00+02:00",
"paid_at": null,
"created_at": "2026-07-23T09:58:11+02:00",
"updated_at": "2026-07-23T10:00:00+02:00"
}
}
subtotalis exclusief,totalinclusief btw.pay_urlis de publieke betaallink voor de klant;nullzolang de factuur niet online betaalbaar is (bv. concept, al betaald, of geen betaalprovider gekoppeld).numberisnullbij een concept.
Concept-factuur aanmaken
/api/v1/invoices| Veld | Type | Uitleg |
|---|---|---|
relation_idverplicht | integer | De klant; moet een relatie van jouw organisatie zijn. |
invoice_dateverplicht | date | Factuurdatum, YYYY-MM-DD. |
due_date | date | Vervaldatum. Weglaten = afgeleid uit de betaaltermijn van de relatie (of de standaardtermijn uit de instellingen). |
reference | string | Jouw kenmerk; doorzoekbaar in LazyBill. |
notes | string | Opmerking op de factuur. |
linesverplicht | array | Minstens één regel. |
lines[].descriptionverplicht | string | Omschrijving van de regel. |
lines[].quantityverplicht | decimal | Aantal. |
lines[].unit_priceverplicht | decimal | Stukprijs, exclusief btw. |
lines[].vat_rateverplicht | number | Btw-percentage: 21, 9 of 0. |
lines[].vat_scheme | string | Bijzonder regime: reverse_charged, exempt of margin; vat_rate mag dan weg. Zie Conventies. |
lines[].product_id | integer | Optionele koppeling aan een product. |
{
"relation_id": 12,
"invoice_date": "2026-07-23",
"due_date": "2026-08-22",
"reference": "Order 554",
"lines": [
{
"description": "Consultancy juli",
"quantity": 8,
"unit_price": "95.00",
"vat_rate": 21
}
]
}
{
"data": {
"id": 88,
"number": null,
"status": "concept",
"subtotal": "760.00",
"vat_total": "159.60",
"total": "919.60",
"...": "..."
}
}
Factuur versturen
/api/v1/invoices/{id}/sendMaakt het concept definitief (nummer toegekend, bevroren) en mailt de factuur als PDF naar de klant, inclusief betaallink als de organisatie online betalen heeft gekoppeld.
| Veld | Type | Uitleg |
|---|---|---|
to | string | E-mailadres van de ontvanger. Weggelaten? Dan gebruikt LazyBill het adres van de relatie of het hoofdcontact. |
message | string | Persoonlijk bericht bovenin de e-mail. |
skip_email | boolean | true = alleen definitief maken, niet mailen (bv. als je 'm zelf verstuurt). |
{
"data": { "id": 88, "number": "2026-0012", "status": "sent", "...": "..." },
"meta": { "mailed_to": "administratie@acme.nl", "mail_error": null }
}
- Een mislukte e-mail blokkeert het versturen niet: de factuur is dan wél definitief en de foutmelding staat in
meta.mail_error. - Op een factuur die geen concept meer is krijg je
409. - Is de proefgrens van het account bereikt, dan krijg je
402en blijft de factuur een concept.
Uren & kosten
Uren en losse kosten zijn declarabele posten: je boekt ze los, en LazyBill bundelt alle onbefactureerde posten automatisch tot één concept-factuur per relatie in de periodieke facturatie (samen met eventuele abonnementen). Je hoeft er dus zelf geen facturen van te maken - al kan dat natuurlijk wel.
/api/v1/billablesUren
Bij uren bepaalt LazyBill het tarief server-side: het uurtarief van de relatie als dat er is,
anders het standaardtarief van de organisatie. Een eigen unit_price of
vat_rate meesturen is dan een validatiefout - zo kan een koppelend systeem nooit
per ongeluk een afwijkend tarief boeken.
{
"kind": "hours",
"relation_id": 12,
"date": "2026-07-23",
"description": "Sprintwerk",
"quantity": 1.5
}
{
"data": {
"id": 51,
"kind": "hours",
"relation_id": 12,
"date": "2026-07-23",
"description": "Sprintwerk",
"quantity": "1.50",
"unit_price": "95.0000",
"vat_rate": 21,
"vat_scheme": null,
"total": "142.50",
"invoice_id": null,
"created_at": "2026-07-23T16:41:09+02:00"
}
}
Kosten
Bij kosten geef je zelf een (netto) bedrag op. quantity is optioneel (standaard 1),
vat_rate ook (standaard 21).
{
"kind": "cost",
"relation_id": 12,
"date": "2026-07-23",
"description": "Reiskosten",
"unit_price": "45.00"
}
| Veld | Type | Uitleg |
|---|---|---|
kindverplicht | string | hours of cost. |
relation_idverplicht | integer | De relatie waarvoor je boekt. |
dateverplicht | date | Datum van de post, YYYY-MM-DD. |
descriptionverplicht | string | Omschrijving; komt straks op de factuurregel. |
quantity | decimal | Uren: verplicht, als decimaal (1,5 uur = 1.5). Kosten: optioneel aantal. |
unit_price | decimal | Alleen bij kosten (verplicht): het netto bedrag. Bij uren verboden. |
vat_rate | number | Alleen bij kosten (optioneel): 21, 9 of 0 (standaard 21). Bij uren verboden. |
vat_scheme | string | Alleen bij kosten: reverse_charged, exempt of margin, zie Conventies. Bij uren verboden. |
In de response zie je het toegepaste unit_price en het regeltotaal.
invoice_id blijft null tot de post gefactureerd is.
Webhooks
Wil je direct weten wanneer een factuur betaald is? Stel via
Instellingen > API een webhook-URL in. LazyBill stuurt dan een
POST naar die URL zodra een factuur op betaald gaat - via de betaallink, via
automatische incasso of handmatig in de app.
Payload
{
"event": "invoice.paid",
"occurred_at": "2026-07-23T14:03:07+02:00",
"invoice": {
"id": 88,
"number": "2026-0012",
"status": "paid",
"total": "919.60",
"relation": { "id": 12, "name": "Acme B.V.", "...": "..." },
"...": "..."
}
}
invoice bevat het volledige factuur-object, precies zoals bij GET /api/v1/invoices/{id}.
Signature verifiëren
Elke aflevering is ondertekend, zodat je zeker weet dat de melding echt van LazyBill komt.
De header X-Lazybill-Signature bevat een HMAC-SHA256 van de rauwe request-body,
berekend met het webhook-secret van je organisatie (te vinden op de API-instellingenpagina).
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_LAZYBILL_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $secret);
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit;
}
hash_equals.
Afleveren en opnieuw proberen
- Beantwoord een webhook binnen 10 seconden met een 2xx-status. Zwaar werk? Zet de melding in een queue en antwoord meteen.
- Mislukte afleveringen worden niet opnieuw geprobeerd; de mislukking is wel zichtbaar in de geschiedenis van de factuur in LazyBill. Bouw je iets kritisch, poll dan als vangnet periodiek
GET /api/v1/invoices?status=paid. - Behandel meldingen idempotent: verwerk dezelfde factuur-id niet twee keer.