From f80eb2900181ba847f1fb3eefcf77f9098e2bb66 Mon Sep 17 00:00:00 2001 From: hallstain Date: Fri, 18 Sep 2026 15:43:30 +0300 Subject: [PATCH 1/2] docs(usd): the dollar reaches Hong Kong and Singapore too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lightnet activated USD SWIFT to HKG and SGP on our profile on 18 September 2026, so this page is no longer about one country. The mainland contract is unchanged and stays the strict one; the two new rails get their own section, because what they ask for is shorter: a longer account and BIC, a company name, and none of the beneficiary address block — Hong Kong has no postal code — no purpose code, and no invoice, since there is no domestic alternative outside the mainland and every payment there is SWIFT. Two things a client must plan for and cannot read out of a field list: these rails carry no payment reference, so a supplier's order number cannot travel with the wire, and correspondent charges on a SWIFT payment are the correspondents' own, so a beneficiary may receive less than was sent. Co-Authored-By: Claude Opus 5 (1M context) --- api-reference/README.md | 2 +- api-reference/china-usd-payments.md | 60 +++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/api-reference/README.md b/api-reference/README.md index da5ffc6..e98eebc 100644 --- a/api-reference/README.md +++ b/api-reference/README.md @@ -11,7 +11,7 @@ Reference groups: - [On-Ramp](https://unigox.gitbook.io/unigox-api/api-documentation/api-reference/on-ramp) - [Off-Ramp](https://unigox.gitbook.io/unigox-api/api-documentation/api-reference/off-ramp) - [Third-party payouts](./third-party-payouts.md) -- [USD bank payments to China](./china-usd-payments.md) +- [USD bank payments to China, Hong Kong and Singapore](./china-usd-payments.md) - [Orders](https://unigox.gitbook.io/unigox-api/api-documentation/api-reference/orders) - [Webhooks](https://unigox.gitbook.io/unigox-api/api-documentation/api-reference/webhooks) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index c1d97d0..82febae 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -1,8 +1,14 @@ -# USD payments to China +# USD payments to China, Hong Kong and Singapore + +The dollar reaches three destinations, each with its own rail and its own rollout: mainland China +(`usd-wire-china`), Hong Kong (`usd-wire-hong-kong`) and Singapore (`usd-wire-singapore`). Most of +this page is the mainland contract, which is the strictest of the three; the Hong Kong and Singapore +contract is at the end and differs in what it asks for. One country being open says nothing about +another, so read availability per destination and never from the currency alone. USD beneficiary details and private invoices can be saved. Whether a payment can be executed is a per-deployment rollout setting, off by default. Onshore business accounts need matching liquidity; NRA/OSA accounts additionally require the original invoice, and stay refused until the original-invoice channel is confirmed. A general USD offer alone cannot enable the corridor. These settings are deployment controls, not request parameters. -## Confirmed bank contract +## Confirmed bank contract (mainland China) Use country `CN`, currency `USD`, network `usd-wire-china` and institution `china-usd-wire`. This is one generic bank method: the beneficiary's own `bank_name` and `swift_code` route the payment. Never replace `bank_name` with the catalogue label “Bank in China (USD)”. `bank_code` is not used. @@ -27,7 +33,7 @@ The authenticated account session at `GET /api/v1/bill-payment/session` returns Use `payout_currency=USD` on `GET /api/v1/bill-payment/payment-rails` and `/institutions`. The institution's actual rail is resolved on our side. To add a separate USD account to an existing supplier, POST `/api/v1/bill-payment/recipients/:id/destinations` with `institution_id`, `payout_currency`, `details` and an `idempotency_key`. Include `beneficiary_type: "business"` in widget details. The recipient's other destinations are retained. Replaying the same key/body returns the saved destination; a changed body returns 409. A caller cannot add to someone else's recipient. -`POST /api/v1/bill-payment/preflight` checks the saved account, amount and live quote. A refusal uses HTTP 200 with `ok: false`, `may_collect: false`, `price: null`; direct bill-create is refused before collection. An enabled onshore account with live liquidity can return `may_collect: true`, then `POST /bills` opens a real USD bill with immutable intent and idempotency. A USD preflight must name `recipient_destination_id`, or it is refused with `destination_required`. An amount below USD 25 is refused with `CHINA_USD_AMOUNT_OUT_OF_RANGE`. NRA/OSA remains refused with `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` until its original-invoice channel is confirmed. After that, preflight and `POST /bills` for an NRA/OSA account must name the uploaded invoice in `invoice_document_id`, or they are refused with `INVOICE_DOCUMENT_REQUIRED`. Validation errors use the endpoint's existing 400/422 envelopes; do not treat a successful discovery response as permission to fund. +`POST /api/v1/bill-payment/preflight` checks the saved account, amount and live quote. A refusal uses HTTP 200 with `ok: false`, `may_collect: false`, `price: null`; direct bill-create is refused before collection. An enabled onshore account with live liquidity can return `may_collect: true`, then `POST /bills` opens a real USD bill with immutable intent and idempotency. A USD preflight must name `recipient_destination_id`, or it is refused with `destination_required`. An amount below USD 25 is refused with `USD_AMOUNT_OUT_OF_RANGE`. NRA/OSA remains refused with `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` until its original-invoice channel is confirmed. After that, preflight and `POST /bills` for an NRA/OSA account must name the uploaded invoice in `invoice_document_id`, or they are refused with `INVOICE_DOCUMENT_REQUIRED`. Validation errors use the endpoint's existing 400/422 envelopes; do not treat a successful discovery response as permission to fund. Private documents use `/api/v1/bill-payment/documents/:uuid`, where `:uuid` is a client-generated UUID in canonical lowercase form: PUT multipart `document`, `recipient_destination_id`, `payout_currency`; GET metadata; GET `/content`; DELETE an unbound draft. GET, GET `/content` and DELETE take `recipient_destination_id` and `payout_currency` as query parameters; to read the invoice retained on a payment, GET and GET `/content` also take that payment's `bill_id`. PDF/JPEG/PNG, 8 MiB maximum. The server binds the original bytes and SHA-256 to the owner, destination and currency. An identical retry is safe; different bytes with the same UUID return 409 `invoice_document_mismatch`. Drafts expire after 7 days; a bound invoice is retained as payment evidence. Storing an invoice here is not the same as the provider accepting it. @@ -66,6 +72,54 @@ Use the currency agreed with the supplier. CNY and USD require separate saved de `usd-wire-china` is a payout (off-ramp) network only. The supported-currencies and supported-payment-rails responses do not filter by direction, so do not offer a rail as an on-ramp option because it is listed. Discovery does not replace execution eligibility checks. +## Hong Kong and Singapore + +Confirmed by the provider and probed live on 18 September 2026. Use country `HK` with network +`usd-wire-hong-kong` and institution `hong-kong-usd-wire`, or country `SG` with +`usd-wire-singapore` and `singapore-usd-wire`. The pair must agree: a Hong Kong rail sent with +country `CN` is refused as a route mismatch rather than resolved to either. + +These rails are SWIFT only — the provider has no domestic alternative outside the mainland — and +they are one generic bank method each: the beneficiary's own `bank_name` and `swift_code` route the +payment. Like the mainland rail, they list their fields at the top level and have no `formats`, and +they pay business beneficiaries. + +| Detail | Constraint | +| --- | --- | +| `company_name` | Required, at most 100 characters | +| `swift_code` | Required, 8–16 alphanumeric characters | +| `account_number` | Required, 6–30 letters and digits with no spaces; retain leading zeros | +| `bank_name` | Optional, at most 100 characters | + +What these two do NOT ask for, and what a client must therefore not send: the beneficiary's postal +address, city, state or postal code, and a purpose-of-remittance code. Hong Kong has no postal code +at all. There is also no NRA/OSA rule here: the provider publishes one account field, the BIC picks +the bank, and `CHINA_USD_ACCOUNT_UNCONFIRMED` has no counterpart beyond the field itself — an +account the field cannot hold is refused with `USD_ACCOUNT_UNCONFIRMED`. + +No invoice is required on either rail, before or after the bill opens, so +`INVOICE_DOCUMENT_REQUIRED` and `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` cannot arise. An invoice may +still be uploaded and retained as payment evidence. + +The platform minimum is USD 25 here too, refused with `USD_AMOUNT_OUT_OF_RANGE`; the maximum is what +the executable quote serves. The provider's flat service charge is the same USD 3 as on the mainland +rail. Correspondent bank charges on a SWIFT payment are the correspondents' own and are not quoted +here, so a beneficiary may receive less than the amount sent. There is no field on these rails for a +payment reference or remark: an order number a supplier asks to see on the wire cannot be carried. + +Delivery is what the provider reports for SWIFT: usually two working days, and a beneficiary bank +without a direct SWIFT connection can take two to three. Provider acceptance is not bank finality, +and a returned payment is refunded through the same manual process as the mainland rail. + +`GET /api/v1/bill-payment/session` lists one payout option per destination, each with its own +`available`, `preparation_available`, `reason` and `bank_rails`. A disabled destination answers +`provider_confirmation_pending`. `GET /api/v1/bill-payment/payment-rails?payout_currency=USD` +returns all three rails; `/institutions` is per rail. Everything else — preflight, `POST /bills`, +documents, destination removal — behaves as described above. + +Advisory recognition imports a Hong Kong or Singapore beneficiary onto that country's rail. It +refuses a country none of the three rails reach, by name. + ## Removing bank details DELETE `/api/v1/bill-payment/recipients/:id/destinations/:destination_id` removes From bc2207ff07f83ed9acdc3cc4ac5913d26a5253d0 Mon Sep 17 00:00:00 2001 From: hallstain Date: Fri, 18 Sep 2026 18:32:12 +0300 Subject: [PATCH 2/2] docs(usd): say what each rail actually answers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review of the implementation against this page found four statements it does not support. The mainland's below-minimum refusal is still CHINA_USD_AMOUNT_OUT_OF_RANGE — a published code clients branch on. Only Hong Kong and Singapore answer with the neutral one, and the page had quietly reassigned the mainland's. On those two rails the BIC alone routes the payment; bank_name is optional and is not sent to the provider at all, so promising it a routing role was wrong. "Two to three days" for a bank without a direct SWIFT connection is the provider's line about the mainland rail, not about these; what they said here is two working days, longer without a direct connection. And the session lists one USD row per destination now, not a single one naming usd-wire-china. Co-Authored-By: Claude Opus 5 (1M context) --- api-reference/china-usd-payments.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index 82febae..66dc576 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -29,11 +29,11 @@ Account details are strings. Whitespace is removed from account numbers before v ## Customer APIs -The authenticated account session at `GET /api/v1/bill-payment/session` returns USD with `available` derived from the rollout setting, `preparation_available: true` and `bank_rails: ["usd-wire-china"]`. When disabled, the reason is `provider_confirmation_pending`. Availability means permission to execute; preparation availability only permits saving details and documents. Missing preparation metadata fails closed. +The authenticated account session at `GET /api/v1/bill-payment/session` returns one USD row per destination — this one is `country: "CN"` with `bank_rails: ["usd-wire-china"]` — each with its own `available` derived from that destination's rollout setting and `preparation_available: true`. When disabled, the reason is `provider_confirmation_pending`. Availability means permission to execute; preparation availability only permits saving details and documents. Missing preparation metadata fails closed. Use `payout_currency=USD` on `GET /api/v1/bill-payment/payment-rails` and `/institutions`. The institution's actual rail is resolved on our side. To add a separate USD account to an existing supplier, POST `/api/v1/bill-payment/recipients/:id/destinations` with `institution_id`, `payout_currency`, `details` and an `idempotency_key`. Include `beneficiary_type: "business"` in widget details. The recipient's other destinations are retained. Replaying the same key/body returns the saved destination; a changed body returns 409. A caller cannot add to someone else's recipient. -`POST /api/v1/bill-payment/preflight` checks the saved account, amount and live quote. A refusal uses HTTP 200 with `ok: false`, `may_collect: false`, `price: null`; direct bill-create is refused before collection. An enabled onshore account with live liquidity can return `may_collect: true`, then `POST /bills` opens a real USD bill with immutable intent and idempotency. A USD preflight must name `recipient_destination_id`, or it is refused with `destination_required`. An amount below USD 25 is refused with `USD_AMOUNT_OUT_OF_RANGE`. NRA/OSA remains refused with `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` until its original-invoice channel is confirmed. After that, preflight and `POST /bills` for an NRA/OSA account must name the uploaded invoice in `invoice_document_id`, or they are refused with `INVOICE_DOCUMENT_REQUIRED`. Validation errors use the endpoint's existing 400/422 envelopes; do not treat a successful discovery response as permission to fund. +`POST /api/v1/bill-payment/preflight` checks the saved account, amount and live quote. A refusal uses HTTP 200 with `ok: false`, `may_collect: false`, `price: null`; direct bill-create is refused before collection. An enabled onshore account with live liquidity can return `may_collect: true`, then `POST /bills` opens a real USD bill with immutable intent and idempotency. A USD preflight must name `recipient_destination_id`, or it is refused with `destination_required`. An amount below USD 25 is refused with `CHINA_USD_AMOUNT_OUT_OF_RANGE` on this rail; the Hong Kong and Singapore rails answer with `USD_AMOUNT_OUT_OF_RANGE`. NRA/OSA remains refused with `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` until its original-invoice channel is confirmed. After that, preflight and `POST /bills` for an NRA/OSA account must name the uploaded invoice in `invoice_document_id`, or they are refused with `INVOICE_DOCUMENT_REQUIRED`. Validation errors use the endpoint's existing 400/422 envelopes; do not treat a successful discovery response as permission to fund. Private documents use `/api/v1/bill-payment/documents/:uuid`, where `:uuid` is a client-generated UUID in canonical lowercase form: PUT multipart `document`, `recipient_destination_id`, `payout_currency`; GET metadata; GET `/content`; DELETE an unbound draft. GET, GET `/content` and DELETE take `recipient_destination_id` and `payout_currency` as query parameters; to read the invoice retained on a payment, GET and GET `/content` also take that payment's `bill_id`. PDF/JPEG/PNG, 8 MiB maximum. The server binds the original bytes and SHA-256 to the owner, destination and currency. An identical retry is safe; different bytes with the same UUID return 409 `invoice_document_mismatch`. Drafts expire after 7 days; a bound invoice is retained as payment evidence. Storing an invoice here is not the same as the provider accepting it. @@ -80,8 +80,8 @@ Confirmed by the provider and probed live on 18 September 2026. Use country `HK` country `CN` is refused as a route mismatch rather than resolved to either. These rails are SWIFT only — the provider has no domestic alternative outside the mainland — and -they are one generic bank method each: the beneficiary's own `bank_name` and `swift_code` route the -payment. Like the mainland rail, they list their fields at the top level and have no `formats`, and +they are one generic bank method each: the beneficiary's own `swift_code` routes the payment. A +`bank_name` may be saved for the payer's own reference and is not sent to the provider. Like the mainland rail, they list their fields at the top level and have no `formats`, and they pay business beneficiaries. | Detail | Constraint | @@ -107,9 +107,10 @@ rail. Correspondent bank charges on a SWIFT payment are the correspondents' own here, so a beneficiary may receive less than the amount sent. There is no field on these rails for a payment reference or remark: an order number a supplier asks to see on the wire cannot be carried. -Delivery is what the provider reports for SWIFT: usually two working days, and a beneficiary bank -without a direct SWIFT connection can take two to three. Provider acceptance is not bank finality, -and a returned payment is refunded through the same manual process as the mainland rail. +Delivery is what the provider reports for SWIFT: usually two working days. A beneficiary bank +without a direct SWIFT connection can take longer, and public holidays move the date. Provider +acceptance is not bank finality, and a returned payment is refunded through the same manual process +as the mainland rail. `GET /api/v1/bill-payment/session` lists one payout option per destination, each with its own `available`, `preparation_available`, `reason` and `bank_rails`. A disabled destination answers