Cardholder login
POST /v2/api-gateway/cardholders/auth/login
No authentication required. Two-step passwordless login for a cardholder of the organization identified by organization_ (your top-level organization id). Step 1: send email + organization_ without code — a 6-digit login code is issued (valid for 10 minutes) and the response is { success: true, message }. Step 2: send the same body plus code — the response is { token, expiresAt }; use the token as Authorization: Bearer <token> on cardholder-scoped V2 routes. Tokens are valid for 30 days; there is no refresh endpoint — repeat the login flow.
Request
Authentication: none.
Body
| Field | Type | Description |
|---|---|---|
email required | string, email | The cardholder's email address, looked up within organization_ and its child organizations. On step 1, if no cardholder has this email one is created. |
organization_ required | string, uuid | UUID of your top-level organization (GET /v2/api-gateway/organizations/main). Cardholders of it and of its child organizations can log in. |
external_ | string | Your identifier for the cardholder. Only used on step 1 when no cardholder with email exists yet: it is stored as external_ of the newly created cardholder. Ignored otherwise. Canonical name for the value this route also accepts as end_. Send one or the other; sending both with different values is rejected. |
end_ | string | Deprecated alias of external_, kept working for existing integrations. Send one or the other; sending both with different values is rejected. |
code | string | The 6-digit login code issued by step 1 (valid for 10 minutes). Omit it to request a code; send it to complete the login and receive a token. |
Example request
cURL
curl --request POST \
--url 'https://api.themooncard.com/v2/api-gateway/cardholders/auth/login' \
--header 'content-type: application/json' \
--data '{
"email": "jane@example.com",
"organization_id": "5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
"external_id": "customer_123",
"end_customer_id": "customer_123",
"code": "482913"
}'Responses
200 Success
Step 1 returns { success, message }; step 2 returns { token, expiresAt }.
| Field | Type | Description |
|---|---|---|
success required | boolean | One of: true |
message required | string | Always "Login code sent to your email". |
JSON
{
"success": true,
"message": "Login code sent to your email"
}400 Error
Validation failed, or the code was rejected: NO_, CODE_, INVALID_.
The body is the standard error object: see Errors.
404 Error
Organization not found (ORGANIZATION_), or no cardholder with this email when verifying a code (CARDHOLDER_).
The body is the standard error object: see Errors.
429 Error
Too many failed code attempts (TOO_); request a new code.
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 August 16, 2026