Browse the docs

Create cardholder

POST /v2/api-gateway/cardholders

Creates a cardholder identified by your external_id. The cardholder is placed in organization_id when it is one of your organizations; otherwise it is created under your top-level organization.

Request

Authentication: your API key in the x-api-key header.

Headers

FieldTypeDescription
Idempotency-KeystringA unique string you choose, up to 255 characters, that makes this request safe to retry. If a request times out or fails in transit, send it again with the same key: the status code and body of the first attempt are returned and the operation is not performed a second time. Stored replies — including failures — are replayed for 24 hours, after which the same key starts a fresh request. Reusing a key for a different request, or sending it while the first request is still running, returns 409. Omit the header and the request behaves exactly as before. Document upload endpoints do not accept it.

Body

FieldTypeDescription
external_idstringYour identifier for the cardholder. Must be unique within the organization; must not be empty. Required — send it as external_id or as end_customer_id.
end_customer_idstringDeprecated alias of external_id, kept working for existing integrations. Send one or the other; sending both with different values is rejected.
emailstring, emailThe cardholder's email address. Used by POST /cardholders/auth/login to identify the cardholder and to filter GET /cardholders?email=.
organization_idstring, uuidUUID of one of your organizations to place the cardholder in. When omitted, or not one of your organizations, your top-level organization is used.
metadataobjectYour own key-value data to store on this cardholder. It is returned verbatim on every read of the cardholder and never affects how the cardholder is processed — use it to carry your order or customer identifier so you do not have to keep a separate mapping. Up to 50 keys, keys up to 40 characters and values up to 500 characters. Values must be strings — nested objects, arrays, numbers and booleans are rejected. On update the map you send replaces the stored map in full: omit the field to leave it unchanged, send {} to clear it, and send a key with an empty string value to remove just that key.

Example request

cURL

curl --request POST \
     --url 'https://api.themooncard.com/v2/api-gateway/cardholders' \
     --header 'x-api-key: YOUR_API_KEY' \
     --header 'content-type: application/json' \
     --data '{
       "external_id": "customer_123",
       "end_customer_id": "customer_123",
       "email": "jane@example.com",
       "organization_id": "5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
       "metadata": {
         "crm_id": "CRM-4821"
       }
     }'

Responses

201 Success
FieldTypeDescription
id requiredstringCardholder id (Moon-assigned).
external_id requiredstring, nullableYour identifier for this cardholder (the end_customer_id you supplied), or null.
organization_id requiredstringOwning organization.
email requiredstring, nullable
delegated_kyc_completed_at requiredstring, nullableWhen delegated KYC was marked complete, or null.
banned_at requiredstring, nullableWhen the cardholder was banned, or null.
auth_enabled_at requiredstring, nullableWhen cardholder authentication was enabled, or null.
last_login_at requiredstring, nullableWhen the cardholder last logged in, or null.
metadata requiredobjectThe key-value data you stored on this cardholder, returned exactly as you sent it. An empty object when you never set any — it is never null.
created_at requiredstring, nullable
updated_at requiredstring, nullable

JSON

{
  "id": "3f5b1c2e-8d4a-4b6f-9e2a-1c7d8e9f0a1b",
  "external_id": "customer_123",
  "organization_id": "5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
  "email": "jane@example.com",
  "delegated_kyc_completed_at": "2026-08-02T14:20:05.000Z",
  "banned_at": null,
  "auth_enabled_at": "2026-08-02T14:21:00.000Z",
  "last_login_at": "2026-08-15T09:03:44.000Z",
  "metadata": {
    "crm_id": "CRM-4821"
  },
  "created_at": "2026-08-01T09:12:44.000Z",
  "updated_at": "2026-08-15T09:03:44.000Z"
}
400 Error

Validation failed (errors[] lists each offending field) or a domain rule rejected the request ({ message, code }).

The body is the standard error object: see Errors.

401 Error

Missing or invalid credentials, e.g. { "message": "Invalid API Key", "code": "UNAUTHORIZED" }.

The body is the standard error object: see Errors.

404 Error

The requested resource does not exist or is not visible to this organization.

The body is the standard error object: see Errors.

409 Error

A cardholder with this external_id or email already exists in the organization (EXTERNAL_ID_ALREADY_EXISTS). The Idempotency-Key you sent is already in use. IDEMPOTENCY_KEY_REUSED — the key was used for a different request; send a new key, or repeat the original request exactly to receive its stored response. IDEMPOTENCY_KEY_IN_PROGRESS — the first request with this key has not finished yet; retry in a few seconds.

The body is the standard error object: see Errors.

500 Error

Unexpected server error. Body is { message: "Internal server error", code: "INTERNAL_SERVER_ERROR" }.

The body is the standard error object: see Errors.

Last updated June 5, 2026