Skip to content

Authentication

The Actuator API supports two authentication modes:

  • Bearer tokens for SDKs, CLIs, scripts, agents, and CI. Send as Authorization: Bearer act_….
  • Session cookies (actuator_session) for the dashboard. Set automatically when a user redeems a magic-link or accepts an invitation.

If both are set on a request, the bearer token wins.

Base URL

https://useactuator.ai

All paths below are relative to /api/v1 unless otherwise noted.

Errors

Errors come back as a flat envelope:

json
{
  "code": "invalid_request",
  "message": "email is required",
  "fields": { "email": "must be a valid email address" }
}

code is machine-readable and stable; message is human-readable and may change. fields is present only on validation errors. See the errors reference for the full code catalog.


http
POST /api/v1/auth/magic-link
Content-Type: application/json

{ "email": "user@example.com" }

Sends a magic-link email containing a single-use, 15-minute token. Rate-limited to 5/hr per email and 20/hr per IP. Always returns 200 { "sent": true } regardless of whether the email exists, to avoid account enumeration.

Response: 200 → { "sent": true }

http
GET /api/v1/auth/link?t=<raw-token>

The endpoint the user clicks from their email. Verifies the token, sets the actuator_session cookie, and 303-redirects to /app/. On failure, redirects to /app/signin?error=<code>.

Response: 303 → /app/ (success) or /app/signin?error=… (failure). Failure codes: magic_link_already_used, magic_link_expired, magic_link_invalid, no_invitation.

Session

Current session

http
GET /api/v1/auth/me
Cookie: actuator_session=…

Returns the current user, their email and display name, and the list of organizations they're a member of (with role).

Response: 200 → Session | 401 unauthorized

Sign out

http
POST /api/v1/auth/logout
Cookie: actuator_session=…

Deletes the session and clears the cookie. Idempotent.

Response: 204 No Content


Public signup (hosted-only)

Mounted only on the hosted SaaS topology. Self-hosted deploys 404 these routes unless ACTUATOR_PUBLIC_SIGNUP=1 is set.

Discover signup state

http
GET /api/v1/auth/install-state

Returns whether the instance has been claimed (zero orgs = unclaimed) and whether public signup is enabled. The signin page uses this to render the right CTA.

Response: 200 → { "claimed": true, "signupEnabled": true }

Sign up

http
POST /api/v1/auth/signup
Content-Type: application/json

{
  "email": "user@example.com",
  "displayName": "Pat Doe",
  "orgName": "Acme"
}

Stages a pending signup, mints a magic-link token, and sends a confirmation email. The user's organization is created when they redeem the link. The org slug is derived server-side from orgName — there's no orgSlug wire field.

Response: 200 → { "sent": true } (always — same enumeration resistance as /auth/magic-link). Errors: 400 invalid_request, 409 slug_unavailable.


First-install bootstrap (self-hosted)

Used once per self-hosted deploy to claim the instance.

http
POST /api/v1/auth/install-claim
Content-Type: application/json

{
  "email": "founder@example.com",
  "displayName": "Pat Doe",
  "orgName": "Acme",
  "orgSlug": "acme"
}

Atomic: creates the user, the org, and an owner-role membership in one transaction; mints a session and sets the actuator_session cookie. Subsequent calls return 409 already_claimed.

Response: 200 → Session with Set-Cookie: actuator_session=… | 409 already_claimed


Invitation accept (unauthenticated)

Preview an invitation

http
GET /api/v1/invitations/{token}

Unauthenticated. Returns the org name, role, and email the invitation was minted for. 404 on unknown / forged token; 410 for terminal states.

Response: 200 → { "orgName", "role", "email" }Errors: 404 not_found, 410 invitation_used, 410 invitation_expired.

Accept an invitation

http
POST /api/v1/invitations/{token}/accept
Content-Type: application/json

{ "displayName": "Pat Doe" }

Unauthenticated. The token is the credential. Creates the user (if new), attaches the membership, marks the invitation used, mints a session, and sets the actuator_session cookie — all in one transaction.

Response: 200 → Session with Set-Cookie: actuator_session=…Errors: 404 not_found, 410 invitation_used, 410 invitation_expired, 410 invitation_email_locked.


API tokens

To use the API from a script or SDK, mint a key from the dashboard (Org settings → API keys → Mint key) or via POST /api/v1/orgs/{slug}/api-keys. The raw token is shown once; store it as a secret. Tokens carry a fixed admin-equivalent scope on the org they're minted for and an optional rate-limit cap and TTL.

Send the token on every request:

http
Authorization: Bearer act_…

Rate-limit headers come back on every authenticated bearer response with status 2xx, 304, or 429:

  • X-RateLimit-Limit — requests per minute cap.
  • X-RateLimit-Remaining — requests remaining in the current bucket.
  • X-RateLimit-Reset — integer seconds until the bucket fully refills.

On 429, also Retry-After: <seconds>.