Sandbox Drafts & Signing Baskets
The sandbox supports the same staged draft and signing-basket flow as production for Domestic Credit Transfer, SE Bank Giro, and SE Plus Giro drafts, using the same endpoints. For field references, validation rules, error codes, and status vocabulary, see Signing Baskets & Drafts.
This page covers what differs when testing against the sandbox.
Flow
- Create a draft (
POST /payments/drafts/{payment-type}) — starts inCREATED - Update it via
PATCHuntilisValidistrue - Approve it, alone or batched with other drafts, via
POST /payments/drafts/approve - Poll
GET /payments/drafts/approve/{requestId}/status— or the draft or payment endpoint — until the payment reaches its final status
Approval requires no user interaction
POST /payments/drafts/approve returns status: APPROVED immediately — no Strong Customer Authentication is required. The returned authUrl behaves the same as for direct payments: it leads to a sandbox page that redirects back to your redirectUrl (see Sandbox Payment Flows).
Approval covers only the authorization step — the resulting payment may still take a moment to complete.
Settle timing
| Draft type | Status right after approval | Becomes |
|---|---|---|
| Domestic Credit Transfer | PENDING | COMPLETED after a few seconds |
| SE Bank Giro / SE Plus Giro | COMPLETED | — (completes on approval) |
A domestic credit transfer draft is not finished the moment approval returns. Poll until the status changes, the same as you would in production. Once it settles, the payment also appears as a posted transaction on the sender account.
Timing differs per payment type, so don’t assume uniform behaviour when approving a basket that mixes payment types.
Cancelling before settlement
DELETE /payments/drafts/domestic-credit-transfer/{id}/payment| When called | Result |
|---|---|
While the draft is PENDING | 204 No Content — payment cancelled, draft returns to CREATED |
After the draft is COMPLETED | 409 Conflict — the payment has already settled |
To test the cancellation path, send the DELETE right after approving.
Example
DRAFT_ID=$(uuidgen)
REQUEST_ID=$(uuidgen)
# 1. Create the draft
curl \
--silent \
--cert client.pem \
--key client.key \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "X-Request-Id: $(uuidgen)" \
--request POST \
--data '{
"draftId": "'${DRAFT_ID}'",
"name": "Rent May"
}' \
https://sandbox.openbanking.prod.lunar.app/aisp-pisp/payments/drafts/domestic-credit-transfer | jq .
# 2. Fill in the required fields
curl \
--silent \
--cert client.pem \
--key client.key \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "X-Request-Id: $(uuidgen)" \
--request PATCH \
--data '{
"receiverAccountBban": "12341234567",
"receiverName": "Test Receiver",
"amount": "150.00",
"currency": "DKK",
"senderAccountId": "'${ACCOUNT_ID}'"
}' \
https://sandbox.openbanking.prod.lunar.app/aisp-pisp/payments/drafts/domestic-credit-transfer/${DRAFT_ID} | jq .
# 3. Approve via a signing basket
curl \
--silent \
--cert client.pem \
--key client.key \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "X-Request-Id: $(uuidgen)" \
--request POST \
--data '{
"requestId": "'${REQUEST_ID}'",
"draftIds": ["'${DRAFT_ID}'"],
"redirectUrl": "https://localhost:8080/callback"
}' \
https://sandbox.openbanking.prod.lunar.app/aisp-pisp/payments/drafts/approve | jq .
# 4. Poll the approval status — PENDING at first, COMPLETED a few seconds later
curl \
--silent \
--cert client.pem \
--key client.key \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "X-Request-Id: $(uuidgen)" \
https://sandbox.openbanking.prod.lunar.app/aisp-pisp/payments/drafts/approve/${REQUEST_ID}/status | jq .The status response reports the basket and each draft in it:
{
"status": "APPROVED",
"drafts": [{ "draftId": "...", "status": "PENDING", "paymentId": "..." }]
}Differences from production
| Aspect | Sandbox | Production |
|---|---|---|
| Approval / SCA | Auto-approved, no user interaction | Real SCA via the Lunar app |
| Settle timing | Fixed short delay (DCT) or immediate (SE Giro) | Depends on real payment rails |
| State persistence | In-memory, ~24h TTL | Persistent database storage |