Docs

API reference / Sandbox

Simulate a scenario

POST/v1/sandbox/users/{id}/simulate

API key requiredSandbox key onlySupports Idempotency-Key

Creates realistic test data for a sandbox user and emits the matching events. Sandbox key only. Scenarios: kyc_approved, kyc_rejected, deposit (approved KYC required), payment (sufficient balance required), withdrawal (approved KYC, sufficient balance, amount ≥ $10). No real money moves. Accepts Idempotency-Key.

Authentication#

Send a secret API key as Authorization: Bearer <key>. This route only accepts sandbox keys (tp_test_…); a live key gets 403 sandbox_only.

Parameters#

Path

Path parameters
NameTypeDescription
idrequiredstringIdentifier of the user.Pattern ^\d{1,9}$

Headers

Header parameters
NameTypeDescription
Idempotency-KeystringIdempotency key (1 to 255 characters, UUID v4 recommended). Responses are replayed for 24 h.

See Idempotency.

Request body#

  • scenariostringrequired

    Scenario to play.

    One ofkyc_approvedkyc_rejecteddepositpaymentwithdrawal

    Example "deposit"

  • amountnumber

    Amount in USD (deposit: 100 by default, payment: 25, withdrawal: 10).

    max: 1000000 · > 0

    Example 100

Example request body
{
  "scenario": "deposit",
  "amount": 100
}

Responses#

200Result of the simulation.

Headers

  • X-Request-Id Request identifier.
  • X-RateLimit-Limit Quota of the current window.
  • X-RateLimit-Remaining Requests remaining in the window.
  • X-RateLimit-Reset Seconds before the quota renews.

Body

  • objectstringrequired

    always "simulation"

    Example "simulation"

  • scenariostringrequired

    Scenario that was run.

    One ofkyc_approvedkyc_rejecteddepositpaymentwithdrawal

    Example "deposit"

  • userobjectrequired

    End user attached to your project.

    • idstringrequired

      Opaque identifier.

      Example "4821"

    • objectstringrequired

      always "user"

      Example "user"

    • phonestringrequired

      Phone number in E.164 format.

      Example "+33612345678"

    • external_idstring | nullrequired

      Your own identifier for this user.

      Example "cust_8f2a"

    • verifiedbooleanrequired

      The phone number has been verified.

      Example true

    • kyc_statusstringrequired

      Identity verification (KYC) status.

      One ofnonependingapprovedrejected

      Example "approved"

    • suspendedbooleanrequired

      The user is suspended and can no longer transact.

      Example false

    • livemodebooleanrequired

      true for a live key, false for a sandbox key.

      Example false

    • wallet_statusstringrequired

      State of the user’s wallet. ready: the wallet exists. pending: its creation is in progress or being retried (never blocks the user; poll GET /v1/users/{id}/wallets). failed: creation was abandoned after repeated errors (contact support). none: no wallet is created at this step.

      One ofnonependingreadyfailed

      Example "ready"

    • created_atstringrequired

      Creation date.

      format: date-time

      Example "2026-10-08T09:30:00.000Z"

    • last_login_atstring | nullrequired

      Last sign-in.

      format: date-time

      Example "2026-10-08T09:30:00.000Z"

  • transactionobject | nullrequired

    Movement of a user (deposit, withdrawal, payment, transfer).

    • idstringrequired

      Opaque identifier.

      Example "90412"

    • objectstringrequired

      always "transaction"

      Example "transaction"

    • user_idstringrequired

      User concerned.

      Example "4821"

    • typestringrequired

      Kind of movement.

      One ofdepositwithdrawalpaymenttransfer

      Example "deposit"

    • directionstringrequired

      Direction, from the wallet’s point of view.

      One ofinout

      Example "in"

    • amount_usdnumberrequired

      Amount in USDC (1 USDC = 1 USD).

      Example 100

    • fee_usdnumber | nullrequired

      Fee applied (the project’s effective fees), when known.

      Example 0.6

    • statusstringrequired

      Status.

      One ofpendingcompletedfailed

      Example "completed"

    • fiat_currencystring | nullrequired

      Fiat currency (deposits and withdrawals).

      One ofEURUSD

      Example "USD"

    • fiat_amountnumber | nullrequired

      Fiat amount.

      Example 100

    • livemodebooleanrequired

      true for a live key, false for a sandbox key.

      Example false

    • created_atstringrequired

      Creation date.

      format: date-time

      Example "2026-10-08T09:31:00.000Z"

  • eventsarray of objectrequired

    Events emitted, in order.

    • idstringrequired

      Identifier evt_….

      Example "evt_0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"

    • typestringrequired

      Type.

      One ofuser.createduser.suspendeduser.unsuspendedkyc.approvedkyc.rejectedtransaction.receivedtransaction.completedwithdrawal.completedwithdrawal.failed

      Example "transaction.completed"

