Known Issues
2 unresolved issues. These are known platform issues, not integration mistakes. Follow the listed workarounds.
We do not display an unverified “all systems operational” page. A green light that cannot detect real incidents can mislead your operational decisions. This page lists the issues we know about.
After a fix, we record it in the changelog, mark the entry on this page as “Fixed”, and keep it here for a while. Deleting it would leave integrators who implemented workarounds for the old behavior with no way to know they can remove them.
Affects: Remittance (and any product line that requires level 2)
Symptoms: L1 has a hosted screen provided by us (POST /v1/kyc/sessions → hosted_url); L2 does not.
Currently, L2 can only be submitted through our own App.
This means that end users of white-label merchants can complete L1 (enabling card issuance, QR payments, Earn, and exchange),
but cannot complete L2, while remittance requires level 2.
Workaround: There is no workaround. This is a missing capability, not a configuration issue. Confirm the delivery schedule with us before integrating remittance.
Until then, use GET /v1/kyc/requirements to check the level required by the product line.
Do not enable lines requiring 2 in your UI yet: allowing a user to reach the final step only to receive
kyc_required is the most costly kind of failure.
For the required documents, to assess the effort involved, see
L2 enhanced verification.
Affects: Members
Symptoms: Creating a member with an email / phone number already belonging to another merchant returns 400 invalid_request
(not 409: we do not expose information that could be used to discover whether a person already exists).
Workaround: There is currently no workaround; the identity-layer unique indexes have not yet been partitioned by merchant. If your user base overlaps with other Zise merchants, inform your account manager in advance.
api_error instead of 4xxAffects: Exchange · Internal transfers · Earn · QR payments · Remittance · Card issuance · Withdrawal addresses
Symptoms: Valid business rejections such as insufficient funds, expired quotes, disabled directions, or invalid address checksums
were exposed as 500 api_error rather than 4xx responses with an explicit code.
The internal codes were missing from the public code catalog and fell through to the default branch.
Fixed on 2026-08-12. These rejections now return 4xx responses with an explicit code. If you implemented a workaround that capped retries for 500 responses, you can remove it for these rejections. However, keeping a retry limit is always appropriate: do not retry 5xx responses other than upstream_timeout (504) indefinitely. We also added tools/check-open-error-codes.mjs: an unregistered internal code, or a registered code with no emission sites, now fails the commit-time check.
Affects: GET /v1/cards/{id}/transactions · GET /v1/earn/products/{id} · POST /v1/cards/{id}/replacements
Symptoms: The first two selected nonexistent columns. The third omitted a reason allowlist, allowing arbitrary strings to violate a database constraint.
Fixed. During the fix, we found a fourth issue of the same kind: GET /v1/cards/{id} selected balance_micro, which had never appeared in any of the 127 migrations (the actual name is upstream_balance). That query supplied the entire card detail response and also guarded the transaction endpoint, so even correct transaction column names could not make the latter accessible. Card transactions now also return direction: amount is an absolute value, so a refund and a purchase look identical if you only inspect the amount.
GET /v1/remittances/{id} exposed an internal status and used an amount format inconsistent with the list endpointAffects: Remittance
Symptoms: The detail endpoint could return pending_merchant_funds (both the list and order-creation endpoints map it to pending).
For the same order, source_amount was a fixed-point integer string without a decimal point in the detail response
but a decimal string in the list: a difference of 10^ledger_scale, with both endpoints returning 200.
Fixed. All three response paths (creation, list, and detail) now use one mapping function instead of three separate ternary expressions, which were the reason this kind of leakage could recur. Amounts consistently use decimal strings, with ledger_scale included alongside them.
status_version is always 0 for 9 events (2 were defects; 7 are intentional)Affects: Webhook
Symptoms: Each affected event is identified in the event catalog. Its status_version does not increment with status changes.
Resolved on 2026-08-13. Originally recorded as one issue, this was actually two:
① Actual defects, 2 events, fixed: card.application.approved and card.status.updated. Their data.id contained the internal member ID, even though data.object said card_application / card. Retrieving the object by that ID inevitably returned 404. The missing version was a consequence of the same problem: we read the version from the table identified by data.id, but never reached the correct table. Both events now contain the actual object ID (application ID / card ID) and the actual version. ⚠ If your handler assumes that these two card events contain a member ID, restore the normal convention.
② Not defects, 7 events, unchanged with explanations added: member.created / member.suspended / kyc.result.updated / deposit.credited / exchange.order.executed / transfer.completed / earn.order.settled (only the flexible-interest-payment variant). Their objects either have no state machine (a deposit only has a credited state; exchanges and transfers execute atomically), or are not orders at all (the KYC object is a person; flexible Earn uses a balance pool). Inventing a version number would not make anything more reliable. The workaround above still applies to these 7 events. Please continue using it.
We also removed data.previous_status: it was always an empty string and had no producers. For the rationale, including why we do not populate it with an actual value, see the Webhook guide.
On our side, the commit-time check tools/check-webhook-route.mjs now fails when a notification call does not tell the bridge which object the notification refers to. Previously this silently fell back: the event was sent, its signature was valid, your endpoint returned 2xx, and only the subsequent retrieval returned 404.
Affects: Sandbox
Symptoms: Injecting timeout / unknown / unknown_status returned 200 and echoed your setting,
but every subsequent order still called the real upstream provider with real credentials.
No business code ever read that setting.
Fixed. The upstream clients for all four product lines now apply the injection layer at the lowest-level fetch call. merchantAuth establishes its scope, requiring both a sandbox hostname and resolution to a shadow entity.
Two deliberate design choices are explained in the sandbox guide: · Only the call that moves funds is replaced. Quotes, decoding, and queries to the same provider still call the real upstream. Replacing every call would make orders fail cleanly before funds were locked, leaving the “funds may already have been released” branch—the one you actually need to test—unreachable. · The injected value is an upstream response, not an order status. We process it using exactly the same classification, retries, audit trail, and state machine as production. What you observe in the sandbox is therefore how the same situation would behave in production.
GET /v1/balances differed from other list endpointsAffects: Balances
Symptoms: It returned { balances: [...] }, while other list endpoints used { data, next_cursor, has_more }.
Fixed on 2026-08-13. This endpoint now returns the standard envelope { data, next_cursor, has_more } (without pagination, so next_cursor is always null and has_more always false). Your generic paginator no longer needs a separate branch.
⚠ balances has not been removed. It is still returned, and points to the same array as data. This is a transitional compatibility field: replacing it outright with data would cause existing integrations that read balances to silently receive undefined at deployment time. Rather than an explicit error, all balances would appear to disappear, and most interfaces would render them as 0.
Follow this order: new integrations must read data; existing integrations reading balances should migrate when scheduled. The removal of balances will be announced in advance in the changelog; there will be no second compatibility period.