Testing in Sandbox
How to run a flow end to end in the test environment.
https://api.sandbox.alfredpay.io
Sandbox is contract-identical to production. The requests, the responses, the event payloads and the errors are all the same. The only difference is that two simulator endpoints exist here. On production they return 404 not_found.
Those two endpoints remove the two waits you cannot otherwise control.
Adding money to a sandbox account
You cannot transfer funds into a sandbox account, and no starting balance is provisioned for you. You simulate a deposit.
POST /v1/test/customers/{customerId}/deposits/simulate
In production a deposit arrives because a payer sent money, so there is nothing for you to call. In sandbox this stands in for the payer.
The simulator runs the real pipeline. Detection, attribution and events all run. Screening, conversion and crediting run as well. What you get back is a deposit that went through the same code as a real one, which is what makes it worth testing against.
Idempotency-Key is required here, the same as on any other POST.
Wait for deposit.credited before you treat the funds as spendable, the same as you would in production.
Skipping the destination review queue
POST /v1/test/linked_accounts/{linkedAccountId}/simulate_activation
This moves a relationship straight to VERIFIED.
Registering a payout destination normally goes through a review, and you do not control how long that review takes. Without the simulator, testing a payout flow means registering a destination and then waiting on someone else's process. With it, the whole flow runs in a minute.
Calling it again returns the same already-verified account.
A full pass
Here is what a complete run looks like and what to check at each point.
- Create a customer with
POST /v1/customersand thenPUTthe profile. Check that it readsNOT_STARTED. - Onboard the customer by submitting the information and uploading the documents. Run preflight until it comes back clean, then submit the verification and check that you reach
ACTIVE. - Request an endorsement, using
fiat_collectionfor collections. Wait forAPPROVEDand checkprovider_endorsements[]as well. - Create a virtual account. Check that
virtual_account.activatedfires and that the deposit instructions come back. - Simulate a deposit and watch the four statuses move. Check that
deposit.creditedarrives. - Read the balance and check that the amount landed in
availableand not only intotal. - Register a destination and then activate it with the simulator. Check that
supportedRails[]is what you expected. - Request a quote and pay out. Check that the rail on the quote matches the destination and that the payout reaches
COMPLETEwithSETTLED.
If any step depends on a status you have not seen before, read all four statuses instead of only the one you were watching.
Cases beyond the successful path
These cases are worth testing explicitly, because they are the ones that tend to cause problems in production.
- Send the same event twice and confirm your handler is idempotent.
- Send an event you do not handle and confirm you return a 2xx instead of failing. See Webhooks and events.
- Let a quote pass
expiresAtand confirm you re-quote instead of retrying. - Revoke an endorsement and confirm your integration notices instead of failing silently later.
- Ask for a route that is not
LIVEand confirm you handleroute_unsupportedbefore anything is locked. - Try to pay out with insufficient balance and confirm you read the balance instead of retrying into the same
422.
You can reach most of these by hand. An expired quote only needs you to wait, and an unsupported route is any combination the registry does not list as LIVE. To produce a rejection or an RFI on a customer, ask your Alfred contact which test values to use.
What sandbox will not tell you
Timing
Sandbox decisions come back in seconds. Real KYB, real settlement and real rail behavior take much longer. Do not build a timeout from what you see here.
Coverage
A route can read SANDBOX in the registry. That means it is available and tested here but not enabled in production. Check the status on each route instead of assuming that something working here also works there.
Updated 5 days ago

