Browse the docs

Data Formats and IDs

Units, types, timestamps and which identifier each endpoint expects.

This is the reference for how values are represented across the API: amounts and units, field types, timestamps, currencies and networks, and identifiers.

Amounts and units

Every amount is in major units of the currency: dollars, not cents. 25.50 means twenty-five dollars and fifty cents. That holds in both directions.

Amounts you send

Send a JSON number. A numeric string is accepted and read as the same value, so these are equivalent:

JSON

{ "amount": 100 }

JSON

{ "amount": "100.00" }

Amounts you read

A response carries an amount in one of two forms, fixed per field:

Each endpoint's reference page gives the form for the fields it returns; read a field as the type shown there rather than inferring it from a sibling.

A field carrying no amount, such as a card that has never been funded or an optional product limit, is null rather than a zero.

A zero balance is "0.00". That string is truthy in JavaScript, so a plain if (card.balance) is true for an empty card. Compare the parsed number:

Node.js

if (Number(card.balance) > 0) {
  // the card has funds
}

_no_rounding fields

On a card-transaction entry, each monetary field appears twice: a rounded value for display, and a _no_rounding companion carrying the same amount at full precision for arithmetic and reconciliation. The rounded field is a JSON number and its full-precision companion is a decimal string.

Rounded — numberFull precision — string
amountamount_no_rounding
amount_fees_in_ledger_currencyamount_fees_in_ledger_currency_no_rounding
amount_in_transaction_currencyamount_in_transaction_currency_no_rounding
amount_fees_in_transaction_currencyamount_fees_in_transaction_currency_no_rounding
fees[].amountfees[].amount_no_rounding

amount is expressed in the card's currency (ledger_currency); amount_in_transaction_currency is the amount the merchant charged, in transaction_currency. See Fetching card transactions for the entry types these fields belong to.

Flags

Flags you send are JSON booleans. {"frozen": true} freezes a card, and the query-string flags include_inactive_cards, inactive_only, include_deleted and include_loads take true or false.

Flags you read come in one of two forms, fixed per field:

FormWhereMeaning
1 / 0Card frozen, terminated, activated; velocity control enabled1 is on, 0 is off

A card frozen with {"frozen": true} therefore reads back as "frozen": 1. A flag with no value set is 0, not null.

If your language distinguishes the two — Go, Java, C# and typed TypeScript models do — declare the card and velocity flags as integers and compare against 1.

Timestamps and dates

Timestamps you read are ISO-8601 in UTC, with milliseconds: "2030-11-27T19:21:39.000Z". That is the form of expiration on a card, created_at and updated_at on every resource, and expires_at on a KYC link.

Card-transaction entries carry the instant twice, and both are the same moment: datetime in that millisecond form, and datetime_iso to the second, "2026-08-16T17:51:01Z".

display_expiration on a card is not a timestamp: it is the card's expiry rendered as MM/YY for printing on the card face, for example "12/29". Use expiration for logic and display_expiration for display.

Dates you send are calendar days. GET /v2/api-gateway/accounts/{id}/transactions takes start_date and end_date as inclusive UTC days in YYYY-MM-DD form, and accepts them as a pair.

cURL

curl --request GET \
     --url 'https://api.themooncard.com/v2/api-gateway/accounts/6c1c9a37-9c1b-4f6c-9e79-8f8a2f0d5f31/transactions?start_date=2026-08-01&end_date=2026-08-16' \
     --header 'accept: application/json' \
     --header 'x-api-key: YOUR_API_KEY'

Currencies, assets and networks

Fiat currencies are ISO-4217 codes. Where a field is open — transaction_currency on a simulated transaction, currency on an account or card product, ledger_currency on a transaction — send or expect the standard three-letter code.

Where the API constrains the choice, these are the accepted values:

FieldAccepted values
card_currency (V1 create card)USD (default), MXN
account_currency (V2 create account)USD (default), MXN

merchant_country_code on a simulated transaction is an ISO-3166 alpha-2 code, for example US.

Identifiers

There are two families of identifier: the one you choose for a cardholder, and the ones Moon assigns.

Your identifier for a cardholder

external_id is your own identifier for a cardholder. It is a free-form string — customer_123 is fine — unique within your organization. V1 request bodies and query strings accept external_id as an alias for the same value.

EndpointTakes
POST /v1/api-gateway/card/{card_product_id}your cardholder identifier in the body
GET /v1/api-gateway/card, GET /v2/api-gateway/cardsyour cardholder identifier as a filter
GET /v1/api-gateway/cardholder/{end_customer_id}/kycyour cardholder identifier in the path
POST /v1/api-gateway/cardholder/{end_customer_id}/whitelabel-kycyour cardholder identifier in the path
PATCH /v1/api-gateway/end-customer/{id}your cardholder identifier in the path
GET /controls/velocity/cardholder/{external_id}/status (V1 and V2)your cardholder identifier in the path
POST /v2/api-gateway/cardholdersexternal_id in the body
POST /controls/velocity (V1 and V2)external_id in the body

Moon's identifiers

Moon assigns these and returns them to you. Store the ones you will need again.

IdentifierFormatWhere it comes fromWhere it is used
Card idUUIDid on any card object{card_id} on every card route, card_id on velocity controls
Cardholder idUUIDid on a cardholder object{id} on the V2 cardholder routes, {cardholder_id} on V1 check and verification-documents, cardholder_id in create-card and velocity-control bodies
Organization idUUIDid from GET /v2/api-gateway/organizations{id} on the organization routes, organization_id on create account, create cardholder, list filters
Transaction idUUID on card-purchase, DECLINE and TOP_UP entries; a number on REFUND, MOON_CREDIT_PURCHASE and DELETION entriesdata.id of a card-transaction entry{transaction_id} on the get-transaction-by-id routes, which take the UUID form
Velocity control idopaque stringid from GET /controls/velocity{id} on the velocity-control routes; send it back verbatim

Card id and transaction id

GET /v1/api-gateway/card/{card_id}/transactions/{transaction_id} takes both, in that order. card_id is the card's id. transaction_id is data.id of an entry in that card's feed; data.transaction_id is the network's own reference on card-purchase entries. The V2 route GET /cards/{card_id}/transactions/{transaction_id} follows the same rule.

Fields for Moon's internal reference

user_sub, ledger_account_id and the numeric card_id inside transaction entries are Moon's internal references and are documented as such in the reference. Identify a card by card.public_id or card_public_id, and a transaction entry by data.id.

Id validation

Path parameters typed as UUIDs are validated before the lookup runs, so an id that is not a well-formed UUID returns a validation 400:

JSON

{
  "errors": [
    { "msg": "Invalid uuid", "param": "cardId", "location": "query" }
  ]
}

A well-formed UUID that does not exist, or that belongs to another organization, returns a 404. An unknown id used as a list filter returns 200 with an empty list — see Pagination. Full error rules are on Errors.

Last updated September 25, 2026