> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.alphax.asia/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.alphax.asia/_mcp/server.

# Swap API quickstart

This walks through the whole flow against the **sandbox**
(`https://public-api.demo.alphax.asia`). Set your key once:

```bash
export ALPHAX_KEY="<YOUR_API_KEY>"
export BASE="https://public-api.demo.alphax.asia/v1"
```

## 1. Onboard a company onto swap

```bash
curl -X POST "$BASE/companies" \
  -H "X-API-Key: $ALPHAX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Ltd",
    "countryCode": "HK",
    "directorName": "Jane Director",
    "directorEmail": "jane@acme.example",
    "directorPhone": "+85251234567",
    "addressLine1": "10 Queens Road",
    "city": "Hong Kong",
    "state": "Hong Kong",
    "postalCode": "999077",
    "products": ["Swap"],
    "redirectUri": "https://your-app.example/onboarding/done"
  }'
```

`products` defaults to `["Card"]` when omitted — send `["Swap"]` explicitly.
Requesting a product you are not entitled to returns `403 PRODUCT_NOT_ENABLED`.

The response carries the company `id` and a `swap` block:

```json
{
  "company": {
    "id": "01KZ2WJKWVFPCVAB6MY106QRZV",
    "name": "Acme Ltd",
    "status": "Pending",
    "swap": {
      "kycUrl": "https://public-api.demo.alphax.asia/kyc/9f2c1d84e7b6a5309c8f",
      "kycStatus": "not_started",
      "tosUrl": "https://public-api.demo.alphax.asia/kyc/9f2c1d84e7b6a5309c8f/terms",
      "tosAccepted": false
    }
  }
}
```

Export the company id — it is the `X-Company-Id` header on every call below.

```bash
export CID="01KZ2WJKWVFPCVAB6MY106QRZV"
```

## 2. Get the company verified

Send the director to `kycUrl`, or embed it in your own site — see the
[Verification page](/swap/kyc-page). You can re-read it any time:

```bash
curl "$BASE/kyc-link" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID"
```

Show `tosUrl` too — the company must accept the terms before it can be endorsed,
and `tosAccepted` tells you whether that is still outstanding.

Wait for `kycStatus` to reach `approved` — `company.kyc_status_changed` tells you
the moment it does, so you do not need to poll.

## 3. Open a payment route

#### On-ramp (fiat in, stablecoin out)

```bash
curl -X POST "$BASE/payment-routes" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "onramp",
    "sourceCurrency": "USD",
    "destinationCurrency": "USDC",
    "destinationPaymentRail": "base",
    "destinationAddress": "0xYourTreasuryAddress"
  }'
```

#### Off-ramp (stablecoin in, fiat out)

```bash
curl -X POST "$BASE/payment-routes" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "offramp",
    "sourceCurrency": "USDC",
    "sourcePaymentRail": "base",
    "destinationCurrency": "USD",
    "destinationPaymentRail": "ach",
    "accountOwnerName": "Acme Ltd",
    "accountNumber": "1234567890",
    "routingNumber": "021000021",
    "checkingOrSavings": "checking",
    "bankName": "Example Bank",
    "addressLine1": "1 Market St",
    "city": "San Francisco",
    "state": "CA",
    "postalCode": "94105"
  }'
```

### Destination bank fields for off-ramp

Which fields are required depends on the currency and rail you pick. A rail that
does not belong to the currency is rejected with a `destinationPaymentRail`
validation error.

| Destination | `destinationPaymentRail` | Also required                                                  |
| ----------- | ------------------------ | -------------------------------------------------------------- |
| USD         | `ach`, `wire`            | `accountNumber`, `routingNumber`, `checkingOrSavings`, `state` |
| USD         | `swift`                  | `accountNumber`, `bic`                                         |
| EUR         | `sepa`                   | `iban`, `bic`, `ibanCountry` (or `country`)                    |
| GBP         | `fps`                    | `accountNumber` (8 digits), `sortCode` (6 digits)              |

`accountOwnerName`, `bankName`, `addressLine1`, `city` and `postalCode` are
always required.

## 4. Tell the payer where to send funds

```bash
export RID="<route id from the response>"

curl "$BASE/payment-routes/$RID" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID"
```

```json
{
  "paymentRoute": {
    "id": "01KZ2RT4Q8N7YB3D5F6G7H8J9K",
    "type": "offramp",
    "status": "active",
    "currency": "USD",
    "feePercent": 0.9,
    "depositAddress": "TTRRSXZza9kJuv1fSUeehfkJsZntqUQSqp",
    "source": {
      "type": "crypto",
      "currency": "USDT",
      "paymentRail": "tron",
      "address": "TTRRSXZza9kJuv1fSUeehfkJsZntqUQSqp"
    },
    "destination": {
      "type": "bank",
      "currency": "USD",
      "paymentRail": "ach",
      "bankName": "Example Bank",
      "bankAccountNumber": "1234567890",
      "bankRoutingNumber": "021000021",
      "bankBeneficiaryName": "Acme Ltd"
    },
    "createdAt": "2026-08-13T09:41:22.000000Z"
  }
}
```

`depositAddress` is the short answer to "where does the payer send money" — the
deposit account number for an on-ramp, the chain address for an off-ramp. The
`source` and `destination` blocks carry the full detail; each is either
`type: "bank"` or `type: "crypto"`, so branch on that rather than on currency.

Two PDFs are available for the customer's bank or counterparty:

```bash
# How to fund the route
curl "$BASE/payment-routes/$RID/payment-instructions" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID"

# Confirmation the deposit account belongs to the company
curl "$BASE/payment-routes/$RID/account-letter" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID"
```

Both return `{ "url": "…" }`. The link is short-lived — fetch it when the
customer asks rather than storing it.

## 5. Follow the money

```bash
# This route only
curl "$BASE/payment-routes/$RID/transactions" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID"

# Everything for the company, across every product
curl "$BASE/transactions?limit=25" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID"

# Narrowed to one route
curl "$BASE/transactions?paymentRouteId=$RID" \
  -H "X-API-Key: $ALPHAX_KEY" -H "X-Company-Id: $CID"
```

Every item is a discriminated union — switch on `type`, then read `data`:

```json
{
  "type": "PaymentRoute",
  "transaction": {
    "id": "01KZ2TXN4Q8N7YB3D5F6G7H8J9",
    "direction": "Credit",
    "amount": "99.10",
    "currency": "USD",
    "description": "[payment route] 100.00 USDT",
    "counterpartyName": "TFrom...",
    "createdAt": "2026-08-13T10:02:11.000000Z"
  },
  "data": {
    "routeId": "01KZ2RT4Q8N7YB3D5F6G7H8J9K",
    "routeType": "offramp",
    "incomingAmount": "100.00",
    "convertedAmount": "99.10",
    "totalFees": "0.90",
    "sourceTxHash": "0x...",
    "destinationTxHash": "0x...",
    "source": { "paymentRail": "tron", "currency": "USDT", "fromAddress": "TFrom..." },
    "destination": { "paymentRail": "ach", "currency": "USD", "bankName": "Example Bank" }
  }
}
```

`transaction` is identical for every product; `data` is specific to `type`. On
`GET /v1/transactions` you may also see `Card`, `PaymentAcceptance` and
`Account`.

All three listings accept `page`, `limit` (5–50), `type` (`Credit` / `Debit`),
`orderBy` (`createdAt` / `amount`) and `orderDirection` (`ASC` / `DESC`), and
default to newest first.