Skip to main content

Issue Card — Flow & Logic

Purpose

A detailed walkthrough of issuing a card — the form, its field-level logic, the branching, and the end-to-end sequence — so a design can be double-checked against it. It builds on the Card Management spec, the two-layer model, and card activation. Endpoint shapes are illustrative.

Scope & who​

"Issue card" is the process of creating a new card, configuring it, assigning it (or leaving it as a bearer card), and activating it. It has two entry points that can combine:

  • Issue virtual card — created instantly, lives in the app.
  • Order physical cards — a production batch; can be single or bulk.

A card's form can be physical, virtual, or both.

RoleIssue cards?
Super admin / Card manager✅ create, configure, assign, activate
Finance / approverapproves balance/limit above threshold & large orders
Auditorread-only

The issue-card form​

Fields are grouped; the Conditional column says when a field shows / applies.

A. Card form​

FieldRequiredOptionsConditional / validation
formYesphysical, virtual (choose one or both)at least one must be selected

B. Assignment​

Assign to one employee or to no-one (bearer)

Assignment is a binary choice: bind the card to one employee, or leave it unassigned = a bearer card (usable by whoever holds it + knows the PIN). Never more than one employee. Picking bearer forces cvm = pin and shows the optional single cost-center picker; picking an employee enforces the hard cap and inherits the employee's default cost center.

FieldRequiredOptionsConditional / validation
assignmentYesemployee | bearer (bearer = no-one)—
employeeIdif employeeemployee pickeremployee must be active; hard cap on cards/employee enforced

C. Cost center (Layer 1, only under the cost-center operating model)​

FieldRequiredOptionsConditional / validation
costCentersauto—assigned → inherited from the employee (read-only, picker hidden, kept in sync); if the employee has several, the default cost center is used
costCenterIdoptionalsingle cost-center pickerbearer only (picker shown only here); at most one; hidden under the monthly-limit model

D. Spending authority (Layer 1)​

FieldRequiredOptionsConditional / validation
cardBalanceYesmoney≥ 0; increase above threshold → approval
balanceReset.modeYesday_of_month | none—
balanceReset.dayOfMonthif day_of_month1–28 / lastreplenishes to allocated amount on that day

Top-up is always available regardless of reset mode.

E. Funding source (Layer 2)​

FieldRequiredOptionsConditional / validation
fundingSourceYescompany_wallet | nonenone ⇒ card balance is stored value (prepaid); allocation/top-up moves value from the company wallet onto the card at that time

F. Cardholder Verification Method (CVM)​

FieldRequiredOptionsConditional / validation
cvmYespin | otpbearer ⇒ pin only (OTP needs a registered holder); one of two, never both

G. Limits & controls​

FieldRequiredNotes
limitsNoper-transaction / daily / monthly caps
channelRulesNoenable/disable tap, printed-QR, manual-entry; contactless floor limit
allowedVendors / categoriesNooptional allow-list
timeWindowsNobusiness hours / days
freezeOnInactivityNoauto-freeze after N days unused

H. Expiry​

FieldRequiredNotes
expiryDate / termNoprinted on physical cards; set for virtual

I. Physical order only​

FieldRequiredNotes
quantityYes≥ 1; large orders → approval
personalizationYesblank | pre-printed name
shippingAddress / contactYesdelivery target(s)

J. Activation variant​

FieldRequiredOptionsConditional
activationMethodYesemployee_claim ✅ | admin_full | bearer_adminbearer_admin when assignment = bearer; the other two when assignment = employee

Branching logic​

