# Payment Router

The RailProtocol payment router sits between your agent and every paid API endpoint. It detects which payment protocol a service requires, handles the full negotiation, and returns the API response. Your agent calls one method. The protocol is invisible.

***

## How detection works

When `rail.pay()` is called, the router sends the original request to the target endpoint. If the response is HTTP 402, the router inspects the response body to determine which protocol to use:

| Indicator                                           | Protocol selected             |
| --------------------------------------------------- | ----------------------------- |
| `X-PAYMENT-REQUIREMENTS` header with `scheme` field | x402                          |
| Response body contains `"protocol": "mpp/1.0"`      | MPP                           |
| Both indicators present                             | x402 (default preference)     |
| Neither indicator present                           | `PaymentProtocolError` thrown |

Detection happens in under 5ms. It adds no meaningful latency to the payment flow.

***

## Protocol preference

By default, RailProtocol prefers x402 when a service supports both protocols. x402 is simpler, requires no session negotiation on the first call, and settles directly on-chain without an intermediary rail.

Override the preference per agent:

```typescript
const rail = new RailProtocol({
  agentId: "my-agent",
  apiKey: process.env.RAIL_API_KEY,
  protocolPreference: "mpp",  // "x402" | "mpp" | "auto" (default: "auto")
});
```

Or via CLI when setting policies:

```bash
rail policies set my-agent --protocol-preference mpp
```

***

## The `pay()` method

```typescript
const result = await rail.pay({
  endpoint: "https://api.service.com/resource",
  method: "GET" | "POST" | "PUT" | "DELETE",
  headers: { "Custom-Header": "value" },  // optional
  body: { ... },                           // optional, for POST/PUT
  idempotencyKey: "unique-key",           // optional, prevents duplicate payments
});
```

**Response:**

```typescript
interface PaymentResult {
  status: number;            // HTTP status from the API (after successful payment)
  protocol: "x402" | "mpp"; // Protocol used
  amountPaid: {
    usdc: number;            // Amount paid in USDC
  };
  txHash: string;            // Solana transaction hash
  solanaExplorer: string;    // Direct link to the on-chain record
  data: unknown;             // The API response body (parsed JSON or raw text)
  headers: Headers;          // Response headers from the API
  sessionId?: string;        // MPP session ID if one was established or reused
}
```

***

## Idempotency

To prevent duplicate payments if your code retries a failed request, pass an `idempotencyKey`:

```typescript
const result = await rail.pay({
  endpoint: "https://api.service.com/resource",
  method: "POST",
  body: { query: "..." },
  idempotencyKey: `run-${runId}-step-3`,
});
```

If a payment with the same key was completed within the last 24 hours, RailProtocol returns the cached result without charging again.

***

## Retry behavior

If the payment succeeds but the API returns a non-2xx status, the router does not retry automatically. The payment was made; the error belongs to the API.

If the payment itself fails (e.g. network error before the request reaches the facilitator), the router retries with exponential backoff:

| Attempt   | Delay                 |
| --------- | --------------------- |
| 1st retry | 500ms                 |
| 2nd retry | 1s                    |
| 3rd retry | 2s                    |
| Give up   | `PaymentError` thrown |

A failed payment does not deduct from the agent's wallet unless on-chain confirmation was received.

***

## Session management

For MPP services, the router maintains a session cache per agent per service domain. Sessions reduce per-request overhead by allowing payment authorization to be reused across multiple calls.

View active sessions:

```bash
rail sessions list --agent my-agent
```

Clear sessions for a service (forces renegotiation on next call):

```bash
rail sessions clear --agent my-agent --domain api.service.com
```

***

## Error types

| Error                    | Cause                                                   |
| ------------------------ | ------------------------------------------------------- |
| `PolicyViolationError`   | Payment would breach a spend policy                     |
| `InsufficientFundsError` | Agent wallet balance too low                            |
| `PaymentProtocolError`   | Endpoint returned 402 but with an unrecognized protocol |
| `PaymentError`           | Payment failed after all retries                        |
| `DomainNotAllowedError`  | Endpoint domain is not on the agent's allowlist         |

***

## Further reading

* [x402 protocol deep-dive](/protocols/x402.md)
* [MPP protocol deep-dive](/protocols/mpp.md)
* [Spend policies](/features/spend-policies.md)
* [TypeScript SDK reference](/sdk/typescript.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.railprotocol.org/features/payment-router.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
