API Versions (V1 vs V2)
Choosing between the V1 and V2 API.
API versions (V1 and V2)
Moon exposes two API versions side by side. Both are active, both authenticate with the same x-api-key, and both operate on the same organization, cardholders and cards. They differ in how you identify cardholders, how cards are funded, and in which resources they expose. Neither is a superset of the other — check the map below before choosing.
| V1 | V2 | |
|---|---|---|
| Base URL (Production) | https://api.themooncard.com/v1/api-gateway | https://api.themooncard.com/v2/api-gateway |
| Authentication | x-api-key | x-api-key; cardholder tokens accepted on most card routes |
| Cardholder identity | Your own external_; cardholders are created implicitly when you issue a card | Moon cardholder id (UUID); external_ holds the same value V1 calls external_ |
| Card funding | Card-level: load a card from the prepaid balance when you issue it, and top it up afterwards with add balance | Account-level: cards are issued against an account and draw on its balance |
| Organizations, accounts, cardholders as resources | No | Yes |
V1
The simplest way to issue and manage cards. You reference cardholders by an external_ you choose; if no cardholder with that id exists when you issue a card, one is created under your organization. Cards are funded from your prepaid balance one card at a time. The Issuing your first card guide uses V1.
V2
A richer model for larger integrations. V2 adds first-class organizations (with children), accounts that hold balances, cardholders as a resource you create, list and update, KYC/KYB status and verification links per cardholder or organization, and cardholder login, which gives a cardholder a token to read and manage their own cards directly.
In V2, POST /cards requires both cardholder_ and account_ (a missing id returns 400), plus the card_ to issue from.
Only V2 has: organizations, accounts and transfers, cardholder create/list/update, GET /webhooks to read the registered URL, cardholder login, and GET /controls/velocity/status, which lets a logged-in cardholder read their own spending status.
Endpoint map
Paths are relative to each version's base URL. "—" means the version has no equivalent.
| Resource | V1 | V2 |
|---|---|---|
| Create card | POST /card/{card_ — body external_, amount, card_, card_ | POST /cards — body cardholder_, account_, card_, card_ |
| List cards / get card | GET /card, GET /card/{card_ | GET /cards, GET /cards/{card_ |
| Freeze / unfreeze | PATCH /card/{card_ → { message, frozen } | PATCH /cards/{card_ → { message } |
| Delete card | DELETE /card/{card_ → 200 { message } | DELETE /cards/{card_ → 200 { message } |
| Activate card, assign cardholder | POST /card/{card_, POST /card/{card_ | POST /cards/{card_, POST /cards/{card_ |
| Card transactions | GET /card/{card_, …/transactions/{transaction_ | GET /cards/{card_, …/transactions/{transaction_ |
| Add balance to card / move balance to the prepaid balance | POST /card/{card_, POST /card/{card_ | — (fund the account) |
| Card products | GET /card/card-products — paginated with current_ / per_ | — (use the V1 endpoint; V2 POST /cards takes the same card_) |
| Cardholders | — (implicit via external_) | POST /cardholders, GET /cardholders, GET /cardholders/{id}, PATCH /cardholders/{id}, GET /cardholders/{id}/cards |
| Cardholder KYC | POST /cardholder/kyc (start), GET /cardholder/{end_, POST /cardholder/{cardholder_, POST /cardholder/{cardholder_ | POST /cardholders/{id}/kyc-link, GET /cardholders/{id}/kyc, POST /cardholders/{id}/verification-documents, POST /cardholders/{id}/check |
| Hosted verification links | POST /cardholder/{end_, POST /organization/{organization_ | — |
| End-customer KYC status update | PATCH /end-customer/{id} | — |
| Cardholder login | — | POST /cardholders/auth/login (no API key) |
| Organizations and KYB | — | POST /organizations, GET /organizations, GET /organizations/main, GET /organizations/{id}, PATCH /organizations/{id}, GET /organizations/{id}/children, GET /organizations/{id}/kyb, POST /organizations/{id}/kyb-link, POST /organizations/{id}/verification-documents, POST /organizations/{id}/check |
| Accounts | — | POST /accounts, GET /accounts, GET /accounts/{id}, GET /accounts/{id}/cards, GET /accounts/{id}/transactions, POST /accounts/transfer |
| prepaid balance | GET /moon-reserve | GET /moon-reserve |
| Velocity controls | POST /controls/velocity, GET /controls/velocity, GET/PATCH/DELETE /controls/velocity/{id}, GET /controls/velocity/{id}/status, GET /controls/velocity/card/{card_, GET /controls/velocity/cardholder/{external_ | Same paths, plus GET /controls/velocity/status — the spending status of the cardholder whose session token is used |
| Webhooks | POST /webhook, DELETE /webhook, POST /webhook/signing-secret/rotate | POST /webhooks, GET /webhooks, DELETE /webhooks, POST /webhooks/signing-secret/rotate |
| Card activation with a one-time code (no API key) | GET /v1/public/2fa/session/id, POST /v1/public/2fa/card/activate/otp, POST /v1/public/2fa/card/activate (absolute paths) | — |
| Health check | GET /healthcheck | GET /healthcheck |
Webhook events (including JIT authorization requests) are delivered to the one URL registered for your organization, whichever version you registered it with — see Webhooks.
Differences to plan for when moving from V1 to V2
- Creating a card. V1 takes the card product in the path and
external_in the body, and can load the card in the same request withid amount(paid from the prepaid balance, incard_). V2 takescurrency card_,product_ id cardholder_andid account_in the body, has noid amount, and requires the cardholder and account to exist already. Create the cardholder withPOST /cardholders(settingexternal_to the value you used asid external_), create or pick an account, fund it, then issue.id - Card object. Identical fields in both versions, with one addition in V2:
card_.type - Freeze response. V1 returns
{ message, frozen }; V2 returns{ message }only. Fetch the card if you need the new state. - Filtering cards by your own id. Both
GET /card(V1) andGET /cards(V2) acceptexternal_; V2 also acceptsid card_.holder_ id - Cardholder tokens. V2 card routes (create, list, get, activate, transactions) accept a cardholder's token from
POST /cardholders/auth/loginin place of your API key, scoped to that cardholder's cards.GET /controls/velocity/statusgoes further: it requires a cardholder token and rejects an API key with403.
You do not have to migrate everything at once: a card issued through V1 is visible through V2 GET /cards, and a cardholder created implicitly by V1 is the same record V2 returns from GET /cardholders, with your id in external_.
Which should I use?
- Use V2 if you need organizations, accounts, cardholders as first-class resources, KYC/KYB per cardholder or organization, or want cardholders to log in and manage their own cards.
If you are unsure, contact our team.
How changes are communicated
- Breaking changes are introduced as a new version rather than by changing an existing one. Additive changes — new endpoints, new optional fields, new enum values — can appear in either version at any time, so parse responses leniently and ignore fields you do not know.
- Response fields are spelled one way only, in
snake_.case - This page and the affected endpoint pages are updated when behaviour changes. Dated entries for each customer-visible change are on Versioning and changelog.
Last updated August 17, 2026