Publish the rebuilt fiat account surface (UHT-520) - #65
Merged
Merged
Conversation
The catalog has reported institution_required:false for interac-e-transfer while creating a destination without one was refused. The backend now resolves an omitted institution to interac-e-transfers; the changelog says so, and both omission lists in the reference name every rail that accepts it — they had stopped at iban-sepa and nip-nigeria, missing faster-payments and the Chinese wallets as well. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The payments route was documented as the incoming payments that credited the account. It lists bank transfers in both directions, the way the bank returns them, so `kind` is `incoming` or `outgoing` and `recipient`, `iban` and `bic` name the other side of the transfer. - Ledger: every balance change, not only transfers. Its `reference` is the id of the operation behind the entry, which for a transfer is the payment `id`. That is how the two lists connect. - Publish the `kind` and `status` values, and say that ledger `type` and `category` are open lists. - Payments can answer 422 ACCOUNT_NOT_PROVISIONED like the ledger does. - Name both operations as fiat account reads, so they no longer read as the customer's whole transaction history on the User Management page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…at the API returns
Read against the handlers, endpoint by endpoint. The names did not say these
were fiat account routes, several descriptions argued for the design rather
than describing the answer, and the error list was missing codes a partner can
actually receive.
- Summaries name the resource: "What fiat accounts you can offer", "Fiat
account holder identity", "Fiat account holder verification status",
"Submit a fiat account holder's identity", "This customer's fiat accounts",
"Issue a fiat account", "One fiat account, with balances".
- Dropped the justifications ("A GET on purpose", "deliberately", "the one
route outside the customer tree"). What the endpoint answers is the
documentation; why it is shaped that way is not.
- Errors: added the codes the handlers reach and the docs never listed, in a
second table for the ones that are about us rather than the request
(`CUSTOMER_LOOKUP_UNAVAILABLE`, `IDENTITY_UNAVAILABLE`, `KYC_UNAVAILABLE`,
`PRICING_UNAVAILABLE`, `PROVISION_UNAVAILABLE`, `DB_NOT_CONFIGURED`,
`LOAD_FAILED`, `CREATE_FAILED`, `PROVISION_FAILED`, `LINK_WRITE_FAILED`,
`STATUS_WRITE_FAILED`), plus `NO_OPERATING_ACCOUNT` and `CUSTOMER_UNKNOWN`.
- A repeat identity submission answers `already_linked: true`, which was
returned but never documented.
- The payments and ledger schemas follow account#631: RFC 3339 times, a closed
list of ledger types, a signed ledger amount, and the counterparty's
`account_number` / `sort_code` on Faster Payments beside `iban` / `bic`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two routes were two views of the same money: the ledger accounted for
the balance but never said who paid, the payments list said who paid but
knew nothing of the conversions, fees and internal transfers around it.
Reading an account meant fetching both and joining them by `reference`.
`GET …/fiat-accounts/{account_id}/transactions` replaces both. One row is
one movement: `type` says what moved it, `direction` and a signed `amount`
say which way, and a bank transfer carries the other side of itself under
`counterparty` with the identifiers that transfer was addressed by.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One resource, addressed by `fiat_account_id`, opened with
`POST /fiat-accounts` and `{user_uuid, currency}`. The identity and
identification endpoints are gone from the docs because they are gone from
the API: they were the banking vendor's KYC push, and a partner has one
identity for a person already.
- Fiat Accounts is its own reference section, a sibling of User Management
rather than a heading inside it.
- The guide follows the flow a partner actually has: config, readiness on
the customer, open, wait for `active`, show the pay-in details, read the
credits.
- Two webhooks documented: `fiat_account.updated` and
`fiat_account.deposit.received`. `retail.settlement.completed` is not a
partner event and is no longer mentioned as one.
- On-ramp funding from the customer's own account is `own_account`, and the
order names the `fiat_account_id` it is funded from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fiat account tag was already right; the two places that reference it
from outside were not.
- An on-ramp order's `fiat_funding_source` is `own_account`, not
`own_iban`, and the order names the `fiat_account_id` it is funded from.
The three prose references to the old value follow.
- `GET /users/{user_uuid}` documents `fiat_account_issuance`
(`ready`, `missing_fields`), which is the only thing this rebuild adds to
User Management.
- The guide says up front that it is the how-to and the reference is the
Fiat Accounts tag, so the two are not read as alternatives.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verified against account before taking it: an omitted institution_id on interac-e-transfer resolves to interac-e-transfers, on alipay-wallet and wechat-wallet to the rail's own institution, and on iban-sepa, nip-nigeria and faster-payments to other-bank — the table in internal/service/partner_accounts.go says exactly that. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The USD rails to China, Hong Kong and Singapore can show up in the catalog, but quote and initiate refuse them with provider_confirmation_pending. Say so in the payouts guide and the changelog instead of publishing the retail bill-payment flow (supersedes #64 and #66). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Publishes UHT-520: one fiat account resource, addressed by
fiat_account_id.What a partner reads now
active, show the pay-in details, read the credits.statusis documented as something to act on (pending,active,failed,closed), and apendingaccount is explicitly one you must not send a customer to.fiat_account.updatedandfiat_account.deposit.received.retail.settlement.completedis no longer described as a partner event.own_account, and the order names thefiat_account_idit is funded from.KYC_NOT_CLEAREDandISSUANCE_NOT_READY(withmissing_fieldsin KYC's own names) replace the holder and identification codes.Also in this release
account/internal/service/partner_accounts.go. Say that an Interac destination needs no institution #63 will show as merged when this lands.api-reference/third-party-payouts.md→ "Dollars to China, Hong Kong and Singapore: not yet", plus a 2026-09-21 changelog line. The USD rails can appear in the catalog, but quote and initiate answer400 INVALID_REQUEST/provider_confirmation_pending(tradesusdPartnerAdmission). This replaces Document USD payouts to Chinese suppliers #64 and docs(usd): the dollar reaches Hong Kong and Singapore too #66, which documented the retail bill-payment flow partners cannot call; both are closed.Merge last
After account (Unigox/account#631), trades (Unigox/trades#495) and the gateway (Unigox/api#62) are deployed. Merging earlier publishes a surface the API does not serve yet.
Checks
swagger-cli validate openapi/swagger.yamlpasses.Still open
The guide page at
/tutorials/fiat-accountsis edited in GitBook by hand rather than synced from this repo, so it needs one manual paste after this merges.Rollout
trades.fiat_account_id, CHECK widened to both spellings (NOT VALID), then the covering index CONCURRENTLY in its own fileaccount migrate, with the deployfiat_account_idown_accountrow)DB_CONNECTION_STRING=… make migrate-seed file=sql/seeds/uht548_own_account_backfill.sqlcount(*) WHERE fiat_funding_source = 'own_iban'= 0Everything in steps 1–4 ships with a normal deploy. The only hand-run step is the backfill, and it is hand-run for a reason: it commits in batches, so it cannot be a migration, and it must not run until every pod reads both spellings.
The one hard edge is 2 before 4. Everything else can slip: until trades ships, partners simply keep seeing
own_ibanand orders carry nofiat_account_id.At no point does a running pod meet a value it does not understand — the agent (step 1) and trades (step 4) both accept the old and the new spelling, and only step 7 takes the old one away.