Start here · zillq.com/sandbox77/mandate

An agent wants to pay.
Prove it was allowed to.

Everyone is building agents that spend. Almost nobody has built the layer that decides whether the spend was authorized — on regulated European rails, under a cap, with evidence that survives a dispute. This is that layer, running, with a public API you can start against today.

ZILLQ / MANDATE_LAYER
CONSENT → MANDATE → AGENT SPEND → PROOFSEPA RAILS
The mandate chain: consent, mandate, agent payment, verifiable proof A diagram of four linked nodes: a wallet granting consent, a signed mandate with a spending cap, an agent paying under the cap, and a hash-chained audit record verifying the payment. 01 / CONSENT 02 / MANDATE 03 / SPEND+PROOF PSU GRANTS, SCA BINDS CAP ≤ HUMAN CAP HASH-CHAINED, VERIFIABLE
OPENAPI 3.0 CONTRACT
103 PATHS, PUBLIC
01 / THE PREMISE

The agent is not the hard part. The permission is.

A payment is no longer a person clicking a button. It is software acting on someone's behalf — and the rails still need to answer: who authorized this, within what limits, and can you prove it six months later?

This sandbox models that answer end to end. A human grants consent through a Strong Customer Authentication flow. That consent becomes a mandate: caps, allowed payees, a time window. An agent then pays inside the mandate — and every step is written to a hash-chained audit trail you can verify independently. Over-limit, off-list, or out-of-window payments are refused, with a machine-readable reason.

It runs on SEPA rails semantics: Verification of Payee before the money moves, ISO 20022 messages behind every response, PSD2 consent mechanics underneath — modeled PSD3/PSR-ready where those rules are settled enough to implement, and clearly marked where they're still proposed. Synthetic money, real rules. The regulation is treated as a specification to implement, not a checkbox to describe.

Everything below is reachable with nothing but HTTP. No SDK, no signup wall, no sales call. The contract is the door — read it, call it, break it.

02 / WHAT YOU CAN BUILD

Four tracks into the API.

Each track is a path through the same OpenAPI contract. Pick one, follow the endpoints, end up with something working.

> RUN "TRACK_AISP",8,1
AIS · READ

Read accounts like a fintech

The classic Account Information Service loop. Create a consent, authenticate the user, read balances and transactions. The pattern every aggregator started with.

  • POST /v1/consents → SCA redirect → validated consent
  • GET /v1/accounts, /balances, /transactions
  • Revoke anytime: DELETE /v1/consents/{id}
> RUN "TRACK_PIS",8,1
PIS · MOVE

Initiate real payment flows

SEPA credit transfers with the full lifecycle: initiation, authorisation, status polling, cancellation. Instant payments included, with VoP gating the creditor check.

  • POST /v1/payments/sepa-credit-transfers
  • SCT Inst: settle in seconds, not days
  • POST /v1/verify-payee before you send a cent
> RUN "TRACK_MANDATE",8,1
MANDATE · DELEGATE

Bind an agent under a cap

The mandate layer itself. Register an e-mandate from a wallet, delegate it to an agent identity with attenuation — agent scope must be a provable subset of the human's — then watch enforcement reject anything wider.

  • POST /sca/eudi/emandate — SD-JWT VC presentation
  • Attenuation: cap ≤ human cap, payees ⊆ human payees
  • Widening mutations fail, with a refusal reason
> RUN "TRACK_PROOF",8,1
AUDIT · PROVE

Verify the chain afterward

Every mandate execution and payment writes to a hash-chained audit log. Re-verify any receipt, walk the chain, find the exact link where a tampered record would break. This is the part disputes are actually won with.

  • Receipt verification against the signing key
  • Chain integrity: intact / partial / broken
  • Explore live in the portal's audit pages
03 / YOUR FIRST CALL

Four requests, one terminal.

Register your own sandbox identity first — the old shared example ID is retired. Then you have a live, SCA-authenticated consent to explore with.

01 · CHECK THE SANDBOX IS ALIVEGET /health
# every response here is synthetic. the mechanics are not.
curl -s https://zillq.com/health
# → {"status":"UP","db":"connected",...}
02 · REGISTER YOUR OWN TPP IDENTITYPOST /ui/api/developers/register
ZILLQ_TPP=$(curl -s https://zillq.com/ui/api/developers/register \
  -H "Content-Type: application/json" \
  -H "Origin: https://zillq.com" \
  -d '{"name": "My sandbox testing"}' | jq -r .tppId)
# → sandbox-<generated-uuid>, with roles ["AISP","PISP"] — save it,
# it can't be recovered later. Anyone with the ID can use it.
03 · CHECK A PAYEE BEFORE PAYING (VoP)POST /v1/verify-payee
curl -s -X POST https://zillq.com/v1/verify-payee \
  -H "X-Request-ID: $(uuidgen)" \
  -H "X-TPP-ID: $ZILLQ_TPP" \
  -H "Content-Type: application/json" \
  -d '{"iban": "DE89370400440532013000", "name": "Max Musterman"}'
# → {"result":"match","accountHolderName":"Max Musterman",
#    "reasonCode":"NAME_MATCH"}
# results are data, not status codes: match / close / no_match / unable
04 · CREATE A CONSENT (AIS)POST /v1/consents
# validUntil must be in the future and at most 90 days out (AIS consents) —
# derive it. This date used to be a fixed calendar date, so the example
# expired permanently the day it drifted past the cap. (GNU date; on macOS:
# date -u -v+30d +%F)
VALID_UNTIL=$(date -u -d "+30 days" +%F)
curl -s -X POST https://zillq.com/v1/consents \
  -H "X-Request-ID: $(uuidgen)" \
  -H "X-TPP-ID: $ZILLQ_TPP" \
  -H "Content-Type: application/json" \
  -d "{
    \"access\": { \"accounts\": [{ \"iban\": \"DE89370400440532013000\" }] },
    \"recurringIndicator\": false,
    \"validUntil\": \"$VALID_UNTIL\",
    \"frequencyPerDay\": 4
  }"
