Skip to content

Log in with email and password

POST
/v1/auth/login
curl --request POST \
--url https://api.aetherpush.com/v1/auth/login \
--header 'Content-Type: application/json' \
--data '{ "email": "adrian@aetherpush.com", "password": "correct horse battery staple" }'

Authenticates an account and returns an access key (session bearer token) usable on subsequent requests. The returned accessKey is the raw token — store it securely; the server cannot return it again.

Rate limited to 5 failed attempts per client address per 15 minutes. The address is the exact one on IPv4 and the /64 prefix on IPv6.

Repeated failures against a single account escalate rather than lock it: past a small number of recent failures the next attempt must carry a Turnstile token in challengeToken, and the server signals that with a 403 carrying code: challenge_required. Where that escalation is not enabled, the account instead stops accepting attempts for an hour after ten recent failures.

Media typeapplication/json
object
email
required
string format: email
password
required
string format: password
challengeToken

Cloudflare Turnstile token. Only needed once an account has accumulated enough recent failed logins for the server to ask for one, which it signals with a 403 carrying code: challenge_required. Sending it before then is harmless and ignored.

string

Example

{
"email": "adrian@aetherpush.com",
"password": "correct horse battery staple"
}

Password accepted. For accounts without MFA the response carries the access key. For MFA-enabled accounts it instead carries a short-lived pending login token; complete the login via /v1/auth/mfa/verify (passkey), /v1/auth/mfa/totp (authenticator code), or /v1/auth/mfa/recovery (recovery code). methods lists which of those this account can use. The two shapes are told apart by the mfaRequired field.

Media typeapplication/json
One of:
Login
object
accessKey
required

Raw bearer token. Send as Authorization: Bearer <accessKey> on subsequent requests. Store securely (cookie or secure storage). The server cannot reveal it again.

string
expires
required

Unix timestamp in milliseconds at which this token expires.

integer format: int64
mfaSetupRequired

Present and true when the account has no MFA method yet. Every account is expected to enroll a passkey or an authenticator app. The dashboard nags until mfaSetupDeadline and blocks navigation after it. Clients that cannot enroll (the CLI) may ignore this field.

boolean
mfaSetupDeadline

Unix timestamp in milliseconds. Present alongside mfaSetupRequired; fixed on the account’s first MFA-less login and stable on later logins. Before it, MFA setup is a dismissible prompt; after it, the dashboard requires setup before continuing.

integer format: int64

Examples

{
"accessKey": "abc123-base64url-secret",
"expires": 1719964800000
}

Email or password missing.

Media typeapplication/json
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "Email and password are required.",
"code": "email_password_required",
"requestId": "req_abc123"
}

Invalid email or password.

Media typeapplication/json
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "Invalid email or password.",
"code": "invalid_credentials",
"requestId": "req_abc123"
}

Either the email address has not been verified, or the account needs a Turnstile token before the attempt is considered. Both carry a code, and its value tells them apart: email_not_verified for the first and challenge_required for the second. Do not read the unverified refusal as confirmation that the password was accepted. Where the challenge is enforced and both apply, the challenge is asked for first, so an unverified account past the failure threshold is answered challenge_required until the attempt carries a solved Turnstile token.

Media typeapplication/json
Any of:
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Examples

{
"error": "Please verify your email address before logging in.",
"code": "email_not_verified",
"requestId": "req_abc123"
}

Too many login attempts.

Media typeapplication/json
RateLimitError

Returned with HTTP 429 when an endpoint-specific rate limit is hit, including the per-tenant management API budget. Not the process-wide address limiter (see GlobalAddressRateLimitErrorResponse). retryAfterSeconds indicates how long the client should wait before retrying, and matches the Retry-After header when that header is set. code names the limit that fired so clients can translate without matching on error.

object
error
required
string
code
required
string
retryAfterSeconds
required
integer
>= 1
requestId
string

Example

{
"error": "Too many login attempts. Please try again later.",
"code": "too_many_login_attempts",
"retryAfterSeconds": 900,
"requestId": "req_abc123"
}

Internal server error.

Media typeapplication/json
Error

Standard error envelope. code is the stable machine identifier for client branching and i18n. The wording of error is a human-readable fallback and is not part of the contract. Specialized responses keep narrower code enums where clients already branch on them.

object
error
required

Human-readable error message.

string
code
required

Stable machine code. Prefer this over matching on error. The values this API can answer with are listed in the ApiErrorCode schema of the raw document at https://api.aetherpush.com/openapi.yaml. The field stays an open string here so that a release adding a value does not invalidate this response.

string
requestId

Unique identifier for the request, also exposed as the X-Request-Id response header.

string

Example

{
"error": "The requested resource was not found.",
"code": "not_found",
"requestId": "req_abc123"
}