Accounts
Create a concept account (customer + connection) and list your accounts with their status.
Accounts
An account is the administrative and legal anchor: the EAN, the address, the machtiging, the sessions, and the payout all hang off it. You create it as a concept — it exists and is attributed to you, but until the customer signs their machtiging it doesn't count toward anything.
Create an account
POST /v1/accounts — scope cpo:accounts:write
curl -X POST https://api.joulo.nl/functions/v1/api/v1/accounts \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"type": "particulier",
"display_name": "Jan de Vries",
"cpo_external_ref": "CUST-100482",
"payout_handler": "joulo_pays_customer",
"iban": "NL91ABNA0417164300",
"person": {
"full_name": "Jan de Vries",
"email": "[email protected]",
"phone": "+31612345678"
},
"connection": {
"ean_code": "871685920000123456",
"street": "Dorpsstraat",
"house_number": "1",
"postal_code": "6971AB",
"city": "Brummen"
}
}'{
"ok": true,
"account_id": "60092124-c768-4191-9c9d-55ef3fc49999",
"connection_id": "2eca20d8-06ed-4354-a80d-c8c840a1640d",
"location_id": "…",
"ean_validation_status": "matched",
"iban_stored": true,
"mandate_status": "pending",
"account": {
"id": "60092124-…",
"type": "particulier",
"display_name": "Jan de Vries",
"payout_handler": "joulo_pays_customer",
"cpo_external_ref": "CUST-100482",
"created_at": "2026-07-20T12:00:00Z"
}
}Body
| Field | Required | Description |
|---|---|---|
type | no | particulier (default), zakelijk, or vve. |
display_name | no | Human-readable name for the account. |
cpo_external_ref | recommended | Your customer number. Used as the idempotency key and to address later session pushes. |
payout_handler | no | joulo_pays_customer (default) — Joulo pays the customer directly. via_cpo_invoicing — the payout settles through you, and Joulo does not pay the customer directly. |
iban | for payout | The customer's IBAN (15–34 chars, spaces ignored). Required before a joulo_pays_customer account can become ERE-eligible; not needed for via_cpo_invoicing. Stored encrypted. You can send it here or add it later by re-posting with the same cpo_external_ref. |
person.full_name | yes | The natural person who will sign the machtiging. |
person.email | no | Signatory email. |
person.phone | no | Signatory phone. |
connection.ean_code | yes | 18-digit EAN of the connection. |
connection.street house_number postal_code city | yes | Address, validated against the EAN. |
connection.house_number_addition | no | Addition (e.g. A). |
Idempotent. Re-posting with the same cpo_external_ref returns the
existing account ("idempotent": true) instead of creating a duplicate. A
re-post that includes iban stores it on the existing account, so you can
add or correct the IBAN after creation.
iban_stored in the response tells you whether the IBAN was saved (false
if you sent none, or if storage failed and you should retry).
EAN exclusivity
An EAN can be booked by exactly one party. If the EAN is already registered
(on your side or elsewhere), you get 409 ean_in_use. The address is checked
against the EAN; a clear mismatch returns 422 ean_address_mismatch.
Errors
| HTTP | error |
|---|---|
| 400 | invalid_type, invalid_payout_handler, invalid_iban, person_required, invalid_ean, address_required |
| 404 | account_not_found (for a different CPO's account) |
| 409 | ean_in_use |
| 422 | ean_address_mismatch |
List accounts
GET /v1/accounts — scope cpo:accounts:read
Paginated with ?limit (default 50, max 200) and ?offset. Returns each
account with its signatory and per-connection registration + mandate state — so
you can track a customer from concept to signed to registered.
{
"accounts": [
{
"id": "60092124-…",
"type": "particulier",
"display_name": "Jan de Vries",
"payout_handler": "joulo_pays_customer",
"cpo_external_ref": "CUST-100482",
"created_at": "2026-07-20T12:00:00Z",
"signatory": { "full_name": "Jan de Vries", "email": "[email protected]" },
"connections": [
{
"connection_id": "2eca20d8-…",
"ean_code": "871685920000123456",
"registration_status": "concept",
"ean_validation_status": "matched",
"mandate_status": "pending",
"mandate_valid_from": null
}
]
}
],
"limit": 50,
"offset": 0,
"count": 1
}mandate_status is pending until the customer signs, then active.
registration_status moves concept → ingediend once signed and submitted
for review.