Sandbox Testing
What is Sandbox?
The sandbox is a testing mode for your account — not a separate environment. It uses the same API, same base URL, and same endpoints as production. The difference is that your sandbox account is connected to a simulated banking provider: transactions are processed instantly, no real funds are transferred, and no PIX is sent to BACEN.
Sandbox and production are separate accounts, each with their own credentials (certificate, clientId, clientSecret). Your certificate may be shared between both accounts depending on your setup — check with your account manager.
Your code doesn't change between sandbox and production. When you're ready to go live, just switch to your production account credentials. No endpoint changes, no code changes.
Sandbox vs Production
| Sandbox | Production | |
|---|---|---|
| Base URL | Same (https://api.avista.global) | Same |
| Account | Dedicated sandbox account | Dedicated production account |
| Credentials | Own certificate + clientId/clientSecret | Own certificate + clientId/clientSecret |
| Balance | Simulated (fictitious) | Real funds |
| Transactions | Simulated (persisted — queryable via API) | Real PIX via BACEN |
| Webhooks | Simulated (~1s delivery), same payload structure | Real, delivery depends on provider |
| X-Sandbox-Scenario | Supported (control webhook outcomes) | Returns 400 Bad Request |
| Documentation | Same as production — all API Reference pages apply | Same |
Getting Started with Sandbox
Contact your account manager or suporte@avista.global to request a sandbox account.
You will receive a certificate and OAuth credentials (clientId + clientSecret) for the sandbox account.
Register your webhook URL via POST /api/webhooks so you can receive simulated webhook events.
In the dashboard, go to Settings to confirm your account is configured in sandbox mode.
Authenticate, make API calls, and use the X-Sandbox-Scenario header to simulate different outcomes.
Overview
The X-Sandbox-Scenario header allows you to control the webhook outcome for any transaction. Without it, the sandbox always returns success webhooks. Use it to test error handling, delays, and edge cases.
Without the X-Sandbox-Scenario header, the sandbox always returns success webhooks. To test error scenarios (insufficient funds, invalid key, etc.), you must explicitly send the header on each request.
This feature works only in sandbox. In production, the header is ignored and the behavior is determined by the actual transaction result.
How It Works
The X-Sandbox-Scenario does not alter the HTTP response -- the API always returns 201 Created with status: "PENDING". The simulated scenario only affects the asynchronous webhook that arrives at your callback URL.
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Your App │──POST──▶│ Avista API │──────▶ │ Mock Provider │
│ │◀──201───│ │ │ │
│ │ │ │ │ Processes the │
│ │ │ │ │ scenario from │
│ │ │ │ │ the header │
│ │◀────────│──webhook─────│◀────────│ │
│ │ │ │ │ ~1s later │
└─────────────┘ └──────────────┘ └─────────────────┘
│ │
│ HTTP Response: always 201 │
│ Webhook: success OR error │
│ (depends on the header) │| Request | HTTP Response | Received Webhook |
|---|---|---|
Without X-Sandbox-Scenario | 201 PENDING | CONFIRMED (success) |
With X-Sandbox-Scenario: success | 201 PENDING | CONFIRMED (success) |
With X-Sandbox-Scenario: error:insufficient-funds | 201 PENDING | ERROR with errorCode |
With X-Sandbox-Scenario: delayed:5s | 201 PENDING | CONFIRMED after 5s extra |
How to Use
Add the X-Sandbox-Scenario header to any Cash-In, Cash-Out, or Refund request:
curl -X POST https://api.avista.global/api/pix/cash-out \
-H "Authorization: Bearer $TOKEN" \
-H "X-Sandbox-Scenario: error:insufficient-funds" \
-H "Content-Type: application/json" \
-d '{
"value": 150.00,
"details": {
"key": "recipient@email.com",
"keyType": "EMAIL",
"name": "Recipient Name",
"document": "39284918812"
},
"externalId": "test-error-001"
}'const response = await axios.post(
'https://api.avista.global/api/pix/cash-out',
{
value: 150.00,
details: {
key: 'recipient@email.com',
keyType: 'EMAIL',
name: 'Recipient Name',
document: '39284918812',
},
externalId: 'test-error-001',
},
{
headers: {
Authorization: `Bearer ${token}`,
'X-Sandbox-Scenario': 'error:insufficient-funds',
},
}
);response = requests.post(
"https://api.avista.global/api/pix/cash-out",
json={
"value": 150.00,
"details": {
"key": "recipient@email.com",
"keyType": "EMAIL",
"name": "Recipient Name",
"document": "39284918812",
},
"externalId": "test-error-001",
},
headers={
"Authorization": f"Bearer {token}",
"X-Sandbox-Scenario": "error:insufficient-funds",
},
)Available Scenarios
Error Scenarios
Simulate different types of webhook failures:
| Header Value | Description | Webhook Status |
|---|---|---|
error:insufficient-funds | Account without sufficient balance | ERROR |
error:invalid-pix-key | PIX key does not exist in DICT | ERROR |
error:document-mismatch | Document does not match the key holder | ERROR |
error:account-blocked | Destination account blocked or closed | ERROR |
error:duplicate-id | Duplicate submission ID | ERROR |
Success Scenario
| Header Value | Description | Webhook Status |
|---|---|---|
success | Forces success (default behavior) | CONFIRMED |
| (no header) | Default sandbox behavior | CONFIRMED |
Delay Scenarios
Simulate slow processing to test timeouts and retry:
| Header Value | Description | Webhook Status |
|---|---|---|
delayed:5s | Success after 5 extra seconds | CONFIRMED |
delayed:30s | Success after 30 extra seconds | CONFIRMED |
delayed:60s | Success after 60 extra seconds | CONFIRMED |
The maximum allowed delay is 120 seconds. Values above will be automatically capped.
Received Webhook Examples
Success Webhook (default)
{
"event": "CashOut",
"status": "CONFIRMED",
"transactionType": "PIX",
"movementType": "DEBIT",
"transactionId": "12345",
"externalId": "test-success-001",
"endToEndId": "E17745159XI4QA0EGFU",
"feeAmount": 0.50,
"originalAmount": 150.00,
"finalAmount": 150.50,
"processingDate": "2026-03-26T10:00:00.000Z",
"errorCode": null,
"errorMessage": null,
"counterpart": {
"name": "Recipient Name",
"document": "*.284.918-**",
"bank": {}
},
"metadata": {}
}Error Webhook (error:insufficient-funds)
{
"event": "CashOut",
"status": "ERROR",
"transactionType": "PIX",
"movementType": "DEBIT",
"transactionId": "12345",
"externalId": "test-error-001",
"endToEndId": null,
"feeAmount": 0.50,
"originalAmount": 150.00,
"finalAmount": 150.50,
"processingDate": "2026-03-26T10:00:00.000Z",
"errorCode": "INSUFFICIENT_FUNDS",
"errorMessage": "Conta sem saldo",
"counterpart": {
"name": null,
"document": null,
"bank": {}
},
"metadata": {}
}When the status is ERROR, the endToEndId field will be null (since the PIX was never confirmed by the Central Bank) and the errorCode and errorMessage fields describe the failure reason.
Compatible Endpoints
The X-Sandbox-Scenario header works with all transactional endpoints:
| Endpoint | Method | Description |
|---|---|---|
/api/pix/cash-in | POST | Generate PIX charge (QR Code) |
/api/pix/cash-out | POST | PIX payment by key |
/api/pix/cash-out/qrcode | POST | PIX payment by QR Code |
/api/pix/refund-in | POST | Refund request |
MED Scenarios
The MED module (Special Refund Mechanism) cannot be tested directly via X-Sandbox-Scenario. MEDs depend on integration with BACEN and with the configured PIX provider (Woovi, Hyperwallet, etc.), so they cannot be simulated by the sandbox mock provider.
To validate your integration with MED webhooks (MedCreated, MedAccepted, MedRejected):
Use POST /api/webhooks/{id}/resend to resend an existing MED webhook from your account to the configured URL. Useful for debugging the handler locally after changes.
Use GET /api/med to list MEDs associated with the account, filter by status, and reconcile with your local transactions.
If you need to simulate an end-to-end MED flow (opening, analysis, approval/rejection), contact suporte@avista.global to request access to the staging environment.
MED webhooks use the same authentication (Basic Auth) and retry structure as other events. The full payload is documented in MedCreated, MedAccepted, and MedRejected.
Behavior
The API processes the request normally and returns 201 Created with status PENDING.
The header does not alter the immediate response -- only the subsequent webhook.
After ~1 second (or more, if using delayed:), the webhook is sent to the configured URL with the simulated scenario.
Your system receives the webhook with the status corresponding to the scenario (CONFIRMED or ERROR) and should process it accordingly.
Important: The X-Sandbox-Scenario header controls only the webhook. The HTTP response from the endpoint always returns success (201 Created) with status: "PENDING", regardless of the chosen scenario. The final result (success or error) arrives via webhook.
Restrictions
The X-Sandbox-Scenario header works exclusively with accounts configured in sandbox mode. If your account is configured with a production provider and you send the header, the API will return an error:
{
"statusCode": 400,
"message": "X-Sandbox-Scenario header is only supported in sandbox mode. This account is not configured with a sandbox provider."
}Make sure your account is in sandbox mode before using this feature. If you receive this error, contact suporte@avista.global to verify your account configuration.
Best Practices
Test all scenarios
Implement handling for each webhook status (CONFIRMED, PENDING, ERROR) before going to production.
Validate error fields
When status is ERROR, use errorCode and errorMessage to determine the appropriate action (retry, notify user, etc.).
Test with delay
Use delayed: scenarios to verify that your system handles webhooks that take longer to arrive correctly.
Idempotency
Use transactionId as an idempotency key. The same webhook may be resent in case of delivery failure.