> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jenzy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Conversions

> Move money from one of your balances to another. Get a quote, then execute it.

A conversion moves money from your balance in one asset to your balance in a
different asset. It has two steps. You get a quote, and then you execute the
quote. Money moves only at the second step.

<Note>
  A conversion is always between one fiat asset and one stablecoin. You can
  convert fiat to stablecoin, or stablecoin to fiat. Hermes does not support
  fiat to fiat, or stablecoin to stablecoin, for now. A request in such a
  pair gets `unsupported_pair` (422).
</Note>

## Get a quote

`POST /v2/conversions/quotes` takes `from_asset`, `to_asset`, and
`from_amount`. You fix what you pay. Hermes tells you what you receive.

```bash theme={null}
curl -X POST https://api.jenzy.com/v2/conversions/quotes \
  -H "Authorization: Bearer $JENZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "from_asset": "USDT", "to_asset": "KES", "from_amount": "100" }'
```

```json theme={null}
{
  "quote_id": "3e7c1b7a-0b1e-4b8f-9d2a-6f1c2e3d4a5b",
  "from_asset": "USDT",
  "to_asset": "KES",
  "from_amount": "100",
  "to_amount": "12840.00",
  "rate": "0.00778816",
  "expires_at": "2026-09-09T10:00:25.000Z"
}
```

* **`to_amount` is what you receive.** Hermes does not change it. A
  conversion delivers exactly `to_amount`, or it fails as a whole.
* **`rate` is for display.** It is `from_amount` divided by `to_amount`, at
  eight decimal places. If you calculate `from_amount` from `rate`, your
  result can be one minor unit different. The two amounts are the truth.
* **A quote holds no money.** Hermes checks your balance when you execute.
* **`from_amount` has a maximum of four decimal places.** More than four gets
  `400 validation_error`.

The pair must be one fiat asset and one stablecoin, in either direction.
Hermes does not support fiat to fiat, or stablecoin to stablecoin, for now.
Such a pair gets `unsupported_pair` (422). The same asset on both sides gets
the same code.

You can get as many quotes as you want, up to 30 in each minute. Above that
rate you get `429 rate_limited`. There is no `Idempotency-Key` on a quote.
Hermes does not list quotes.

## Execute the quote

`POST /v2/conversions` takes `{ quote_id }` and needs an
**`Idempotency-Key`** header. A request with no key gets `400`.

```bash theme={null}
curl -X POST https://api.jenzy.com/v2/conversions \
  -H "Authorization: Bearer $JENZY_API_KEY" \
  -H "Idempotency-Key: 5b2d8c1e-7f3a-4e9b-8c6d-1a2b3c4d5e6f" \
  -H "Content-Type: application/json" \
  -d '{ "quote_id": "3e7c1b7a-0b1e-4b8f-9d2a-6f1c2e3d4a5b" }'
```

```json theme={null}
{
  "id": "8c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
  "from_asset": "USDT",
  "to_asset": "KES",
  "from_amount": "100",
  "to_amount": "12840.00",
  "rate": "0.00778816",
  "status": "completed",
  "failure": null,
  "created_at": "2026-09-09T10:00:10.000Z"
}
```

The call waits for the result. In the normal case the response is
`completed` or `failed`. Hermes debits `from_amount` from your `from_asset`
balance and credits `to_amount` to your `to_asset` balance in one movement.

## Expiry, and a used quote

A quote is valid until `expires_at`. The window is short, about 25 seconds.
Execute soon after you get the quote.

* Execute after `expires_at` gets `quote_expired` (422). Get a new quote.
* Execute a quote a second time, with a different `Idempotency-Key`, gets
  `quote_used` (409). The first execution stands. Read it with `GET`.
* Execute a quote a second time, with the **same** key, returns the first
  response again. This is the safe way to send a request again after a
  timeout.

A quote is for one execution. Get a new quote for each conversion.

## The `processing` status

In rare cases the response shows `status: "processing"`. This means that
Hermes accepted the conversion, but does not have the result yet. It lasts
seconds, not hours. Read `GET /v2/conversions/{id}` again until the status is
`completed` or `failed`.

Do not send the request again with a new key. The first conversion exists
and gets to a result on its own. If you send the same key again, you get the
current state of the same conversion.

## When a conversion fails

A `failed` conversion moved no money. Your `from_asset` balance is as it was
before. The `failure` object gives one of four codes:

| Code                    | Meaning                                      | Send again?                         |
| ----------------------- | -------------------------------------------- | ----------------------------------- |
| `provider_declined`     | The provider declined the conversion.        | Yes. Get a new quote and try again. |
| `limit_exceeded`        | The amount is above the limit for this pair. | Yes, with a smaller amount.         |
| `liquidity_unavailable` | The pair has no liquidity at this time.      | Yes, later.                         |
| `provider_unavailable`  | Conversions are not available at this time.  | Yes, later.                         |

Everything that you can correct yourself is a `4xx` at quote time or at
execute time, and makes no conversion. See [Errors](/v2/errors).

## How to learn the result

**Webhooks (recommended)** — Hermes sends `conversion.completed` or
`conversion.failed`. The delivery carries `api_version: "v2"`, and `data` is
the conversion in the shape above. See [Webhooks](/webhooks).

**A poll** — read `GET /v2/conversions/{id}`. `GET /v2/conversions` lists your
conversions, newest first, with keyset pagination. Filter with `asset`, which
matches `from_asset` or `to_asset`.

## Limits

Each asset has a minimum and a maximum for one conversion. Hermes checks them
at quote time, before it gives you a quote.

* Below the minimum gets `amount_below_minimum` (422).
* Above the maximum gets `limit_exceeded` (422).

Your `available` in `from_asset` is the other ceiling. If it is too low at
execute time, you get `insufficient_funds` (402), and no money moves.

<Note>
  A conversion has no `fee` field. What you pay is `from_amount`. What you
  receive is `to_amount`. The economics are in the two figures.
</Note>
