Issuing Your First Card
This page will help you get started with Moon's Card Issuing API
This walkthrough issues a virtual card in three requests: list the card products you can issue from, create a card for one of your customers, then read the card back. It uses the V1 API; the V2 equivalents are listed at the end.
Every request carries your API key in the x-api-key header. See Authentication and Environments.
1. List the card products you can issue
Card products are the templates cards are issued from. The list contains only the products enabled for your organization, one page at a time: current_ (default 1) and per_ (default 10) choose the page, and a pagination object beside card_ tells you how many products there are and whether another page follows.
cURL
curl --request GET \
--url 'https://api.themooncard.com/v1/api-gateway/card/card-products?current_page=1&per_page=10' \
--header 'accept: application/json' \
--header 'x-api-key: YOUR_API_KEY'Take the id of the product you want to issue from; it is the card_ in the next request. minimum_ / maximum_ are the smallest and largest amounts that can be loaded onto a card of that product (null when unrestricted); denominations lists fixed load amounts when the product only allows those.
Keep requesting pagination.next_ until it is null to walk the whole catalogue. A page past the end returns an empty card_ list, still with the correct total.
Reference: List card products.
2. Issue a card
Create the card with POST /v1/api-gateway/card/{card_. The request body takes:
| Field | Required | Description |
|---|---|---|
end_ | yes | Your own identifier for the customer who will hold the card. Must not be blank. If no cardholder with this id exists in your organization yet, one is created for you. |
amount | no | Initial balance to load onto the card, in major units (25.50). Taken from your prepaid balance. Omit it, or send 0, to issue the card unfunded. |
card_ | no | USD (default) or MXN. Must match the currency of your prepaid balance. |
card_ | no | VIRTUAL (default) or PHYSICAL. |
What the card costs you
Your Prepaid balance — the balance on your account that is not yet allocated to any card — pays for the card. Issuing one debits it by amount, plus any card-creation fee agreed for that product, less any discount on your account. The fee is charged once, when the card is created; where a fee is a flat amount or has a floor, it applies even when you issue the card unfunded. Topping a card up later with POST /card/{card_ 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: no card is issued and nothing is debited.
Your organization must already hold a prepaid balance in the currency you ask for, whether or not you send an amount — see Adding a balance for how to fund it.
Before the card is issued
Every check below runs before anything is created, so a rejected request issues no card and leaves no cardholder behind:
card_requires physical cards to be enabled for your organization;type: "PHYSICAL" - if your organization requires cardholder identity verification, a cardholder that already exists for this
end_must be approved first. A first-timecustomer_ id end_is not gated — there is nothing to verify yet;customer_ id card_must match the currency of your prepaid balance;currency - a positive
amountrequires a product that accepts balance loads, must clear the minimum first load (5 USD in production, 1 USD in Sandbox), and must be covered — together with the fee — by your available prepaid balance.
cURL
curl --request POST \
--url https://api.themooncard.com/v1/api-gateway/card/8f1c611d-098d-4f61-b106-f7b6d344b1ae \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '{
"end_customer_id": "customer_123",
"card_type": "VIRTUAL"
}'The response is 201 Created with the card object itself (no wrapper), in the same shape as GET /v1/api-gateway/card/{card_:
The request above sent no amount, so the card starts empty. Had it sent one, balance and available_ on this response would already show the loaded amount.
A few things to know about this object:
idis the card id you use on every other card endpoint. Store it.expirationis an ISO-8601 UTC timestamp;display_is theexpiration MM/YYform to print on the card.panandcvvare the full card credentials — treat them as sensitive data.
Reference: Create card by card product id.
Errors you may see on the first call
Errors return { "message", "code" }; validation failures return { "errors": [ { "msg", "param", "location" } ] }. Branch on the HTTP status first, then on code. See Errors.
| Status | code | When |
|---|---|---|
| 400 | — (errors[]) | end_ is missing from the body: {"errors":[{"msg":"Invalid value","param":"end_. Also returned when amount is negative or not a number, or card_ is neither USD nor MXN. |
| 400 | END_ | end_ is present but blank ("" or only spaces). |
| 400 | CARD_ | The card_ in the path is not enabled for your organization, or you sent an amount for a product that does not accept balance loads. Use an id from step 1. |
| 400 | USER_ | card_ was sent and physical cards are not enabled for your organization. |
| 400 | CARDHOLDER_ | Your organization requires cardholder identity verification and the existing cardholder for this end_ is not approved. |
| 400 | USER_ | Your organization has no prepaid balance yet. Fund it first — see Adding a balance. |
| 400 | INVALID_ | card_ differs from the currency of your prepaid balance. |
| 400 | AMOUNT_ | amount is below the minimum first load — 5 USD in production, 1 USD in Sandbox. Does not apply to reloadable products. |
| 400 | INSUFFICIENT_ | Your available prepaid balance does not cover amount plus the card-creation fee. message states both figures. |
| 401 | UNAUTHORIZED | Missing or invalid x-api-key — or, with a valid key and the message Please contact Moon support., card issuance is not enabled for your account yet. |
| 403 | HTTP_ | A card-issuance limit for your organization or this product has been reached; the message explains which one. Contact support to raise it. |
| 404 | CARDHOLDER_ | The cardholder for end_ could not be resolved in your organization. |
JSON
{
"message": "Card product 9798d4d8-c1ea-4a52-820f-ba98af5e478f does not support this operation",
"code": "CARD_PRODUCT_NOT_SUPPORTED"
}3. Read the card back
Fetch the card at any time with its id.
cURL
curl --request GET \
--url https://api.themooncard.com/v1/api-gateway/card/8d2cd518-5ef2-4280-91e8-63b1a0e672e7 \
--header 'accept: application/json' \
--header 'x-api-key: YOUR_API_KEY'An unknown id, or a card that belongs to another organization, returns 404 with code: "CARD_.
Reference: Get card by id.
Next: fund the card
The card issued above holds 0.00, because the request sent no amount. To load money onto it — or to build up the prepaid balance that both an initial amount and later top-ups are paid from — continue with Adding a balance.
In V2
The V2 API models the same flow with explicit organizations, accounts and cardholders: you create the cardholder and the account first, fund the account, then issue the card against it. Quickstart (V2) walks through it request by request.
See API versions for how the two versions relate.
Last updated August 17, 2026