Create cardholder
POST /v2/api-gateway/cardholders
Creates a cardholder identified by your external_. The cardholder is placed in organization_ 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
| Field | Type | Description |
|---|---|---|
Idempotency-Key | string | A 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
| Field | Type | Description |
|---|---|---|
external_ | string | Your identifier for the cardholder. Must be unique within the organization; must not be empty. Required — send it as external_ or as end_. |
end_ | string | Deprecated alias of external_, kept working for existing integrations. Send one or the other; sending both with different values is rejected. |
email | string, email | The cardholder's email address. Used by POST /cardholders/auth/login to identify the cardholder and to filter GET /cardholders?email=. |
organization_ | string, uuid | UUID of one of your organizations to place the cardholder in. When omitted, or not one of your organizations, your top-level organization is used. |
metadata | object | Your 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
| Field | Type | Description |
|---|---|---|
id required | string | Cardholder id (Moon-assigned). |
external_ required | string, nullable | Your identifier for this cardholder (the end_ you supplied), or null. |
organization_ required | string | Owning organization. |
email required | string, nullable | |
delegated_ required | string, nullable | When delegated KYC was marked complete, or null. |
banned_ required | string, nullable | When the cardholder was banned, or null. |
auth_ required | string, nullable | When cardholder authentication was enabled, or null. |
last_ required | string, nullable | When the cardholder last logged in, or null. |
metadata required | object | The 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_ required | string, nullable | |
updated_ required | string, 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_ or email already exists in the organization (EXTERNAL_). The Idempotency-Key you sent is already in use. IDEMPOTENCY_ — the key was used for a different request; send a new key, or repeat the original request exactly to receive its stored response. IDEMPOTENCY_ — 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_.
The body is the standard error object: see Errors.
Last updated June 5, 2026