Browse the docs

Fetching Card Transactions

GET /v1/api-gateway/card/{card_id}/transactions returns the activity feed of one card: purchases, refunds, declines, the card's own creation and deletion, and — on request — balance loads. It is the record you reconcile against; webhooks are how you learn about the same events as they happen.

Request

cURL

curl --request GET \
     --url 'https://api.themooncard.com/v1/api-gateway/card/7c9e6679-7425-40de-944b-e07fc1f90ae7/transactions?current_page=1&per_page=10' \
     --header 'accept: application/json' \
     --header 'x-api-key: YOUR_API_KEY'
ParameterInDefaultNotes
card_idpath—The card's id (a UUID), as returned by the card endpoints.
current_pagequery1Page number, starting at 1.
per_pagequery10Items per page. Minimum 1, maximum 50.
include_loadsqueryfalseSet to true to include balance loads to this card as TOP_UP entries.

This endpoint returns 200 with an empty transactions array for a card_id outside your organization. To confirm a card exists, fetch it with GET /v1/api-gateway/card/{card_id}, which returns 404 for an id you cannot see.

Response

JSON

HTTP/1.1 200 OK

{
  "transactions": [
    {
      "type": "CARD_TRANSACTION",
      "data": {
        "id": "500539aa-d323-4fe7-9162-8d6123515ec5",
        "card": {
          "public_id": "3f0c8a12-5b6d-4e7f-9a01-2b3c4d5e6f70",
          "name": "Marketing spend",
          "type": "Moon 1X Card",
          "last_four": "4242"
        },
        "transaction_id": "500539aa-d323-4fe7-9162-8d6123515ec5",
        "transaction_status": "SETTLED",
        "datetime": "2026-08-16T17:51:01Z",
        "datetime_iso": "2026-08-16T17:51:01Z",
        "merchant": "Starbucks",
        "merchant_mid": "mid_123",
        "amount": 14.85,
        "amount_no_rounding": "14.85",
        "ledger_currency": "USD",
        "amount_fees_in_ledger_currency": 0.15,
        "amount_fees_in_ledger_currency_no_rounding": "0.1485",
        "amount_in_transaction_currency": 85,
        "amount_in_transaction_currency_no_rounding": "85",
        "transaction_currency": "BRL",
        "amount_fees_in_transaction_currency": 0.85,
        "amount_fees_in_transaction_currency_no_rounding": "0.85",
        "fees": [
          {
            "type": "TRANSACTION_FEE",
            "amount": 0.15,
            "amount_no_rounding": "0.1485",
            "fee_amount_in_transaction_currency": 0.85,
            "fee_description": "A 1.00% fee (minimum of $0.00) is charged on all transactions"
          }
        ]
      }
    }
  ],
  "pagination": {
    "total": 42,
    "last_page": 5,
    "prev_page": null,
    "next_page": 2,
    "per_page": 10,
    "current_page": 1,
    "from": 1,
    "to": 10
  }
}

pagination is the same envelope every list endpoint returns: 1-based current_page, prev_page and next_page null at the ends, and from/to both 0 when the page is empty. See Pagination.

The transactions array is a union

Each entry is { "type": ..., "data": ... }, and type tells you which data shape to expect. Branch on type before reading anything else, and ignore entries whose type you do not recognise — the feed can gain new kinds of activity.

typeWhat it isShape of data
CARD_TRANSACTIONA purchase, at any point in its lifecycle.The full transaction shape shown above.
CARD_AUTHORIZATION_REFUNDA refund of a card purchase.The same shape as CARD_TRANSACTION.
DECLINEA declined authorization.id, datetime, merchant, merchant_mid, amount, customer_friendly_description, card_public_id, card_id, card.
DELETIONThe card being deleted, with any amount returned.id, datetime, merchant, amount, card_id, refunded_amount_usd.
TOP_UPA balance load onto this card. Only present with include_loads=true.id, created_at, user_sub, merchant_name, amount, datetime, card_id, card_public_id.

Two mappings to hold on to:

  • A decline appears in the feed as type: "DECLINE". The webhook for the same event is CARD_DECLINE.
  • card_id and user_sub are Moon's internal references wherever they appear above. Identify the card by card.public_id or card_public_id, and the entry by data.id.

