Skip to main content
The sandbox is a fully production‑like environment running on the same infrastructure as production. Isolation is enforced at the database level: sandbox data lives in a dedicated PostgreSQL schema (safariat_sandbox), never mixed with production rows. sk_test_* keys route to the sandbox schema; sk_live_* keys route to production. The routing is a hard SQL boundary, not an application filter.

How to get a sandbox key

Self‑service signup at console.safarapi.com/signup. Magic link in your inbox within 30 seconds. Sandbox key visible once on the activation page — copy it then.

What’s in the sandbox

The sandbox mirrors the production catalogue: every adventure, agency, rate pack, and category visible to your sk_live_* key has a clone in the sandbox schema. The clone refreshes every Sunday at 03:00 UTC, so a new prod adventure shows up in your sandbox within the week.
ResourceSourceNotes
AdventuresFull production catalogueSlugs suffixed -sandbox (cosmetic — helps you spot sandbox URLs in logs)
Rate packsFull cloneSame prices and schedules as prod
AgenciesFull cloneReal agency names — your bank can show realistic partner data to QA
CategoriesFull clonedesert, mountain, beach, culture, urban, etc.
The Sunday refresh never touches your sandbox bookings, quotes, or idempotency keys. Your integration history stays intact across weeks.

Pre-defined test travellers

The sandbox simulates real payment behaviours based on the traveler email you pass on POST /bookings:
EmailBehaviour
test.success@bank.exampleBooking confirmed, voucher generated, email sent to sandbox sink
test.cancellation@bank.exampleBooking confirmed, but cancellation API behaves with tier 1 refund
test.insufficient_credit@bank.examplePOST /bookings returns 403 partner.credit.limit.exceeded
test.amount_mismatch@bank.examplePOST /bookings returns 422 payment.amount.mismatch
test.duplicate_ref@bank.exampleSecond POST /bookings with same partner_reference returns 409
Any other email behaves like test.success@bank.example. Real email addresses are accepted but no email is ever sent in sandbox — all messages route to sandbox-sink@safariat.ma.

Rate limits in sandbox

100 requests per minute per key. Sufficient for integration testing; if you need more for load testing, contact us.

Resetting your sandbox

The console exposes a “Reset sandbox data” button under Settings. Clicking it erases all bookings, quotes, and idempotency keys created with your test key in the last 30 days. The cloned catalogue (adventures, rate packs, agencies) remains untouched.

What’s different from production

AspectSandboxProduction
Settlement invoicesNot generatedGenerated monthly
Email to travellerSink (sandbox-sink@safariat.ma)Real recipient
Webhook payloadevent.test = trueevent.test = false
Rate limit100 req/min600 req/min (negotiable)
Credit limit50 000 MAD defaultPer‑contract value
Pricing rulesZero margin (passthrough)Your negotiated rules

Isolation guarantees (for your compliance team)

Sandbox isolation is enforced at the PostgreSQL level, not at the application level:
  • Dedicated schema — every sandbox table lives in safariat_sandbox.*, structurally identical to but separate from safariat.* (production).
  • Per‑request routing — each authenticated request opens its database connection with SET search_path TO <schema>, public. A sk_test_* key forces search_path = safariat_sandbox, public; a sk_live_* key forces safariat, public. The mapping is set by an authentication filter that runs before any business logic touches the database.
  • Connection hygiene — every connection returning to the pool has its search_path reset to the production default, eliminating cross‑request leak risk. Validated by an automated test that performs 100 sandbox checkouts followed by a live checkout against a real PostgreSQL instance.
  • Audit trail — the partner_api_audit_log table records every request with its environment tag (LIVE or TEST), preserved indefinitely.
This satisfies the “where is my test data stored?” question for Bank Al‑Maghrib externalisation reviews. See security/overview for the broader compliance posture.

Time‑limited sandbox keys

A sandbox key is active for 30 days of inactivity. If you don’t make any request for 30 days, the key is auto‑disabled. Re‑activate it with a single click from the console. There’s no calendar expiration — as long as you keep using it, your sandbox key works indefinitely.