Sandbox overview
Sandbox mirrors production behavior while using simulated funds:- Same API endpoints: Use identical API calls as production
- Simulated funding: Mock bank transfers and crypto deposits
- Real webhooks: Receive actual webhook notifications
- No real money: All transactions use test funds
- Isolated environment: Sandbox data never affects production
Sandbox is perfect for development, testing, and demonstrating ramp
functionality before going live.
Getting started
Create sandbox credentials
- Log into the Grid dashboard
- Navigate to Settings → API Keys
- Click Create API Key and select Sandbox environment
- Save your API key ID and secret securely
Configure sandbox webhook
Set up a webhook endpoint for sandbox notifications:Testing on-ramps (Fiat → Crypto)
Simulate the complete on-ramp flow in sandbox:Step 1: Create a test customer
In sandbox, customers are automatically approved for testing.
Step 2: Create an external account for the destination wallet
Step 3: Create an on-ramp quote (just-in-time funding)
Step 4: Simulate funding
Use the sandbox endpoint to simulate receiving the fiat payment:The reference code must match the one provided in the quote’s payment
instructions.
Step 5: Verify completion
Within seconds, you’ll receive a webhook notification confirming the on-ramp completed:Testing off-ramps (Crypto → Fiat)
Simulate the complete off-ramp flow:Step 1: Fund internal account with crypto
Simulate a Bitcoin deposit to the customer’s internal account using the sandbox funding endpoint:InternalAccount:btc001 with your actual BTC internal account ID.
You’ll receive an
ACCOUNT_STATUS webhook showing the updated balance.Step 2: Create external bank account
In sandbox, you can use special account number patterns to test different scenarios. The last 3 digits determine the behavior: 002 (insufficient funds), 003 (account closed), 004 (transfer rejected), 005 (timeout/delayed failure). Any other ending succeeds normally. See “Testing transfer failures” below for details.
Step 3: Create and execute off-ramp quote
In sandbox, off-ramp conversions complete instantly. In production, bank
settlement may take 1-3 business days.
Testing transfer failures
External account test patterns
The flows for creating external accounts in sandbox are the same as in production. The last 3 digits of an external account’s primary identifier (account number, IBAN, CLABE, Spark wallet address, etc.) determine the test scenario when that account is used in transfers or quotes. For identifiers with a domain part (e.g. PIX email keys), append the test digits to the username portion — for example,testuser.002@pix.com.br.
| Suffix | Behavior |
|---|---|
| 002 | Insufficient funds — transfer fails immediately |
| 003 | Account closed/invalid — transfer fails immediately |
| 004 | Transfer rejected — bank rejects the transfer |
| 005 | Timeout/delayed failure — stays pending ~30s, then fails |
| Any other | Success — transfer completes normally |
Beneficiary name verification
For account types that support beneficiary name verification, you can simulate different verification outcomes in sandbox. Use account identifiers with a1xx suffix to trigger verification scenarios (this range is reserved for verification and does not conflict with transfer or quote test patterns):
| Suffix | beneficiaryVerificationStatus | Behavior |
|---|---|---|
| 102 | NOT_MATCHED | Account is valid but name does not match |
| 103 | PARTIAL_MATCH | Account is valid, name is a fuzzy match |
| 104 | PENDING | Verification still in progress |
| 105 | (error) | Returns 400 — invalid account |
| 106 | UNSUPPORTED | Payment rail does not support name verification |
| 107 | CHECKED_BY_RECEIVING_FI | Verification deferred to receiving financial institution (e.g., ACH) |
| 109 | (error) | Returns 500 — simulated API error |
| Any other | MATCHED | Account is valid, name matches exactly |
Test scenarios
Successful conversions
The complete on-ramp and off-ramp flows described in the sections above demonstrate successful conversion scenarios. For quick reference: On-ramp test (USD → BTC):- Create customer and quote with payment instructions
- Use
/sandbox/sendto simulate funding - Verify completion via webhook
- Fund BTC internal account with
/sandbox/internal-accounts/{accountId}/fund - Create external bank account (use default account number for success)
- Create and execute quote
- Verify completion via webhook
Failed conversions
Test error scenarios systematically using the magic account patterns: 1. Test external account insufficient funds (002):Global Account magic values
The Grid sandbox accepts a small set of magic values that bypass real auth and credential checks for Global Account flows, so you can exercise the full request shape without standing up Turnkey, WebAuthn, or an OIDC provider. These values are sandbox-only — production enforces real signature verification, WebAuthn assertion, and OIDC nonce binding. A wrong magic value (or any other value) returns401 UNAUTHORIZED with a reason field that names the specific check that failed.
Email OTP code
Pass000000 as the body otp on POST /auth/credentials/{id}/verify when the credential type is EMAIL_OTP. The sandbox skips OTP delivery and accepts this value as a valid response to the issued challenge.
401 UNAUTHORIZED with reason: "Invalid OTP code".
Passkey assertion signature
Passsandbox-valid-passkey-signature as assertion.signature on POST /auth/credentials/{id}/verify when the credential type is PASSKEY. The sandbox accepts the rest of the assertion as-is and skips the WebAuthn signature check.
401 UNAUTHORIZED with reason: "Invalid passkey signature". clientPublicKey is still required — the magic value bypasses the credential check, not the HPKE plumbing that seals the session signing key to the public key you supply.
OAuth (OIDC) token
Passsandbox-valid-oidc-token as the body oidcToken on both POST /auth/credentials (OAUTH create) and POST /auth/credentials/{id}/verify (OAUTH).
401 UNAUTHORIZED with reason: "Invalid OIDC token".
OAUTH create still requires a JWT-shaped token. On the initial
POST /auth/credentials (OAUTH create), the oidcToken must be a structurally valid JWT (header.payload.signature) so Grid can decode the iss claim and resolve the provider name. The literal sandbox-valid-oidc-token works on verify but not on create — for create, sign your own dummy JWT with any payload that includes a recognized iss claim. The sandbox bypasses signature verification, not JWT structure parsing.Wallet signature header
Passsandbox-valid-signature as the Grid-Wallet-Signature HTTP header on any signed-retry flow:
POST /auth/credentials(add-additional-credential signed retry)DELETE /auth/credentials/{id}(revoke credential)DELETE /auth/sessions/{id}(revoke session)POST /internal-accounts/{id}/export(export wallet)POST /quotes/{quoteId}/execute(when source is an embedded wallet)
401 UNAUTHORIZED with reason: "Invalid Grid-Wallet-Signature".
Moving to Production
When you’re ready to move to production:- Generate production API tokens in the dashboard
- Swap those credentials for the sandbox credentials in your environment variables
- Remove any sandbox-specific test patterns from your code
- Configure production webhook endpoints
- Test with small amounts first
Next steps
- Webhooks - Handle real-time notifications
- Fiat-to-Crypto Conversion - Implement production flows
- Self-Custody Wallets - Advanced wallet integration
- Platform Configuration - Configure production settings
- API Reference - Complete API documentation