The form's answers drive the outcome. Key rules (enforced server-side, not just UI):

  1. form — at least one of physical/virtual; both may be selected for one card.
  2. assignment = employee → require employeeId; check the hard cap; inherit the employee's cost center(s) and hide the cost-center picker — if the employee has several, use the default; allow employee_claim / admin_full.
  3. assignment = bearer → no employee; force cvm = pin; show the cost-center picker for an optional single costCenterId (cost-center model only); activation is bearer_admin.
  4. Operating model — cost-center fields apply only under the cost-center model; under the monthly-limit model they are hidden (a card never draws the monthly limit).
  5. cvm = otp requires an assigned employee with a registered channel → invalid for bearer.
  6. fundingSource = none → the card is stored value; allocating the balance / topping up debits the company wallet and loads value onto the card; at spend time no wallet is drawn.
  7. fundingSource = company_wallet → the card balance is a spend limit; funds are drawn from the wallet at spend time (the wallet/credit must have headroom).
  8. Approval — cardBalance or limits above the configured threshold, or a large physical quantity, routes to a finance/approver before the card is created/activated.
  9. Virtual cards reveal credentials only in the app; physical cards enter production and ship (ISSUED under a batchId).

End-to-end sequence (virtual, employee-assigned)​

For bearer and physical variants the tail differs: a bearer card skips the employee claim (admin sets the PIN → ISSUED → ACTIVE); a physical card is produced and shipped before activation (see card activation).

Order physical cards — workflow​

A physical card is produced in a batch and shipped, so issuing it is a longer, asynchronous flow than a virtual card: assignment and activation usually happen after the cards are in hand. Every card in the batch stays ISSUED (inactive) through production and shipping — it can't move money until it is delivered, assigned (or left bearer), and activated.

This is the customer / admin side

For what happens inside xperise behind these milestones — token provisioning, HSM key management, the personalization bureau, and logistics — see Physical Card Production (issuer side).

Roles​

  • Card manager / super admin — place and track the order, then assign and drive activation.
  • Finance / approver — approve large orders (quantity or aggregate value over the threshold).

End-to-end flow​

Step by step​

  1. Place the order — quantity; personalization (blank vs pre-printed name); shipping address(es) + contact; per-card policy defaults (balance + reset, funding source, CVM, limits, channels); and an optional pre-assignment CSV (card slot → employee).
  2. Approval — a large quantity or high aggregate value routes to a finance/approver first.
  3. Create the batch — cards are created ISSUED under a batchId; the batch enters production.
  4. Track fulfillment — the batch moves through the states below; the admin can watch progress in the portal.
  5. Receive & distribute — on delivery the admin marks the batch Delivered and hands cards out.
  6. Assign — map each card to one employee (single or bulk CSV) → ASSIGNED, or leave it a bearer card. (Skipped if the batch was pre-assigned at order time.)
  7. Activate — employee-claim (scan/tap + OTP + set PIN), admin full-activation, or, for bearer, admin sets the PIN → ACTIVE. Only now is the card spendable.

Batch fulfillment states​

Batch stateMeaningCard status
REQUESTEDOrder placed (awaiting approval if required)ISSUED
IN_PRODUCTIONBeing manufactured / personalizedISSUED
QAQuality check before dispatchISSUED
SHIPPEDDispatched, with trackingISSUED
DELIVEREDReceived by the companyISSUED → then assign / activate

Personalization & when to assign​

OptionAssign timingBest for
Blank stockAssign on receipt (cards arrive ISSUED in the pool)Flexible allocation; bearer/pool cards
Pre-printed namePre-assign at order (CSV; cards tied to employees before production, arrive ASSIGNED)Named employee cards

The per-employee hard cap is enforced at assignment — including pre-assignment at order time (an over-cap row is rejected in the CSV report).

Edge cases​

  • Partial delivery / damaged in transit — reorder the affected cards; the rest proceed.
  • Lost in transit — harmless: the cards are still ISSUED and can't authorize; cancel/reissue.
  • Cancel before production — a REQUESTED batch can be cancelled outright.
  • Bulk assign after receipt — via CSV (cardToken → employee), with a per-row result report.

Illustrative API​

POST /v1/cards/orders # place a physical batch order → { batchId }
GET /v1/cards/orders/{batchId} # fulfillment status
POST /v1/cards/orders/{batchId}/cancel
POST /v1/cards/assign # bulk assign (CSV: cardToken → employee)

Validation & errors (illustrative)​