# → 201 {"consentId":"...","consentStatus":"received",
#      "_links":{"scaRedirect":"...","status":"/v1/consents/{id}/status"}}
# open scaRedirect in a browser, sign in as max.musterman / 12345,
# approve — then re-check status: "consentStatus":"valid"

Every request needs a fresh X-Request-ID (a UUID) and your own registered X-TPP-ID from step 02 — the old shared PSDDE-FAKENCA-TPP01 example now returns ROLE_INVALID. In this sandbox these headers are sandbox context, not proof a human authenticated. The SCA flow in step 04 is where that proof comes from, and that gap is exactly what the mandate layer exists to close.

04 / THE PATH

From spec to
working integration.

The order below is deliberate. Each step's output is the next step's input, same as the real rails.

  1. 01

    Read the contract.

    Open openapi.yaml — OpenAPI 3.0.3, 103 paths, 119 operations. Start with the tags: Consents, Accounts, Payments, VoP, SCA. The spec is the door; everything else is decoration.

  2. 02

    Get a consent through SCA.

    Create one, follow the scaRedirect, log in as the demo user (max.musterman / 12345). A consent that skipped authentication is not a consent — watch the status machine prove it.

  3. 03

    Move money under the rules.

    Initiate a SEPA transfer, poll the status, see the ISO 20022 rendering of what actually moved. VoP runs before execution; a name mismatch blocks the payment as data, not as an exception.

  4. 04

    Delegate, then try to cheat.

    Bind a mandate to an agent with a cap. Pay under it — works. Pay over it — refused with a reason. Widen the agent's scope past the human's — the attenuation check fails. This is the mandate layer doing its job; poke it until you feel the edges.

  5. 05

    Verify, or tamper and watch it break.

    Take a receipt, verify it. Then edit one byte and verify again. The chain tells you exactly where it broke. That property — falsifiable, not trust-me — is the whole point of the layer.

05 / THE DOCUMENT TO START FROM

One file. Implement against it.

The OpenAPI spec below is the same contract this sandbox enforces, published from the same repo the services build from. Point your code generator, your tests, or your curiosity at it. Where the spec and the live service disagree, the disagreement is a bug — and this sandbox is small enough that reporting one gets it fixed.

Regulatory basis is cited inline: PSD2 consent mechanics, the Instant Payments Regulation's Verification-of-Payee requirement (mandatory since October 2025, EPC VoP Rulebook now at v1.1, effective 20 September 2026), EPC scheme rules for SCT/SCT Inst (2025 rulebook v1.1). Read the spec and the citations together; they are the difference between a mock and a model.

openapi.yaml

OPENAPI 3.0.3 · 103 PATHS · PUBLIC

  • Consents · create / status / terminate (AIS)
  • Accounts · list / balances / transactions
  • Payments · SCT · SCT Inst · periodic · bulk
  • VoP · verify-payee · bulk · result algebra
  • SCA · redirect · embedded · passkey · e-mandate
  • PSD3/PSR · readiness modeling — proposed, not yet in force
EPC VoP Rulebook: v1.1 · effective 20 Sep 2026
SCT / SCT Inst Rulebook: 2025 v1.1 · in force since 5 Oct 2025
Regulatory baseline reviewed: 20 Sep 2026
BEFORE YOU START_
Is this a real bank or a licensed provider?

No. ZILLQ is an independent sandbox. The money is synthetic, the rails are simulated, and nothing here is a substitute for a licensed ASPSP or your own compliance review. What is real is the mechanics: the consent state machine, the VoP matching, the signature checks, and the audit chain all behave the way the rulebook says they must.

Why should the API contract be the starting point?

Because everything else — dashboards, docs, demos — is a view of the contract. If you can read YAML and send HTTP, you have full access to everything this sandbox does. The portal at /ui/ is the same surface wearing a face, useful for watching state change while your code drives it.

What happens when my agent exceeds its mandate?

The payment is refused before it reaches the ledger, and the refusal carries a machine-readable reason (cap exceeded, payee not approved, window expired). The refusal itself is audit-logged, so the record shows not just what the agent did but what it tried to do. Enforcement you can only observe when it passes is not enforcement.

Can I use this for a real integration later?

Use it the way you use a flight simulator: to learn the controls, not to skip the license. The request shapes, status flows, and error semantics match the Berlin Group / EPC conventions closely enough that code written against this transfers. The regulated part — QWAC certificates, licensed counterparties, production data — stays yours to obtain.

I found something that contradicts the spec.

Good. That is exactly the kind of thing this sandbox exists to surface. Send the request, the response, and the spec section to hello@zillq.com — verified discrepancies get fixed, and the fix note usually ends up in the blog series that documents this build.

REVIEW THE BOUNDARIES

Take the questions
into a design review.

Explore 16 synthetic mandate test cases, compare consent and SCA, or read the 2027 investor brief.

READY_

The mandate layer is not a whitepaper.
It answers on port 443.

Start with the spec, make the health call, create your first consent. If the story in the blog series is right, you'll feel the difference within ten requests.