Sandbox
How the sandbox works, the limits of the suppliers' own test systems and what to expect while testing.
Sandbox keys (fxp_test_...) connect to the test systems of our suppliers: Amadeus test, Duffel test, the hotel supplier's test contract and the eSIM supplier's sandbox. Nothing you do with a sandbox key moves your real balance, and sandbox data is kept apart from live data: a sandbox key never sees a live booking and the other way round.
What the sandbox does
| Behaviour | Detail |
|---|---|
| No balance debit | Financial operations are skipped: bookings do not charge your balance and GET / has nothing to show. |
| Real data structures, test fares | Flight offers have the shape of live offers but come from test systems. Not every route exists. |
| Same limits as live | The rate limits are the same, so load tests belong on a schedule, not in a loop. |
| No webhooks | FX-Port does not call a URL of yours, in the sandbox or on live. Poll the booking: get booking for flights, booking detail for everything else. |
environment everywhere | Every JSON body says "environment": "sandbox" and every response has the X-Environment: sandbox header. |
GET / answers an envelope with an empty list, not a bare array:
{
"environment": "sandbox",
"success": true,
"data": [],
"meta": {
"agencyId": "00000000-0000-0000-0000-000000000000",
"environment": "sandbox",
"financialOperationsEnabled": false,
"message": "Sandbox bookings do not debit live balances; no sandbox balance ledger is exposed."
}
}Flights
The sandbox has two flight suppliers (see Suppliers):
| Supplier | Routes | Notes |
|---|---|---|
duffel_1 | Worldwide | Instant ticketing only. Duffel allows one booking per search: to book a second offer, search again (a second booking on the same search fails with offer_request_already_booked). Offers are randomised and expire: an offer that searched fine can be gone when you price it, so search again. |
amadeus_aqc_dz_1 | Routes that touch Algeria | Supports hold and same-day void. Some airlines or routes reject at the booking step even though they were in the results: try another route (for example ALG → ORN) or another offer. The cheapest holds fail more often than the others. |
To test an ACD booking, see ACD documents: CDG → ALG on amadeus_aqc_dz_1 returns offers with ACD pricing.
Set send_email to false on bookings while you test, so no message goes to the address you typed.
Hotels
See Hotel sandbox testing for the ids, a copy-paste script that books and cancels a test hotel, the date rules and the known quirks.
eSIMs
- A sandbox order really provisions an eSIM on the supplier's sandbox, with a dummy LPA string and a QR code image hosted by the sandbox.
- Order and top-up answers carry three flags that make the "no money moved" rule visible:
balanceDebited,transactionCreatedandpayoutCreated(twinsbalance_debited,transaction_created,payout_created), allfalse. - Usage is static: a sandbox eSIM reports
ACTIVEwith its full allowance and the figures do not move. - A sandbox top-up works end to end and shows a balance of
0.0. - Sandbox bookings appear in your history and booking detail like any other.
Testing checklist
Before you switch to live keys, verify in the sandbox:
- Search: your request is accepted and returns offers. Try one bad input on purpose (a wrong airport code) and check that you handle the error.
- Price: re-pricing an offer works and the pricing fields are what you expect.
- Book (issue): the instant booking completes and returns a PNR and an e-ticket.
- Book (hold): a hold on
amadeus_aqc_dz_1creates a reservation withstatus: awaiting_payment. - Pay:
pay_orderwith theorder_idof the hold succeeds. - Get booking:
get_bookingreturns the booking, andbucket_link(the PDF) once it exists. - Cancel a hold:
cancel_orderreleases an unpaid held PNR. Voiding or refunding an issued ticket, exchanges and re-issues stay manual support operations (Duffel orders can use the self-service cancel quote). - Hotels: run the whole flow on
test_hotel_do_not_bookand cancel it. - eSIMs: order a package, read its instructions and usage, top it up.
- Failure paths: a read-only key on a write endpoint (
403), an unknown id (404), a missing field (400), and a retry after a429.