03 — Get started
Quickstart
Take a €49.00 test payment through its full lifecycle in three API calls: create it, check its status, and receive the webhook. No SDK required — everything below is plain curl against https://api.finscale.dev/v1, and every step also runs one click at a time in the API playground.
1. Get your API keys
Every Finscale account starts in test mode with two keys:
| Key | Prefix | Use |
|---|---|---|
sk_test_51FinscaleDemo… | sk_test_ | Secret key. Server-side only — authenticates every API call in this guide. |
pk_test_51FinscaleDemo… | pk_test_ | Publishable key. Safe for browsers; used only with hosted checkout sessions. |
You'll find both in the Dashboard under Developers → API keys. Test keys only ever move test money; live keys (sk_live_••••••••••••) come later, after activation. Read Authentication for rotation and key-handling rules.
Never embed sk_test_ or sk_live_ keys in mobile apps, browser code, or public repos. Anyone holding the key can move money on your account.
2. Create your first payment
One POST /v1/payments creates the payment and routes it. Here the customer pays €49.00 for order ord_9f21_0716 with iDEAL:
curl https://api.finscale.dev/v1/payments \
-H "Authorization: Bearer sk_test_51FinscaleDemo…" \
-H "Idempotency-Key: idem_ord_9f21_0716" \
-H "Content-Type: application/json" \
-d '{
"amount": 4900,
"currency": "EUR",
"payment_method": "ideal",
"reference": "ord_9f21_0716",
"customer": { "email": "anna@example.com" },
"return_url": "https://shop.example.com/checkout/return"
}'
Note the two conventions that apply to every write call: money is integer minor units (4900 = €49.00), and the Idempotency-Key header makes the request safe to retry.
The API answers immediately:
{
"id": "pay_8Q2mX4nT1cVb",
"object": "payment",
"amount": 4900,
"currency": "EUR",
"status": "requires_action",
"payment_method": "ideal",
"reference": "ord_9f21_0716",
"provider": "prov_eu_acq_01",
"next_action": {
"type": "redirect",
"url": "https://pay.finscale.dev/r/8Q2mX4nT"
},
"created_at": "2026-07-16T09:24:31Z",
"livemode": false
}
Two fields matter right now:
status: "requires_action"— iDEAL is a redirect method, so the customer must approve the payment at their bank. Send them tonext_action.url. In test mode that page shows a simulated bank where you click Approve.provider: "prov_eu_acq_01"— the provider Finscale routed this transaction to. It's an opaque id; your integration never changes based on it.
3. Check the payment status
After the customer approves and lands back on your return_url, fetch the payment to check its state:
curl https://api.finscale.dev/v1/payments/pay_8Q2mX4nT1cVb \
-H "Authorization: Bearer sk_test_51FinscaleDemo…"
{
"id": "pay_8Q2mX4nT1cVb",
"object": "payment",
"amount": 4900,
"currency": "EUR",
"status": "succeeded",
"payment_method": "ideal",
"reference": "ord_9f21_0716",
"provider": "prov_eu_acq_01",
"created_at": "2026-07-16T09:24:31Z",
"livemode": false
}
status: "succeeded" — the €49.00 is captured. The full state machine (requires_action → processing → succeeded | failed) is documented in Payments.
4. Receive the webhook
Polling works, but production integrations should fulfil orders from webhooks. Register an endpoint (Dashboard → Developers → Webhooks, or POST /v1/webhook_endpoints) and Finscale delivers a signed event for every state change:
# Headers
Finscale-Signature: t=1784193904,v1=5f8c1e7a9d2b4f6c8e0a1b3d5f7c9e1a3b5d7f9c1e3a5b7d9f1c3e5a7b9d1f3c
# Body
{
"id": "evt_5s8Y2kLmN0Ta",
"object": "event",
"type": "payment.succeeded",
"created_at": "2026-07-16T09:25:04Z",
"data": {
"object": {
"id": "pay_8Q2mX4nT1cVb",
"object": "payment",
"amount": 4900,
"currency": "EUR",
"status": "succeeded",
"reference": "ord_9f21_0716",
"provider": "prov_eu_acq_01"
}
}
}
Verify the Finscale-Signature header (HMAC-SHA256 over t.body with your whsec_FinscaleDemo000… secret) before trusting the payload, respond 2xx quickly, and fulfil order ord_9f21_0716. Signing, retries, and event types are covered in Webhooks.
What just happened
You made one API call; Finscale did the orchestration:
-
Routing decision
At
POST /v1/payments, the routing engine scored the providers connected to your account against this transaction — currency (EUR), method (iDEAL), cost, and each provider's live authorization rate — and pickedprov_eu_acq_01. -
Provider abstraction
Finscale translated your request into that provider's own protocol and normalized the reply back into one payment object. Same request shape, same response shape, whichever provider runs the transaction.
-
Failover, when needed
If
prov_eu_acq_01had timed out or soft-declined, Finscale would have retried the same transaction onprov_eu_acq_02automatically — one API call, two attempts, zero code on your side. Try it yourself with the magic amount4999in Testing.
Next steps
- Testing — test cards, simulated local methods, and forcing a failover with amount
4999. - Payments — the full lifecycle and state machine.
- Routing & providers — how routing scores providers and how per-merchant rules work.
- Webhooks — signature verification and delivery retries.
- API reference — every endpoint and field.