Skip to main content
Sandbox runs the same API with simulated funds. Use it to make failure modes visible while they are still cheap. Production is messier than any code review suggests. Webhooks arrive late or twice, users hesitate until a quote expires, payouts get rejected for a bad account number. This guide shows how to produce those conditions on purpose.

How sandbox works

Sandbox is not a separate host. It is a separate credential. The secret you sign with decides which environment you are in.
Because the host is the same, a misconfigured secret sends a test request to production. Keep the two secrets in separate configuration, and never load both into one process.

Set up a sandbox workspace

1

Create a sandbox key

In the dashboard, open Settings > API Keys and create a sandbox key. See Set up your account.
2

Keep it in its own configuration file

3

Expose your webhook endpoint

Run a tunnel, such as ngrok http 3000, and set the public URL as your webhook URL in the dashboard.
4

Confirm your credentials

Call /api/whoami. A 200 means signing works, and the response returns "environment": "sandbox" so you can confirm which environment the secret selects.

Simulate an inbound deposit

Virtual accounts have a sandbox-only endpoint that fakes an inbound transfer. The request body is optional: you can send an idempotency_key.
The response confirms the credit and the new balance:
This drives the same downstream path a real deposit does, so your balance update and your webhook handler both get exercised. See Simulate deposit.
The endpoint is rejected in production. It is safe to leave in a test suite.

Test quote expiry

Quote expiry only shows up when a user hesitates, which is exactly when nobody is watching.
1

Create a quote

Call Create payout quote and record expires_at.
2

Wait past expires_at

Do nothing until the timestamp passes.
3

Try to execute

Initialize the payout against the expired quote_id and observe the error.
4

Check your interface

Confirm it blocks the attempt, explains why, and offers a fresh quote.
See Quote lifecycle and expiry.

Test webhook delivery and retries

A 5xx, a timeout, and most 4xx responses are retried, up to the retry count set on your webhook. 400, 404, 410, 422, and 451 are final. Retries and duplicates has the full rules. Confirm your handler behaves under retries before you rely on it.
1

Return a failure on purpose

2

Trigger an event

Simulate a deposit and watch the retries arrive in your log.
3

Confirm the retry count and spacing

Compare what you see against the documented behavior in Retries and duplicates. By default the delays are about 1, 5, 15, and 60 minutes, so a full retry cycle takes over an hour.
4

Process the same event twice

Return 200 and replay the event. Confirm the second run changes nothing.

Test the failure paths

Most integrations test the happy path and ship. These four errors are worth producing deliberately, because each one reaches production eventually. The last row is the one people skip. Retries are finite, so an endpoint that is down long enough misses events permanently. Reconciliation is what recovers them. See Webhooks and event handling.

Edge cases worth reproducing

  • A webhook arrives before your database has written the reference.
  • Two webhooks for the same flow arrive in parallel.
  • A payment arrives just after a quote expires.
  • A user sends an asset you did not expect.
  • The same reference is reused across two attempts.
  • A signature header is missing or altered.

Build a repeatable script

Ad-hoc testing stops happening once the feature ships. Put the flow in a script instead:
  1. Create a quote and store it.
  2. Execute against the quote.
  3. Simulate the deposit.
  4. Record every webhook received, with its event name and reference.
  5. Assert the final state matches what you expect.
Run it in CI against sandbox credentials. A broken webhook handler then fails a build rather than a payment.

Before you go live

1

Swap the secret, nothing else

The base URL and CLIENT_ID do not change. Only CLIENT_SECRET does.
2

Point the webhook URL at production

Update it in the dashboard and confirm the endpoint is reachable over HTTPS.
3

Confirm compliance is approved

Production requires completed verification. See Set up your account.
4

Run one small real transaction

Send the smallest amount the corridor allows and follow it end to end before you open the flow to users.

Next

Continue to Observability, logging, and traceability, which covers what to record so that a production question has an answer.