> 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 webhooks

Verification and settlement complete in the background. Subscribe to webhooks to
be notified the moment state changes, instead of polling.

Manage your webhook endpoints (URL, subscribed events, signing secret) in the
**Partner Platform → Developer → Webhook endpoints**.

## Payload structure

Every event is delivered as an **HTTP POST** with this envelope:

```json
{
  "id": "unique_event_id",
  "type": "payment_route.transaction.created",
  "payload": {
    // event-specific fields (see below)
  },
  "createdAt": "2026-08-13T12:00:00Z"
}
```

## Verifying signatures

Each request includes an `X-Signature` header — an HMAC-SHA256 hex digest of the
raw request body, keyed by your endpoint's signing secret. Recompute it and
compare using a constant-time function. See the general
[Webhook](/webhook) guide for full code samples (Node.js, PHP, Go, Java).

## Retries

If your endpoint doesn't return a `2xx`, we retry with exponential backoff, up to
5 attempts (10s, 30s, 1m, 2m, 5m).

## Events

| Event                               | When it fires                                   |
| ----------------------------------- | ----------------------------------------------- |
| `company.onboarding_status_changed` | A company's onboarding status changes           |
| `company.kyc_link.issued`           | A company's verification link becomes available |
| `company.kyc_status_changed`        | Identity verification moves on                  |
| `payment_route.created`             | A payment route is opened                       |
| `payment_route.transaction.created` | Money lands on a payment route                  |

### company.kyc\_link.issued

Fires once the company is provisioned for swap. Carries the same `swap` block as
`GET /v1/kyc-link`, so you can store both links without a follow-up call.

```json
{
  "companyId": "01KZ2WJKWVFPCVAB6MY106QRZV",
  "kycUrl": "https://public-api.alphax.com/kyc/9f2c1d84e7b6a5309c8f",
  "kycStatus": "not_started",
  "tosUrl": "https://public-api.alphax.com/kyc/9f2c1d84e7b6a5309c8f/terms",
  "tosAccepted": false
}
```

### company.kyc\_status\_changed

```json
{
  "companyId": "01KZ2WJKWVFPCVAB6MY106QRZV",
  "kycStatus": "approved"
}
```

`kycStatus` is one of `not_started`, `incomplete`, `awaiting_questionnaire`,
`awaiting_ubo`, `under_review`, `approved`, `rejected`, `paused` or `offboarded` —
see the [Verification page](/swap/kyc-page) for what each means and which need
your customer to act.

### payment\_route.created

`paymentRoute` is the same object `GET /v1/payment-routes/{id}` returns.

```json
{
  "companyId": "01KZ2WJKWVFPCVAB6MY106QRZV",
  "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"
  }
}
```

This also fires for routes opened in the AlphaX app rather than through the API,
so your records stay in step either way.

### payment\_route.transaction.created

The body is `companyId` plus the exact object the transaction endpoints return —
so you can hand it to the same code whether you polled for it or were pushed it.

```json
{
  "companyId": "01KZ2WJKWVFPCVAB6MY106QRZV",
  "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" }
  }
}
```

For an on-ramp, `data` carries the payer's details instead:

```json
{
  "routeId": "01KZ2RT4Q8N7YB3D5F6G7H8J9K",
  "routeType": "onramp",
  "senderName": "ACME LLC",
  "senderBankRoutingNumber": "021000021",
  "incomingAmount": "500.00",
  "convertedAmount": "497.50",
  "destinationTxHash": "0x..."
}
```

## A note on emails

If you onboarded a company, **AlphaX does not email its people** — no deposit
notifications, no receipts. You own that relationship, and these webhooks are how
you learn what to tell them.