Session cookie
aropay_session: httpOnly, SameSite=Lax, 24-hour TTL. Minted by
POST /auth/login (or passkey login). What the dashboard uses.API key
Authorization: Bearer aro_sk_…, a 256-bit secret shown once at
creation. What your integration uses. See API keys.Precedence
If a request carries anAuthorization header, it takes precedence over any
cookie and must be a Bearer scheme. A malformed or unknown key fails with
401 invalid_api_key even if a valid session cookie is also present.
Session-only endpoints
Operations that mint or destroy credentials (or export key material) require a browser session. Called with an API key, they return403 session_required:
The net effect: an API key can move sandbox money (once you have accepted the
privacy terms), but it can never escalate itself. No new keys, no password
changes, no key exports.
Step-up
A session is not enough on its own to create a credential that outlives it, or to take one away. Creating an API key, starting a passkey registration, exporting a custodial wallet key, and turning 2FA on or off also ask you to re-enter your password, plus a fresh authenticator code when 2FA is on:
Codes are single-use, as at sign-in. A wrong password or code returns
401 invalid_credentials / invalid_totp_code (totp_required when the
code is missing, which isn’t counted; totp_code_reused for a replay). A
wrong password counts toward the step-up password lock, which is separate
from sign-in’s: five in a row lock step-up and password change for 15
minutes, while sign-in keeps working (and failed sign-ins never block a
step-up). Each code tried counts toward the same 2FA code cap as the sign-in
code step: five without an accepted one lock code entry for 15 minutes,
doubling with each further failure up to 24 hours. Each lock answers
423 account_locked
(lockout behavior). Someone holding
a stolen session cookie therefore can’t plant a key or passkey without also
knowing your password and holding your authenticator, nor export a wallet
key.
Recovery and credential revocation
Changing your password, or an admin resetting your password or 2FA, revokes everything a past session could have left behind:
An admin password reset also lifts every lock on the account, the 2FA code
lock included; 2FA stays on, so sign-in still asks for a code. Changing your
own password leaves the 2FA code lock in place.
Revocation is permanent: a reactivated account gets its sign-in back, never
its old keys. Create new keys after signing in (with step-up). Each revoked
key shows why in
revokedReason (password_changed, admin_password_reset,
admin_2fa_reset, account_deactivated, or user when you revoked it
yourself).
Login flows
- Password
- Passkey
{ "requiresTotp": true, "pendingToken": "…" };
complete it with POST /auth/login/totp.POST /auth/logout clears
the cookie; GET /auth/session returns the current principal and which
authMethod authenticated it (session or api_key).
First login & account provisioning
Accounts are provisioned by Aro Media admins; you receive a one-time temporary password and are forced to set a new one on first login (the user object carriesmustChangePassword: true until you do). Password policy:
at least 10 characters with an uppercase letter, a lowercase letter, and a
digit.
Before your first transaction you also accept the AroPay
data-privacy terms in the dashboard. Until you do,
money movement and Aro Mail send and claim return 403 terms_not_accepted,
for API keys as well as sessions.
Request conventions
- Write requests must send
Content-Type: application/json; anything else is rejected with415 unsupported_media_type. This doubles as CSRF protection: cross-origin HTML forms can’t produce that content type without a preflight the API never allows. - Every response uses the shared envelope:
{ "ok": true, "data": … }or{ "ok": false, "error": { "code", "message", "details?" } }. See Errors.
Login protections
- Rate limit: 10 login attempts per 15 minutes per IP.
- Lockout: 5 consecutive wrong passwords lock sign-in for 15 minutes
(
423 account_locked). Failed step-up passwords count toward a separate lock, so someone who only knows your email can’t stop you from changing your password while signed in. 2FA codes have their own cap, shared by the sign-in code step and step-up, which escalates up to 24 hours and which a correct password doesn’t reset (details). - Generic errors: failed logins return the same
invalid_credentialsregardless of which part was wrong. - Disabled accounts fail with
403 account_disabledon any auth path.