---
description: >-
  Sandbox testing for the Remittances API — force transaction status transitions, trigger compliance holds and rejections, and simulate 3DS, ACH settlement, and payout events.
---

# Sandbox Testing

The sandbox lets you drive a transaction through its entire lifecycle without real money movement. Two tools make this possible: **test data** that triggers specific compliance behaviors, and a **force-event endpoint** that simulates the external callbacks (gateway webhooks, payout-network notifications) that normally advance a transaction.

***

### Forcing Status Transitions

**Endpoint:** `POST /organizations/{tenant}/fx/transactions/{transactionId}/events`\
**Authentication:** Agent-level\
**Availability:** Sandbox/staging **only** — this endpoint does not exist in production.

```bash
curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/events \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --header "x-agent-id: $AGENT_ID" \
  --header "x-agent-api-key: $AGENT_KEY" \
  --data '{
  "event": "payment_authorized",
  "message": "Optional description"
}'
```

#### Available Events

| Event | Valid from state | What it simulates |
| ----- | ---------------- | ----------------- |
| `payment_authorized` | `WaitingChallenge3ds` | The gateway 3DS callback. Resumes the flow — dispatches payout integration. Use when the transaction is stuck waiting for 3DS. |
| `ach_settled` | `WaitingSettlement` | The ACH settlement callback. Moves the transaction through `PaymentSettled` → `PaymentCaptured` and triggers payout confirmation. |
| `payment_captured` | `ReviewApproved` | A card capture confirmation. Triggers payout confirmation. |
| `payout_paid` | `PayoutAccepted`, `PayoutReleased`, `WaitingPayout` | The payout network confirming the beneficiary received funds. Moves the transaction to `Paid` → `Completed`. |
| `payout_void` | `PayoutAccepted`, `PayoutHold`, `PayoutReleased`, `WaitingPayout` | The payout network voiding the transaction. Depending on tenant configuration, auto-reverses or parks at `PendingReversalApproval`. |
| `hold_released` | `PayoutHold` | The payout network releasing a compliance hold. Resumes the flow. |
| `cancelled` | Any cancellable state | Client-initiated cancellation (voids the payment and cancels the payout). |
| `refunded` | Refundable states | Marks the transaction as refunded. |

If the transaction is not in a valid state for the event, the endpoint returns an error explaining the current state.

#### Typical Test Flow (Card + 3DS)

1. Create the transaction → status `WaitingChallenge3ds`, `paymentStatus: ActionRequired`
2. Force 3DS: `{ "event": "payment_authorized" }`
3. Transaction auto-progresses → `PayoutAccepted` → `ManualReview` → `ReviewApproved` → `PaymentCaptured` → `WaitingPayout`
4. Force delivery: `{ "event": "payout_paid" }`
5. Transaction reaches `Completed`

#### Typical Test Flow (ACH)

1. Create the transaction → progresses to `WaitingSettlement`
2. Force settlement: `{ "event": "ach_settled" }`
3. Transaction auto-progresses → `PaymentSettled` → `PaymentCaptured` → `WaitingPayout`
4. Force delivery: `{ "event": "payout_paid" }`
5. Transaction reaches `Completed`

***

### Compliance Test Scenarios

Certain sender data values trigger specific compliance behaviors in the sandbox:

| Parameter | Value | Result | Compliance Status | Payout Status |
| --------- | ----- | ------ | ----------------- | ------------- |
| `address.zipcode` | `99999` | Transaction REJECTED | `Rejected` | `Cancelled` |
| `phoneNumber` | `+14155550000` | Transaction REJECTED | `Rejected` | `Cancelled` |
| `firstName` + `lastName` | `BLOCK LIST MATCH` | Transaction REJECTED | `Rejected` | `Cancelled` |
| `firstName` + `lastName` | `OFAC MATCH` | Transaction held for verification | `Pending` | `Pending` (status `PayoutHold`) |

#### Payout-Network Hold

Use sender name **JULIO SOLANO** to trigger a compliance hold at the payout network:

```json
{ "firstName": "JULIO", "lastName": "SOLANO", "...": "..." }
```

The transaction reaches `PayoutHold` after payout integration. Release it with the `hold_released` force event (or it is released by the network/backoffice).

**Expected flow:**

```
... → ProcessingPayout → PayoutHold → [hold released] → PayoutReleased → ...
```

#### Payout-Network Rejection (Missing Receiver Data)

Use zipcode **96738** on the sender's billing address to trigger receiver-data validation failure at the payout network:

```json
{ "address": { "zipcode": "96738", "...": "..." } }
```

> The rejection depends on missing beneficiary data (country, city, state). If the recipient has complete address data, the transaction is accepted regardless of zipcode.

#### Payment Decline

Use a card token that the gateway sandbox rejects, or an expired token. The transaction reaches `PaymentDeclined` → `Cancelled`.

#### 3DS Decline

When redirected to the 3DS challenge page, choose "Decline" (where the sandbox offers it). The gateway reports the failure and the transaction moves to `PaymentDeclined` → `Cancelled`.

***

### Test Cards

Use sandbox test cards to simulate 3DS scenarios. The full list is in [Payments Gateway test cards](../payments-gateway/apis/test-data/cards.md). Common ones:

| Card Number | Scheme | 3DS Behavior |
| ----------- | ------ | ------------ |
| `4462030000000000` | VISA | 3DS Challenge |
| `4035874000424977` | VISA | Frictionless (no challenge) |
| `5425230000004415` | Mastercard | 3DS Challenge |
| `4000000000000002` | VISA | Declined |

> Tokenize these via the gateway SDK, then pass the token as `paymentMethod.token` when [creating a funding account](sender-funding-account.md).

***

### Polling for Status

After creating a transaction, poll `GET /organizations/{tenant}/fx/transactions/{transactionId}` and watch:

| Field | What to expect |
| ----- | -------------- |
| `complianceStatus` | `Pending` → `Approved` (after payout integration) |
| `payoutStatus` | `Pending` → `Processing` → `Completed` |
| `paymentStatus` | `ActionRequired` → `null` (after 3DS completes) |
| `receipt` | Regulatory disclosures (available from creation) |

For the full transition audit trail, use `GET /fx/transactions/{transactionId}/status` — see [Transaction](transaction.md#get-transaction-status-history). In production, prefer [webhooks](webhooks.md) over polling.
