Public API

Auth and API keys

Authentication modes for agents, iOS clients, and admin surfaces.

Auth and API keys

Agent Notifier uses two public authentication modes:

1. API keys for agents and automation. New automation should prefer account-scoped keys; legacy project-scoped keys remain supported.

2. Bearer JWTs for app-owned user, project, feed, device, and settings endpoints.

Admin endpoints use a separate server-side admin key and are not part of the public agent workflow.

Agent API keys

Agents authenticate with X-API-Key:

X-API-Key: an_key_REPLACE_WITH_YOUR_KEY

Use API keys for:

  • POST /v1/messages
  • POST /v1/media
  • GET /v1/action-responses
  • POST /v1/action-responses/{id}/ack

Account-scoped keys are owned by the signed-in user account. For POST /v1/messages, the request project field is resolved as a user-owned project name and is created on first send if needed. Existing project-scoped keys continue to bind directly to their original project and preserve legacy automation behavior.

The backend stores hashed secrets; full keys are only returned when created or rotated. Each user account is limited to 10 active API keys for now. Successful API-key authentication updates last_used_at when available so clients can show “Last used” without exposing the secret.

Customer-facing CLIs and examples must never embed a shared production key. Prefer AGENT_NOTIFIER_API_KEY, a local untracked .env.local, a platform secret store, or a user-scoped config file with owner-only permissions. Passing --api-key on a command line is acceptable for one-off testing but can leak into shell history and process listings.

Creating account API keys

Authenticated app clients create account-scoped keys with:

POST /v1/api-keys
Authorization: Bearer <access_token>

The response includes the full key once. Store it in a local .env.local file or secret manager and never commit it.

Key lifecycle

Authenticated app clients list keys with GET /v1/api-keys. List responses include IDs, scopes, lifecycle status, prefixes, project binding, creation time, optional last_used_at, and revocation time, but never the secret or hash.

Revoke one account-scoped or project-scoped key by ID with:

DELETE /v1/api-keys/{id}
Authorization: Bearer <access_token>

Rotate one active key by ID with:

POST /v1/api-keys/{id}/rotate
Authorization: Bearer <access_token>

The rotate-by-ID response includes a replacement full key once. The replacement preserves the old key's scope and project binding. Rotating an active key at the 10-key account limit is allowed because the old key is revoked as part of the same lifecycle operation.

Legacy project key rotation

Existing project-key clients can still rotate all active project-scoped keys for a project with:

POST /v1/api-keys/rotate
Authorization: Bearer <access_token>

Request body:

{
  "project_id": "00000000-0000-0000-0000-000000000000"
}

The response includes the new project-scoped full key once. Existing automation should be updated immediately after rotation. New account-scoped key management should prefer the ID-based revoke/rotate endpoints.

Bearer JWTs

The iOS app exchanges a Sign in with Apple identity token at POST /v1/auth/apple. It receives an access token and refresh token. Bearer JWTs are required for user-owned endpoints such as:

  • GET /v1/me
  • GET /v1/notifications
  • GET /v1/projects
  • GET /v1/api-keys
  • POST /v1/actions

Agents normally do not need Bearer JWTs; they should use API keys instead.

Google auth placeholder

POST /v1/auth/google exists only as a safe placeholder for D38. It validates basic request shape (id_token is required), then returns HTTP 501 with status: "unsupported". The current product does not accept Google ID tokens, does not link Google accounts, and does not ship a real Google login flow.

Apple remains the only real sign-in provider until provider-neutral account linking, nonce/state validation, privacy review, and security sign-off are complete.

Subscription and checkout scaffolds

Authenticated clients may see OpenAPI entries for future billing endpoints:

  • POST /v1/subscriptions/verify validates that signed_transaction_jws was supplied, then returns HTTP 503 with status: "unconfigured", apple_jws_verification_configured: false, and entitlement_updated: false until real Apple JWS/App Store Server API verification is configured.
  • POST /v1/subscriptions/stripe/checkout returns HTTP 501 with status: "unsupported"; no Stripe checkout flow is available today.

Do not treat either endpoint as proof of paid entitlement enforcement.

Secret handling rules

  • Never place an API key in source control.
  • Never ship helper scripts with a baked-in shared API key; every customer should use their own account-scoped key.
  • Prefer environment variables, local .env.local files, or platform secret stores.
  • Log non-reversible fingerprints or omit key identifiers; do not log full keys or prefixes from failed authentication attempts.
  • Rotate a key if it appears in terminal history, issue comments, screenshots, CI logs, or crash reports.