Browse the docs

Metadata

Attach your own key-value data to a Moon resource and read it back verbatim.

metadata is your own key-value data, stored on a Moon resource and handed back to you verbatim on every read.

Its purpose is to remove the lookup table you would otherwise keep. Store your order id on the card you issue for it, and every later read of that card — including the transaction webhook — tells you which order it belongs to, without a second system to reconcile.

JSON

{
  "cardholder_id": "3f8c6f2e-9c41-4a5f-9c73-0c9f4c0b7a11",
  "account_id": "6c1c9a37-9c1b-4f6c-9e79-8f8a2f0d5f31",
  "card_product_id": "8f1c611d-098d-4f61-b106-f7b6d344b1ae",
  "metadata": {
    "order_id": "ORD-90210",
    "customer_ref": "acct_2291",
    "campaign": "summer-launch"
  }
}

Where you can set it

ResourceSet when creatingChange later
Cardyesyes, PATCH /cards/{card_id}
Cardholderyesyes, PATCH /cardholders/{id}
Velocity controlyesyes, PATCH /controls/velocity/{id}
Accountyesno

Creating a card works on both surfaces: POST /v1/api-gateway/card/{card_product_id} and POST /cards.

Limits

Keys per resource50
Key length40 characters, and never empty
Value length500 characters
Value typeString

Values must be strings. A nested object, an array, a number or a boolean is rejected with a validation 400 — send "1500", not 1500, and serialize anything structured yourself.

Changing it

The map you send replaces the stored map in full. It is not merged key by key, which is the one thing worth reading twice:

You sendResult
Nothing — field omittedThe stored map is left exactly as it was
{"order_id": "ORD-1"}The map becomes only order_id; every other key is gone
{}The map is cleared
{"order_id": ""}order_id is removed, and any key you did not name is also gone

To add a key without losing the others, read the resource, add your key to the map you got back, and send the whole map:

cURL

curl --request PATCH \
     --url https://api.themooncard.com/v2/api-gateway/cards/24c96f22-0638-44ab-821f-336f9fde8f05 \
     --header 'content-type: application/json' \
     --header 'x-api-key: YOUR_API_KEY' \
     --data '{
  "metadata": {
    "order_id": "ORD-90210",
    "customer_ref": "acct_2291",
    "refund_case": "RC-77"
  }
}'

An empty-string value is how you delete a single key, and it is subject to the same rule: the keys you leave out still disappear.

Reading it

metadata is always an object on a response, never null and never absent. A resource you never set it on reads as {}, including resources created before the field existed — so card.metadata.order_id is safe to read without checking whether the map is there first.

Keys come back exactly as you sent them: same spelling, same case, same order-independent set.

In webhooks

The CARD_TRANSACTION and CARD_AUTHORIZATION_REFUND events carry the card's map at data.card.metadata, so a transaction can be matched to your own order without calling back for the card:

JSON

{
  "type": "CARD_TRANSACTION",
  "data": {
    "card": {
      "public_id": "24c96f22-0638-44ab-821f-336f9fde8f05",
      "metadata": { "order_id": "ORD-90210" }
    }
  }
}

The field is always present on those events. If it arrives empty for a card you know carries keys, read the card to get the current map.

What not to put in it

metadata is stored as you send it and is returned in full to anyone holding your API key, and it appears in the webhook payloads above. Keep cardholder personal data, card numbers, credentials and anything you would not want echoed back out of it — use the fields the API provides for those, which are handled accordingly.

  • Data formats and IDs — types and identifier rules for the rest of the request.
  • Errors — the validation envelope you get when a key or value is out of bounds.

Last updated August 24, 2026