> For the complete documentation index, see [llms.txt](https://developers.unigox.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.unigox.com/changelog.md).

# Changelog

Notable changes to the Unigox partner API, newest first.

## 2026-07-22

**New KYC status `UNDER_REVIEW` for customers under manual compliance review.** When a customer's screening needs human compliance review (rather than a quick automated check), their KYC status is now surfaced as `UNDER_REVIEW` instead of `IN_PROGRESS`. It appears wherever a KYC status is reported: `GET /partner/users/{user_uuid}/verification-status`, `GET /partner/users/{user_uuid}` (`kyc.status`), the KYC submit response (`kyc_status`), and the `user.kyc.updated` webhook. A manual review can take **up to 24 hours**, unlike an automated check of a couple of minutes — so you can now tell the two apart and set the right expectation with your customer.

`UNDER_REVIEW` is **not cleared**: treat it exactly like `IN_PROGRESS` for gating — keep polling and do not unlock onramp/offramp until the customer reaches `VERIFIED`. This is an additive status value; if you already wait for `VERIFIED` before transacting, no action is needed.

## 2026-07-21

**On-ramp and off-ramp price estimates are public.** `POST /api/v1/partner/onramp/estimate` and `POST /api/v1/partner/offramp/estimate` no longer require an API key — same as `GET /api/v1/partner/liquidity`. They return indicative pricing only (no liquidity reservation, no quote or order). Executable pricing and order creation still need a key: `POST /partner/onramp/quote`, `/onramp/initiate`, `/offramp/quote`, and `/offramp/initiate`.

No code changes required if you already call estimate with a key; the key is simply optional for these two endpoints.

## 2026-07-13

**You can now charge your end users a per-order markup.** Pass an optional `partner_fee_pct` on the quote and estimate requests for both ramps (`POST /onramp/estimate`, `/onramp/quote`, `/offramp/estimate`, `/offramp/quote`) — `1` means 1%. The markup is declared per order (no stored config), computed in crypto on the same base as the platform fee. For off-ramp, the markup is settled on-chain to your partner wallet when the order releases successfully; cancelled or failed orders refund the full amount, markup included. For on-ramp there's no separate transfer leg: the full buyer amount lands in your partner wallet at release, and the markup is captured by withholding it when the crypto bridges out at send-out (a crypto-anchored order is sized up beforehand so the requested amount still arrives net of the withheld fee).

* Quote, estimate, initiate, and GET/LIST order responses report the real figures in `fee_breakdown.partner_fee` and `fee_breakdown.partner_fee_pct`, and webhook event `data` carries `partner_fee` / `partner_fee_pct` too. The compact action responses (`confirm-payment-sent`, `confirm-fiat-received`, `cancel`, `authorize-crypto-transfer`) don't carry `fee_breakdown` — re-fetch `GET /orders/{order_id}` for the fee figures after those calls.
* `crypto_amount` stays partner-fee-exclusive in composition, with two exceptions to what it's exclusive of: a crypto-anchored onramp order (request declares `crypto_amount`) is sized up so the delivered amount stays exact after the markup is withheld, so `crypto_amount` there includes the markup; a crypto-anchored offramp order (request declares `crypto_amount`) carves both fees out of the pinned deposit, so `crypto_amount` and `platform_fee` both move relative to a no-markup request even though the markup itself stays out of `crypto_amount`. Every other case is unaffected — the markup shows only in `fee_breakdown` (and in the offramp funding amount from transfer authorization, which is `crypto_amount + partner_fee`). Apply your fee on the **same side** the platform does, or your receipts won't reconcile with our orders.
* Fiat-anchored offramp orders have no cap — the deposit grows to cover the markup. Every other case hits a formula domain bound, not a business cap: onramp rejects `partner_fee_pct >= 100` (the withheld or grossed-up fee would consume the entire buyer amount, leaving nothing delivered); crypto-anchored offramp rejects it when the fee would consume the entire pinned deposit.

Pricing is unchanged if you don't charge a markup: omit `partner_fee_pct` (or send `0`) and every amount is what it was — only `partner_fee` and `partner_fee_pct` read zero. The platform fee still applies, so `platform_fee` and `total_fee` keep reporting it (with `total_fee` equal to `platform_fee`).

One formatting change does reach every partner, including those not charging a markup: in REST responses, `fee_breakdown.partner_fee` previously always returned the literal `"0.00"` and now carries the token's own decimals, like the other fee fields — a zero-fee USDT order reports `"0.000000"`. The value is unchanged; parse it as a decimal rather than comparing strings. This does not apply to the webhook's `data.partner_fee` field, which always used (and still uses) the shortest round-tripping representation — `"1"`, not `"1.00"`; `"0"` for no fee.

## 2026-06-27

**Customers must be KYC-cleared before onramping or offramping.** Quote and initiate calls (`POST /onramp/quote`, `/onramp/initiate`, `/offramp/quote`, `/offramp/initiate`) return `422 KYC_NOT_CLEARED` if the customer is not cleared; `error.details.kyc_status` carries their current status. Poll `GET /partner/users/{user_uuid}/verification-status` until the customer reaches `VERIFIED`.

No action needed if you already wait for `VERIFIED` before transacting.

## 2026-06-24

**Recipient validation moved to recipient creation.** The recipient field check that previously ran when you created an offramp order now runs earlier — when you **create the recipient** (`POST /api/v1/partner/users/{user_uuid}/payment-details`):

* If a required recipient field is missing or malformed for the payout corridor, the create is rejected with **`422 Unprocessable Entity`** and the offending fields are named in the `error` message — so you fix the recipient once, up front, before it can be used in any order.
* `POST /api/v1/partner/offramp/initiate` **no longer returns `RECIPIENT_UNVERIFIABLE`**: a recipient you successfully created has already passed field validation. Quote and funds checks at initiate are unchanged.
* This is now the single point of recipient validation — there is no second check at order time. It confirms the recipient details are complete and well-formed for the corridor; it does not by itself guarantee the destination account exists. If the validation service is temporarily unavailable the recipient is still created (creation is never blocked by an outage), so create recipients before you need them.

No action needed if you already create recipients before ordering. Corridors without recipient requirements are unchanged.

## 2026-06-22

**Offramp orders now validate the recipient details before the order is created.**

For payout corridors with recipient requirements (e.g. mobile money and bank transfers in Nigeria, Ghana, and Kenya), `POST /api/v1/partner/offramp/initiate` now checks the recipient up front:

* If a required recipient field is missing or malformed, the request is rejected with **`422 RECIPIENT_UNVERIFIABLE`** and the offending fields are listed in `error.details.fields` — so you can fix the details and create a new order, instead of creating one that can't be paid out.
* Name requirements are corridor-specific: bank transfers expect the account-holder name, while mobile money pays to the number and accepts a placeholder name.
* This confirms the recipient details are complete and well-formed; it does not by itself guarantee the destination account exists. If the up-front check can't be completed, the order is still created and the recipient is re-checked before payout — so an outage never blocks order creation.

Corridors without recipient requirements are unchanged; no action required.
