Browse the docs

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.

V1V2
Base URL (Production)https://api.themooncard.com/v1/api-gatewayhttps://api.themooncard.com/v2/api-gateway
Authenticationx-api-keyx-api-key; cardholder tokens accepted on most card routes
Cardholder identityYour own external_id; cardholders are created implicitly when you issue a cardMoon cardholder id (UUID); external_id holds the same value V1 calls external_id
Card fundingCard-level: load a card from the prepaid balance when you issue it, and top it up afterwards with add balanceAccount-level: cards are issued against an account and draw on its balance
Organizations, accounts, cardholders as resourcesNoYes

V1

The simplest way to issue and manage cards. You reference cardholders by an external_id 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_id and account_id (a missing id returns 400), plus the card_product_id 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.

ResourceV1V2
Create cardPOST /card/{card_product_id} — body external_id, amount, card_currency, card_typePOST /cards — body cardholder_id, account_id, card_product_id, card_type
List cards / get cardGET /card, GET /card/{card_id}GET /cards, GET /cards/{card_id}
Freeze / unfreezePATCH /card/{card_id}/freeze → { message, frozen }PATCH /cards/{card_id} → { message }
Delete cardDELETE /card/{card_id} → 200 { message }DELETE /cards/{card_id} → 200 { message }
Activate card, assign cardholderPOST /card/{card_id}/activate, POST /card/{card_id}/assign-cardholderPOST /cards/{card_id}/activate, POST /cards/{card_id}/assign-cardholder
Card transactionsGET /card/{card_id}/transactions, …/transactions/{transaction_id}GET /cards/{card_id}/transactions, …/transactions/{transaction_id}
Add balance to card / move balance to the prepaid balancePOST /card/{card_id}/add-balance, POST /card/{card_id}/move-balance— (fund the account)
Card productsGET /card/card-products — paginated with current_page / per_page— (use the V1 endpoint; V2 POST /cards takes the same card_product_id)
Cardholders— (implicit via external_id)POST /cardholders, GET /cardholders, GET /cardholders/{id}, PATCH /cardholders/{id}, GET /cardholders/{id}/cards
Cardholder KYCPOST /cardholder/kyc (start), GET /cardholder/{end_customer_id}/kyc, POST /cardholder/{cardholder_id}/verification-documents, POST /cardholder/{cardholder_id}/checkPOST /cardholders/{id}/kyc-link, GET /cardholders/{id}/kyc, POST /cardholders/{id}/verification-documents, POST /cardholders/{id}/check
Hosted verification linksPOST /cardholder/{end_customer_id}/whitelabel-kyc, POST /organization/{organization_id}/whitelabel-kyb—
End-customer KYC status updatePATCH /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 balanceGET /moon-reserveGET /moon-reserve
Velocity controlsPOST /controls/velocity, GET /controls/velocity, GET/PATCH/DELETE /controls/velocity/{id}, GET /controls/velocity/{id}/status, GET /controls/velocity/card/{card_id}/status, GET /controls/velocity/cardholder/{external_id}/statusSame paths, plus GET /controls/velocity/status — the spending status of the cardholder whose session token is used
WebhooksPOST /webhook, DELETE /webhook, POST /webhook/signing-secret/rotatePOST /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 checkGET /healthcheckGET /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_id in the body, and can load the card in the same request with amount (paid from the prepaid balance, in card_currency). V2 takes card_product_id, cardholder_id and account_id in the body, has no amount, and requires the cardholder and account to exist already. Create the cardholder with POST /cardholders (setting external_id to the value you used as external_id), create or pick an account, fund it, then issue.
  • 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) and GET /cards (V2) accept external_id; V2 also accepts card_holder_id.
  • Cardholder tokens. V2 card routes (create, list, get, activate, transactions) accept a cardholder's token from POST /cardholders/auth/login in place of your API key, scoped to that cardholder's cards. GET /controls/velocity/status goes further: it requires a cardholder token and rejects an API key with 403.

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_id.

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