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=standard is standard issuance through POST /cards. Both c_015 virtual and physical cards use this flow.

flow=application is 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 flow value.

Virtual Card Issuance 💳

GET /cards/bins
    → Create or reuse cardholder
    → Complete preparation if required
    → POST /cards
    → GET /cards

Call 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 Usable

A local cardId only 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 Limitation

Get card private info follows its existing protocol and declares no explicit permission yet; credentials in explicit_permissions mode 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 modeQuote requiredApplicationPhysical stock
merchant_stockNoNoMerchant-owned stock
platform_deliveryYesYesPlatform-arranged stock

Both modes use flow=standard for c_015 physical cards.

Merchant Stock: merchant_stock

Merchant 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

The 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:

  • applicationId
  • createFee
  • shippingFee
  • totalFee
  • expiresAt

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: The applicationId returned 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.OperationEndpointKey Notes
1Quote (platform delivery only)Quote a physical cardOnly for platform_delivery; pass cardChannelId, cardHolderId, and shippingAddress. Save applicationId, fees, and expiresAt.
2Create cardPOST /cardsBoth modes use this endpoint with cardType=physical, cardOptions.fulfillmentMode, and cardOptions.callId.
3Query card recordGet card listBoth modes support card lookup using cardId or callId.
4Query applicationGet physical applicationOnly for platform_delivery; pass exactly one of applicationId or callId.
5BindBind a physical cardAfter receiving the physical card and verifying end-user identity, submit encrypted cardId, cardNumber, operation, and timestamp.
6Query operationGet physical operationPass cardId and optional operationId; also receive card.physical.activation.code through Webhook.
7ActivateActivate a physical cardAfter 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.
8Confirm resultCard and operation queriesPoll until activation is complete and the card is active. Binding completion alone does not mean activation succeeded.

Quote and Create Constraints

ConstraintDescription
Quote validity15 minutes from creation.
Quote side effectsNo debit and no stock reservation.
Delivery scopeSupported countries and required address fields are returned as shippingRequirements by physical preparation.
Create idempotencyRetries of POST /cards must reuse the same callId and issuance identity.
Quote referenceFor platform delivery, pass the quote's applicationId as physicalApplicationId.
Quote expiry before creationIf the quote expires before POST /cards is submitted, obtain a new quote and use a new callId for the new issuance attempt.
Uncertain create resultIf 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 ownershipA claimed callId cannot be moved to another card or issuance identity.
Identifier scopeVirtual 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 Reassigned

Configure 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 Binding

Activation 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 activationCode as 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

EventPurpose
card.physical.application.updatePlatform-delivery application state updates.
card.status.updateCard state updates.
card.physical.activation.codePhysical card activation-code delivery.

The Webhook envelope is:

{event_id, event_identifier, data}

Activation data may contain:

  • id or cardId
  • accountId
  • activationCode
  • applicationId for 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.

ParameterValue
PasswordAuthenticated API Key, UTF-8; use the parent key for child-account operations.
SaltLowercase hexadecimal SHA256(API Key) immediately followed by dogpay-open-api-v1; pass the concatenation as UTF-8, not hex-decoded.
Key derivationPBKDF2-HMAC-SHA256, 100000 iterations, 32 output bytes.
CipherAES-256-GCM with a fresh 16-byte random IV per request and no additional authenticated data.
PlaintextComplete JSON object in UTF-8; PIN, card number, and activation code stay strings.
Request bodyencrypted 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

ScenarioHandling
Retrying card creationReuse the same callId and issuance identity.
Create request times outQuery GET /cards using the original callId before retrying.
Quote expires before creationObtain a new quote and use a new callId for the new issuance attempt.
Create result is unknownDo not assume failure or create a replacement card. Check the original callId first.
Reusing a ciphertextNot allowed; re-encrypt with a new timestamp and fresh IV when a retry is appropriate.
pending / unknownUnresolved; poll the operation status instead of blindly resubmitting.
completedConfirms that operation only; query the card state separately.
failedRequires business or manual intervention.
Query card by callIdUse GET /cards?callId=... for both virtual and physical card records.
Query delivery applicationUse the physical application endpoint only for platform_delivery.

Key Constraints 🚧

  • Check the product's issuance capabilities before issuance.
  • For c_015, both virtual and physical cards use flow=standard.
  • Physical card fulfillment is selected through fulfillmentMode, not by changing flow.
  • Use merchant_stock for merchant-owned physical stock and platform_delivery for platform-arranged delivery.
  • Complete preparation and confirm isPrepared=true when the product requires preparation.
  • Create card uses channelId; preparation and quote use cardChannelId. Both represent the same product ID.
  • Both physical fulfillment modes use POST /cards to 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.
  • applicationId is specific to platform-delivery application flows; do not assume it exists for merchant-stock cards.
  • A successful POST /cards creates a card record but does not mean the physical card has been bound or activated.
  • Application or binding status bound does 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 callId before creating another card.
  • isPrepared=true means preparation is ready; it does not guarantee successful card issuance.

Did this page help you?