> ## Documentation Index
> Fetch the complete documentation index at: https://rain-sandbox-trial.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfer Transactions

> Simulate payment route transfers for MXN (SPEI) and USD (ACH/wire) flows.

<Warning>
  **Beta Feature**

  The Transaction Simulations feature is currently in beta. API endpoints and behavior may change as we continue to refine the product. Simulation endpoints are available in development environments only — production returns `404 Not Found`.
</Warning>

Trigger a `transfer` transaction against a configured payment route, exactly as if a real deposit had triggered it. Use this to test the transfer and webhook lifecycle end-to-end.

## Prerequisites

Before you can simulate a payment route, you must create a payment route in the sandbox environment. The payment route creation endpoint is separate from the simulation endpoint.

**For onramps** (fiat → crypto), create a payment route:

```bash theme={null}
curl -X POST https://api-dev.rain.xyz/v1/payment-routes \
  -H "Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "11111111-1111-1111-1111-111111111111",
    "source": {
      "currency": "usd",
      "rail": "ach"
    },
    "destination": {
      "currency": "usdc",
      "rail": "base",
      "address": {
        "type": "onchain",
        "address": "0x1234567890abcdef1234567890abcdef12345678"
      }
    }
  }'
```

**For offramps** (crypto → fiat), first create a payment account, then create a payment route:

```bash theme={null}
# Step 1: Create a payment account
curl -X POST https://api-dev.rain.xyz/v1/payment-accounts \
  -H "Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "externalFiatAccount",
    "userId": "11111111-1111-1111-1111-111111111111",
    "nickname": "Test Checking Account",
    "externalFiatAccount": {
      "currency": "usd",
      "rail": "ach",
      "thirdParty": false,
      "beneficiaryFirstName": "John",
      "beneficiaryLastName": "Doe",
      "beneficiaryType": "individual",
      "beneficiaryAddress": {
        "addressLine1": "123 Main St",
        "city": "New York",
        "region": "NY",
        "postalCode": "10001",
        "countryCode": "US"
      },
      "bankName": "Test Bank",
      "bankAddress": {
        "addressLine1": "456 Bank Ave",
        "city": "New York",
        "region": "NY",
        "postalCode": "10002",
        "countryCode": "US"
      },
      "accountNumber": "123456789",
      "routingNumber": "000000000",
      "bankAccountType": "checking"
    }
  }'

# Step 2: Create the offramp payment route (use the payment account ID from step 1)
curl -X POST https://api-dev.rain.xyz/v1/payment-routes \
  -H "Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "11111111-1111-1111-1111-111111111111",
    "source": {
      "currency": "usdc",
      "rail": "base"
    },
    "destination": {
      "currency": "usd",
      "rail": "ach",
      "address": {
        "type": "paymentAccount",
        "id": "PAYMENT_ACCOUNT_ID_FROM_STEP_1"
      }
    },
    "refundAddress": "0x1234567890abcdef1234567890abcdef12345678"
  }'
```

The response includes the `id` field — this is your `paymentRouteId` to use in simulation requests.

See the [Onramps](/docs/onramps) and [Offramps](/docs/offramps) documentation for complete details on creating payment routes.

## Endpoint

```
POST /v1/simulate/payment-routes
```

<Info>
  The `/v1/simulate/automations` endpoint has been renamed to `/v1/simulate/payment-routes`. The old path remains available as a deprecated alias during migration. Update your integrations to use `/v1/simulate/payment-routes` as the deprecated endpoint will be removed in a future release.
</Info>

<Info>
  Supported payment route types:

  * **MXN payment routes** — Both onramps (SPEI → crypto) and offramps (crypto → SPEI)
  * **USD payment routes** — Both onramps (ACH/wire → crypto) and offramps (crypto → ACH/wire)

  Simulating any other payment route type returns `422 Unprocessable Entity`.
</Info>

## Supported chains

Payment routes can only be created on the following testnet chains in the sandbox environment:

* Ethereum Sepolia
* Avalanche Fuji
* Solana Devnet
* Polygon Amoy
* Base Sepolia

Once you have a payment route on a supported chain, you can simulate transfers against it.

## Minimum amount

Onramp and offramp simulations require a minimum amount of \$2.

## Headers

| Header            | Type                | Required | Description                                                                                                                                  |
| ----------------- | ------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `Api-Key`         | string              | Yes      | Your tenant API key. The tenant scope is resolved from this key.                                                                             |
| `Content-Type`    | `application/json`  | Yes      | —                                                                                                                                            |
| `Idempotency-Key` | string (1–64 chars) | No       | Optional idempotency/correlation key. If you omit it, the server generates a fresh UUID per request, so each call produces a new simulation. |

