# HTTP API overview

The Werkmail API is the same origin as the product. There is no separate “v1” host.

**Base URL:** `https://werkmail.eu`

```
https://werkmail.eu/api/practices/{id}/send
```

OpenAPI (YAML): [`https://werkmail.eu/openapi.yaml`](https://werkmail.eu/openapi.yaml)  
Health: [`https://werkmail.eu/healthz`](https://werkmail.eu/healthz)

## Authentication

Send `Authorization: Bearer <token>` on `/api/*` (except the public and auth routes below).

| Token | Prefix / source | Scope |
| --- | --- | --- |
| Session | OTP or password login | The signed-in member |
| Team API token | `wm_team_…` | Manage the workspace |
| Project API token | `wm_proj_…` | Send and read for one project |
| Operator admin | `ADMIN_API_TOKEN` or `X-Admin-Token` | Platform admin |

Details: [Authentication](/api/authentication).

## Public routes (no bearer)

| Method | Path | Purpose |
| --- | --- | --- |
| `POST` | `/api/auth/request-otp` | Email a sign-in code |
| `POST` | `/api/auth/verify-otp` | Exchange code for a session bearer |
| `POST` | `/api/auth/login-password` | Password login when enabled |
| `GET\|POST` | `/api/public/unsub` | Signed one-click unsubscribe |
| `GET` | `/api/public/confirm` | DOI confirm |
| `POST` | `/api/public/abuse` | Abuse report |
| `POST` | `/api/public/hooks/{id}/send` | Inbound send hook (`X-Werkmail-Hook-Secret`) |
| `GET` | `/openapi.yaml` | OpenAPI document |

## Practice-scoped routes

Most product APIs live under `/api/practices/{id}/…`. `{id}` is the workspace UUID. Flat aliases exist for a few resources (`/api/projects`, `/api/emails/send-bulk`, `/api/suppressions`) and still require a project/workspace id in the body or query.

## Topic guides

- [Send](/api/send) — single, batch, bulk, score, test
- [Messages](/api/messages) — list, get, search, timeline
- [Webhooks](/api/webhooks) — subscribe and verify signatures
- [Suppressions](/api/suppressions) — team / project / route blocks
- [Inbound](/api/inbound) — receive + send hook
- [Lists and campaigns](/api/lists-and-campaigns)
- [Errors](/api/errors)
- [Endpoint reference](/api/reference)

## Idempotency and errors

Retry-safe writes accept `Idempotency-Key`. Errors are JSON `{ "code", "error", "hint" }` — see [Errors](/api/errors).
