Reference
Authentication
This page is about your app's end users — the people signing into the product you build on Aaly. Connecting an AI coding agent to the platform itself is a separate flow; see Connect an AI agent.
Sign up
POST /<your-slug>/<project>/auth/signup
{ "name": "Ada", "password": "...", "email": "ada@example.com" }
name and password are required; at least one of email, phone, or
username identifies the account. Returns 201 with:
{ "jwt": "...", "user": { "id": "USR1", "name": "Ada", "tenantId": "TNT1", "email": "ada@example.com" } }
Tenant behavior on signup depends on the project's publicSignup and
multiTenant flags — see Multi-tenancy for the full
model. In short: signing up without a tenantId only creates a new tenant
at all when the project's publicSignup is true; signing up with an
existing tenantId joins that tenant and shares its data with every other
user in it. tenantName only names a tenant you're creating — it's ignored
when joining an existing one via tenantId.
Sign in
POST /<your-slug>/<project>/auth/signin
{ "identifier": "ada@example.com", "password": "..." }
identifier is an email, phone, or username. Returns the same
{ jwt, user } shape as signup — unless the identifier belongs to more than
one tenant and no tenantId was passed, in which case you get a
tenant-selection response instead:
{ "requiresTenantSelection": true, "tenants": [{ "tenantId": "TNT1", "tenantName": "Acme" }] }
Re-send the request with the chosen tenantId to complete sign-in.
Google sign-in
POST /<your-slug>/<project>/auth/google
{ "idToken": "<Google OIDC ID token from your client-side sign-in>" }
Verifies the token against Google, then signs in an existing user matched by email, or registers a new one (auto-creating a tenant). Same tenant-selection response as sign-in if the email matches more than one tenant.
Tokens
- JWT — returned by signup/signin/Google,
HS256-signed, 7-day expiry. Send asAuthorization: Bearer <jwt>. - API key —
aaly_<id>_<secret>, for CI, scripts, and server-to-server calls that shouldn't hold a short-lived user token:
POST /<your-slug>/<project>/auth/keys
Authorization: Bearer <jwt>
Returns the full token once:
{ "id": "KEY1", "token": "aaly_KEY1_xxxxxxxxxxxxxxxx", "createdAt": "2026-08-24T10:00:00.000Z" }
GET /auth/keys lists key metadata (never the secret again);
DELETE /auth/keys/{id} revokes one. A JWT and an API key are accepted
identically on every endpoint — nothing is JWT-only.
What's not here yet
Password reset ships shortly — see Limits for what's still genuinely missing (refresh tokens, MFA, RBAC).