A declined entry looks like this:

JSON

{
  "type": "DECLINE",
  "data": {
    "id": "8c2f1b45-6a7d-4e83-9012-3f4a5b6c7d8e",
    "datetime": "2026-08-16T18:02:44Z",
    "merchant": "ONLINE STORE",
    "merchant_mid": "mid_456",
    "amount": 29.99,
    "customer_friendly_description": "Insufficient Funds",
    "card_public_id": "3f0c8a12-5b6d-4e7f-9a01-2b3c4d5e6f70",
    "card_id": 918273,
    "card": {
      "public_id": "3f0c8a12-5b6d-4e7f-9a01-2b3c4d5e6f70",
      "name": "Marketing spend",
      "type": "Moon 1X Card",
      "last_four": "4242"
    }
  }
}

Amounts, fees and currencies

ledger_currency is the card's own currency; transaction_currency is the currency the merchant charged in. When they differ, the amount was converted at the rate in force at the time. Both are ISO 4217 codes.

  • amount and amount_in_transaction_currency are the principal excluding fees, rounded for display.
  • amount_fees_in_ledger_currency and amount_fees_in_transaction_currency are the total fees for this entry in each currency.
  • Every field ending in _no_rounding is the same value as a full-precision string. Use those for arithmetic and reconciliation; use the numbers for display.

fees breaks the total down, one object per fee line:

FieldTypeNotes
typestringTRANSACTION_FEE.
amountnumberThe fee in the ledger currency, rounded to that currency's decimal places.
amount_no_roundingstringThe same fee at full precision.
fee_amount_in_transaction_currencynumber | nullThe fee in the transaction currency, or null when it cannot be expressed there.
fee_descriptionstringHuman-readable explanation of the fee.

sum(fees[].amount) equals amount_fees_in_ledger_currency exactly: the total is the sum of the rounded lines, not a separately rounded figure. When the rounded total is zero — a small refund whose proportional fee lands below one cent, for instance — fees is an empty array while the _no_rounding fields still carry the real figure.

Timestamps

Both timestamp fields are ISO-8601 UTC, in two precisions:

  • datetime_iso — 2026-08-16T17:51:01Z. Read this one.
  • datetime — 2026-08-16T17:51:01.000Z, the same instant with milliseconds.

created_at on the MOON_CREDIT_PURCHASE and TOP_UP entries has the same millisecond form as datetime. See Data formats and ids.

Fetching a single transaction

GET /v1/api-gateway/card/{card_id}/transactions/{transaction_id} (reference) returns one entry in the same { type, data } form, with no list or pagination envelope around it. transaction_id is the data.id of an entry in the feed.

It answers 404 with CARD_NOT_FOUND when the card is unknown or not yours, and TRANSACTION_NOT_FOUND when the transaction is not on that card.

In V2

GET /v2/api-gateway/cards/{card_id}/transactions (reference) returns the identical envelope and the identical union, and takes the same current_page, per_page and include_loads. Two differences:

  • It also accepts a cardholder session token. With a session token the card must belong to that cardholder; if it does not, the response is 404 rather than an empty list. With an API key the empty-list behaviour is the same as V1. See Authentication.
  • Its single-transaction sibling is GET /v2/api-gateway/cards/{card_id}/transactions/{transaction_id}.

V2 adds GET /v2/api-gateway/accounts/{id}/transactions (reference), which returns the same union for every card on an account plus account-level credits, and adds three filters — start_date, end_date (inclusive UTC calendar days, YYYY-MM-DD, valid only together) and transactionType. Those keys are camelCase as well. transactionType selects by activity type (CARD_PURCHASE, CARD_DECLINE, CARD_AUTHORIZATION_REFUND, CARD_CREATION, DELETION, MOON_CREDIT_PURCHASE, TOP_UP); the entries come back with the type values in the union table above, so transactionType=CARD_DECLINE selects entries whose type is DECLINE.

See API versions for the full V1 ↔ V2 map.

Testing this in Sandbox

A new card's feed is empty. Generate activity first with the simulate endpoint, then read it back here — the entries and the webhooks you receive describe the same events.

Last updated August 17, 2026