Authentication

Sign up, log in, CLI auth flow, and API key management.

zectre-api has no global auth middleware. Routes enforce credentials only when their handler extracts AuthUser. Those routes are marked 🔒 in the other API pages.

Two credential types are accepted:

  • Authorization: Bearer <jwt> — HS256 token issued by /api/v1/auth/login (7-day expiry), or an RS256 token from zectre-catalog when JWKS is configured.
  • X-API-Key: <key> — opaque key from /api/v1/api-keys.

POST /api/v1/auth/signup

Create a user account.

Body

  • email (string): Must contain @
  • password (string): Minimum 8 characters
  • name (string, optional)

Example

curl -X POST http://localhost:47080/api/v1/auth/signup \
  -H 'Content-Type: application/json' \
  -d '{"email":"alice@example.com","password":"hunter2abc","name":"Alice"}'
// 201 Created
{ "id": "uuid", "email": "alice@example.com", "name": "Alice", "is_admin": false, "created_at": "..." }

Errors400 INVALID_EMAIL, 400 WEAK_PASSWORD, 409 EMAIL_EXISTS

POST /api/v1/auth/login

Exchange email + password for a JWT.

Example

TOKEN=$(curl -sf -X POST http://localhost:47080/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"alice@example.com","password":"hunter2abc"}' \
  | jq -r .token)
// 200 OK
{
  "token": "eyJ...",
  "expires_at": "2026-04-03T10:00:00Z",
  "user": { "id": "uuid", "email": "alice@example.com", "name": "Alice", "is_admin": false, "created_at": "..." }
}

Errors401 INVALID_CREDENTIALS, 403 ACCOUNT_DISABLED

GET /api/v1/user 🔒

Returns the currently authenticated user.

curl -H "Authorization: Bearer $TOKEN" http://localhost:47080/api/v1/user
{ "id": "uuid", "email": "alice@example.com", "name": "Alice", "is_admin": false, "created_at": "..." }

CLI auth flow

The CLI opens a browser to authenticate. Three endpoints coordinate the handoff.

POST /api/v1/auth/cli/initiate

The CLI calls this first to get a one-time code and a browser URL.

Bodydevice_name (string, optional)

curl -X POST http://localhost:47080/api/v1/auth/cli/initiate \
  -H 'Content-Type: application/json' \
  -d '{"device_name":"My MacBook"}'
// 200 OK
{
  "code": "a1b2c3d4...",
  "auth_url": "http://localhost:47081/auth/cli?code=a1b2c3d4...",
  "expires_at": "2026-03-27T10:10:00Z"
}

The CLI opens auth_url in the browser and begins polling.

GET /api/v1/auth/cli/status/

The CLI polls this until status is completed.

// still waiting
{ "status": "pending", "api_key": null, "user": null }
 
// done — CLI reads api_key and stores it
{ "status": "completed", "api_key": "zec_...", "user": { ... } }

status is one of: pending, completed, expired.

POST /api/v1/auth/cli/complete

The dashboard submits the user's credentials once they log in.

Body

  • code (string): From initiate
  • email (string)
  • password (string)
// 200 OK
{ "success": true, "api_key": "zec_...", "user": { ... } }

API keys 🔒

API keys are long-lived credentials. The raw key is only shown once at creation — store it securely.

GET /api/v1/api-keys

List your keys. Raw secrets are never returned.

{
  "data": [
    {
      "id": "uuid", "name": "CI key", "prefix": "zec_1234",
      "scopes": ["*"], "expires_at": null, "last_used_at": "...", "created_at": "..."
    }
  ]
}

POST /api/v1/api-keys

Body

  • name (string): Required
  • scopes (array, optional): Defaults to ["*"]
  • expires_in_days (number, optional): Leave null for no expiry
curl -X POST http://localhost:47080/api/v1/api-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"GitHub Actions","scopes":["*"],"expires_in_days":90}'
// 201 Created — save key now, it will not be shown again
{ "id": "uuid", "name": "GitHub Actions", "key": "zec_1234abcd...", "prefix": "zec_1234", "created_at": "..." }

DELETE /api/v1/api-keys/

204 No Content. Only deletes keys that belong to you.

Error codes

CodeMeaning
INVALID_EMAILMalformed email on signup
WEAK_PASSWORDPassword shorter than 8 characters
EMAIL_EXISTSEmail already registered
INVALID_CREDENTIALSWrong email or password
ACCOUNT_DISABLEDUser account is inactive
INVALID_CODECLI auth code not found
CODE_EXPIREDCLI auth code expired (10-minute window)
SESSION_USEDCLI auth session already completed