> ## Documentation Index
> Fetch the complete documentation index at: https://developers.javalibridge.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing in the sandbox

> The sandbox runs the same API as production. One extra endpoint, available only in the sandbox, lets you move any record to the status you want to test.

Sandbox base URL: `https://sandbox.javalibridge.com`. Sandbox credentials are separate from production credentials and are issued at onboarding.

## How it works

In production, validation, compliance, FX, Pix and time move your records from one status to the next. In the sandbox each record waits: **you choose the next status** with one endpoint, [`POST /v1/sandbox/simulations`](https://developers.javalibridge.com/en/api-reference/create-sandbox-simulation).

```bash theme={null}
curl -X POST https://sandbox.javalibridge.com/v1/sandbox/simulations \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -d '{
    "resourceType": "REMITTANCE",
    "resourceId": "rem_123",
    "status": "PROCESSING"
  }'
```

```json theme={null}
{
  "resourceType": "REMITTANCE",
  "resourceId": "rem_123",
  "previousStatus": "RECEIVED",
  "status": "PROCESSING",
  "simulatedAt": "2026-11-11T14:02:10Z"
}
```

| Rule | Detail |
| - | - |
| Your integration does not change | You call the production endpoints exactly as you will in production. The simulation is a separate call, which you can also make from the portal. |
| One step per call | Each call moves the record one status forward and sends the webhook of that change. |
| Real rules | Only the transitions that exist in production are accepted. An invalid one returns `409` with the accepted statuses in `details`. |
| Real records | A sender moved to `ACTIVE` is `ACTIVE` and can be used in withdrawals. Balances, limits and lots move with each remittance. |
| Same payloads | Requests, responses and webhooks have the same schema as production. |
| Sandbox only | The endpoint does not exist in production and returns `404` there. |

Errors of the simulation endpoint:

| HTTP | When |
| - | - |
| 400 `VALIDATION_ERROR` | Unknown `resourceType`, `status` or `rejectedCode`, or `REJECTED` without `rejectedCode`. |
| 404 `NOT_FOUND` | The record does not exist in your sandbox account. |
| 409 `INVALID_STATUS_TRANSITION` | The record cannot go from its current status to the one requested. |

> [!NOTE]
>
> Every simulation is also available in the sandbox portal: open the record and use **Simulate**. It calls the same endpoint.

## What you can simulate

| `resourceType` | `resourceId` | `status` accepted | Webhook sent |
| - | - | - | - |
| `SENDER` | Sender ID | `ACTIVE`, `PENDING`, `RESTRICTED`, `BLOCKED`, `INACTIVE` | `sender.status_changed` |
| `BENEFICIARY` | Beneficiary ID | `ACTIVE`, `PENDING`, `BLOCKED`, `INACTIVE` | `beneficiary.status_changed` |
| `PIX_KEY` | Payment instruction ID (`paymentInstructionId`) | `APPROVED`, `REJECTED` | `beneficiary.status_changed` |
| `QUOTE` | Quote ID | `EXPIRED` | — |
| `REMITTANCE` | Remittance ID | `PROCESSING`, `ON_HOLD`, `COMPLETED`, `REJECTED` (with `rejectedCode`), `FUNDS_HELD_BRL`, `REFUNDED_BY_USER` | `remittance.status_changed` |
| `SETTLEMENT` | Batch ID | `CLOSED`, `DUE`, `OVERDUE`, `COMPLETED` | — |
| `INVENTORY_OBLIGATION` | Inventory lot ID | `OVERDUE`, `PAID` | — |

## Senders

`POST /v1/senders` creates the sender as `PENDING`. It stays `PENDING` until you simulate the compliance decision.

| To test | Simulate `SENDER` → |
| - | - |
| Sender approved | `ACTIVE` |
| Sender blocked, restricted or deactivated | `BLOCKED`, `RESTRICTED`, `INACTIVE` |
| Sender back in review | `PENDING` |

Any status can move to any other. The compliance level is calculated from the data you send, as in production: a sender without the `LEVEL_1` data is `LEVEL_0` and cannot send, even when `ACTIVE`.

## Beneficiaries and Pix keys

`POST /v1/beneficiaries` creates the beneficiary as `PENDING` and each Pix key as `SCREENING`. They are approved separately, as in production.

| To test | Simulate |
| - | - |
| Beneficiary approved | `BENEFICIARY` → `ACTIVE` |
| Pix key approved | `PIX_KEY` → `APPROVED` |
| Pix key rejected | `PIX_KEY` → `REJECTED` |
| Beneficiary blocked or deactivated | `BENEFICIARY` → `BLOCKED`, `INACTIVE` |

A remittance needs the beneficiary `ACTIVE` and the Pix key `APPROVED`. Any Pix key is accepted at registration.

## Quotes

`POST /v1/fx/quotes` returns a quote `AVAILABLE` with the standard validity. To test an expired quote without waiting, simulate `QUOTE` → `EXPIRED`.

## Withdrawals

`POST /v1/remittances/withdrawals` creates the remittance as `RECEIVED` and sends the first `remittance.status_changed`. `RECEIVED` means the request was accepted and is waiting for validation. In the sandbox it stays `RECEIVED` until you simulate the next step.

| From | You can simulate |
| - | - |
| `RECEIVED` | `PROCESSING` (validation passed), `REJECTED` (validation failed) |
| `PROCESSING` | `COMPLETED`, `ON_HOLD`, `REJECTED`, `FUNDS_HELD_BRL` |
| `ON_HOLD` | `PROCESSING` (review approved), `REJECTED` (review declined) |
| `COMPLETED` | `REFUNDED_BY_USER` |
| `REJECTED`, `FUNDS_HELD_BRL`, `REFUNDED_BY_USER` | Nothing. These are final. |

The paths you will want to test:

| To test | Simulate, in order | Webhooks in total |
| - | - | - |
| Remittance completed | `PROCESSING`, `COMPLETED` | 3 |
| Rejected in validation | `REJECTED` with a `rejectedCode` | 2 |
| Compliance review approved | `PROCESSING`, `ON_HOLD`, `PROCESSING`, `COMPLETED` | 5 |
| Compliance review declined | `PROCESSING`, `ON_HOLD`, `REJECTED` with `COMPLIANCE_REJECTED` | 4 |
| Funds held in BRL | `PROCESSING`, `FUNDS_HELD_BRL` | 3 |
| Refunded by the beneficiary | `PROCESSING`, `COMPLETED`, `REFUNDED_BY_USER` | 4 |

Effects are the same as in production:

* `PROCESSING` reserves the credit limit, the prefunded balance or the Inventory volume.
* `REJECTED` releases what was reserved. The quote and the external ID are not released: use new ones to try again.
* `COMPLETED` makes the receipt available in `GET /v1/remittances/{remittanceId}/receipt`. A `CREDIT` remittance enters the open settlement batch.
* `FUNDS_HELD_BRL` and `REFUNDED_BY_USER` return nothing to your wallets or to your credit line.

> [!NOTE]
>
> Simulating `PROCESSING` runs the real validation. If the withdrawal has a real problem, such as a sender that is still `PENDING`, the remittance goes to `REJECTED` with the real code instead, and the response of the simulation shows it.

## Rejections

A rejection is a remittance that goes to `REJECTED`, with the cause in `rejectedCode` and a readable text in `rejectedDescription`. To test one, create a withdrawal and simulate `REJECTED` with the code you want:

```bash theme={null}
curl -X POST https://sandbox.javalibridge.com/v1/sandbox/simulations \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -d '{
    "resourceType": "REMITTANCE",
    "resourceId": "rem_123",
    "status": "REJECTED",
    "rejectedCode": "CREDIT_LIMIT_EXCEEDED"
  }'
```

The webhook and `GET /v1/remittances/{remittanceId}` then return:

```json theme={null}
{
  "status": "REJECTED",
  "rejectedCode": "CREDIT_LIMIT_EXCEEDED",
  "rejectedDescription": "The credit limit is not enough."
}
```

`rejectedCode` is required when you simulate `REJECTED`. `rejectedDescription` is filled with the standard text of the code: you do not send it.

From `RECEIVED`, these codes are accepted:

| `rejectedCode` | What it represents |
| - | - |
| `QUOTE_EXPIRED` | The quote expired before the withdrawal was validated. |
| `QUOTE_ALREADY_USED` | The quote was used by another withdrawal. |
| `QUOTE_USAGE_MISMATCH` | The quote was created for another use. |
| `SENDER_NOT_ELIGIBLE` | The sender is not `ACTIVE`, is `LEVEL_0` or cannot send this remittance. |
| `BENEFICIARY_NOT_ELIGIBLE` | The beneficiary is not `ACTIVE` or cannot receive this remittance. |
| `RELATIONSHIP_MISMATCH` | The relationship does not match the beneficiary type. |
| `PAYMENT_INSTRUCTION_INVALID` | The Pix key cannot be used. |
| `PIX_KEY_NOT_FOUND` | The Pix key does not exist. |
| `FUNDING_MODE_NOT_ENABLED` | The funding mode is not enabled for the account. |
| `CREDIT_LIMIT_EXCEEDED` | The credit limit is not enough. |
| `INSUFFICIENT_PREFUNDED_BALANCE` | The USD balance is not enough. |
| `AMOUNT_BELOW_MINIMUM`, `AMOUNT_ABOVE_LIMIT` | The amount is outside the limits per remittance. |
| `ACCOUNT_DAILY_LIMIT_EXCEEDED` | The account reached its daily limit. |
| `INVENTORY_NOT_FOUND`, `INVENTORY_SUSPENDED`, `INSUFFICIENT_INVENTORY_BALANCE` | The Inventory lot cannot fund the remittance. |

From `PROCESSING` or `ON_HOLD`, the only code accepted is `COMPLIANCE_REJECTED`: compliance declined the remittance, and the reason is not detailed.

## Errors in the response

Only errors about the request itself are returned in the HTTP response of the withdrawal. You reproduce them with a real request, without the simulation endpoint.

| Error | How to reproduce |
| - | - |
| `VALIDATION_ERROR` (400) | Send an invalid or incomplete payload. |
| `DUPLICATE_EXTERNAL_ID` (409) | Repeat an `externalId` with a new `Idempotency-Key`. |
| `IDEMPOTENCY_CONFLICT` (409) | Repeat an `Idempotency-Key` with a different payload. |
| `UNAUTHORIZED` (401) | Send an expired or invalid token. |

## Settlements

A batch opens when the first `CREDIT` remittance of the day is `COMPLETED`. Time and the USDC payment are simulated.

| To test | Simulate `SETTLEMENT` → |
| - | - |
| Batch closed | `CLOSED` |
| Payment due | `DUE` |
| Payment late | `OVERDUE` |
| Batch paid, credit line released | `COMPLETED` (from `CLOSED`, `DUE` or `OVERDUE`) |

`CLOSED`, `DUE` and `OVERDUE` follow this order. Batches do not send webhooks: read them in `GET /v1/settlements`.

## Inventory lots

`POST /v1/inventories` creates the lot with `status = ACTIVE` and `obligation.status = PENDING`.

| To test | Do this |
| - | - |
| Lot `DEPLETED` | Create withdrawals until `available` is 0. |
| Obligation late, lot `SUSPENDED` | Simulate `INVENTORY_OBLIGATION` → `OVERDUE`. |
| Obligation paid, lot back to `ACTIVE` or `DEPLETED` | Simulate `INVENTORY_OBLIGATION` → `PAID`. |

## Not available in the sandbox

`RATE_LIMITED` (429), `INTERNAL_ERROR` (500) and `SERVICE_UNAVAILABLE` (503) cannot be reproduced on demand. Follow the retry rules in [API conventions](https://developers.javalibridge.com/en/api-reference/conventions.md).

## Balances and limits

The sandbox account starts with a prefunded USD balance, a credit line and the funding modes agreed for your account. Check them with `GET /v1/wallets`, `GET /v1/credit-line` and `GET /v1/limits`. No real money moves.

## Before going live

Run each scenario below. A scenario is done when the API returned what is expected and every webhook shows a `2xx` delivery in `GET /v1/webhooks/{webhookId}/deliveries`.

| # | Scenario | Create with | Then simulate |
| - | - | - | - |
| 1 | Token and webhook endpoint | `POST /v1/auth/token`, `POST /v1/webhooks` | — |
| 2 | Sender approved | `POST /v1/senders` | `SENDER` → `ACTIVE` |
| 3 | Sender blocked | — | `SENDER` → `BLOCKED` |
| 4 | Beneficiary and Pix key approved | `POST /v1/beneficiaries` | `BENEFICIARY` → `ACTIVE`, `PIX_KEY` → `APPROVED` |
| 5 | Pix key rejected | `POST /v1/beneficiaries` | `PIX_KEY` → `REJECTED` |
| 6 | Remittance completed, receipt read | Withdrawal | `PROCESSING`, `COMPLETED` |
| 7 | Rejected: expired quote | Withdrawal | `REJECTED` with `QUOTE_EXPIRED` |
| 8 | Rejected: funding (limit or balance) | Withdrawal | `REJECTED` with `CREDIT_LIMIT_EXCEEDED` or `INSUFFICIENT_PREFUNDED_BALANCE` |
| 9 | Rejected: Pix key not found | Withdrawal | `REJECTED` with `PIX_KEY_NOT_FOUND` |
| 10 | Compliance review approved | Withdrawal | `PROCESSING`, `ON_HOLD`, `PROCESSING`, `COMPLETED` |
| 11 | Compliance review declined | Withdrawal | `PROCESSING`, `ON_HOLD`, `REJECTED` with `COMPLIANCE_REJECTED` |
| 12 | Funds held in BRL | Withdrawal | `PROCESSING`, `FUNDS_HELD_BRL` |
| 13 | Refunded by the beneficiary | Withdrawal | `PROCESSING`, `COMPLETED`, `REFUNDED_BY_USER` |
| 14 | Duplicate external ID | Withdrawal repeated | — |
| 15 | `CREDIT`: batch closed and paid | `CREDIT` withdrawal, simulated to `COMPLETED` | `SETTLEMENT` → `CLOSED`, `COMPLETED` |
| 16 | `INVENTORY`: lot created and used | `POST /v1/inventories`, withdrawal | `PROCESSING`, `COMPLETED` |
| 17 | `INVENTORY`: lot suspended, then paid | Lot, then a withdrawal | `INVENTORY_OBLIGATION` → `OVERDUE`; withdrawal → `PROCESSING`, rejected for real with `INVENTORY_SUSPENDED`; `INVENTORY_OBLIGATION` → `PAID` |

Scenarios 15 to 17 apply only to the funding modes enabled for your account.

> [!TIP]
>
> **What you do**
>
> * Build your integration against the production endpoints only. Nothing in it is specific to the sandbox.
> * Use the simulation endpoint, or **Simulate** in the portal, to move each record to the next status.
> * Check that every webhook was delivered before asking for production credentials.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.