// blog/developer/
Back to Blog
Developer · August 29, 2026 · 9 min read · By the ToolForte team

Dutch Test Data from a Script: BRP and UPA Generators over API and MCP

Two of the more specialised tools on ToolForte are now callable from code: the BRP/GBA test data generator and the UPA file generator. Not a cut-down version of them either. Every slider, dropdown and checkbox on those pages is a field in the request body, and anything you leave out falls back to the same default the page starts with.

This post covers what that changes, with the exact requests, and where the limits are.

* * *

The tool page, and what it can set

Here is the BRP generator as it looks in a browser. Record count, the seed, the age band, how many unusual names, how many surnames with a tussenvoegsel, how many addresses with a house number addition, and below that a weight per life event.

Every one of those is now a field you can send. The screenshot is worth studying for a moment, because it is also the parameter list.

The BRP Test Data Generator page with sliders for record count, seed, age range, unusual names, tussenvoegsels and house number additions
The BRP Test Data Generator page with sliders for record count, seed, age range, unusual names, tussenvoegsels and house number additions
* * *

Three reasons to call it instead of clicking

Test automation. The seed fixes the output. Ask for ten people with seed 42 today and you get the same ten in six months, so an assertion on a specific BSN keeps passing. That is the difference between generated fixtures and a CSV somebody committed in 2024 that slowly stopped matching the schema.

Development and CI. One call fills a database, including the records nobody types by hand: names with diacritics, a person living abroad with no Dutch postcode, an address with a house number addition, someone who died halfway through the period. Those are the rows that break an import, and they are exactly the rows a developer inventing test data will never think of.

Reliable AI output. Ask a language model for Dutch test data and it produces BSNs that look completely convincing and fail the elfproef, because it is guessing nine digits. Over MCP the model calls the generator instead of guessing, so the numbers actually validate. The same is true of UPA XML, which no model can produce correctly from memory.

Key takeaway

**Test automation.** The seed fixes the output.

* * *

BRP: the requests

The smallest useful request is an empty body. It returns fifty people with the page defaults.

`bash curl -X POST https://toolforte.com/api/v1/tools/brp-test-data-generator \ -H "Content-Type: application/json" \ -H "X-API-Key: your_api_key_here" \ -d '{}' `

Something more specific: twenty-five people over sixty, married or widowed, in a fixed set you can reproduce.

`bash curl -X POST https://toolforte.com/api/v1/tools/brp-test-data-generator \ -H "Content-Type: application/json" \ -H "X-API-Key: your_api_key_here" \ -d '{ "options": { "count": 25, "seed": 20270101, "minAge": 60, "maxAge": 80, "referenceYear": 2027, "eventMix": { "married": 5, "widowed": 3 } } }' `

One detail that matters: naming any life event replaces the whole mix. The request above returns married and widowed people, nothing else. Leave eventMix out entirely and you keep the default spread across all fifteen events. The first version of this endpoint merged your weights into the defaults, which meant asking for widows returned mostly ordinary people. That is the kind of quiet mismatch that makes a generated test set worse than useless, so it was changed.

For a CSV with your own column names, in the delimiter your import expects:

`json { "format": "csv", "options": { "count": 100, "outputMode": "own", "headerRow": "burgerservicenummer;achternaam;geboortedatum", "delimiter": ";", "excelHint": true } } `

And "format": "gba-totaalfile" returns the GBA totaalfile layout instead of a readable sheet.

* * *

Documentation that cannot drift

An endpoint with eighteen options is only usable if the reference is exhaustive, and an exhaustive reference maintained by hand is wrong within a month. Somebody widens a range, and the docs still quote the old one.

So the parameters are declared once, in code, and three things read from that single declaration: the validation that runs on your request, the reference table below, and the OpenAPI schema. Change a range and the table changes with it on the next build. Add an option to the generator without describing it and the test suite fails, which is the entire point.

The full reference lives at /developers/generators.

The generator reference page showing a table of every option with its type, range, default and description
The generator reference page showing a table of every option with its type, range, default and description
* * *

Errors that tell you what to fix

The caller here is a script or an agent, neither of which can see the slider that would have prevented the mistake. So the errors name the field and the allowed values.

`json // {"options": {"cuont": 10}} { "error": "Unknown option: cuont. Valid options: count, seed, minAge, ..." }

// {"options": {"count": 99999}} { "error": "Field options.count must be a whole number between 1 and 5000." }

// {"options": {"eventMix": {"getrouwd": 5}}} { "error": "Unknown key \"getrouwd\" in options.eventMix. Valid keys: plain, married, registeredPartnership, ..." } `

Note that a misspelled option is refused rather than ignored. Ignoring it would hand back data that quietly differs from what was asked for, and you would only find out when a test passed for the wrong reason.

* * *

UPA: pension declarations, including broken ones

The UPA generator produces the Uniforme Pensioenaangifte XML that a payroll system sends to a pension administrator. Same approach: every setting on the page is a field.

`bash curl -X POST https://toolforte.com/api/v1/tools/upa-file-generator \ -H "Content-Type: application/json" \ -H "X-API-Key: your_api_key_here" \ -d '{ "format": "xml", "options": { "schemeType": "FPR", "population": { "count": 50, "seed": 20270101 }, "period": { "startYear": 2027, "startMonth": 1, "months": 3 } } }' `

The part worth knowing about is the defect switches. A test that only ever feeds a system correct files does not tell you whether the system rejects incorrect ones, and rejection is where pension interfaces actually fail.

`json { "options": { "population": { "count": 10 }, "defects": { "invalidBsn": true, "totalsMismatch": true } } } `

The response says so plainly, so nobody mistakes them for a bug in the generator:

`json { "warnings": ["2 deliberate defects injected. These files are meant to be rejected."] } `

XML gets large quickly. Use "format": "summary" when you only need file names and warnings, which is most of the time in a pipeline.

Key takeaway

The UPA generator produces the Uniforme Pensioenaangifte XML that a payroll system sends to a pension administrator.

* * *

The UPA page, for reference

The tool page carries more configuration than the BRP one: the declaring employer, the scheme rows and their variants, the period type and how files are split. All of it is settable in the request, and the complete default object is printed on the reference page so you can see the exact shape rather than reading a description of it.

The UPA File Generator page showing declaration settings, scheme rows and period configuration
The UPA File Generator page showing declaration settings, scheme rows and period configuration
* * *

From an AI assistant

Both generators are on the ToolForte MCP server as generate_brp_test_data and generate_upa_files. Add the server to Claude, ChatGPT or Cursor and you can ask in plain language:

Generate 10 Dutch test people over 60, half of them widowed, and put them in a CSV I can import.
Make a UPA declaration for 20 employments over 3 months in 2027, and include a file with a mismatched total so I can test the rejection.

The counts are capped lower over MCP than over REST, deliberately. An assistant pays for every token it reads back, so generating five thousand records into a chat window is a waste of money on both sides. For bulk work, ask the assistant to write a script against the REST endpoint instead, which is a thing it is good at.

Key takeaway

Both generators are on the [ToolForte MCP server](/mcp) as `generate_brp_test_data` and `generate_upa_files`.

* * *

Limits, and what it costs

Five thousand people per call for BRP, ten thousand employments for UPA, the same ceilings the tool pages have. Both endpoints need a free API key, which you can get on the developers page.

The browser versions stay free and unmetered, and they always will. Nothing you can do on the page has been moved behind the API. The API exists because a browser is the wrong place to be when the thing you actually want is a filled database, a CI fixture or a diff against last month's declaration.