Browse the docs

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_id (your top-level organization id). Step 1: send email + organization_id 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

FieldTypeDescription
email requiredstring, emailThe cardholder's email address, looked up within organization_id and its child organizations. On step 1, if no cardholder has this email one is created.
organization_id requiredstring, uuidUUID of your top-level organization (GET /v2/api-gateway/organizations/main). Cardholders of it and of its child organizations can log in.
external_idstringYour identifier for the cardholder. Only used on step 1 when no cardholder with email exists yet: it is stored as external_id of the newly created cardholder. Ignored otherwise. Canonical name for the value this route also accepts as end_customer_id. Send one or the other; sending both with different values is rejected.
end_customer_idstringDeprecated alias of external_id, kept working for existing integrations. Send one or the other; sending both with different values is rejected.
codestringThe 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 }.

FieldTypeDescription
success requiredbooleanOne of: true
message requiredstringAlways "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_ACTIVE_CODE, CODE_EXPIRED, INVALID_CODE.

The body is the standard error object: see Errors.

404 Error

Organization not found (ORGANIZATION_NOT_FOUND), or no cardholder with this email when verifying a code (CARDHOLDER_NOT_FOUND).

The body is the standard error object: see Errors.

429 Error

Too many failed code attempts (TOO_MANY_ATTEMPTS); 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_SERVER_ERROR" }.

The body is the standard error object: see Errors.

Last updated August 16, 2026