Sandbox test values and helpers
Drive a test customer through every state on the sandbox, from EAN check to ERE position, without waiting for Joulo.
The sandbox (https://api-staging.joulo.nl) runs the same code as production,
on test data. A real customer waits for a review by Joulo, needs a charger that
syncs, and receives mail. On the sandbox you drive those steps yourself: test
values pick an outcome, and helper endpoints move a customer to the next state.
The helpers work with your sandbox credentials only: a Partner API key
(jpk_test_) or a CPO client. They reach your own customers only. Production
answers every /sandbox path with 404.
Test values
The sandbox never calls the Dutch EAN register. It answers from the input, so each outcome can be reproduced on purpose.
EAN, by its last four digits:
| EAN ends in | ean_validation_status | Why |
|---|---|---|
0002 | unverified | The register does not know the address. Typical for a new build. |
0003 | mismatched | The address holds a different EAN. |
0005 | mismatched | The EAN exists at another address. The customer may explain. |
0009 | unavailable | The register is down. A customer cannot submit a registration until it answers. |
| anything else | matched | The EAN sits at exactly this address. |
House number, for address lookups without an EAN:
| House number | Result |
|---|---|
404 | No leverpunt on this address. |
300 | Two leverpunten, additions A and B: the customer picks one. |
| anything else | One leverpunt, with a sandbox EAN that ends in 0001. |
Email address of the customer:
| Address | Review outcome |
|---|---|
contains +afgekeurd before the @ | afgekeurd |
| anything else | goedgekeurd |
A submitted registration is reviewed automatically two minutes after submission. Use the review helper to decide sooner or to change the outcome.
To test ean_in_use, register the same EAN for a second customer.
Helpers
Base URL: https://api-staging.joulo.nl/functions/v1/api. A draft {id} is a
draft id or your external_ref from GET /partner/drafts. A customer {id}
is a customer_ref from GET /partner/customers, or an account id from the
CPO API or the claim helper.
| Method and path | Does |
|---|---|
GET /sandbox/v1 | Lists the helpers and the test values. |
GET /sandbox/v1/mail?to=&limit= | Mail your customers would have received, newest first, with the links in it. |
POST /sandbox/v1/drafts/{id}/claim | Claims a draft as the customer, and submits the registration if you ask. |
POST /sandbox/v1/customers/{id}/review | Approves or rejects a submitted registration. |
POST /sandbox/v1/customers/{id}/chargers | Connects a virtual charger and collects its sessions. |
POST /sandbox/v1/customers/{id}/sync | Collects the virtual charger's newest sessions now. |
POST /sandbox/v1/positions/refresh | Recomputes ERE positions now instead of within 15 minutes. |
POST /sandbox/v1/fixtures | Creates ready-made customers, one in each state. |
POST /sandbox/v1/reset | Deletes all your sandbox customers and drafts. |
Claim a draft as the customer
On production the customer opens the claim link, signs the machtiging and submits the registration. This helper does those steps for you, through the same code the claim page and the registration form use:
curl -X POST "https://api-staging.joulo.nl/functions/v1/api/sandbox/v1/drafts/CRM-42/claim" \
-H "Authorization: Bearer jpk_test_..." \
-H "Content-Type: application/json" \
-d '{"submit": true}'{
"ok": true,
"draft_id": "0d2b7a1e-…",
"external_ref": "CRM-42",
"account_id": "4c1f…",
"draft_status": "active",
"registration_status": "ingediend",
"steps": { "claim": "ok", "activate": "ok", "submit": "ok" },
"next": "The automatic review decides in about two minutes. …"
}Without submit the helper stops after the machtiging, like a customer who
has not filled in the registration yet. With submit: true it also connects a
virtual charger and submits the registration from the data you uploaded.
| Field | Default | Meaning |
|---|---|---|
submit | false | Also connect a virtual charger and submit the registration. |
iban | the test IBAN NL91ABNA0417164300 with submit, else none | Stored as the customer would enter it. ERE is allocatable only with an IBAN, so send null to test a customer without one. |
ean | the uploaded EAN, else the sandbox EAN for the address | A test value here picks the EAN outcome. |
charger | {"mid": true, "since": "2026-01-01"} | The virtual charger, with the fields of the charger helper. |
phone, street | a test number and street | Fields the upload does not carry. |
A missing name, postcode, house number or city falls back to a test value.
submit works for particulier drafts. A business registration needs a KvK
extract, so finish it on staging.joulo.nl.
The helper never logs into an account you did not create, on the sandbox
either. It answers 409 existing_account when the draft's email already
belongs to an account, and 409 claimed_by_customer when someone claimed the
draft through its link.
Read the mail a customer received
The sandbox sends no mail to your test addresses. It keeps every mail instead, so you can open the claim link or the login link yourself.
curl "https://api-staging.joulo.nl/functions/v1/api/sandbox/v1/mail?to=test%[email protected]" \
-H "Authorization: Bearer jpk_test_..."{
"mail": [
{
"captured_at": "2026-09-23T12:04:11Z",
"to": "[email protected]",
"subject": "Je laadstation levert geld op",
"template": "partner-draft-invite",
"links": ["https://staging.joulo.nl/claim/3f0c…"]
}
]
}Review a registration
curl -X POST "https://api-staging.joulo.nl/functions/v1/api/sandbox/v1/customers/1a2b3c4d/review" \
-H "Authorization: Bearer jpk_test_..." \
-H "Content-Type: application/json" \
-d '{"outcome": "goedgekeurd"}'The customer must have submitted the registration first. Otherwise the
helper answers 409 not_submitted.
Connect a virtual charger
curl -X POST "https://api-staging.joulo.nl/functions/v1/api/sandbox/v1/customers/1a2b3c4d/chargers" \
-H "Authorization: Bearer jpk_test_..." \
-H "Content-Type: application/json" \
-d '{"mid": true, "kwh_per_session": 12, "sessions_per_week": 4, "since": "2026-01-01"}'| Field | Default | Meaning |
|---|---|---|
mid | true | false connects a charger without a MID meter. Its sessions never count toward ERE. |
kwh_per_session | 12 | Average energy per session. |
sessions_per_week | 4 | Average sessions per week, 0 to 7. |
power_kw | 11 | Charging power. Sets how long a session lasts. |
since | 2026-01-01 | First day with sessions. |
The charger's sessions are generated the same way every time, and they enter through the same pipeline as a real charger's. New sessions appear every 15 minutes, or at once with the sync helper.
See ERE positions
A session counts once the registration is approved and three days have passed since the session ended. Refresh the positions, then read them as usual:
curl -X POST "https://api-staging.joulo.nl/functions/v1/api/sandbox/v1/positions/refresh" \
-H "Authorization: Bearer jpk_test_..."
curl "https://api-staging.joulo.nl/functions/v1/api/partner/ere-positions?year=2026" \
-H "Authorization: Bearer jpk_test_..."A virtual charger with sessions since January gives a customer ERE right
away. Pick a recent since date to test a customer whose ERE is still in
the three-day settle buffer.
Get ready-made customers
One call gives you a customer in every state your integration has to handle, without a claim or a review:
curl -X POST "https://api-staging.joulo.nl/functions/v1/api/sandbox/v1/fixtures" \
-H "Authorization: Bearer jpk_test_..."| State | The customer |
|---|---|
concept | Attributed to you, nothing submitted yet. |
approved_with_ere | Approved, MID charger with sessions since January, ERE to sell. |
rejected | Rejected at review. |
mismatched_ean | Approved, but the EAN check found another EAN on the address. |
non_mid_charger | Approved, charger without a MID meter: sessions, no ERE. |
recent_charger | Approved, MID charger since two days ago: ERE still in the settle buffer. |
Send {"states": ["rejected", "mismatched_ean"]} to get only some of them.
Each fixture has a confirmed login on an example.com address that never
receives mail. It appears in GET /partner/customers and
GET /partner/ere-positions as a customer you uploaded, so you see its name
and status. Its external_ref names the state, such as
FIXTURE-APPROVED-WITH-ERE.
Start over
POST /sandbox/v1/reset deletes every customer attributed to you on the
sandbox, and every draft you uploaded. Your key keeps working.