> For the complete documentation index, see [llms.txt](https://developers.klasha.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.klasha.com/transfers/stablecoin-wallets-swaps-and-payouts.md).

# Stablecoin Wallets, Swaps and Payouts

### Integration steps

* provision a business stablecoin wallet and fetch its deposit addresses
* validate a destination wallet address and fetch fees before you send
* initiate a business stablecoin payout and track it to completion
* generate a swap quote and confirm it to move between fiat and stablecoins, or between `USDT` and `USDC`

### Coverage

**Supported stablecoins**

* `USDT`
* `USDC`

**Supported networks**

* `ERC20`
* `TRC20`
* `BEP20`

### Before you begin

1. Create and verify your Klasha account.
2. Generate your merchant public key from the dashboard.
3. Generate a bearer token from the authentication API.
4. Retrieve your merchant encryption key. Some endpoints below require an encrypted `message` payload; others accept plain JSON — check each endpoint's **Request body** section before integrating.

{% hint style="info" %}
Encryption requirements vary per endpoint in this flow. Wallet creation/lookup and `v2/create-payout` accept plain JSON; `create-payout` (v1), `create-swap`, and `confirm-swap` require an encrypted `message` body using your merchant encryption key. The encryption algorithm can be gotten here.
{% endhint %}

### Generate a bearer token

Use the authentication API here to generate a bearer token and set your `Authorization` header to the token obtained. Most requests below also require the headers listed here.

| Header          | Value                                                           |
| --------------- | --------------------------------------------------------------- |
| `Authorization` | `Bearer {{token}}`                                              |
| `x-auth-token`  | Your merchant public key — required on business (B2B) endpoints |
| `Content-Type`  | `application/json`                                              |

### Create a stablecoin wallet

Create a stablecoin wallet for a business and provision network-specific deposit addresses.

> **POST** `{{env_url}}/wallet/stable-coin/create-wallet`

**Request body**

| Name             | Type   | Description                                                       |
| ---------------- | ------ | ----------------------------------------------------------------- |
| `businessId`\*   | Number | Klasha business ID.                                               |
| `currencyCode`\* | String | `USDT` or `USDC`. If omitted, Klasha provisions both stablecoins. |

**Example request**

```json
{
  "businessId": 12345,
  "currencyCode": "USDT"
}
```

**Example response**

```json
{
  "message": "success",
  "error": null,
  "data": [
    {
      "id": 91,
      "walletId": 501,
      "businessId": 12345,
      "userId": null,
      "currency": "USDT",
      "network": "ERC20",
      "address": "0x8d7f..."
    },
    {
      "id": 92,
      "walletId": 501,
      "businessId": 12345,
      "userId": null,
      "currency": "USDT",
      "network": "TRC20",
      "address": "TXYZ..."
    },
    {
      "id": 93,
      "walletId": 501,
      "businessId": 12345,
      "userId": null,
      "currency": "USDT",
      "network": "BEP20",
      "address": "0x4ac2..."
    }
  ]
}
```

**Notes**

* If the wallet already exists, the API returns the existing addresses and only creates missing networks.

### Get a business stablecoin wallet

Fetch previously provisioned addresses for a business wallet.

> **GET** `{{env_url}}/wallet/stable-coin/get-wallet?currency={{currency}}`

**Query params**

| Name         | Type   | Description      |
| ------------ | ------ | ---------------- |
| `currency`\* | String | `USDT` or `USDC` |

**Example response**

```json
{
  "message": "success",
  "error": null,
  "data": [
    {
      "id": 91,
      "walletId": 501,
      "businessId": 12345,
      "userId": null,
      "currency": "USDT",
      "network": "TRC20",
      "address": "TXYZ..."
    }
  ]
}
```

### Validate a destination wallet address

Validate a destination address before you initiate a payout. This endpoint is shared across B2B and B2C integrations.

> **POST** `{{env_url}}/wallet/stable-coin/validate-address`

**Request body**

| Name         | Type   | Description                  |
| ------------ | ------ | ---------------------------- |
| `currency`\* | String | `USDT` or `USDC`             |
| `network`\*  | String | `ERC20`, `TRC20`, or `BEP20` |
| `address`\*  | String | Wallet address to validate   |

**Example request**

```json
{
  "currency": "USDT",
  "network": "TRC20",
  "address": "TXYZ1234567890"
}
```

### Get stablecoin fees

Retrieve the pricing Klasha will apply to stablecoin receive, payout, and swap flows. This endpoint is also shared across B2B and B2C integrations.

> **GET** `{{env_url}}/wallet/stable-coin/fees?currency={{currency}}&amount={{amount}}&flow={{flow}}&network={{network}}`

**Query params**

| Name         | Type   | Description                                                                                                                                                         |
| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `currency`\* | String | `USDT` or `USDC`                                                                                                                                                    |
| `amount`\*   | String | Amount to price                                                                                                                                                     |
| `flow`       | String | Defaults to `payout`. Supported values: `receive` (aliases: `topup`, `deposit`), `send` (aliases: `payout`, `transfer`), `fiat-to-stablecoin`, `stablecoin-to-fiat` |
| `network`    | String | Recommended for payout pricing — fees can be network-specific                                                                                                       |

**Example response**

```json
{
  "message": "success",
  "error": null,
  "data": {
    "currency": "USDT",
    "flow": "send",
    "amount": 100.00,
    "fee": 1.00,
    "totalDebit": 101.00
  }
}
```

**Notes**

* `totalDebit` is the full amount debited from the wallet for a payout.
* `fiat-to-stablecoin` and `stablecoin-to-fiat` are priced with the same swap fee — only the label differs by direction.

### B2B stablecoin payout

Use this flow to send `USDT` or `USDC` from a business wallet to an external wallet address.

**Integration steps**

1. Validate the destination address.
2. Fetch the expected fee.
3. Initiate the payout.
4. Track status via your webhook, or query by `requestId`.

#### Initiate a payout

Encrypted request version:

> **POST** `{{env_url}}/wallet/stable-coin/create-payout`

Plain JSON version:

> **POST** `{{env_url}}/wallet/stable-coin/v2/create-payout`

**Request body** (plain payload shown; the encrypted variant sends this same payload as an encrypted `message` string)

| Name                         | Type   | Description                                                                                                         |
| ---------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `currency`\*                 | String | `USDT` or `USDC`                                                                                                    |
| `amount`\*                   | String | Amount to send                                                                                                      |
| `narration`\*                | String | Payout description                                                                                                  |
| `requestId`\*                | String | Unique payout reference. Re-sending the same `requestId` returns the existing payout instead of creating a new one. |
| `walletDestinationAddress`\* | String | Recipient stablecoin address                                                                                        |
| `network`                    | String | `ERC20`, `TRC20`, or `BEP20`. Strongly recommended for routing and fee calculation.                                 |

**Example request (v2, plain JSON)**

```json
{
  "currency": "USDT",
  "amount": "100.00",
  "narration": "Vendor settlement",
  "requestId": "scp-20260722-0001",
  "walletDestinationAddress": "TXYZ1234567890",
  "network": "TRC20"
}
```

**Example response**

```json
{
  "amount": 100.00,
  "fee": 1.00,
  "narration": "Vendor settlement",
  "payoutStatus": "PENDING",
  "requestId": "scp-20260722-0001",
  "transactionReference": null,
  "providerReference": null,
  "failureReason": null,
  "walletDestinationAddress": "TXYZ1234567890",
  "createdAt": "2026-07-22 14:10:00",
  "updatedAt": "2026-07-22 14:10:00"
}
```

**Notes**

* Klasha validates wallet balance before debit and returns an error if `amount + fee` exceeds the available balance.
* The initial response is an accepted payout request, not a final result — provider completion happens asynchronously and is delivered to your configured webhook URL.
* `payoutStatus` moves through `PENDING` → `IN_PROGRESS` → a terminal state (`SUCCESSFUL`, `FAILED`, `CANCELLED`, or `REVERSED`). Your webhook fires on the terminal states.

{% hint style="warning" %}
Polling payout status by `requestId` is not fully reliable in the current environment. We recommend relying on the `create-payout` response plus your webhook events for payout status, rather than the query-by-reference endpoint below, until this is confirmed stable.
{% endhint %}

#### Query a payout

Fetch the current state of a previously created stablecoin payout.

> **POST** `{{env_url}}/wallet/stable-coin/get-payout?requestId={{requestId}}`

**Query params**

| Name          | Type   | Description                                           |
| ------------- | ------ | ----------------------------------------------------- |
| `requestId`\* | String | The same `requestId` used when the payout was created |

### B2B stablecoin swap

Use this flow to:

* swap fiat to stablecoin
* swap stablecoin to fiat
* swap `USDT` to `USDC`
* swap `USDC` to `USDT`

#### Generate a quote

> **POST** `{{env_url}}/wallet/stable-coin/create-swap`

This endpoint expects an encrypted `message` body. The plain payload before encryption looks like this:

| Name                    | Type   | Description                                    |
| ----------------------- | ------ | ---------------------------------------------- |
| `sourceCurrency`\*      | String | Wallet currency to debit                       |
| `destinationCurrency`\* | String | Wallet currency to credit                      |
| `sourceAmount`          | String | Amount to debit when `mode` is `SOURCE`        |
| `destinationAmount`     | String | Amount to receive when `mode` is `DESTINATION` |
| `mode`\*                | String | `SOURCE` or `DESTINATION`                      |

**Example plain quote payload**

```json
{
  "sourceCurrency": "NGN",
  "destinationCurrency": "USDT",
  "sourceAmount": "100000.00",
  "mode": "SOURCE"
}
```

**Example quote response**

```json
{
  "message": "success",
  "error": null,
  "data": {
    "id": 81,
    "sourceCurrency": "NGN",
    "sourceAmount": 100000.000000,
    "destinationCurrency": "USDT",
    "destinationAmount": 99.700000,
    "destinationFees": 0.000000,
    "sourceFees": 0.000000,
    "rate": 0.000997,
    "transactionStatus": "PENDING",
    "transactionReference": "swap-7bc0d94d-7b7f-4f0b-b1bc-3c7f8d9f4d21",
    "quoteToken": "4a66fe86-d8e1-49ef-9b8b-0f07c7888f0e",
    "createdAt": "2026-07-22T14:22:00",
    "updatedAt": "2026-07-22T14:22:00"
  }
}
```

#### Confirm a swap

> **POST** `{{env_url}}/wallet/stable-coin/confirm-swap`

This endpoint also expects an encrypted `message` body.

**Request body**

| Name           | Type   | Description                           |
| -------------- | ------ | ------------------------------------- |
| `quoteToken`\* | String | Quote token returned by the quote API |

**Example plain request**

```json
{
  "quoteToken": "4a66fe86-d8e1-49ef-9b8b-0f07c7888f0e"
}
```

**Notes**

* Transaction status values: `PENDING`, `PROCESSING`, `SUCCESSFUL`, `FAILED`, `CANCELLED`, `REVERSED`, `PENDING_REVERSAL`.
* A quote becomes unusable once it has been consumed by a successful confirm, or if the market rate has moved since the quote was generated — confirm will return a rate-changed error in that case. Quotes do not expire on a fixed timer.
* Generate a fresh quote via **Generate a quote** if a confirm attempt fails due to a rate change.

#### Query a swap

Fetch a swap by reference.

> **POST** `{{env_url}}/wallet/stable-coin/get-swap`

**Request body**

| Name          | Type   | Description                                                |
| ------------- | ------ | ---------------------------------------------------------- |
| `requestId`\* | String | Use the `transactionReference` returned from the quote API |

**Example request**

```json
{
  "requestId": "swap-7bc0d94d-7b7f-4f0b-b1bc-3c7f8d9f4d21"
}
```