## Body

| Field            | Type             | Required | Description                                                                                                                                                                                          |
| ---------------- | ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paymentRouteId` | string (UUID)    | Yes      | The Rain payment route ID to simulate. Must belong to the calling tenant.                                                                                                                            |
| `amount`         | string (decimal) | Yes      | The source-currency amount to simulate, as a positive decimal string in major units (for example, `"100"` or `"25.50"`). Must be greater than `0` and at most `100`. Pattern: `^[0-9]+(\.[0-9]+)?$`. |

```json title="Example request" theme={null}
{
  "paymentRouteId": "a7f2c5b1-9e8d-4c3a-bb22-12c0a4d5b8f9",
  "amount": "100"
}
```

## Response

The response format depends on the payment route type.

### MXN payment routes (synchronous)

MXN payment route simulations return immediately with the transaction ID:

```json title="200 OK" theme={null}
{
  "transactionId": "f3a1c92e-1b3d-4d1e-bb22-12c0a4d5b8f9"
}
```

| Field           | Type          | Description                                                                                              |
| --------------- | ------------- | -------------------------------------------------------------------------------------------------------- |
| `transactionId` | string (UUID) | The transaction ID. Call `GET /v1/transactions/:transactionId` to fetch full details and current status. |

<Info>
  The transaction starts in `pending` and progresses asynchronously, just like one triggered by a real deposit. Your webhook URL receives the same `transactionTransfer` webhooks as a real transfer:

  * action `created` — sent when the transfer is initiated.
  * action `updated` — sent on intermediate status changes (for example, `awaiting_transfer` → `processing`).
  * action `completed` — sent when the transfer settles.

  See [Webhooks](/docs/transfers#webhooks) for payload schemas.
</Info>

### USD payment routes (asynchronous)

USD payment route simulations return HTTP 202 to indicate the request was accepted and the simulation is processing asynchronously:

```json title="202 Accepted — USD onramp" theme={null}
{
  "simulationId": "dep_7f3a9b2c4e1d",
  "flow": "usd_onramp",
  "status": "accepted",
  "provider": {
    "accountProviderDepositId": "dep_7f3a9b2c4e1d"
  }
}
```

```json title="202 Accepted — USD offramp" theme={null}
{
  "simulationId": "mnt_8e2b5d6f9a3c",
  "flow": "usd_offramp",
  "status": "accepted",
  "provider": {
    "minterDepositId": "mnt_8e2b5d6f9a3c"
  }
}
```

| Field                               | Type   | Description                                                                                                                                |
| ----------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `simulationId`                      | string | Identifier for tracking the simulation. For onramps, this is the account provider deposit ID. For offramps, this is the minter deposit ID. |
| `flow`                              | string | The simulation flow type: `usd_onramp` or `usd_offramp`.                                                                                   |
| `status`                            | string | Always `accepted` for successful submissions.                                                                                              |
| `provider`                          | object | Provider-specific identifiers for the simulated deposits.                                                                                  |
| `provider.accountProviderDepositId` | string | The account provider's deposit identifier. Present only for onramp flows.                                                                  |
| `provider.minterDepositId`          | string | The minter's deposit identifier. Present only for offramp flows.                                                                           |

<Info>
  USD simulations trigger real webhook callbacks asynchronously. Your webhook URL receives `transactionTransfer` webhooks as the simulation progresses through the transfer lifecycle.
</Info>

## Errors

| Status                      | When                                                                                                                                   |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`           | Invalid body — missing required fields or malformed `amount`.                                                                          |
| `404 Not Found`             | Endpoint not available in production, or the `paymentRouteId` doesn't exist or belong to your tenant.                                  |
| `422 Unprocessable Entity`  | The payment route exists but is not eligible for simulation (for example, an unsupported flow type or missing required configuration). |
| `500 Internal Server Error` | Unhandled error during simulation.                                                                                                     |

## Behavior notes

* **No real funds move.** Simulations are fully mocked end-to-end — you don't need to pre-fund anything.
* **MXN simulations create immediate transaction records.** The transaction is queryable via the transactions API and fires webhooks as it progresses.
* **USD simulations process asynchronously.** The 202 response indicates the simulation request was accepted. Webhooks fire as the underlying providers process the simulated deposits.

## Example

```bash theme={null}
curl -X POST https://api-dev.rain.xyz/v1/simulate/payment-routes \
  -H "Api-Key: YOUR_API_KEY" \
  -H "Idempotency-Key: unique-request-id-12345" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentRouteId": "a7f2c5b1-9e8d-4c3a-bb22-12c0a4d5b8f9",
    "amount": "100"
  }'
```
