Werkende TypeScript-code voor debiteurenbeheer in Moneybird: openstaande facturen ophalen, betalingen uitlezen en interpreteren, betalingen registreren, webhooks afhandelen en rapportages genereren.
Dit project bestaat in twee smaken. Ze concurreren niet met elkaar; ze zitten op een andere laag.
| skill-moneybird | skill-moneybird-typescript (deze repo) | |
|---|---|---|
| Wat het is | een Claude Agent Skill: een set instructies in het Nederlands | broncode: TypeScript-functies die de Moneybird-API aanroepen |
| Wie doet het werk | een AI-assistent, via Moneybirds eigen AI-koppeling | jouw eigen programma |
| Hoe je het gebruikt | je vraagt in gewone taal: "welke klanten hebben een betalingsachterstand?" | je roept een functie aan: getOpenInvoicesWithDetails() |
| Nodig | Claude of ChatGPT, plus een Moneybird-koppeling | Node.js, TypeScript, en ergens om het te draaien |
| Programmeren | nee | ja |
| Draait vanzelf | nee — jij begint het gesprek | ja — cron, webhook of trigger naar keuze |
| Uitkomst | een antwoord in de chat, per keer net iets anders geformuleerd | dezelfde input geeft altijd dezelfde output |
| Installeren | map kopiëren naar ~/.claude/skills/ |
npm install in je eigen project |
De skill vertelt een AI hoe hij zich moet gedragen. Deze repo doet het werk zelf.
Daar volgt de rest uit. Een skill is tekst: hij bevat regels, volgorde en waarschuwingen, maar voert niets uit — dat doet het model, via een koppeling. Deze code voert wél uit, en doet dat elke keer identiek. Dat is een voordeel als je betrouwbaarheid nodig hebt, en een nadeel als je flexibiliteit wilt.
| Wat je wilt | Kies |
|---|---|
| Even weten wie er nog moet betalen | skill |
| Een bankafschrift verwerken terwijl je meekijkt en goedkeurt | skill |
| Vragen stellen die je vooraf niet had bedacht | skill |
| Geen code aanraken | skill |
| Elke maandagochtend automatisch een debiteurenoverzicht mailen | TypeScript |
| Direct reageren als er een betaling binnenkomt (webhooks) | TypeScript |
| Dit inbouwen in een eigen dashboard of portaal | TypeScript |
| Precies kunnen nalezen wat er gebeurt, regel voor regel | TypeScript |
Twijfel je? Begin bij de skill-versie. Die is in vijf minuten werkend en dekt verreweg de meeste situaties. Kom je daar iets tegen dat niet kan — plannen, herhalen, integreren — dan is deze repo de volgende stap.
Je kunt ze ook naast elkaar gebruiken: de skill voor de dagelijkse vragen, deze code voor wat automatisch moet draaien.
- De duplicaatcontrole — dezelfde regel, dezelfde sleutel (bedrag + datum), dezelfde uitkomsten.
- De veiligheidsregels — read-only beginnen, goedkeuring per handeling, nooit een token in een bestand.
- De setup — beide hebben een administratie-ID en een API-token nodig, op dezelfde manier aangemaakt.
- De licentie — MIT.
Wat er niet hetzelfde is: alleen deze repo kan webhooks ontvangen en kan als MCP-server worden aangeboden (mcp-manifest.json).
Een vraag twee keer stellen is gratis. Een betaling twee keer registreren is dat niet.
Een eerdere versie van deze code registreerde betalingen zonder te kijken of ze er al stonden. Bij het opnieuw verwerken van een bankafschrift werden bestaande betalingen nog een keer toegevoegd, met dubbele betalingen in de administratie als gevolg.
Er zat geen fout in de API en geen fout in de aanroep. De handeling deed exact wat er stond — maar wist niet dat hij al eerder was uitgevoerd.
registerPayment() doet daarom altijd eerst een duplicaatcontrole:
| Situatie | Uitkomst |
|---|---|
| Zelfde bedrag, zelfde datum bestaat al | duplicate → niet registreren |
| Zelfde bedrag, andere datum | needs_review → voorleggen aan een mens |
total_unpaid is al 0 |
nothing_open → niets te doen |
| Geen match | ok → registreren |
De datum maakt het verschil. Dedupliceer je alleen op bedrag, dan gooi je legitieme tweede betalingen weg — een klant kán twee keer betalen, maar vrijwel nooit op dezelfde dag.
Moneybird kent geen "ongedaan maken" voor geregistreerde betalingen. Wat je niet hebt vastgelegd vóórdat je schreef, moet je erna reconstrueren uit je geheugen.
const before = await snapshotOpenInvoices();
// ... betalingen registreren ...
const changes = await diffAgainstSnapshot(before);
console.log(changes.new_payments, changes.difference);snapshotOpenInvoices() legt alle openstaande facturen vast inclusief hun bestaande betalingen. Bewaar de uitkomst — het is je nulmeting én het materiaal om handmatig terug te draaien.
diffAgainstSnapshot() laat daarna zien wat er precies veranderd is: welke betalingen erbij kwamen en hoeveel het totaal openstaand verschoof.
Twee regels die dit samenvatten: altijd controleren, en zorgen voor een backup.
Dat klinkt vanzelfsprekend en is het niet — het gaat mis op de dag dat je haast hebt. Daarom staan beide hier als functie en niet als advies: controleren moet weinig moeite kosten, anders gebeurt het niet.
Loop de eerste verwerkingen ook echt met de hand na. Niet steekproefsgewijs — helemaal.
Er is een manier om de duplicaatcontrole te omzeilen, en die is met opzet onhandig:
await registerPayment(invoiceId, payload, { force: true });Gebruik het alleen nadat een mens de needs_review heeft beoordeeld. Niet in een lus, niet als standaardinstelling.
| Bestand | Inhoud |
|---|---|
src/invoices-service.ts |
de kern: facturen ophalen, betalingen interpreteren, status en restschuld berekenen, betalingen registreren (mét duplicaatcontrole), momentopname en diff, webhooks beheren, rapportages |
src/webhook-handler.ts |
ontvangst en verwerking van Moneybird-webhooks |
src/examples.ts |
voorbeeldaanroepen van alle functies |
mcp-manifest.json |
manifest om dit als MCP-server aan te bieden |
npm install
cp .env.example .env # en vul je gegevens in
npm run typecheckJe hebt twee gegevens uit Moneybird nodig. Beide maak je zelf aan.
1. Administratie-ID — staat in de URL zodra je bent ingelogd:
https://moneybird.com/1415161819/feed
└────────┘
2. API-token — via Instellingen → Externe en AI-koppelingen. Bij het aanmaken kies je:
- Rechten:
Read onlyof read-write. Kies read-only tenzij je betalingen moet registreren. - Onderdelen: geef alleen wat nodig is — facturen, plus bank bij het verwerken van afschriften.
⚠️ Read-only of read-write is niet achteraf te wijzigen. Omzetten betekent: nieuw token aanmaken, oude verwijderen.
⚠️ Een read-only token kan alleen de eigenaar van de administratie aanmaken.
3. Optioneel: financiële rekening. Zet MONEYBIRD_FINANCIAL_ACCOUNT_ID als je betalingen altijd op dezelfde rekening boekt. Het ID vraag je op met getFinancialAccounts().
De code leest alles uit omgevingsvariabelen en stopt met een duidelijke foutmelding als er iets ontbreekt — je loopt dus niet op een onverklaarbare 401.
Zet een token nooit in een bronbestand. Gebeurt dat toch: het bestand aanpassen is niet genoeg. Trek het token in Moneybird in en maak een nieuw aan, want het blijft geldig en staat inmiddels ook in je git-historie, backups en eventuele screenshots.
- Begin read-only. Bevragen levert al veel op en kan niets kapotmaken.
- Maak een momentopname vóór elke schrijfactie en vergelijk erna. Zie hierboven.
- Draai nooit een hele batch opnieuw zonder te weten wat er al verwerkt is. Breekt een verwerking halverwege af, leg dan vast tot waar hij kwam.
- Controleer de eerste verwerkingen handmatig, en houd dat langer vol dan comfortabel voelt.
- Anonimiseer klantnamen en bedragen in screenshots, blogs en trainingsmateriaal.
- Moneybird stelt expliciet dat configuratie en gebruik je eigen verantwoordelijkheid zijn.
Deze code wijzigt gegevens in je boekhouding. Test met een read-only token voordat je gaat schrijven, en controleer de eerste verwerkingen met de hand. De auteur is niet aansprakelijk voor onjuiste boekingen of gevolgschade — zie LICENSE.
- API-token aanmaken en beheren
- Read-only API-token aanmaken
- AI-koppeling (MCP)
- Zelf een koppeling maken
MIT — zie LICENSE.
Gemaakt door Eelco Wynia — AI-trainer en interim online marketeer. Zoek je de versie zonder code? → skill-moneybird