Application, card, shipping, and supplementary-document states advance independently. This page lists all four state axes.
State Machines and Values
Card issuance has four independent state axes. None is derived from another:
| Axis | Where to read it | What advances it |
|---|---|---|
| Application | status in GET /v1/cards/applications/{id} | Our executor + upstream |
| Card | status in GET /v1/cards/{id} | Member actions + risk controls + upstream |
| Shipping | status in GET /v1/cards/applications/{id}/shipment | Our operations team |
| Supplementary documents | supplement.status in application details | Upstream requests + member submissions |
⚠ Delivered shipping does not mean the card can be used. These are separate axes. After receiving the parcel,
the member must still bind and activate the card. Likewise, a terminal application status does not imply an
activecard:physical cards remain
unactivatedafter their application reaches its final stage.
1. Application States
Only Three Values at Order Creation
POST /v1/cards/applications returns a reduced status set containing only:
| Value | Meaning |
|---|---|
submitted | Accepted; we are progressing the application |
pending_review | The product requires manual review; awaiting our review |
pending | Accepted and queued |
Subsequent Queries Expose the Full Internal Set
GET /v1/cards/applications/{id} returns unfiltered internal status values. You may encounter any of these 20 values:
In progress (15 states)
| Value | Meaning | Responsible party |
|---|---|---|
pending_merchant_funds | Your prepaid balance is insufficient; the order is queued | You must fund your prepaid account |
awaiting_payment | Draft; not yet charged | Automatically canceled after 30 minutes without payment |
submitted | Submitted | Us |
pending_review | Awaiting manual review | Us |
accepted | Accepted; awaiting upstream confirmation | Upstream |
processing | Processing upstream | Upstream |
need_docs | Upstream requires additional cardholder documents | Member |
docs_submitted | Documents submitted; awaiting upstream review | Upstream |
allocating | Physical card: reserving inventory | Us |
picking | Physical card: awaiting shipment (platform-operated only) | Our operations team |
shipped | Physical card dispatched (platform-operated shipping only) | Member receipt |
awaiting_bind | Ready to bind. Downstream inventory applications enter this state directly after approval; platform-operated applications enter it after delivery or handover | Member |
binding | Binding verification in progress | Us + upstream |
pending_activation | Bound; awaiting activation | Member |
refunding | Cancellation refund in progress | Us |
Terminal (5 states) — These do not change afterward:
| Value | Meaning |
|---|---|
issued | Card issued (successful endpoint for virtual cards) |
failed | Failed; inspect failure_code |
rejected | Rejected by manual review or upstream |
refunded | Fees refunded |
canceled | Canceled |
⚠
pendingin the creation response andpending_merchant_fundsin a subsequent query describe the same condition.The former is normalized; the latter is the internal value. Your status mapping must recognize both,
or an order queued for insufficient prepaid funds will appear as an unknown state in your system.
This inconsistency is recorded under Known Issues.
We will standardize on the normalized value. Treat
pending_merchant_fundsaspendingnow,and you will not need to change your integration when that happens.
⚠
pending_activationis the effective final application stage for physical cards, despite appearing under in-progress states.No code advances the application beyond it: activation updates the card axis.
Do not wait for the application to become
issuedbefore proceeding; that will never happen for a physical card.
Do Not Enumerate Every In-Progress State
Decide whether to keep polling by checking whether the status is one of the 5 terminal states. Do not enumerate in-progress states. We may add an in-progress state without notice. If your explicit list omits it, the order can silently disappear from your list, making the user believe their application is gone.
2. Card States
┌──────────── 会员自助 ────────────┐
pending → unactivated → active ⇄ frozen
│ ↑ ↓
│ freezing / unfreezing(过渡态)
│
├─→ risk_frozen 风控冻结,**会员解不了**
├─→ suspended 运营暂停,**会员解不了**
├─→ lost → reissuing → replaced
├─→ expired
└─→ closing → closed
| Value | Spending allowed | Top-ups allowed | Member can unfreeze |
|---|---|---|---|
pending | ✗ | ✓ | — |
unactivated | ✗ | ✗ | — |
active | ✓ | ✓ | — |
freezing | ✗ | ✗ | ✗ (wait for frozen) |
frozen | ✗ | ✓ | ✓ |
unfreezing | ✗ | ✗ | ✗ (wait for active) |
risk_frozen | ✗ | ✗ | ✗ Risk-control action |
suspended | ✗ | ✗ | ✗ Operations action |
lost / reissuing / replaced | ✗ | ✗ | — |
expired / closing / closed | ✗ | ✗ | — |
⚠
frozenpermits top-ups; transitional states do not. This is intentional: freezing stops spending,not incoming funds. During
freezing/unfreezing, the card's state is unsettled,so funding it would send money into a card with an uncertain status.
⚠ Freezing and unfreezing are separate transitions, not a shared processing state.
Combining them caused a production issue with invalid-card-status errors and failed top-ups after unfreezing.
⚠ Do not show an Unfreeze button for
risk_frozenorsuspended.Member attempts will fail. Risk-control and operations teams respectively remove these restrictions.
Handling transitions: for freezing / unfreezing / pending / closing / reissuing, wait. Do not issue another command; repeated freeze requests do not accelerate completion.
3. Shipping States
There are 15 states. Our operations team updates them; polling does not advance them. While a parcel is in transit, the server does not progress automatically. Polling every 45 seconds merely wastes requests.
In transit: draft · created · awaiting_pickup · in_transit · out_for_delivery · returning
Exceptions (surface these separately for operations; buried in an in-transit list, they can remain unattended indefinitely): address_issue · delivery_failed · stalled · refused · returning · lost · damaged
Terminal: received · returned · lost · damaged
There is also delivered (the carrier reports delivery), which is not terminal. received, meaning the member confirmed receipt, is terminal.
⚠ Returned parcels never automatically return to available inventory. Processing after
returnedfollows a separate flow,outside this state axis.
4. Supplementary Documents
When an application enters need_docs, supplement in its details changes from null to an object:
"supplement": {
"status": "pending",
"required_items": ["cert_front", "address"],
"deadline": "2026-08-20T00:00:00.000Z",
"submit_url": ""
}
Without an outstanding request, supplement is null, not an omitted field.
status | Meaning |
|---|---|
pending | Awaiting member documents — remind the member |
submitted | Submitted; awaiting upstream review — reminders will not help |
Values of required_items
cert_front · cert_back · address · name · birth · cert_id · other
⚠ If we do not recognize the upstream reason, we return
["other"], never an empty array.A documents-required screen with no listed items leaves users unable to determine what to submit.
Handle unknown items similarly: display Other documents; contact support.
deadline Is Enforced
After expiry, the application becomes failed and the issuing fee is refunded. This deliberately differs from remittance supplements, which return null for their deadline; card applications have a fixed database deadline.
You can safely use it for a countdown. An empty string means legacy data has no expiry configured.
How to Submit: Obtain a Hosted-Page Link
POST /v1/cards/applications/{id}/supplement-sessions
→ 201 { "hosted_url": "https://…/hosted/card-supplement/kyc_…", "expires_at": … }
Give hosted_url to the end user. They retake and upload their document images on that page, and the application resumes automatically after submission. You do not need another API call.
submit_via in the details always contains this endpoint name, not a ready-to-use URL: The link is single-use, bound to the member and request, and valid for 24 hours. Including it in application details would effectively make it persistent: details are repeatedly read, logged, and forwarded, yet that URL permits document submission as the member. Request it only when documents are about to be submitted.
⚠ A ticket is issued only when
supplement.statusispending. If documents have already been submitted(
submitted), the endpoint returns 404. Issuing another link would give the user an entry pointthat can only report the link as expired.
Why a Hosted Page Instead of a JSON Submission Endpoint?
Submitting documents here means replacing identity-document images. The images sent upstream come from the member's current verified identity profile, not fields in a submission body. Therefore:
- Images share the KYC boundary and never pass through your servers;
- An endpoint accepting only scalar values would consume this supplement request and resend the same old photographs, show Submitted to the user, and then receive a second rejection for the same reason. This flow is single-use: once the request expires, the application becomes
failed, the issuing fee is refunded, and no card is issued.
⚠ Items such as
name/birth/cert_id/addresscannot be changed on this page, which clearly indicates this. Identity changes require renewed identity review;
replacing a photograph with a clearer one does not. Requiring review for a better photograph would suspend this member's transfers, wealth products, and
remittances for insufficient verification level merely to satisfy one issuer's image-quality request.
Direct users to your support team for these items.
Failure Reasons (failure_code)
Details for terminal failed / rejected applications include failure_code. It uses public error-code mappings, not internal codes. Internal codes disclose implementation details; exposing them would turn the internal state machine into a public contract.
If a specific reason is unavailable, the value is an empty string, not null.