ConditionResult
No form selected400 form_required
assignment = employee but cap reached409 card_cap_exceeded (lists employee)
assignment = bearer with cvm = otp422 bearer_requires_pin
Cost center set under monthly-limit model422 cost_center_not_applicable
cardBalance/quantity above threshold202 approval_required (pending)
fundingSource = none but wallet lacks headroom to load balance402 insufficient_wallet
Employee inactive / not in company422 invalid_employee

Post-issue state​

VariantResulting statusSpendable when
Employee-assignedISSUED → ASSIGNED → ACTIVEafter claim / admin full-activation
BearerISSUED → ACTIVEafter admin sets PIN
Physical (either)production → delivered → activatedafter activation

Bulk issue​

The same form runs in bulk via CSV (one row per card: form, employee-or-bearer, balance, reset, funding, CVM, limits). Validation returns a per-row report (created / pending-approval / error); the hard cap and CVM/funding rules are enforced per row.

API sketch​

POST /v1/cards # issue one (virtual) or a physical order; body = form payload
POST /v1/cards/bulk # CSV / array; per-row result
POST /v1/cards/{id}/assign # employee (→ ASSIGNED) — omitted for bearer
POST /v1/cards/{id}/activate # employee_claim / admin_full / bearer_admin

Open questions (confirm for the design)​

  1. Under the cost-center model, if an employee has multiple cost centers, which one does a card payment draw? Resolved: the employee's default cost center.
  2. For a stored-value (unfunded) card, is the balance loaded from the company wallet at issue, or can it be loaded from another source?
  3. Should bulk issue support mixing employee-assigned and bearer cards in one file (assumed yes)?

Designed screen — Issue Virtual Card​

The Claude Design mock for Issue Virtual Card implements this flow for the virtual + employee-assigned path:

  • ▶ Open the Issue Virtual Card design
  • A self-contained, interactive copy of the screen is kept in the repo at design-mockups/issue-virtual-card.html (open it locally in a browser). It is intentionally not part of the deployed site build.

Screen layout​

A two-column screen inside the corporate portal shell (top bar with company switcher + admin, left nav with Cards active, breadcrumb Cards › Issue virtual card):

  • Left — the form, in 4 numbered steps:
    1. Employee — searchable dropdown; shows a live "holds N of 3 cards" line and a red hard-cap warning when the employee is at the cap (Issue disabled).
    2. Card balance & funding — initial balance ($); Funding source = Company wallet (spend limit drawn at pay time) / None (prepaid stored value); Balance reset = Day of month / No reset, with "Top-up is always available regardless of reset setting."
    3. Limits & controls — per-transaction / daily / monthly caps; Cost center (inherited, read-only on the employee path; a picker only for bearer cards); CVM = PIN / OTP ("one of two, never both"); Payment channels = contactless tap, printed QR, manual entry (tap + QR on by default).
    4. Activation — Employee claim (Recommended) / Admin full-activation.
  • Right — sticky summary: a virtual card preview (holder + balance) and a Summary panel (employee, card form, balance, funding, reset, CVM, cost center, activation), an info note that credentials appear only in the employee's app after they claim it, and Issue card + Save as draft buttons. An approval note appears when the balance is above the threshold.

Constants the design pins down​

SettingValue in the design
Hard cap (cards/employee)3
Approval threshold (balance)> $1,500 routes to finance approval
Defaultsbalance $1,200; funding company_wallet; reset day_of_month; CVM pin; activation employee_claim; channels tap + QR on

Reconciliation notes​

  • ✅ Consistent with this flow: hard cap, funding (wallet/none/stored-value), reset (day-of-month/none + always-available top-up), CVM (PIN/OTP), channels, activation variants, and the "credentials only in-app" rule.
  • ✅ Resolved (cost center): for an employee-assigned card the cost center is inherited and the picker is hidden (option a); the manual picker is shown only for bearer cards (option c). When the employee belongs to multiple cost centers, one is the default and the card uses that default. The mockup now shows an inherited, read-only "Cost center (inherited)" field on the employee path.
  • Scope: the mock is the virtual + employee path. It doesn't cover bearer cards, physical orders, the card-form (physical/virtual/both) selector, or expiry — those are in this spec and the Card Management spec and would extend the screen.