Skip to Content
API referenceAuthentication

Authentication

This page is for developers writing their own programs against almyty’s HTTP API (the web addresses programs call to do what the app does, such as listing agents or starting a run). Every call has to prove who is making it, and this page explains how: sign in for a short-lived token, or use an API key (a long secret string that works like a password for programs). For example, a script that runs your nightly report agent sends an API key with each call. If you only use the app, you can skip this section; you sign in as usual.

The almyty API uses JWT (JSON Web Tokens) for authentication. The web UI receives its token as an httpOnly SameSite=lax cookie so JavaScript running in the page can’t read it, cross-site scripting and compromised npm packages can’t steal the session. Programmatic clients and CLIs use a Bearer Authorization header with an API key, minted by the CLI login flow or with POST /auth/api-keys.

Every token carries iss: "almyty" + aud: "almyty-api" claims and is signed with HS256. Both claims are enforced on verification, tokens issued by another service that happens to share the signing secret are refused with “jwt issuer invalid”.

Rate limits on auth endpoints

Brute-force, enumeration, and credential-stuffing surface is gated at the route level, on top of the global 100 req/60s throttle:

EndpointLimit
POST /auth/register10 per hour per IP
POST /auth/login10 per 5 minutes per IP
POST /auth/token10 per 5 minutes per IP
POST /auth/forgot-password5 per hour per IP
POST /auth/refresh20 per 5 minutes per IP
GET /auth/check-organization-name30 per minute per IP
POST /:org/:gw/register (OAuth dynamic client registration)10 per 5 minutes per IP

Registration

Create a new account:

curl -X POST https://api.almyty.com/auth/register \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "SecurePass123!", "firstName": "Jane", "lastName": "Doe", "organizationName": "Acme Corp" }'

Parameters

ParameterTypeRequiredDescription
emailstringYesValid email address
passwordstringYesMinimum 8 characters, must include uppercase, lowercase, and number
firstNamestringYesUser’s first name
lastNamestringYesUser’s last name
organizationNamestringYesName for the default organization

Response

The session is the access_token cookie in the Set-Cookie header. The body carries no token:

{ "success": true, "data": { "expiresIn": 86400 }, "message": "Registration successful" }

Login

POST /auth/login is the browser login. It sets the httpOnly access_token cookie and returns no token in the body, so nothing a script on the page can read ever holds one:

curl -X POST https://api.almyty.com/auth/login \ -H "Content-Type: application/json" \ -c cookies.txt \ -d '{ "email": "user@example.com", "password": "SecurePass123!" }'
{ "success": true, "data": { "expiresIn": 86400 }, "message": "Login successful" }

Login for non-browser clients

POST /auth/token takes the same body and returns an access token and a refresh token in the body, and sets no cookie. It refuses any request that carries an Origin header, which every browser sends on a POST, so a page cannot call it. For scripts and CI, an API key (below) is usually the better fit.

curl -X POST https://api.almyty.com/auth/token \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "SecurePass123!" }'
{ "success": true, "data": { "accessToken": "eyJhbGciOiJIUzI1NiIs...", "refreshToken": "eyJhbGciOiJIUzI1NiIs...", "expiresIn": 86400 }, "message": "Login successful" }

Using the token

Include the access token in the Authorization header of API requests:

curl https://api.almyty.com/apis \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Token expiration

Access tokens expire after 24 hours. A client that signed in with /auth/token redeems its refresh token at POST /auth/refresh for a new pair; each refresh token works once, and presenting a spent one ends the session. Like /auth/token, /auth/refresh refuses browser pages. For long-lived programmatic access, use API keys instead.

API keys

API keys are long-lived tokens for scripts, CI pipelines, and CLI tools. They expire only when created with an expiresAt, stop working when revoked, and act with the full permissions of the user who minted them. Keys scoped to one gateway are separate: see Gateway authentication.

Getting an API key

The simplest way is npx @almyty/auth login: it opens your browser, mints a key named after the machine and date, and stores it at ~/.almyty/credentials.json so every CLI picks it up.

From a script, with a session cookie or an existing key:

curl -X POST https://api.almyty.com/auth/api-keys \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "CI pipeline" }'

name is required; expiresAt (an ISO date) and organizationId are optional. The response carries apiKey, the full key, once. The key looks like almyty_3f9c.... Use it anywhere a Bearer token is expected:

curl https://api.almyty.com/apis -H "Authorization: Bearer almyty_3f9c..."

GET /auth/api-keys lists your keys (name, prefix, last use, expiry, whether active); DELETE /auth/api-keys/:keyId revokes one.

Profile

Get profile

curl https://api.almyty.com/auth/profile \ -H "Authorization: Bearer $TOKEN"

Update profile

curl -X PATCH https://api.almyty.com/auth/profile \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Jane Smith", "timezone": "Europe/Berlin" }'

timezone is an IANA time zone name (null clears it back to UTC). Emails that wait for the morning, such as the daily model digest, go out at 08:00 in it.

Change password

curl -X PATCH https://api.almyty.com/auth/change-password \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "currentPassword": "OldPass123!", "newPassword": "NewPass456!" }'

Organization context

Most API endpoints are scoped to an organization. How the backend picks the current organization depends on how many memberships the caller has:

  • Single-org users: the backend auto-selects your one membership. No extra header is required.
  • Multi-org users: you must send the X-Organization-Id header on every request. Without it the API returns 400 NO_ORGANIZATION. This is a deliberate choice so that multi-org users never silently operate on the wrong org’s data.
# Multi-org user: pick which org to operate on curl https://api.almyty.com/gateways \ -H "Authorization: Bearer $TOKEN" \ -H "X-Organization-Id: 9c0e2f3a-..."

Some endpoints also accept the org id as a path parameter (e.g. /organizations/{orgId}/tools). In that case the path wins and the header is ignored, so the path must match a membership you actually have.

Switching orgs from a multi-org account

If your account belongs to more than one org, just change the X-Organization-Id header on your next request. The backend validates that the requested org matches one of your memberships; otherwise the request is rejected with 400 NO_ORGANIZATION.

Logout

curl -X POST https://api.almyty.com/auth/logout \ -H "Authorization: Bearer $TOKEN"

This invalidates the current token server-side.