Authentication & API Keys
Passwordless email auth, client vs agent API keys, permissions, and team-based access control.
Owlmetry uses passwordless email verification for human users and API keys for programmatic access. All resources are scoped to teams with role-based access control.
Passwordless Email Authentication
There are no passwords or separate registration flows. Authentication works in two steps:
- Send a verification code --
POST /v1/auth/send-codewith your email address. The server sends a 6-digit code. - Verify the code --
POST /v1/auth/verify-codewith the email and code. The server returns a JWT.
If the email has never been seen before, an account is automatically created on first verification.
The JWT is identity-only (contains sub and email), has no expiry (web sessions never expire until sign-out), and is set as an HTTP cookie. Cross-subdomain auth is supported via the COOKIE_DOMAIN environment variable.
API Keys
API keys are used by SDKs, the CLI, and automated systems to interact with the Owlmetry API. There are three types:
| Type | Prefix | Used By | Purpose |
|---|---|---|---|
| Client | owl_client_ | SDKs | Event ingestion from apps |
| Agent | owl_agent_ | CLI, automation | Querying data and managing resources |
| Import | owl_import_ | Bulk import scripts | Historical data import via POST /v1/import (app-scoped) |
Storage
API key secrets are stored as plaintext in the secret column of the api_keys table so they can be retrieved at any time from the API Keys page in the dashboard. Protect your database the same way you would protect any other production secret store.
When an app is created, a client key is automatically generated. The key is returned in the client_secret field of the app response and is always accessible via the API Keys page.
Key Management
API keys are managed by users through the dashboard or the API (POST /v1/auth/keys). Key management operations (create, update, delete) require user authentication -- agent keys cannot manage other keys.
Each key records who created it via the created_by field (linked to a user). Keys support soft deletion and optional expiration (expires_in_days).
Permissions
Each API key has a permissions array that controls what it can do. Permissions are scoped by key type -- client keys and agent keys have different allowed permissions.
| Permission | Client | Agent | Import | Description |
|---|---|---|---|---|
events:write | Yes | -- | Yes | Ingest events and identity claims |
users:write | Yes | Yes | Yes | Claim anonymous users and set user properties |
events:read | -- | Yes | -- | Query events |
funnels:read | -- | Yes | -- | Read funnel definitions and analytics |
funnels:write | -- | Yes | -- | Create and update funnel definitions |
apps:read | -- | Yes | -- | List and get apps |
apps:write | -- | Yes | -- | Create and update apps |
projects:read | -- | Yes | -- | List and get projects |
projects:write | -- | Yes | -- | Create and update projects |
metrics:read | -- | Yes | -- | Read metric definitions and query metrics |
metrics:write | -- | Yes | -- | Create and update metric definitions |
audit_logs:read | -- | Yes | -- | View audit log entries |
integrations:read | -- | Yes | -- | List and get third-party integrations |
integrations:write | -- | Yes | -- | Create, update, and sync integrations |
jobs:read | -- | Yes | -- | List and get background job runs |
jobs:write | -- | Yes | -- | Trigger and cancel background jobs |
issues:read | -- | Yes | -- | List and get issues and comments |
issues:write | -- | Yes | -- | Update, merge, comment on, and resolve issues |
Default permissions: Keys are created with all permissions allowed for their type. Custom permissions can be specified at creation time.
Routes enforce permissions via the requirePermission() middleware. Users authenticated with a JWT bypass permission checks -- their access is governed by team role instead.
Restrictions
Certain operations are user-only regardless of API key permissions:
- Deleting apps and projects (agent keys receive 403)
- Managing API keys (create, update, delete)
- Team management (members, invitations, settings)
Team Roles
Users belong to teams with one of three roles:
| Role | Level | Can Manage |
|---|---|---|
owner | 3 (highest) | Everything, including promoting others to owner |
admin | 2 | Members, invitations, resources |
member | 1 | Read access, limited write access |
Role hierarchy is strictly enforced: you can only manage users with a lower role than your own. The last owner of a team cannot be demoted or removed.
Users can belong to multiple teams. The dashboard includes a team switcher when you have more than one team, and the CLI supports multi-team profiles.
Team Invitations
New members are added via email invitations, not directly:
- An admin or owner sends an invitation via
POST /teams/:teamId/invitations - The invitee receives an email with a token-based accept link
- The invitee visits the link, authenticates (or creates an account), and joins the team
Invitations expire after 7 days and can be re-sent (which regenerates the token). The public route GET /invites/:token shows invitation details without requiring authentication.
When a member is removed from a team, their agent API keys for that team are automatically revoked.
Auth Flow Summary
| Actor | Auth Method | Access Control |
|---|---|---|
| Dashboard user | JWT (email verification) | Team role (owner > admin > member) |
| SDK (client) | owl_client_ API key | permissions array on the key |
| CLI / automation (agent) | owl_agent_ API key | permissions array on the key |
| Bulk import script | owl_import_ API key | permissions array on the key (app-scoped) |
For CLI authentication details, see the CLI authentication guide. For API details, see the authentication API reference.
