01
model & vocabulary
Four concerns, kept separate. Each can be reasoned about and reported on its own.
| concern | the question | determined by |
| Verification requirement | Must the holder register / pass KYC before this card is usable? | the design: requires registration and/or KYC, keyed per (program, design) |
| Usability state | Is this card usable yet? | the card: held (in a verification sub-state) vs usable |
| Funding timing | Does the initial load land at activation, or at verification? | follows usability — deferred while Pending, immediate when usable |
| Consumer signal | What does each surface (holder, operator) see and do? | the card's state + reason — see the consumer contracts (§02) |
Usability is one binary axis — held vs usable (an open card is simply never held) — but "held" decomposes into the verification sub-states below.
verification sub-states — "Pending" is not one state
A held card sits in exactly one of these. axis enforces only the binary (held vs usable); the sub-state — the reason — is the verdict authority's (core's), surfaced to consumers. None of these are processor card-statuses.
Not activated→
Awaiting registration→
Awaiting KYC→
Verified · usable
· off-ramps:
Registration failedKYC failed
→ OPEN-4
| sub-state | means | enforced as | consumers show | exit → |
| Awaiting registration | activated & held; the holder hasn't registered the card | held | held; offer the registration path | registers → Awaiting KYC (KYC design) or Verified (reg-only); fails → Registration failed |
| Registration failed | the holder attempted registration and it was rejected | held | held; reason = registration failed | OPEN-4 |
| Awaiting KYC | registered; required KYC not yet satisfied | held | held; awaiting KYC | passes → Verified; fails → KYC failed |
| KYC failed | the holder attempted KYC and failed | held | held; reason = KYC failed | OPEN-4 |
| Verified | registration (+ any required KYC) satisfied | usable | usable; deferred funds load once (or short → OPEN-7) | terminal — the card is now Active & usable |
Reg-only designs never enter Awaiting KYC / KYC failed. Open designs enter none of this (activate → usable). A card that simply never progresses — the holder never acts — is the "never arrives" half of OPEN-4, distinct from an explicit failure. A failure leaves axis's held state unchanged — "failed" is the verdict authority's reason, read on demand (C-STATE), never pushed to axis; only a success (the release event) flips the card usable, so a retry just succeeds through the normal release (REQ-C6).
Rule 0 — the requirement belongs to the design; the state belongs to the card. A program can hold designs with different requirements, so "this program requires verification" is never meaningful — only "this design requires verification," evaluated per card. Verification-required ⇔ registration ∨ KYC. Registration is the entry; KYC nests inside it, graduated by amount (screening · CDD 1–3). Verification attaches to the person, not the card.
Principle — money follows usability, and exactly once. No funds move for a card while it is Pending Verification; the initial load is deferred and lands only when the card becomes usable. An open card funds immediately. A card is funded exactly once for that initial load — a verification result delivered more than once must not double-fund it.
02
consumer contracts
What each surface that shows or acts on the verification state MUST do. Behavioural — encoding is the design's choice. Referenced by the requirements as C-xx.
C-STATEThe platform makes a card's verification state distinguishable — the held/usable fact on the card, the precise reason from the verdict authority on demand.
- From the card a consumer can tell: open vs held vs usable; that it needs registration and/or KYC; and the deferred amount if any.
- The precise sub-state — awaiting-registration vs registration-failed vs awaiting-KYC vs KYC-failed — is the verdict authority's (core's), obtainable on demand. It is not all required on the card object; the card need only carry enough to be held correctly and route the holder.
- No two fields or endpoints may report conflicting answers to "does this need verification."
as-built: the card carries requiresKyc / kycLocked / deferredLoadAmount — KYC-only, so even "held + needs registration" is wrong today (DEV-890); a second, conflicting field exists (DEV-842/885); the failed sub-states aren't surfaced at all.
C-FUNNELThe cardholder site holds back a verification-required card and offers the path to complete it.
- Given a card whose design needs registration and/or KYC, not yet verified When the holder views it Then it is shown as held and the holder is offered the registration / verification path.
- This fires for registration-only designs as well as KYC designs — both are "verification required."
- When the card is verified Then it behaves like an ordinary usable card.
as-built: card-balance / shoppingcentre.cards gate on a KYC-only signal, so registration-only cards are never prompted (DEV-890).
C-ADMINSetldHub shows that a card is held and why, and never presents not-yet-loaded funds as spendable.
- A pending card shows as held with the reason — awaiting registration, awaiting KYC, or both.
- Deferred (not-yet-loaded) funds are never shown as a spendable/available balance. Beyond that — show the real (zero) balance, mask it, how prominently to surface the deferred amount, whether SetldHub differs from the cardholder site — is an open display choice (OPEN-6).
as-built gap: SetldHub keys this off a KYC-only signal, so a registration-only held card is treated as an ordinary card — labelled "Registration Required: No" and displayed unlike a KYC-held card. The mislabel and the inconsistency are the bugs; the right held-card display itself is OPEN-6. DEV-890 / DEV-685.
C-VERDICTThe compliance authority is the single source of "is verification satisfied," answered live at money-movement.
- Funds move only on a current "verified for this holder and this amount" — never a cached verdict.
- The verdict is amount-aware: a load needing a deeper level than the holder holds does not pass.
- For a registration-only design there is no KYC level — "verified" means registration is confirmed; the amount-band check applies only where the design requires KYC.
- If the verdict is unavailable (authority down or times out), the operation fails closed — no release, no money — never fail-open.
as-built: core-service owns the verdict (person KYC results, graduated by amount); axis reads it.
04
requirements
Behavioural rules with pass/fail criteria. SETTLED = testable now · OPEN-n = pending a decision (§05) · SECURITY = must-fix invariant.
A · Activation
REQ-A1An open-design card is usable immediately on activation; bare activate moves no money.settled
- Given an open design When
activate Then the card is Active & usable and no funds move.
Table: R1 · Consumers: —
REQ-A2A verification-required card is held after activation — Pending Verification, no money moved — unless the linked holder is already verified.settled
- Given a design needing registration and/or KYC, holder not yet verified When
activate Then the card is Pending Verification (suspended at the processor — the real enforcement) and no funds move.
- True for registration-only designs, not just KYC.
- Exception: if the holder is already linked and verified for this design (a re-issue / replacement), the card activates directly to usable — no hold. The hold is for unverified holders only.
Table: R3, R5, R14 (already-verified) · Consumers: C-STATE
REQ-A3A pending card's held state and reason are visible to every consumer.settled
- Given a Pending card When any consumer reads it Then it can tell the card is held and why (registration / KYC / both).
Table: R3–R7 · Consumers: C-STATE, C-FUNNEL, C-ADMIN
REQ-A4Re-activating a card never disturbs its state or re-triggers funding.settled
- Given an already-activated card (usable or held) When
activate Then its state and funds are unchanged. Whether the call errors or returns an idempotent success is an API choice, not fixed here.
Table: — · Consumers: —
as-built: rejects with IllicitModificationException — the reject-vs-idempotent shape was assumed, not specified.
REQ-A5Bare activate on a fund-after-verification design — behaviour pending decision.open-1
- With no amount, a fund-after-verification design has nothing to load when it becomes usable. See OPEN-1 for the candidate outcomes.
Decision: OPEN-1
B · Loading & funding
REQ-B1A load on a usable card checks funds, then funds the card now.settled
- Given a usable card When
load A Then a balance check runs and, if it passes, A lands on the card.
Table: R9 · Consumers: —
REQ-B2An activate-with-load on a verification-required card defers the load; no money moves until verification.settled
- Given a verification-required design, holder not yet verified When
activateWithLoad A Then the card is Pending, A is recorded as the deferred load, and no money moves.
- Exception: an already-verified holder activates-and-loads now (no defer), like an open card — REQ-A2's carve-out on the load path.
- A card holds one deferred-load record (amount and currency). If OPEN-2 resolves to "extend", extension updates that one record — so "funded exactly once" (REQ-B4) is once in total, not once per parked entry.
Table: R4, R6, R7, R14 · Consumers: C-STATE
REQ-B3No path commits a load — now or deferred — without a balance check where the money actually moves.settled
- An immediate load is checked at request time; a deferred load is checked at verification, before the funds move.
- A deferring request MAY also validate fundability up front as an early signal, but the binding check is at fund-movement.
- If the at-verification check fails (funds short by release time) Then the shortfall is a reported outcome — never a silent drop and never a strand; the card is still verified. Exact handling (release-unfunded-and-notify vs hold-the-load) is OPEN-7.
Table: R8, R9 · Consumers: C-VERDICT · Decision: OPEN-7
REQ-B4An initial load funds the card exactly once.settled
- Given a card with a deferred load When a verification/release signal arrives more than once (retry, duplicate, concurrency) Then the card is funded once and only once.
- Concurrent operations resolve to one outcome: a release racing a
load funds once (not both); a release racing a replacement funds the destination once. (The locking / atomic-claim mechanism is §07.)
Table: R8, R12 · Consumers: —
REQ-B5Loading a card that is still Pending — behaviour pending decision.open-2
- A load must never succeed without the funds reaching the card. Reject vs extend-the-deferred is OPEN-2.
Table: R10 · Decision: OPEN-2
REQ-B6A later load that crosses into a higher KYC band — re-gate vs proceed, pending decision.open-3
- If a load pushes a usable card into a higher amount band, must it re-verify first? OPEN-3.
Table: R11 · Decision: OPEN-3
C · Release / verification
REQ-C1A verification event releases the card: it becomes usable and any deferred funds load — exactly once.settled
- Given a Pending card When registration (and any required KYC) completes Then the card becomes usable and the deferred load lands, once.
- Usability flips on the same trust as the money: an authorised release (REQ-C4 / OPEN-0) confirmed against the live verdict — not on any inbound signal. A stale or spoofed nudge makes the card neither usable nor funded.
Table: R8 · Consumers: C-FUNNEL, C-VERDICT · Depends: OPEN-0
REQ-C2No entry path can strand a card — every Pending card has a working route to usable.settled
- Given a card made Pending by any path (plain
activate, activateWithLoad, group, transfer) When verification completes Then it releases cleanly — never a permanent suspend.
Table: R3–R8 · Consumers: C-FUNNEL · Guards: D1, D2, stranded cards, DEV-890
REQ-C3Money moves on release only on a fresh, amount-aware "verified" — never cached.settled
- Given a release/verify signal When funds would move Then the compliance authority is asked, live, "verified for this holder and amount?" — a stale/replayed/wrong signal funds nothing.
Table: R8 · Consumers: C-VERDICT
REQ-C4The release / verify operation is a privileged capability, authorised against the card's owning partner and the named holder — not the caller's own scope.security
- Given a release call (the legitimate caller is a cross-partner orchestrator) When it names a card + a holder Then the card is resolved and authorised against the card's owning partner / the holder's entitlement — quoting an arbitrary card id or person id must not release another holder's card.
- It is a privileged role (the release orchestrator only), not the broad partner role — and "caller's own scope" is the wrong test, since the legitimate caller owns no partner scope.
Consumers: C-VERDICT · Depends: OPEN-0 · Guards: the release-path IDOR (DEV-827 class)
REQ-C5Verification fails or never arrives — fate pending decision.open-4
- A card sits Pending; the holder fails or never completes. Stay pending / expire / partner-driven — and the held-card expiry-anchor interaction — is OPEN-4.
Decision: OPEN-4
REQ-C6A verification failure leaves the card held — only a success releases it, so a retry recovers normally.settled
- A failure does not change axis's state — the card stays held. "Failed" is the verdict authority's reason, read on demand (C-STATE); axis is never pushed a failure verdict and caches none.
- Given a held card When the holder retries and verification succeeds Then the normal success release (REQ-C1) flips it usable — failure is not terminal (its terminal fate is OPEN-4).
Sub-states: Registration/KYC failed (a core reason, not an axis state) · Consumers: C-STATE, C-VERDICT · Decision: OPEN-4 (terminal fate)
D · Replacement / transfer
REQ-D1A replacement carries the source's usability and funding state to the new card; the source is retired; no re-verification.settled
- Given a usable source with a balance When replaced Then the balance moves to the new card (usable); the holder is already verified, so no re-verification.
Table: R13 · Consumers: —
REQ-D2A pending source carries its Pending state and deferred funds to the replacement, releasing on verification; funded exactly once across the move.settled
- Given a Pending source (deferred, never funded) When replaced Then the Pending state + deferred load carry to the new card; a retried/concurrent move must not fund the destination twice.
Table: R12 · Consumers: C-STATE · Guards: double-fund risk
E · Consumer signal & display
REQ-E1A held card is exposed as needing registration and/or KYC — never as "no verification required."settled
- Given a registration-only held card When a consumer reads it Then it sees the card needs registration — not "no verification required." (The finer awaiting-vs-failed sub-state comes from the verdict authority — C-STATE.)
Consumers: C-STATE · Guards: D4, DEV-890
REQ-E2The cardholder registration path fires for every verification-required, not-yet-verified card — reg-only included.settled
- Given a held reg-only card When the holder visits the cardholder site Then they are offered the registration path.
Table: R3, R4 · Consumers: C-FUNNEL · Guards: DEV-890
REQ-E3SetldHub labels the held reason correctly and displays a held reg-only card the same as any held card.settled
- Given a held reg-only card When SetldHub shows it Then it reads "registration required" and is displayed like a held KYC card — deferred funds not shown as spendable.
Consumers: C-ADMIN · Display: OPEN-6 · Guards: DEV-685, DEV-890
REQ-E4Every "requires verification" signal derives from one source — multiple emitted fields are allowed, but they can never disagree.settled
- Given any card When two fields or endpoints report whether it needs verification Then they agree, because both derive from a single source of truth. (Derived fields may persist for compatibility — this does not mandate collapsing to one field; they must just never diverge.)
Consumers: C-STATE · Guards: DEV-842 / DEV-885 (dual-field conflict)
F · Group activation
REQ-F1Group activation applies the per-card rules to every card uniformly.settled
- Given a group activation — a batch is a single design (enforced precondition) — When activated Then every card follows the same per-card rules above (all-open or all-verification-required).
Table: R1–R7 · Consumers: —
REQ-F2A verification-required batch moves no money up front; it validates the total deferred amount, then funds each card at its own verification.settled
- Given a verification-required batch When activated Then no funds move up front; the total deferred amount is validated up front (advisory — the binding check is per-card at each card's release, REQ-B3); per-card funding happens at each card's verification.
- Partial failure: if the up-front total check fails, the whole batch is rejected (no card activates). If a card fails mid-batch, its outcome is independent and reported — a partial failure neither strands the rest nor silently drops the failures.
Table: R4, R6, R8 · Consumers: C-VERDICT
G · Safe migration
REQ-G1Changing how the verification state is encoded rolls out with no window where a card is mis-displayed.settled
- Given a change to the signal encoding When it ships Then consumers tolerate old and new and are deployed first (expand/contract) — at no point does a card show the wrong held/usable state.
- Assumes every branching consumer is internal — pending the external-consumer investigation (the open Investigate card).
Consumers: C-STATE, C-FUNNEL, C-ADMIN
05
open decisions
Each carries both candidate outcomes so it becomes a hard test the moment it's decided. ◆ = needs product / Grif · ⛔ = design-blocking (a design can't freeze until it's answered); unmarked ones are safely parameterised. The design §08 tracks the same set — numbering isn't 1:1 (it carries OPEN-0; this adds OPEN-6/7).
OPEN-0 ◆ ⛔
The release operation — split it, and who may invoke it
"Release" is today an overloaded endpoint (a single verifyIndividual branched on a person id: with it → release = activate + load; without → a KYC inquiry/screening that moves no money). It is reachable by two legitimate callers — core-service and card-balance-api (forwarding the cardholder) — neither of which is the owning partner. The whole release contract (REQ-C1–C4) rests on resolving this.
Split it — a dedicated release operation (orchestrator-only role), separate from the cardholder-facing inquiry/screening.
Keep overloaded, gate the release branch by role + card scope.
Blocks: REQ-C4 (the entire auth model) and the release contract — a design can't freeze without it. Lean: split. → design + Grif
OPEN-1 ◆ ⛔
Bare activate on a fund-after-verification design
No amount, so a fund-after-verification design has nothing to load when it becomes usable.
Require activateWithLoad — amount captured at activation, deferred.
Allow bare activate — becomes usable with no funds; funds arrive via a later load.
Program-default amount — activate defers a configured standard load.
Parameterizes: REQ-A5, table R3/R5. Need: the real partner flow. → Grif
OPEN-2
Loading a card that is Pending Verification
What does load do when the target isn't usable yet? (It must never succeed without funds reaching the card.)
Reject — "card pending verification."
Defer / extend — add to the pending amount that loads at verification.
Parameterizes: REQ-B5, table R10. Lean: reject, for a clear contract — unless top-up-before-verification is a real need.
OPEN-3 ◆
Does a later load re-gate an already-usable card?
A load that pushes a verified card into a higher amount band — re-verify first, or not?
Re-gate — hold the crossing load until the deeper KYC clears.
Proceed — banding applies only to the initial load.
Parameterizes: REQ-B6, table R11. Need: compliance intent. → Grif
OPEN-4 ◆ ⛔
Verification fails or never arrives
A card sits in Registration failed, KYC failed, or simply never progresses (the holder never acts). What is its fate — and what do we tell the partner? The explicit-failure and never-arrives cases may warrant different answers.
Stay pending indefinitely.
Expire / cancel after a window, with notification.
Partner-driven — an explicit action to resolve it.
Parameterizes: REQ-C5. Cross-feature: card-expiry counts from the activation date, so a held card burns its expiry window while unusable (DEV-819). Need: product + compliance. → Grif
OPEN-5
Retire the dead fund-timing flags?
Funding follows usability; the per-design fund-timing flags have no readers in any service.
Ignore + document now, schedule removal with the schema owner.
Keep — only if a real case needs funding decoupled from usability.
Lean: retire (cross-schema, core-owned). Guards: D5.
OPEN-6 ◆
What a held card displays — balance & deferred amount, per surface
A Pending card has no spendable funds yet (real available ≈ 0) and a deferred amount to come. The settled rule is only: never present the deferred amount as spendable, and show reg-only the same as KYC. Beyond that, what should each surface actually show? (Wrongly asserted as settled in an earlier draft — it isn't.)
Mask the balance (hidden / ***), surface the deferred amount.
Show the real (zero) available + the deferred amount, clearly separated.
Differ by surface — cardholder site minimal / non-confusing; SetldHub shows the full truth (operator tool).
Parameterizes: C-ADMIN, C-FUNNEL, REQ-E3. Need: UX / product. → Grif
OPEN-7
Deferred load, but funds short at release
Verification completes (the card is now usable), but the funding source can't cover the deferred load (parked for days/weeks). Settled invariant: the card becomes usable and the shortfall is a reported outcome — never a strand or a silent drop (REQ-B3). What then?
Release usable, load fails + notify — verified/usable with no balance; the partner retries the load.
Hold the load for retry — usable, but the deferred load stays pending a fundable retry window.
Parameterizes: REQ-B3. Need: product. → Grif