From 67f63f32bf02e33b0505651d7234e5e990059d38 Mon Sep 17 00:00:00 2001 From: hallstain Date: Wed, 16 Sep 2026 13:07:26 +0300 Subject: [PATCH 1/7] docs(china-usd): document USD payouts to Chinese suppliers Adds the reference page for paying a Chinese company in USD over the `usd-wire-china` network: the bank contract and its field constraints, the USD 25 minimum, onshore domestic USD versus NRA/OSA over SWIFT with the original invoice up front, the invoice document endpoints, and the rollout state a client reads (`provider_confirmation_pending`, `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE`). The partner quote/initiate for USD to CN stays closed and the page says so. The page is linked from the API reference index, and the changelog gets a China USD entry. The spec gains `directions` on a payment rail. It was already returned and already filtered discovery, but was undocumented. Co-Authored-By: Claude Opus 5 --- api-reference/README.md | 1 + api-reference/china-usd-payments.md | 76 +++++++++++++++++++++++++++++ changelog.md | 15 ++++++ openapi/swagger.yaml | 6 +++ 4 files changed, 98 insertions(+) create mode 100644 api-reference/china-usd-payments.md diff --git a/api-reference/README.md b/api-reference/README.md index 2a66cf0..da5ffc6 100644 --- a/api-reference/README.md +++ b/api-reference/README.md @@ -11,6 +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) - [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 new file mode 100644 index 0000000..dcb5d35 --- /dev/null +++ b/api-reference/china-usd-payments.md @@ -0,0 +1,76 @@ +# USD payments to China + +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 + +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. + +The schema has top-level fields and `beneficiary_type: "business"`; it does not have `formats`. Clients must support this single-shape schema as well as legacy formatted rails. + +| Detail | Constraint | +| --- | --- | +| `company_name` | Required, at most 50 characters | +| `swift_code` | Required, 8–13 alphanumeric characters; do not restrict to 8 or 11 | +| `bank_name` | Required, at most 100 characters | +| `account_number` | Required, 6–20 alphanumeric characters; retain leading zeros and NRA/OSA prefixes | +| `recipient_address` | Required, at most 256 characters; beneficiary address, not bank address | +| `recipient_city` | Required, at most 30 characters | +| `recipient_state` | Required, at most 20 characters; short province name | +| `recipient_postal_code` | Required, exactly 6 digits | + +Account details are strings. Whitespace is removed from account numbers before validation. Preserve strings end-to-end to avoid losing leading zeros. Company details must match the existing recipient identity. The platform minimum is USD 25. There is no universal USD 10,000 ceiling: the executable quote and the configured corridor determine the maximum. + +## 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. + +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. NRA/OSA remains refused with `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` until its original-invoice channel is confirmed. 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`: PUT multipart `document`, `recipient_destination_id`, `payout_currency`; GET metadata; GET `/content`; DELETE an unbound draft. PDF/JPEG/PNG, 8 MB 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 conflict. 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. + +Advisory recognition uses authenticated `POST /api/v1/bill-payment/recipient-receipt`, +multipart field `receipt`. It accepts PDF/JPEG/PNG/WebP up to **7 MB**, with at most +five PDF pages. HTTP 200 returns the extraction object directly, with +`requires_review: true`; it creates no recipient or payment. These limits differ +from the retained original-invoice upload above. The UI offers only a readable, +positive `amount_due` in the selected currency; `amount_paid` is never proposed +as a new invoice total. Malformed numeric grouping is refused, and a SWIFT code +marked uncertain cannot establish the bank country. HK/SG accounts are not +imported into the mainland-China payment flow. Review the original before saving. + +The partner-funded quote/initiate plane remains closed for USD/CN. Its refusal +uses `INVALID_REQUEST` with `provider_confirmation_pending`. Read-only estimates +follow the rollout setting so customer bill preflight can use real matching. +Do not infer readiness from the HTTP status of discovery. + +The provider has supplied these account rules: + +| Recipient account | Route condition | Invoice | +| --- | --- | --- | +| Begins with NRA or OSA | SWIFT only; domestic USD cannot be used | Required before sending | +| Onshore account with exactly 10 digits and no NRA/OSA prefix | Domestic USD | Not required upfront; the beneficiary bank contacts the recipient for documents | +| Another account format | Route has not been confirmed | To be confirmed with the provider | + +The 6–20-character field constraint is separate from route eligibility. Numeric accounts of another length may be saved intact, but execution is refused with `china_usd_account_unconfirmed` until their route is confirmed. Never shorten an account or remove a prefix. The account number alone does not prove bank or beneficiary eligibility or give the client a domestic/SWIFT selector. Route selection and the original-invoice upload are still being confirmed with the provider. A receipt parsed to help fill bank details is not automatically an accepted invoice. + +Provider delivery clarification (14 September 2026): the provider states that domestic USD is delivered the same day for the supported onshore route. SWIFT usually takes two working days; a small rural or other bank without a direct SWIFT connection can take 2–3 days. These are payout timings reported by the provider. They do not authorize routing an unconfirmed account through domestic USD. Provider acceptance of a payout is not bank finality. + +## Payment intent + +Use the currency agreed with the supplier. CNY and USD require separate saved destinations under the same recipient; do not change the currency of an existing destination. Changing currency starts a separate amount draft and requires a fresh quote. A value of 10,000 CNY must never become 10,000 USD through a currency label change. Alipay and WeChat Pay remain CNY only. + +## Discovery + +Payment network configuration can declare `directions: ["offramp"]`. The supported-currencies and supported-payment-rails responses respect that restriction. Omitted or null configuration preserves legacy bidirectional discovery; an explicit empty list permits neither direction. Discovery does not replace execution eligibility checks. + +## Removing bank details + +DELETE `/api/v1/bill-payment/recipients/:id/destinations/:destination_id` removes +one owned saved account. The supplier and all other accounts remain. Unknown, +foreign or mismatched IDs return 404; a destination with an open bill returns 409 +`destination_in_use`. Creating a bill on a destination and removing that destination +cannot overlap. Completed payment history is retained. The UI supports removal, an +inline confirmation, server errors, and adding new details after the last account is removed. diff --git a/changelog.md b/changelog.md index c2b615a..c7f81b9 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,21 @@ Notable changes to the Unigox partner API, newest first. +## Unreleased — China USD preparation + +**China USD payouts are behind a per-deployment rollout setting.** Where it is +off, the corridor answers as unavailable with `provider_confirmation_pending`. +An existing USD offer does not enable China. See [USD payments to China](api-reference/china-usd-payments.md) +for the account and invoice rules and the distinction between storage and +provider acceptance. + +- Payment rails can expose `directions`. Discovery respects a payout-only network; + listing an offramp rail does not make it an onramp option. +- Quote and initiate enforce China USD readiness on the server before a new payment + is funded. The partner-funded quote/initiate for USD to CN stays closed and answers + `INVALID_REQUEST` with `provider_confirmation_pending`. No China USD bank list, + price or arrival-time guarantee is published. + ## 2026-09-14 **An on-ramp order can now be paid from the fiat account you issued the customer.** Instead of wiring the order's fiat to a vendor and calling `confirm-payment-sent`, the customer transfers the amount into their own account and the order moves on by itself. diff --git a/openapi/swagger.yaml b/openapi/swagger.yaml index 2522cda..e5011ac 100644 --- a/openapi/swagger.yaml +++ b/openapi/swagger.yaml @@ -5470,6 +5470,12 @@ components: Rails: type: object properties: + directions: + type: array + description: Directions permitted by network configuration. Returned rails are filtered for the requested direction; this is discovery, not payment authorization. + items: + type: string + enum: [onramp, offramp] slug: type: string description: Payment rail slug identifier From 74c83870f3d6963f4d4bd3b667a75fb977a3a42e Mon Sep 17 00:00:00 2001 From: hallstain Date: Wed, 16 Sep 2026 13:07:51 +0300 Subject: [PATCH 2/7] docs(china-usd): account lengths, cut-off and returns as the provider confirmed them The provider corrected the onshore account length: it is 20 digits, not 10, which was a typo in their earlier answer. An NRA or OSA account is that three-character prefix followed by 20 digits and goes over SWIFT only. Domestic USD is not same-day in every case. It usually arrives the same working day when sent before about 3pm Singapore time, and a public holiday in Singapore or China moves it to the next working day. A payment the beneficiary's bank returns is refunded by hand, which can take up to 7 days. The corridor has no per-payment volume limit, so the maximum comes from the executable quote alone. Co-Authored-By: Claude Opus 5 --- api-reference/china-usd-payments.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index dcb5d35..5559ca8 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -19,7 +19,7 @@ The schema has top-level fields and `beneficiary_type: "business"`; it does not | `recipient_state` | Required, at most 20 characters; short province name | | `recipient_postal_code` | Required, exactly 6 digits | -Account details are strings. Whitespace is removed from account numbers before validation. Preserve strings end-to-end to avoid losing leading zeros. Company details must match the existing recipient identity. The platform minimum is USD 25. There is no universal USD 10,000 ceiling: the executable quote and the configured corridor determine the maximum. +Account details are strings. Whitespace is removed from account numbers before validation. Preserve strings end-to-end to avoid losing leading zeros. Company details must match the existing recipient identity. The platform minimum is USD 25. There is no USD 10,000 ceiling and no per-payment volume limit on the corridor: the executable quote determines the maximum. ## Customer APIs @@ -50,13 +50,13 @@ The provider has supplied these account rules: | Recipient account | Route condition | Invoice | | --- | --- | --- | -| Begins with NRA or OSA | SWIFT only; domestic USD cannot be used | Required before sending | -| Onshore account with exactly 10 digits and no NRA/OSA prefix | Domestic USD | Not required upfront; the beneficiary bank contacts the recipient for documents | +| NRA or OSA prefix followed by 20 digits | SWIFT only; domestic USD cannot be used | Required before sending | +| Onshore account of exactly 20 digits, with no prefix | Domestic USD | Not required upfront; the beneficiary bank contacts the recipient for documents | | Another account format | Route has not been confirmed | To be confirmed with the provider | The 6–20-character field constraint is separate from route eligibility. Numeric accounts of another length may be saved intact, but execution is refused with `china_usd_account_unconfirmed` until their route is confirmed. Never shorten an account or remove a prefix. The account number alone does not prove bank or beneficiary eligibility or give the client a domestic/SWIFT selector. Route selection and the original-invoice upload are still being confirmed with the provider. A receipt parsed to help fill bank details is not automatically an accepted invoice. -Provider delivery clarification (14 September 2026): the provider states that domestic USD is delivered the same day for the supported onshore route. SWIFT usually takes two working days; a small rural or other bank without a direct SWIFT connection can take 2–3 days. These are payout timings reported by the provider. They do not authorize routing an unconfirmed account through domestic USD. Provider acceptance of a payout is not bank finality. +Provider delivery clarification (15 September 2026): domestic USD to a supported onshore account usually arrives the same working day when sent before about 3pm Singapore time; public holidays in Singapore or China move it to the next working day. SWIFT usually takes two working days; a small rural or other bank without a direct SWIFT connection can take 2–3 days. These are payout timings reported by the provider. They do not authorize routing an unconfirmed account through domestic USD. Provider acceptance of a payout is not bank finality. A payment returned by the beneficiary's bank is refunded through a manual process that can take up to 7 days. ## Payment intent From 37de8635d71eed9b3620b944f286ff8cba17062c Mon Sep 17 00:00:00 2001 From: hallstain Date: Wed, 16 Sep 2026 15:24:34 +0300 Subject: [PATCH 3/7] docs(china-usd): the account field holds 23 characters, and NRA/OSA takes exactly 20 digits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An NRA/OSA account is its three-letter prefix followed by 20 digits, which the 6–20 bound could not describe. A prefix followed by anything else is saved but refused at execution, like any other unconfirmed format. Co-Authored-By: Claude Opus 5 --- api-reference/china-usd-payments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index 5559ca8..0a60b6f 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -13,7 +13,7 @@ The schema has top-level fields and `beneficiary_type: "business"`; it does not | `company_name` | Required, at most 50 characters | | `swift_code` | Required, 8–13 alphanumeric characters; do not restrict to 8 or 11 | | `bank_name` | Required, at most 100 characters | -| `account_number` | Required, 6–20 alphanumeric characters; retain leading zeros and NRA/OSA prefixes | +| `account_number` | Required, 6–23 letters and digits with no spaces; an NRA/OSA account is its prefix followed by exactly 20 digits (23 characters); retain leading zeros and NRA/OSA prefixes | | `recipient_address` | Required, at most 256 characters; beneficiary address, not bank address | | `recipient_city` | Required, at most 30 characters | | `recipient_state` | Required, at most 20 characters; short province name | @@ -54,7 +54,7 @@ The provider has supplied these account rules: | Onshore account of exactly 20 digits, with no prefix | Domestic USD | Not required upfront; the beneficiary bank contacts the recipient for documents | | Another account format | Route has not been confirmed | To be confirmed with the provider | -The 6–20-character field constraint is separate from route eligibility. Numeric accounts of another length may be saved intact, but execution is refused with `china_usd_account_unconfirmed` until their route is confirmed. Never shorten an account or remove a prefix. The account number alone does not prove bank or beneficiary eligibility or give the client a domestic/SWIFT selector. Route selection and the original-invoice upload are still being confirmed with the provider. A receipt parsed to help fill bank details is not automatically an accepted invoice. +The 6–23-character field constraint is separate from route eligibility. Accounts of another shape may be saved intact, including numeric accounts of another length and an NRA/OSA prefix not followed by exactly 20 digits, but execution is refused with `china_usd_account_unconfirmed` until their route is confirmed. Never shorten an account or remove a prefix. The account number alone does not prove bank or beneficiary eligibility or give the client a domestic/SWIFT selector. Route selection and the original-invoice upload are still being confirmed with the provider. A receipt parsed to help fill bank details is not automatically an accepted invoice. Provider delivery clarification (15 September 2026): domestic USD to a supported onshore account usually arrives the same working day when sent before about 3pm Singapore time; public holidays in Singapore or China move it to the next working day. SWIFT usually takes two working days; a small rural or other bank without a direct SWIFT connection can take 2–3 days. These are payout timings reported by the provider. They do not authorize routing an unconfirmed account through domestic USD. Provider acceptance of a payout is not bank finality. A payment returned by the beneficiary's bank is refunded through a manual process that can take up to 7 days. From 8b54782d3c570ee7098d1aab9e5ab2d561ff1bf6 Mon Sep 17 00:00:00 2001 From: hallstain Date: Wed, 16 Sep 2026 16:06:51 +0300 Subject: [PATCH 4/7] Drop the direction filter the discovery endpoints do not apply The supported-currencies and supported-payment-rails responses do not read a network's `directions`, and rails carry no such property. Say that usd-wire-china is payout-only instead, and remove the claim from the changelog and the Rails schema. Co-Authored-By: Claude Opus 5 --- api-reference/china-usd-payments.md | 2 +- changelog.md | 2 -- openapi/swagger.yaml | 6 ------ 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index 0a60b6f..94ea51c 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -64,7 +64,7 @@ Use the currency agreed with the supplier. CNY and USD require separate saved de ## Discovery -Payment network configuration can declare `directions: ["offramp"]`. The supported-currencies and supported-payment-rails responses respect that restriction. Omitted or null configuration preserves legacy bidirectional discovery; an explicit empty list permits neither direction. Discovery does not replace execution eligibility checks. +`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. ## Removing bank details diff --git a/changelog.md b/changelog.md index c7f81b9..498f14c 100644 --- a/changelog.md +++ b/changelog.md @@ -10,8 +10,6 @@ An existing USD offer does not enable China. See [USD payments to China](api-ref for the account and invoice rules and the distinction between storage and provider acceptance. -- Payment rails can expose `directions`. Discovery respects a payout-only network; - listing an offramp rail does not make it an onramp option. - Quote and initiate enforce China USD readiness on the server before a new payment is funded. The partner-funded quote/initiate for USD to CN stays closed and answers `INVALID_REQUEST` with `provider_confirmation_pending`. No China USD bank list, diff --git a/openapi/swagger.yaml b/openapi/swagger.yaml index e5011ac..2522cda 100644 --- a/openapi/swagger.yaml +++ b/openapi/swagger.yaml @@ -5470,12 +5470,6 @@ components: Rails: type: object properties: - directions: - type: array - description: Directions permitted by network configuration. Returned rails are filtered for the requested direction; this is discovery, not payment authorization. - items: - type: string - enum: [onramp, offramp] slug: type: string description: Payment rail slug identifier From da9f2f16489de0fdca96e9c33e93afc6e5ab96b8 Mon Sep 17 00:00:00 2001 From: hallstain Date: Wed, 16 Sep 2026 16:10:06 +0300 Subject: [PATCH 5/7] Document the query parameters invoice document reads require GET, GET /content and DELETE refuse a request without recipient_destination_id and payout_currency, and a retained invoice is read through its bill_id. The identifier must be the canonical lowercase UUID, the limit is 8 MiB, and a byte conflict returns 409 invoice_document_mismatch. Co-Authored-By: Claude Opus 5 --- api-reference/china-usd-payments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index 94ea51c..7a08e1a 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -29,7 +29,7 @@ Use `payout_currency=USD` on `GET /api/v1/bill-payment/payment-rails` and `/inst `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. NRA/OSA remains refused with `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` until its original-invoice channel is confirmed. 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`: PUT multipart `document`, `recipient_destination_id`, `payout_currency`; GET metadata; GET `/content`; DELETE an unbound draft. PDF/JPEG/PNG, 8 MB 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 conflict. 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. +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. Advisory recognition uses authenticated `POST /api/v1/bill-payment/recipient-receipt`, multipart field `receipt`. It accepts PDF/JPEG/PNG/WebP up to **7 MB**, with at most From 107934bb6237c4e8ee5151f0e74c81727ecba26f Mon Sep 17 00:00:00 2001 From: hallstain Date: Wed, 16 Sep 2026 16:10:52 +0300 Subject: [PATCH 6/7] Document the USD refusal codes and invoice_document_id on bills Preflight and POST /bills refuse an NRA/OSA account without invoice_document_id once the invoice channel is confirmed, and refuse an amount below USD 25 with CHINA_USD_AMOUNT_OUT_OF_RANGE. Both endpoints spell the unconfirmed-account code in upper case. Co-Authored-By: Claude Opus 5 --- api-reference/china-usd-payments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index 7a08e1a..16025a1 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -27,7 +27,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. NRA/OSA remains refused with `CHINA_USD_INVOICE_CHANNEL_UNAVAILABLE` until its original-invoice channel is confirmed. 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`. 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. @@ -54,7 +54,7 @@ The provider has supplied these account rules: | Onshore account of exactly 20 digits, with no prefix | Domestic USD | Not required upfront; the beneficiary bank contacts the recipient for documents | | Another account format | Route has not been confirmed | To be confirmed with the provider | -The 6–23-character field constraint is separate from route eligibility. Accounts of another shape may be saved intact, including numeric accounts of another length and an NRA/OSA prefix not followed by exactly 20 digits, but execution is refused with `china_usd_account_unconfirmed` until their route is confirmed. Never shorten an account or remove a prefix. The account number alone does not prove bank or beneficiary eligibility or give the client a domestic/SWIFT selector. Route selection and the original-invoice upload are still being confirmed with the provider. A receipt parsed to help fill bank details is not automatically an accepted invoice. +The 6–23-character field constraint is separate from route eligibility. Accounts of another shape may be saved intact, including numeric accounts of another length and an NRA/OSA prefix not followed by exactly 20 digits, but bill preflight and `POST /bills` refuse them with `CHINA_USD_ACCOUNT_UNCONFIRMED` until their route is confirmed. Never shorten an account or remove a prefix. The account number alone does not prove bank or beneficiary eligibility or give the client a domestic/SWIFT selector. Route selection and the original-invoice upload are still being confirmed with the provider. A receipt parsed to help fill bank details is not automatically an accepted invoice. Provider delivery clarification (15 September 2026): domestic USD to a supported onshore account usually arrives the same working day when sent before about 3pm Singapore time; public holidays in Singapore or China move it to the next working day. SWIFT usually takes two working days; a small rural or other bank without a direct SWIFT connection can take 2–3 days. These are payout timings reported by the provider. They do not authorize routing an unconfirmed account through domestic USD. Provider acceptance of a payout is not bank finality. A payment returned by the beneficiary's bank is refunded through a manual process that can take up to 7 days. From 51282e2217fbe97ed8eccb9fc6d873f3b54e63ff Mon Sep 17 00:00:00 2001 From: hallstain Date: Wed, 16 Sep 2026 16:12:46 +0300 Subject: [PATCH 7/7] Stop promising a beneficiary type on the USD rail response The payment-rails response has no beneficiary type field, so a client waiting for beneficiary_type on usd-wire-china never gets it. Say the rail's top-level fields are the business schema instead. Co-Authored-By: Claude Opus 5 --- api-reference/china-usd-payments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-reference/china-usd-payments.md b/api-reference/china-usd-payments.md index 16025a1..c1d97d0 100644 --- a/api-reference/china-usd-payments.md +++ b/api-reference/china-usd-payments.md @@ -6,7 +6,7 @@ USD beneficiary details and private invoices can be saved. Whether a payment can 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. -The schema has top-level fields and `beneficiary_type: "business"`; it does not have `formats`. Clients must support this single-shape schema as well as legacy formatted rails. +The rail lists its fields at the top level and has no `formats`. The rail response carries no beneficiary type: this rail pays business beneficiaries, so treat its top-level fields as the business schema. Clients must support this single-shape schema as well as legacy formatted rails. | Detail | Constraint | | --- | --- |