Card Issuance Guide
This guide covers virtual and physical card issuance. Complete Card Product Discovery and Issuance Preparation first and make sure isPrepared=true.
Two Issuance Paths
flow=standardis standard issuance throughPOST /cards. Both c_015 virtual and physical cards use this flow.
flow=applicationis used only when explicitly advertised by a product's issuance capabilities. c_015 does not use this flow.Physical cards also require choosing a fulfillment mode from the product's
fulfillmentModes:
merchant_stock: The merchant provides its own physical card stock.platform_delivery: The platform arranges physical card fulfillment and delivery.The fulfillment mode determines the physical card process, not the
flowvalue.
Virtual Card Issuance 💳
GET /cards/bins
→ Create or reuse cardholder
→ Complete preparation if required
→ POST /cards
→ GET /cardsCall Create a new card with channelId, cardType=virtual, and cardOptions (including cardHolderId and your callId), plus any amount or budget settings.
For products requiring preparation, confirm isPrepared=true before creating the card. Products with requiresPreparation=false can proceed directly to issuance.
c_015 supports one virtual card per request.
Save the returned cardId. Query the card state using Get card list with cardId or callId. If the create response is unclear, query using the original callId before attempting another creation.
Subscribe to card.status.update as a notification and refresh signal.
A Local cardId Does Not Mean the Card Is UsableA local
cardIdonly means the card record exists. The actual card state must be confirmed through a query result.Preparation readiness does not replace balance, product-limit, card-count, or holder identity checks.
private-info Permission LimitationGet card private info follows its existing protocol and declares no explicit permission yet; credentials in
explicit_permissionsmode cannot call it.Do not switch authorization mode to bypass this restriction.
Physical Card Issuance 📮
Both physical-card fulfillment modes create a card placeholder through POST /cards before binding.
The difference is whether a physical card quote and platform delivery application are required:
| Fulfillment mode | Quote required | Application | Physical stock |
|---|---|---|---|
merchant_stock | No | No | Merchant-owned stock |
platform_delivery | Yes | Yes | Platform-arranged stock |
Both modes use flow=standard for c_015 physical cards.
Merchant Stock: merchant_stock
merchant_stockMerchant stock means the merchant already owns physical card stock, typically purchased offline. This API does not purchase physical stock.
Before creating the card, make sure the product supports merchant_stock and complete preparation if required.
Call POST /cards with:
channelId: The product ID.cardType=physical.cardOptions.cardHolderId: The local cardholder ID.cardOptions.callId: Your unique issuance identifier.cardOptions.fulfillmentMode=merchant_stock.- Optional
label. - Optional
budgetId.
Omit amount, shippingAddress, and physicalApplicationId.
This flow does not create a platform delivery application, require a shipping quote, reserve platform inventory, or initiate platform shipping and review.
Fee handling: POST /cards applies the configured card issuance fee, if applicable. Binding the physical card does not charge the card issuance fee again. Offline stock purchase does not automatically mean the API issuance fee is zero.
After creation, receive the physical card, verify the end-user identity, and proceed with binding and activation.
Platform Delivery: platform_delivery
platform_deliveryThe platform arranges physical card fulfillment and delivery. This flow requires a quote before card creation.
Step 1: Obtain a quote
Call Quote a physical card with:
cardChannelId: The product ID.cardHolderId: The local cardholder ID.shippingAddress: The delivery address.- Optional
budgetId. - Optional
language.
Read and save the returned:
applicationIdcreateFeeshippingFeetotalFeeexpiresAt
Step 2: Create the card
Call POST /cards with:
channelId: The same product ID used in the quote.cardType=physical.cardOptions.cardHolderId: The same cardholder ID.cardOptions.callId: Your unique issuance identifier.cardOptions.fulfillmentMode=platform_delivery.cardOptions.physicalApplicationId: TheapplicationIdreturned by the quote.- The applicable budget information.
Omit amount and shippingAddress from the create request when the quoted application already contains the delivery information.
Successful creation charges the quoted fees and reserves the required stock for review according to the platform delivery process.
Step 3: Track the application
Use Get physical application with applicationId or callId to read review, shipment, and refund progress.
Rejection refunds the applicable fees and marks the card as rejected. Only after rejection may DELETE /cards be used to soft-delete the card.
Shipping information may include carrier, trackingNumber, and trackingUrl.
Physical Card Issuance Operations
| No. | Operation | Endpoint | Key Notes |
|---|---|---|---|
| 1 | Quote (platform delivery only) | Quote a physical card | Only for platform_delivery; pass cardChannelId, cardHolderId, and shippingAddress. Save applicationId, fees, and expiresAt. |
| 2 | Create card | POST /cards | Both modes use this endpoint with cardType=physical, cardOptions.fulfillmentMode, and cardOptions.callId. |
| 3 | Query card record | Get card list | Both modes support card lookup using cardId or callId. |
| 4 | Query application | Get physical application | Only for platform_delivery; pass exactly one of applicationId or callId. |
| 5 | Bind | Bind a physical card | After receiving the physical card and verifying end-user identity, submit encrypted cardId, cardNumber, operation, and timestamp. |
| 6 | Query operation | Get physical operation | Pass cardId and optional operationId; also receive card.physical.activation.code through Webhook. |
| 7 | Activate | Activate a physical card | After binding is confirmed and the activation code is received, let the user set a six-digit PIN and submit encrypted cardId, activationCode, pinCode, operation, and timestamp. |
| 8 | Confirm result | Card and operation queries | Poll until activation is complete and the card is active. Binding completion alone does not mean activation succeeded. |
Quote and Create Constraints
| Constraint | Description |
|---|---|
| Quote validity | 15 minutes from creation. |
| Quote side effects | No debit and no stock reservation. |
| Delivery scope | Supported countries and required address fields are returned as shippingRequirements by physical preparation. |
| Create idempotency | Retries of POST /cards must reuse the same callId and issuance identity. |
| Quote reference | For platform delivery, pass the quote's applicationId as physicalApplicationId. |
| Quote expiry before creation | If the quote expires before POST /cards is submitted, obtain a new quote and use a new callId for the new issuance attempt. |
| Uncertain create result | If POST /cards times out or its result is unknown, query using the original callId first. Do not immediately obtain a new quote or create another card. |
callId ownership | A claimed callId cannot be moved to another card or issuance identity. |
| Identifier scope | Virtual cards, physical cards, and soft-deleted cards share the account-level callId namespace. |
Platform-delivery application status values:
quoted, pending_review, awaiting_shipment, shipped, binding, bound, bind_failed, rejected.
Approval leads to shipment. Rejection refunds applicable fees and returns rejectionReason.
Shipping information may contain carrier, trackingNumber, and trackingUrl.
Binding and Activation
Binding Is Irreversible and Cannot Be ReassignedConfigure an eligible HTTPS activation-code Webhook recipient before binding.
Once binding succeeds, it cannot be undone or reassigned. If binding fails, do not automatically bind another card or automatically refund a dispatched card. Contact technical support.
An Activation Code Does Not Confirm BindingActivation codes are delivered through Webhook. The platform's Webhook delivery/retry window is up to 30 minutes. This describes the platform delivery mechanism only; it does not define the bank-issued activation code's validity period.
There is no activation-code retrieval endpoint or manual replay mechanism.
Queue completion does not guarantee successful Webhook delivery.
Verify the Webhook signature, deduplicate events using
event_id, and securely persist the activation code before returning HTTP 200 or 201.Keep
activationCodeas a string, including leading zeroes. Follow the bank's actual verification requirements for code validity.
PIN rules
- Exactly six digits.
- Cannot consist of six identical digits.
- Must not appear within the holder's phone number.
- Must not end with the card's last four digits.
Webhook responsibilities
| Event | Purpose |
|---|---|
card.physical.application.update | Platform-delivery application state updates. |
card.status.update | Card state updates. |
card.physical.activation.code | Physical card activation-code delivery. |
The Webhook envelope is:
{event_id, event_identifier, data}
Activation data may contain:
idorcardIdaccountIdactivationCodeapplicationIdfor platform-delivery application flows, when applicable
applicationId is not a universal field for all physical-card fulfillment modes. Merchant-stock cards do not have a platform-delivery application.
Encryption, Retries and Identifiers 🔐
Binding and activation reuse the existing CardDataEncryption protocol with a request body containing only encrypted, iv, and tag.
The key is derived from the authenticated API Key, not the API Secret and not an uploaded public key.
See ApiPhysicalBindPayload and ApiPhysicalActivatePayload for the plaintext schema.
| Parameter | Value |
|---|---|
| Password | Authenticated API Key, UTF-8; use the parent key for child-account operations. |
| Salt | Lowercase hexadecimal SHA256(API Key) immediately followed by dogpay-open-api-v1; pass the concatenation as UTF-8, not hex-decoded. |
| Key derivation | PBKDF2-HMAC-SHA256, 100000 iterations, 32 output bytes. |
| Cipher | AES-256-GCM with a fresh 16-byte random IV per request and no additional authenticated data. |
| Plaintext | Complete JSON object in UTF-8; PIN, card number, and activation code stay strings. |
| Request body | encrypted hex ciphertext; iv as 32 hex characters; tag as 16 bytes encoded as 32 hex characters. |
For either physical-card fulfillment mode:
- Bind uses
{operation: "bind", cardId, cardNumber, timestamp}. - Activate uses
{operation: "activate", cardId, activationCode, pinCode, timestamp}.
timestamp is Unix milliseconds generated immediately before encryption and must stay within ±2 seconds of server time.
A ciphertext is single-use. If the operation result is unclear, query the operation status before retrying. Never reuse the same ciphertext.
Retry and Query Rules
| Scenario | Handling |
|---|---|
| Retrying card creation | Reuse the same callId and issuance identity. |
| Create request times out | Query GET /cards using the original callId before retrying. |
| Quote expires before creation | Obtain a new quote and use a new callId for the new issuance attempt. |
| Create result is unknown | Do not assume failure or create a replacement card. Check the original callId first. |
| Reusing a ciphertext | Not allowed; re-encrypt with a new timestamp and fresh IV when a retry is appropriate. |
pending / unknown | Unresolved; poll the operation status instead of blindly resubmitting. |
completed | Confirms that operation only; query the card state separately. |
failed | Requires business or manual intervention. |
Query card by callId | Use GET /cards?callId=... for both virtual and physical card records. |
| Query delivery application | Use the physical application endpoint only for platform_delivery. |
Key Constraints 🚧
- Check the product's
issuancecapabilities before issuance. - For c_015, both virtual and physical cards use
flow=standard. - Physical card fulfillment is selected through
fulfillmentMode, not by changingflow. - Use
merchant_stockfor merchant-owned physical stock andplatform_deliveryfor platform-arranged delivery. - Complete preparation and confirm
isPrepared=truewhen the product requires preparation. - Create card uses
channelId; preparation and quote usecardChannelId. Both represent the same product ID. - Both physical fulfillment modes use
POST /cardsto create the card record. - Only platform delivery requires a quote and has a platform-delivery application.
- Use
GET /cards?callId=...to query card records for either physical fulfillment mode. - Use the physical application endpoint to track platform-delivery review, shipment, and refund progress.
applicationIdis specific to platform-delivery application flows; do not assume it exists for merchant-stock cards.- A successful
POST /cardscreates a card record but does not mean the physical card has been bound or activated. - Application or binding status
bounddoes not mean activation succeeded; continue querying card and operation results. - Activation codes arrive through Webhook. The 30-minute window refers to platform delivery/retry, not the bank-issued code's validity.
- Binding is irreversible. After failure, do not automatically bind another card or issue an automatic refund.
- For uncertain creation results, query the original
callIdbefore creating another card. isPrepared=truemeans preparation is ready; it does not guarantee successful card issuance.
Updated about 11 hours ago
