Get started
Quickstart
Sign up a payer, register a payee, sign a mandate, get a decision, and send a simulated payout. About five minutes, no real money.
The sandbox is shared, and each payer email and funding IBAN can be used once, so these snippets generate fresh ones on every run. Add X-API-Key (or api_key= in Python) if your sandbox has authentication on.
# pip install "whire>=0.2" (Python 3.11+, async)
# Run the snippets below in `python -m asyncio`, or inside an async function.
import random, uuid
from whire import WhireClient
client = WhireClient(environment="sandbox")
run = uuid.uuid4().hex[:8]
def random_iban() -> str: # a valid throw-away NL IBAN
bban = "ABNA" + "".join(random.choices("0123456789", k=10))
check = 98 - int("".join(str(int(c, 36)) for c in bban + "NL00")) % 97
return f"NL{check:02d}{bban}"# needs curl, jq and python3
API=https://sandbox.whire.ai
J='content-type: application/json'
RUN=$(date +%s)
IBAN=$(python3 - <<'PY'
import random
b = 'ABNA' + str(random.randrange(10**9, 10**10))
print('NL%02d%s' % (98 - int(''.join(str(int(c, 36)) for c in b + 'NL00')) % 97, b))
PY
)Check what the deployment can do. No key needed:
The payer is the customer whose money moves. It opens pending_verification; a person records the verification.
payer = await client.payers.create(
legal_name=f"Merchant {run} B.V.",
account_type="business",
contact_first_name="Eva",
contact_last_name="Jansen",
email=f"eva.{run}@example.com",
phone="+31612345678",
funding_sources=[{"type": "sepa", "destination": random_iban()}],
)
payer = await client.payers.activate(
payer.payer_id, verified_by="Compliance Officer"
)
print(payer.status) # activePAYER=$(curl -s -X POST $API/api/payers -H "$J" -d '{
"legalName": "Merchant '$RUN' B.V.", "accountType": "business",
"contactFirstName": "Eva", "contactLastName": "Jansen",
"email": "eva.'$RUN'@example.com", "phone": "+31612345678",
"fundingSources": [{ "type": "sepa", "destination": "'$IBAN'" }]
}' | jq -r .data.payerId)
curl -s -X POST $API/api/payers/$PAYER/activate -H "$J" \
-d '{ "verifiedBy": "Compliance Officer" }' | jq -c .data.status
# "active"The mandate lets this payer pay this payee up to 100 EUR per payment and 250 EUR in total. See Mandates.
payee = await client.beneficiaries.create(
full_name="Acme Supplies BV",
iban="DE89370400440532013000",
reference=f"acme-{run}",
)
mandate = await client.mandates.create(
beneficiary_id=payee.beneficiary_id,
payer_id=payer.payer_id,
mandate_reference=f"SHOP-{run}",
signed_by="Finance",
max_amount=100,
max_total_amount=250,
)PAYEE=$(curl -s -X POST $API/api/beneficiaries -H "$J" -d '{
"fullName": "Acme Supplies BV", "iban": "DE89370400440532013000",
"reference": "acme-'$RUN'"
}' | jq -r .data.beneficiaryId)
MANDATE=$(curl -s -X POST $API/api/mandates -H "$J" -d '{
"beneficiaryId": "'$PAYEE'", "payerId": "'$PAYER'",
"mandateReference": "SHOP-'$RUN'", "signedBy": "Finance",
"currency": "EUR", "maxAmount": 100, "maxTotalAmount": 250
}' | jq -r .data.mandateId)Nothing moves yet. The answer is a signed receipt that says yes or no, and which checks failed.
receipt = await client.authorizations.create(
mandate_id=mandate.mandate_id,
beneficiary_id=payee.beneficiary_id,
amount=50,
)
print(receipt.decision, receipt.failed_checks) # approved []
refused = await client.authorizations.create(
mandate_id=mandate.mandate_id,
beneficiary_id=payee.beneficiary_id,
amount=150,
)
print(refused.decision, refused.failed_checks) # refused ['per_payment_limit']curl -s -X POST $API/api/authorize -H "$J" \
-d '{ "mandateId": "'$MANDATE'", "amount": 50, "beneficiaryId": "'$PAYEE'" }' \
| jq -c '.data | {decision, failedChecks}'
# {"decision":"approved","failedChecks":[]}Create a draft under the mandate, submit it for KYC review, wait for the decision, execute. The sandbox decides KYC after about three seconds.
payout = await client.payouts.create(
beneficiary_id=payee.beneficiary_id,
mandate_id=mandate.mandate_id,
amount="20.00",
reason="Invoice 2026-091",
)
await client.payouts.submit(payout.payout_id)
payout = await client.payouts.wait_for_kyc(payout.payout_id, timeout=30)
print(payout.status) # approved
execution = await client.payouts.execute(payout.payout_id)
print(execution.status, execution.provider_reference) # paid SIM-...
await client.close()PAYOUT=$(curl -s -X POST $API/api/payouts -H "$J" -d '{
"beneficiaryId": "'$PAYEE'", "mandateId": "'$MANDATE'",
"amount": 20, "currency": "EUR", "reason": "Invoice 2026-091"
}' | jq -r .data.payoutId)
curl -s -X POST $API/api/payouts/$PAYOUT/submit | jq -c .data.status
# "pending_kyc"
sleep 4 # the sandbox decides KYC after a moment
curl -s $API/api/payouts/$PAYOUT | jq -c .data.status
# "approved"
curl -s -X POST $API/api/payouts/$PAYOUT/execute \
| jq -c '.data | {status, providerReference}'
# {"status":"paid","providerReference":"SIM-..."}