Core Concepts

Error Simulation

Test your error handling paths with special headers and magic values. Ghostbox returns errors in the target API's native format.

Error injection headers

Add the X-Sandbox-Errorheader to any request to force a specific error response. The error is returned in the target API's native format, so your error handling code sees exactly what it would see from the real API. Both symbolic names (e.g. rate_limit) and numeric HTTP status codes (e.g. 429) are accepted.

HeaderStatusEffect
X-Sandbox-Error: rate_limit429Returns the service's native rate limit error format.
X-Sandbox-Error: auth401Returns a simulated authentication error in the API's format.
X-Sandbox-Error: not_found404Returns a not-found error in the API's format.
X-Sandbox-Error: bad_request400Returns a bad request / validation error.
X-Sandbox-Error: server_error500Returns an internal server error in the API's format.
Example: simulate a rate limit errorbash
curl -X POST https://ghostbox.dev/sandbox/stripe/v1/customers \
  -H "Authorization: Bearer sandbox_test_abc123..." \
  -H "X-Sandbox-Error: rate_limit" \
  -d "email=test@example.com"

# Returns Stripe-formatted 429 error:
# {
#   "error": {
#     "type": "rate_limit_error",
#     "code": "rate_limit",
#     "message": "Rate limit exceeded. Please retry after a moment."
#   }
# }

Latency injection

Add the X-Sandbox-Latency header with a value in milliseconds to add artificial delay to any response. Use this to test timeout handling, loading states, and retry logic.

Add 2 seconds of latencybash
curl -X GET https://ghostbox.dev/sandbox/stripe/v1/customers \
  -H "Authorization: Bearer sandbox_test_abc123..." \
  -H "X-Sandbox-Latency: 2000"

You can combine X-Sandbox-Latency with X-Sandbox-Error to simulate a slow failure -- for example, a 3-second delay followed by a 500 error.

Idempotency-Key header

For Stripe sandbox requests, you can pass the Idempotency-Key header just like you would with the real API. Repeated requests with the same key return the original response without creating duplicate resources.

bash
curl -X POST https://ghostbox.dev/sandbox/stripe/v1/customers \
  -H "Authorization: Bearer sandbox_test_abc123..." \
  -H "Idempotency-Key: unique-request-id-12345" \
  -d "email=test@example.com"

# Send the same request again with the same key:
# Returns the same customer object (no duplicate created)

Magic values

Certain input values trigger special sandbox behaviors, similar to Stripe's test card numbers. Use these to test specific edge cases without needing special headers.

Stripe

ValueEffect
source[number]: 4000000000000002Triggers a card_declined error.
source[number]: 4000000000009995Triggers an insufficient_funds decline.
source[number]: 4000000000000069Triggers an expired_card decline.
source[number]: 4000000000000127Triggers an incorrect_cvc decline.
amount: 999999999Triggers an amount_too_large error.

Resend

ValueEffect
to: bounce@resend.devSimulates a hard bounce. Email last_event set to 'bounced'.
to: complaint@resend.devSimulates a spam complaint. Email last_event set to 'complained'.

PostHog

ValueEffect
distinct_id: opt-out-userSimulates an opted-out user for feature flag evaluation.

Platform errors vs API errors

Ghostbox returns two distinct types of errors. Understanding the difference is important for correct error handling in your code.

Platform errors

Come from Ghostbox itself: invalid key, quota exceeded, missing endpoint. Use the sandbox_error key.

json
{
  "sandbox_error": "quota_exceeded",
  "message": "Free tier limit exceeded.",
  "timestamp": "2026-03-28T12:00:00Z"
}

Simulated API errors

Returned in the target API's native format. These are what your code should handle in production.

json
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit",
    "message": "Rate limit exceeded."
  }
}

If your error handling code checks for the sandbox_errorkey, it can distinguish between “Ghostbox has a problem” and “the simulated API returned an error.”