Example 200 response
{
  "object": "simulation",
  "scenario": "deposit",
  "user": {
    "id": "4821",
    "object": "user",
    "phone": "+33612345678",
    "external_id": "cust_8f2a",
    "verified": true,
    "kyc_status": "approved",
    "suspended": false,
    "livemode": false,
    "created_at": "2026-10-08T09:30:00.000Z",
    "wallet_status": "ready",
    "last_login_at": null
  },
  "transaction": {
    "id": "90412",
    "object": "transaction",
    "user_id": "4821",
    "type": "deposit",
    "direction": "in",
    "amount_usd": 100,
    "fee_usd": 0.6,
    "status": "completed",
    "fiat_currency": "USD",
    "fiat_amount": 100,
    "livemode": false,
    "created_at": "2026-10-08T09:31:00.000Z"
  },
  "events": [
    {
      "id": "evt_0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21",
      "type": "transaction.completed"
    }
  ]
}
400Invalid request (malformed JSON, unknown cursor, invalid Idempotency-Key header).

Example: invalid_cursor

Example 400 response
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_cursor",
    "message": "starting_after: unknown cursor",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}
401Missing, invalid or revoked API key.

Example: missing_api_key

Example 401 response
{
  "error": {
    "type": "authentication_error",
    "code": "missing_api_key",
    "message": "Provide your API key as \"Authorization: Bearer <key>\"",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}

Example: invalid_api_key

Example 401 response
{
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "The API key is invalid",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}

Example: api_key_revoked

Example 401 response
{
  "error": {
    "type": "authentication_error",
    "code": "api_key_revoked",
    "message": "This API key has been revoked",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}
403Organization suspended, live access not enabled, or sandbox-only route.

Example: organization_suspended

Example 403 response
{
  "error": {
    "type": "permission_error",
    "code": "organization_suspended",
    "message": "Your organization is suspended. Contact Tilt Pay support.",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}

Example: live_not_enabled

Example 403 response
{
  "error": {
    "type": "permission_error",
    "code": "live_not_enabled",
    "message": "Live access has not been enabled for your organization yet",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}

Example: sandbox_only

Example 403 response
{
  "error": {
    "type": "permission_error",
    "code": "sandbox_only",
    "message": "This route is only available with a sandbox API key",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}
404Resource not found (or belonging to another project or environment).

Example: user_not_found

Example 404 response
{
  "error": {
    "type": "invalid_request_error",
    "code": "user_not_found",
    "message": "User not found",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}
409Conflict with the state of the resource, or the original idempotent request is still in progress.

Example: user_already_exists

Example 409 response
{
  "error": {
    "type": "invalid_request_error",
    "code": "user_already_exists",
    "message": "A user with this phone number already exists",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}
422Invalid parameter, or Idempotency-Key reused with a different body.

Example: validation_error

Example 422 response
{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_error",
    "message": "phone: has an invalid format",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}

Example: idempotency_key_reuse

Example 422 response
{
  "error": {
    "type": "idempotency_error",
    "code": "idempotency_key_reuse",
    "message": "This Idempotency-Key was already used with a different request",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}
429Rate limit exceeded. Retry after the Retry-After delay (seconds).

Example: rate_limit_exceeded

Example 429 response
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Slow down and retry after the delay in Retry-After.",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}
500Internal error. Retry; give request_id to support if it persists.

Example: internal_error

Example 500 response
{
  "error": {
    "type": "api_error",
    "code": "internal_error",
    "message": "Internal server error",
    "request_id": "0b2c8f6e-41f9-4c0a-8d55-6f6a3c1d9e21"
  }
}

Headers on every response

  • X-Request-Id Identifier of the request, also present in error bodies. Give it to support.
  • X-API-Version Version of the API that answered.
  • X-RateLimit-Limit / -Remaining / -Reset Quota of your key for the current window. See Rate limits.

Errors#

Errors share one format, described in Errors. Branch your code on error.code.

Errors this route can return
StatusCodeMeaning
400invalid_cursorInvalid request (malformed JSON, unknown cursor, invalid Idempotency-Key header).
401missing_api_keyMissing, invalid or revoked API key.
401invalid_api_keyMissing, invalid or revoked API key.
401api_key_revokedMissing, invalid or revoked API key.
403organization_suspendedOrganization suspended, live access not enabled, or sandbox-only route.
403live_not_enabledOrganization suspended, live access not enabled, or sandbox-only route.
403sandbox_onlyOrganization suspended, live access not enabled, or sandbox-only route.
404user_not_foundUnknown user (or belonging to another project or environment).
409user_already_existsConflict with the state of the resource, or the original idempotent request is still in progress.
409invalid_stateThe scenario does not apply to the current state (e.g. KYC already approved).
409kyc_requiredKYC must be approved (deposit, withdrawal).
409insufficient_fundsInsufficient sandbox balance (payment, withdrawal).
409user_suspendedThe user is suspended.
422validation_errorInvalid parameter, or Idempotency-Key reused with a different body.
422idempotency_key_reuseInvalid parameter, or Idempotency-Key reused with a different body.
429rate_limit_exceededRate limit exceeded. Retry after the Retry-After delay (seconds).
500internal_errorInternal error. Retry; give request_id to support if it persists.