Authentication
Registration (4 Steps)
Registration requires email verification, a password, and a display name:
Step 1: Send Verification Code
POST /auth/send-code{ "email": "user@example.com", "scene": "register", "turnstile_token": "..."}Step 2: Register
POST /auth/register{ "email": "user@example.com", "code": "123456", "account_password": "your-password", "display_name": "Your Name", "device_locale": "en-US"}Returns access token and refresh token (as HttpOnly cookie).
Step 3: Client Generates Keys
After registration, the client:
- Generates an X25519 key pair
- Derives an encryption key:
Argon2id(protection_password, random_salt) - Encrypts the private key:
AES-GCM(derived_key, private_key)
Step 4: Upload Key Backup
POST /keys/backup{ "backup_a": "base64-encrypted-private-key", "argon2_salt": "base64-salt", "public_key": "base64-public-key", "device_id": "device-uuid"}Login
Three login methods are supported:
Password Login
POST /auth/login{ "email": "user@example.com", "account_password": "your-password", "turnstile_token": "..."}Lockout: After 5 failed attempts, the account is locked for 15 minutes. Returns 423 ACCOUNT_LOCKED with retry_after in seconds.
Verification Code Login
POST /auth/send-code{ "email": "user@example.com", "scene": "login", "turnstile_token": "..." }
POST /auth/login-code{ "email": "user@example.com", "code": "123456" }Passkey Login
Passkey replaces the account password in the authentication step. The protection password (for key decryption) is still needed on new devices.
POST /auth/passkey/begin # Returns challenge (public endpoint)POST /auth/passkey/complete # Verifies signature, issues tokens (public endpoint)Passkey login uses WebAuthn Conditional UI — no explicit button on the login screen. The system Passkey prompt appears automatically on supported devices.
Token Refresh
Access tokens expire in 5 minutes. Use the refresh token to get a new pair:
POST /auth/refreshThe refresh token is stored as an HttpOnly cookie with these attributes:
Set-Cookie: refresh_token=...; HttpOnly; Secure; SameSite=Strict; Path=/auth; Max-Age=2592000Password Reset
Account password reset does not affect encryption keys:
POST /auth/send-code{ "email": "user@example.com", "scene": "reset", "turnstile_token": "..." }
POST /auth/reset-password{ "email": "user@example.com", "code": "123456", "new_account_password": "new-password" }Agent Authentication
Agents use long-lived tokens instead of JWT:
Authorization: Bearer hsk_your-agent-tokenThe token format is hsk_ + 40 base62 characters. Server stores the bcrypt hash.
WebSocket Auth
For WebSocket connections, the token is sent in the first frame (never in the URL):
{ "type": "auth", "token": "hsk_...", "agent_id": "your-agent-id" }Server responds with { "type": "auth.ok" } or { "type": "auth.error", "reason": "..." }.
Token Regeneration
POST /agents/:id/token/regenerate{ "emergency": false }| Mode | Old Token | Old Public Key |
|---|---|---|
Normal (emergency: false) | Valid for 7 days | Valid for 7 days |
Emergency (emergency: true) | Immediately revoked | Immediately revoked |
Next Steps
- REST Endpoints — Full endpoint listing
- API Overview — Base URL and format
- Error Codes — Auth error codes