Skip to main content

Authentication

Authentication​

Haltless uses two kinds of credentials, and which one you use depends on what you're doing:

CredentialUse it forHow you send it
Bearer access tokenReading and managing data as a signed-in user (machines, alerts, settings, reports)Authorization: Bearer <access-token>
API keyMachine-to-machine access: streaming machine data in from the Edge Agent or your own scripts, or a service account for an integrationX-API-Key: <api-key>

Both are sent over HTTPS to https://api.haltless.io/api/v1. Never put a credential in a URL query string or commit one to source control.

Bearer access tokens​

Access tokens identify a signed-in person. You obtain one by logging in, send it on every request, and refresh it when it expires.

TokenLifetime
Access token30 minutes
Refresh cookie7 days, replaced on every refresh
MFA pending token (between password and code)5 minutes

Log in to get a token​

curl -X POST https://api.haltless.io/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com", "password": "<your-password>" }'
{
"access_token": "<your-access-token>",
"token_type": "bearer",
"mfa_required": false,
"tenant_choice_required": false,
"tenants": [],
"password_expired": false
}

Alongside the response, Haltless sets a refresh token in a secure, httpOnly cookie. You never read that cookie yourself; it's used only to obtain new access tokens (see Refreshing).

One email in several organizations. The same email address can belong to more than one company. If the password matches in more than one, the response has "tenant_choice_required": true, an empty access_token, and the organizations in tenants (each with tenant_id and name). Send the same request again with the chosen tenant_id:

curl -X POST https://api.haltless.io/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com", "password": "<your-password>", "tenant_id": "<chosen-tenant-id>" }'

Expired password. Passwords expire after 90 days. When password_expired is true, the token can still be used to change the password, but other requests return 403 until you do. See Errors.

Send the token​

Include the access token in the Authorization header on every authenticated request:

curl https://api.haltless.io/api/v1/machines \
-H "Authorization: Bearer <your-access-token>"

Refreshing a token​

When an access token expires, exchange your refresh cookie for a fresh access token instead of logging in again:

curl -X POST https://api.haltless.io/api/v1/auth/refresh \
--cookie "refresh_token=<refresh-cookie>"

Each refresh issues a new access token and rotates the refresh cookie; the previous one stops working. Build your client to refresh automatically when a request comes back 401 Unauthorized, then retry the request once. If two requests refresh at the same time, only the first succeeds, so share one refresh between them.

Logging out​

curl -X POST https://api.haltless.io/api/v1/auth/logout \
-H "Authorization: Bearer <your-access-token>"

Logging out invalidates your current session so the tokens can no longer be used.

Multi-factor authentication​

If MFA is enabled on an account, POST /auth/login responds with "mfa_required": true, and its access_token is a pending token that is only good for completing the sign-in. Complete it by verifying a code from the authenticator app, or a single-use recovery code:

curl -X POST https://api.haltless.io/api/v1/auth/mfa/verify \
-H "Content-Type: application/json" \
-d '{
"mfa_token": "<access_token-from-login>",
"code": "123456"
}'

A successful verification returns a normal access token and sets the refresh cookie, exactly like a password-only login.

note

Teams can sign in through their own identity provider (OIDC single sign-on). If SSO is set up for your organization, your login flow may redirect you there instead of asking for a password. Your account admin manages this.

Sign-in errors​

StatusMeaning
401Wrong email or password
403Correct password, but the email address has not been verified yet
423Your company's account is suspended
429Too many failed attempts: 10 failures within 10 minutes lock the account for 15 minutes

API keys​

API keys are built for machine-to-machine traffic. Unlike an access token, a key does not expire on a short clock (you can still give it an expiry date), which makes it a good fit for long-running agents and integrations. There are two kinds:

KindWhat it can do
Edge agent (default)What its scopes allow. The default scopes, ingest and agent, send readings and call the edge-agent heartbeat. read:machines and read:sensors add read-only access to machines and their readings
Service accountActs as a member of your company with the role you give it, through the same endpoints a person in that role uses. It cannot manage users, roles, API keys, or sign-in settings

Create a key​

Create keys from the dashboard under Settings → API Keys, or from the API. Creating a key requires an account admin's access token:

curl -X POST https://api.haltless.io/api/v1/settings/api-keys \
-H "Authorization: Bearer <admin-access-token>" \
-H "Content-Type: application/json" \
-d '{ "name": "line-3-edge" }'
{
"id": "8c7b1e2a-0f3d-4a9c-9b21-1d6e5f0a7c34",
"name": "line-3-edge",
"key": "<your-new-api-key>",
"scopes": ["ingest", "agent"],
"tenant_id": "1d6e5f0a-7c34-4a9c-9b21-8c7b1e2a0f3d",
"created_at": "2026-08-07T10:00:00Z",
"kind": "edge_agent",
"role": null
}
warning

The full key value is shown only once, at creation. Copy it immediately and store it in a secret manager. If you lose it, create a new key and revoke the old one.

For scopes, service accounts, and expiry dates, see Managing API keys.

Send a key​

Pass the key in the X-API-Key header:

curl -X POST https://api.haltless.io/api/v1/ingest \
-H "X-API-Key: <your-api-key>" \
-H "Content-Type: application/json" \
-d '{ "readings": [ /* ... */ ] }'

Rotate a key​

Give each agent or integration its own key so you can rotate one without disrupting the others. To rotate:

  1. Create a new key with POST /api/v1/settings/api-keys.
  2. Update the agent or integration to use the new key.
  3. Confirm data is still flowing, then revoke the old key with DELETE /api/v1/settings/api-keys/{key_id}.

Use a distinct name per key so you can tell them apart later. For a full walkthrough of rotation and best practices, see Managing API keys.

Handling auth errors​

StatusMeaningWhat to do
401 UnauthorizedMissing, invalid, or expired credentialFor an access token, refresh and retry; for an API key, check the header and that the key is still active
402 Payment RequiredEdge-agent key used while your company's account is suspendedNothing to fix on your side; the Edge Agent pauses and resumes by itself once the account is reinstated
403 ForbiddenAuthenticated, but not permitted for this actionUse a credential with the right access, for example a key with the needed scope, or a Bearer token for management endpoints
429 Too Many RequestsRate limit exceededBack off and retry. See Rate limiting

See Errors for the full error format.

Next steps​