Skip to main content

Sandbox mode

Develop and test integrations against sk_test_* keys without touching production data, payments, emails, or calendars. Webhook deliveries DO fire to your configured endpoints so you can test your handler end to end.

What sandbox mode does

When you authenticate with an sk_test_* key, three isolation layers activate:

Layer 1 - Data isolation

You see only the per-agency sandbox tour pool (3 fixture tours: one Active, one Coming Soon, one Sold). You can read and write these tours freely. You cannot see your live production tours.

  • Sandbox tour numbers are prefixed snd_ (e.g. snd_active_a1b2c3)
  • A sandbox key requesting a live tour number receives 404 (not 403) to prevent tour number enumeration across the sandbox boundary

Layer 2 - Side-effect shunting

Real charges, emails, calendar events, and storage uploads are replaced with safe equivalents:

IntegrationSandbox behavior
SquareUses Square's own sandbox environment (no real charges)
MailjetWrites a SandboxMailLog row (no real email sent)
Google CalendarReturns a synthetic success response (no real event created)
Composio storageReturns a synthetic storage URL (no real upload)

Layer 3 - Rate-limit isolation

Sandbox requests use a separate Redis rate-limit bucket from live requests. A runaway sandbox script or load test cannot consume your sk_live_* rate limit budget.

Webhook deliveries in sandbox mode

Webhooks are intentionally NOT shunted. Sandbox webhooks fire to your configured endpoints with "is_sandbox": true in the event payload.

This follows the Stripe model: your handler receives both sandbox and live events at the same URL. The is_sandbox field lets you branch:

const payload = JSON.parse(rawBody);
if (payload.is_sandbox) {
// Test/staging branch - log to a separate table, skip real side effects
} else {
// Production branch
}

If you want a dedicated URL for sandbox traffic, configure two webhook endpoints with different URLs and use the Environment toggle in /manage/webhooks/endpoints to assign each to sandbox or live events.

Sandbox tour pool

On your first sk_test_* key creation, we automatically seed your agency with 3 sandbox fixture tours. You can re-seed at any time via POST /api/v1/agency/sandbox/seed (idempotent - will not duplicate).

The fixtures have realistic structure:

TourStatusTour number prefix
Fixture 1Activesnd_active_
Fixture 2Coming Soonsnd_coming_soon_
Fixture 3Soldsnd_sold_

All fixtures have: gallery placeholder images, synthetic Matterport URL, placeholder floorplan PDF, and synthetic Demo City CA addresses.

What you can do in sandbox mode

  • Read, update, and cancel sandbox tours via the public API
  • Upload media to sandbox tours (uploads complete with synthetic URLs)
  • Read sandbox tour orders and users in your agency hierarchy
  • Trigger all 18 webhook event types - deliveries fire to your configured endpoints with is_sandbox: true
  • Test your idempotency handling: replay any sandbox webhook delivery via /manage/webhooks/logs -> Replay
  • Run load tests against the sandbox endpoint without risk

What you cannot do in sandbox mode

  • See or modify live (sk_live_*) tours from a sandbox key (returns 404)
  • Trigger real Square charges, real Mailjet emails, real Google Calendar events, or real Composio uploads
  • Bypass the 500 requests/minute or 60 console-runs/minute sandbox rate limits

Migrating from sandbox to live

Once your integration works correctly against sandbox:

  1. Create a sk_live_* key with the same scopes as your sandbox key.
  2. Swap the X-API-Key: sk_test_* header for the live key in your deployment environment.
  3. Confirm webhook delivery to your production endpoint:
    • Navigate to /manage/webhooks/health
    • Verify your endpoint shows a recent successful delivery
  4. Monitor /manage/developer/analytics for unexpected error rate spikes in the first 24 hours after cutover.

Sandbox visibility in the dashboard

Sandbox requests are clearly labeled throughout the developer settings UI:

  • /manage/developer/access-log: sandbox requests show a "Sandbox" badge in the Environment column
  • /manage/developer/analytics: filterable by Environment (All / Live / Sandbox)
  • /manage/webhooks/logs: filterable by is_sandbox flag

Troubleshooting

See the operations runbook for common sandbox issues: Sandbox environment troubleshooting or the vault note runbooks/sandbox-environment-troubleshooting.md.

Common problems and quick fixes:

ProblemLikely causeFix
Sandbox key returns 404 on all toursPool not seededPOST /api/v1/agency/sandbox/seed
429 on first sandbox requestRate bucket confusionCheck inject_sandbox_state dependency
Revoked key still worksIn-process cache (60s TTL)Wait 60s or restart worker
Sandbox webhook not deliveredEndpoint configured for live-onlyEnable Sandbox toggle in endpoint settings