Private betaRequest early access →

Sign in

Quickstart

This walks through the full loop: sign up → authenticate → call an endpoint. Examples use curl against a Perihelium instance at $BASE (e.g. your self-hosted URL).

Bringing an AI agent? An agent can run this entire loop on your behalf — opening the organization with you as its owner (you verify, set a passkey, and approve; the agent gets its own governed credential) and configuring roles, invites, and SSO as code with exactly one review. See Agent-driven onboarding & IAM-as-code.

Shell
BASE="https://your-zynth-auth-host"

On a self-hosted instance with a self-signed certificate (IP-only, no domain yet), add -k to curl for local testing. Use a real certificate in production.

1. Join the private beta (creates your organization)

While the beta is private, POST /api/v1/auth/signup answers 403 with a reason naming the early-access page — the door is closed by default (PUBLIC_SIGNUP_ENABLED=false). Invited testers get in one of two ways: their agent opens the organization at the bootstrap door with the invite code (POST /api/v1/onboard/bootstrap with invite_code, see Joining the private beta), or the organization was provisioned for them and they set a password from the invite email and sign in at step 3. A self-hosted install that keeps sign-up open (PUBLIC_SIGNUP_ENABLED=true) uses the call below as written.

Signing up creates a user, an organization (tenant), and an owner membership linking them, and it proves your mailbox first. The organization slug is derived from the name (e.g. "Acme Inc" → acme-inc).

Ask. This creates nothing; it mails the address a one-time link and answers the same (202) whether or not the address already has an account:

Shell
curl -X POST "$BASE/api/v1/auth/signup" \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "organization_name": "Acme Inc"}'

Finish. Open the link in the email (…/signup#token=…) and choose a password in the console, or redeem it yourself: the link's token goes in the body, with the password you choose.

Shell
curl -X POST "$BASE/api/v1/auth/signup/complete" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "<the token from the link>",
    "password": "a-strong-passphrase-min-12-chars"
  }'

Response 201 (or 403 while sign-up is closed — see the note above):

JSON
{
  "access_token": "eyJhbGci...",
  "refresh_token": "eyJhbGci...",
  "token_type": "bearer"
}

2. Call an authenticated endpoint

Use the access token as a Bearer credential. GET /api/v1/me returns your identity and tenant context:

Shell
curl "$BASE/api/v1/me" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Response 200:

JSON
{
  "user_id": "…",
  "tenant_id": "…",
  "role": "owner",
  "is_owner": true,
  "tenant_isolation": "pooled",
  "population": "workforce",
  "email_verified": true
}

3. Log back in later

Logging in requires the organization slug (a user may belong to more than one org):

Shell
curl -X POST "$BASE/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "password": "…", "tenant_slug": "acme-inc"}'

4. Refresh an expired access token

Access tokens are short-lived (15 minutes by default). Exchange a refresh token for a new pair — no re-entry of credentials:

Shell
curl -X POST "$BASE/api/v1/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "'"$REFRESH_TOKEN"'"}'

Next

  • API reference — endpoints, fields, and errors for the core surfaces (hand-maintained; some families are documented in their guides instead).
  • Verifying tokens — validate tokens in your own services.
  • Troubleshooting — if something didn't work.