Browse the docs

Create card by card product id

POST /v1/api-gateway/card/{card_product_id}

Issues a card from the given card product for end_customer_id. If no cardholder with that end_customer_id exists it is created under your organization. When amount is greater than 0 the card is issued and loaded with amount from your organization's prepaid balance (GET /moon-credit), so the returned balance already reflects it. Your prepaid balance is charged the card amount plus any card-creation fee agreed for the product, less any discount on your account; the fee is charged once, at creation — topping a card up later with POST /card/{card_id}/add-balance costs only the amount you add. Issuing the card, charging for it and loading it happen together: if any part fails, none of it is applied and you are not charged. Every eligibility and funding check (product enabled for you, card form factor, cardholder verification, prepaid balance, card_currency, product support, minimum) runs before the card is issued: a rejected request issues nothing and creates no cardholder. card_currency must match the currency of your prepaid balance.

Request

Authentication: your API key in the x-api-key header.

Path parameters

FieldTypeDescription
card_product_id requiredstring, uuidUUID of a card product enabled for your organization (GET /card/card-products).

Headers

FieldTypeDescription
Idempotency-KeystringA unique string you choose, up to 255 characters, that makes this request safe to retry. If a request times out or fails in transit, send it again with the same key: the status code and body of the first attempt are returned and the operation is not performed a second time. Stored replies — including failures — are replayed for 24 hours, after which the same key starts a fresh request. Reusing a key for a different request, or sending it while the first request is still running, returns 409. Omit the header and the request behaves exactly as before. Document upload endpoints do not accept it.

Body

FieldTypeDescription
amountnumberInitial balance to load onto the card from your organization's prepaid balance (GET /moon-credit), in major units (e.g. 25.50). Omit it, or send 0, to issue the card unfunded and load it later with POST /card/{card_id}/add-balance. Your prepaid balance must cover this amount plus the card-creation fee for the product, less any discount on your account (INSUFFICIENT_BALANCE); the card itself is always loaded with the full amount you asked for. The product must accept balance loads (CARD_PRODUCT_NOT_SUPPORTED), and products that are not reloadable have a minimum first load (AMOUNT_LESS_THAN_MINIMUM). The load is reflected in the returned balance. A card-creation fee, where one applies, is charged even when this is 0.
external_idstringYour identifier for the end customer who will hold the card. If no cardholder with this id exists in your top-level organization, one is created. Must not be blank. If your organization requires cardholder identity verification, an existing cardholder must be approved before a card can be issued to it (CARDHOLDER_KYC_NOT_APPROVED). Required — send it as external_id or as end_customer_id.
end_customer_idstringDeprecated alias of external_id, kept working for existing integrations. Send one or the other; sending both with different values is rejected.
card_currencystringCurrency of the card: USD (default) or MXN. Must match the currency of your organization's prepaid balance; any other value is rejected with INVALID_CARD_CURRENCY before the card is issued.One of: USD, MXN
card_typestringCard form factor: VIRTUAL (default) or PHYSICAL. PHYSICAL is only accepted if physical cards are enabled for your organization; otherwise the request is rejected with USER_NOT_ALLOWED_PHYSICAL_CARD before the card is issued.One of: VIRTUAL, PHYSICAL
metadataobjectYour own key-value data to store on this card. It is returned verbatim on every read of the card and never affects how the card is processed — use it to carry your order or customer identifier so you do not have to keep a separate mapping. Up to 50 keys, keys up to 40 characters and values up to 500 characters. Values must be strings — nested objects, arrays, numbers and booleans are rejected. On update the map you send replaces the stored map in full: omit the field to leave it unchanged, send {} to clear it, and send a key with an empty string value to remove just that key.

Example request

cURL

curl --request POST \
     --url 'https://api.themooncard.com/v1/api-gateway/card/a8358b18-be77-41b7-af4d-f4e9760cba86' \
     --header 'x-api-key: YOUR_API_KEY' \
     --header 'content-type: application/json' \
     --data '{
       "amount": 25.5,
       "external_id": "customer_123",
       "end_customer_id": "customer_123",
       "card_currency": "USD",
       "card_type": "VIRTUAL",
       "metadata": {
         "order_id": "ORD-10294"
       }
     }'

Responses

201 Success
FieldTypeDescription
id requiredstringCard id.
balance requiredstring, nullableCurrent balance, "0.00" style string, or null.
true_balance requiredstring, nullableBalance including pending holds, or null.
expiration requiredstring, date-time, nullableExpiration as an ISO-8601 timestamp, or null when unknown.
display_expiration requiredstringExpiration formatted for display, e.g. "12/29".
available_balance requiredstring, nullableSpendable balance, or null.
terminated requiredinteger1 when the card is terminated, otherwise 0.One of: 0, 1
card_product_id requiredstringCard product this card was issued from.
pan requiredstringFull card number.
cvv requiredstring, nullableCard verification value, or null when not available.
support_token requiredstringToken to reference this card in support requests.
frozen requiredinteger1 when the card is frozen, otherwise 0.One of: 0, 1
activated requiredinteger1 when the card is activated, otherwise 0.One of: 0, 1
metadata requiredobjectThe key-value data you stored on this card, returned exactly as you sent it. An empty object when you never set any — it is never null.

JSON

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "balance": "25.50",
  "true_balance": "25.50",
  "expiration": "2029-12-31T23:59:59Z",
  "display_expiration": "12/29",
  "available_balance": "25.50",
  "terminated": 0,
  "card_product_id": "a8358b18-be77-41b7-af4d-f4e9760cba86",
  "pan": "4111111111111111",
  "cvv": "123",
  "support_token": "ST-8QK2-4M7D",
  "frozen": 0,
  "activated": 1,
  "metadata": {
    "order_id": "ORD-10294"
  }
}
400 Error

The body is the standard error object: see Errors.

401 Error

Missing or invalid credentials, e.g. { "message": "Invalid API Key", "code": "UNAUTHORIZED" }.

The body is the standard error object: see Errors.

403 Error

A card-issuance limit for your organization or this product has been reached.

The body is the standard error object: see Errors.

404 Error

Your organization, account or the cardholder could not be found (ORGANIZATION_NOT_FOUND, ACCOUNT_NOT_FOUND, CARDHOLDER_NOT_FOUND), the card product no longer exists (CARD_PRODUCT_NOT_FOUND), or the issued card has no ledger account to load (CARD_LEDGER_ACCOUNT_NOT_FOUND).

The body is the standard error object: see Errors.

409 Error

The Idempotency-Key you sent is already in use. IDEMPOTENCY_KEY_REUSED — the key was used for a different request; send a new key, or repeat the original request exactly to receive its stored response. IDEMPOTENCY_KEY_IN_PROGRESS — the first request with this key has not finished yet; retry in a few seconds.

The body is the standard error object: see Errors.

500 Error

Unexpected server error. Body is { message: "Internal server error", code: "INTERNAL_SERVER_ERROR" }.

The body is the standard error object: see Errors.

Last updated April 30, 2026