API playground
Every request on this page is a real HTTPS call from your browser to the live sandbox at api.finscale.dev. Pick a scenario, send it, and read the same responses your integration will see — no signup, no server, no money moved.
Guided tour
The core loop of a payments integration in four requests. Run them in order — each button fills the console, sends the request, and jumps to the response.
POST /v1/payments — a card payment for 4900 minor units (€49.00). It authorizes and captures in one call.
Same request with the magic amount 4999: the first provider times out and Finscale retries the next. Read provider_attempts.
GET /v1/payments/{id} — fetches the payment from step 02 by id, straight from your request history.
POST /v1/refunds against the same payment — it works whichever provider processed the original.
Your sandbox key
Test keys are auto-provisioned for the playground: the sandbox accepts any sk_test_ key and moves no money.
sk_test_pg_…
This key was generated in your browser and lives only in this browser's storage. Rotating replaces it — the sandbox treats every sk_test_ key identically, so nothing breaks. Real key management is covered in Authentication.
Scenario presets
Each preset fills the console — edit anything before sending. The write scenarios use exact payload shapes from the API reference; the special behaviors are driven by magic amounts.
| Scenario | What to look for | Docs |
|---|---|---|
status: succeeded, amount_captured: 4900, and an approved risk decision. |
Payment lifecycle | |
Amount 4999: two entries in provider_attempts — a timeout, then an approval on the next provider. |
Smart routing | |
Amount 4002: status: failed with failure_code: card_declined — a final decline, never retried. |
Errors | |
Amount 4991: held in processing with risk.decision: "review" and a score in the review band. |
Risk engine | |
Amount 4992: failed with failure_code: payment_blocked — no provider is ever attempted. |
Risk engine | |
status: requires_action — the customer approves the collect request in their UPI app. |
Real-time payments | |
requires_action with next_action.type: "redirect" — send the customer to their bank, then back to return_url. |
Bank redirects | |
requires_action: the customer receives a reference to pay in cash at a store. |
Vouchers | |
capture_method: "manual" authorizes to requires_capture; the second button sends POST /capture. Add an amount for a partial capture — it keeps the authorization open. |
Authorization flows | |
Uses the newest pay_ id from your history. The refund returns status: succeeded. |
Refunds | |
GET by id — the same object shape the create call returned. |
Payment lifecycle | |
The cursor-paginated list envelope: object: "list", data, has_more. |
Pagination | |
Your opaque provider set — prov_… ids with methods, regions, and auth_rate_7d. |
Provider health | |
available vs pending funds, per currency, in minor units. |
Balance | |
KYB starts at kyb_status: "kyb_pending" — capabilities pending, mid: null until the merchant is active. |
Merchant API | |
dp_ objects with reason, stage, status, and the evidence_due_by deadline. |
Disputes |
Console
Response
// Send a request — the response appears here.
Request history
Your last 20 requests, stored only in this browser. Click one to restore both the request and its response. The refund, retrieve, and capture presets read the newest pay_ id from here — if you haven't created a payment yet, they fall back to the demo payment pay_8Q2mX4nT1cVb.
How this works
There is no application server behind this page — your browser calls https://api.finscale.dev directly. The domain fronts a CloudFront distribution, and a Lambda@Edge function answers every /v1/* request at the edge: a stateless, deterministic mock of the documented API. Any sk_test_ key authenticates, live keys are rejected, and no money exists anywhere in the loop. The sandbox allows cross-origin requests from any page, which is what lets the console call it without a proxy.
Deterministic means reproducible. The same request always produces the same shape, and creates are idempotent — the same Idempotency-Key always maps to the same object id, which is why the console stamps every write with a fresh key. Special test behaviors hang off magic amounts:
| Amount | Test-mode behavior |
|---|---|
4999 |
First provider times out; automatic failover approves on the next. Both attempts appear in provider_attempts. |
4991 |
Risk review: held in processing with risk.decision: "review". |
4992 |
Risk blocked: failed with failure_code: payment_blocked; no provider attempted. |
4002 |
Final decline: failed with failure_code: card_declined. Never retried on another provider. |
The full test matrix — test cards, simulated redirect flows, webhook testing — is on Testing. Field-by-field schemas for everything you can send from here are in the API reference.