# API endpoint reference

Base: `https://werkmail.eu`. Authenticated routes need `Authorization: Bearer` unless marked public. `{id}` is the workspace UUID.

This table is curated from the live router. [`/openapi.yaml`](https://werkmail.eu/openapi.yaml) may lag; prefer this page plus the topic guides.

## Meta and auth

| Method | Path | Auth | Notes |
| --- | --- | --- | --- |
| `GET` | `/healthz` | public | Process health |
| `GET` | `/openapi.yaml` | public | OpenAPI document |
| `GET` | `/api/meta` | session/token | Attachment policy, safe mode, flags |
| `POST` | `/api/auth/request-otp` | public | |
| `POST` | `/api/auth/verify-otp` | public | Returns session bearer |
| `POST` | `/api/auth/login-password` | public | When enabled |
| `POST` | `/api/auth/logout` | session | |
| `GET` | `/api/auth/me` | session | |

## Workspaces (practices)

| Method | Path | Notes |
| --- | --- | --- |
| `GET` | `/api/practices` | List tenants the caller can see |
| `POST` | `/api/practices` | Create (admin / onboarding) |
| `GET` | `/api/practices/{id}` | |
| `POST` | `/api/practices/{id}/pause` | `{ "reason" }` |
| `POST` | `/api/practices/{id}/resume` | |
| `POST` | `/api/practices/{id}/plan` | Platform admin |
| `GET` | `/api/practices/{id}/overview` | Plan usage, DNS, reputation |
| `GET` | `/api/practices/{id}/onboarding` | Wizard status |
| `GET` | `/api/practices/{id}/cutover` | Go-live checklist |
| `GET` | `/api/practices/{id}/billing` | |
| `POST` | `/api/practices/{id}/billing/checkout` | Creem checkout |
| `POST` | `/api/practices/{id}/billing/portal` | Customer portal |
| `PATCH` | `/api/practices/{id}/compliance` | Impressum / legal |

`POST /api/projects` is an alias of practice create (managed host when `domain` is omitted).

## DNS and deliverability

| Method | Path | Notes |
| --- | --- | --- |
| `GET` | `/api/practices/{id}/dns-records` | Records to publish |
| `POST` | `/api/practices/{id}/check-dns` | Live DNS |
| `GET` | `/api/practices/{id}/dns-records/cloudflare-zone` | BIND export |
| `GET` | `/api/practices/{id}/domain-connect/cloudflare` | One-click apply URL |
| `GET` | `/api/practices/{id}/deliverability` | SPF/DKIM/DMARC/BIMI + mix |
| `GET` | `/api/practices/{id}/channel-mix` | `?days=` |
| `GET` | `/api/practices/{id}/placement` | Subject risk |
| `POST` | `/api/practices/{id}/custom-domain` | `{ "domain" }` |
| `POST` | `/api/practices/{id}/restore-managed-domain` | |

Public Domain Connect: `GET /api/public/domain-connect/template`, `GET /api/public/domain-connect/meta`.

## Send and messages

| Method | Path | Notes |
| --- | --- | --- |
| `POST` | `/api/practices/{id}/send` | 202 |
| `POST` | `/api/practices/{id}/send/batch` | ≤500 |
| `POST` | `/api/practices/{id}/send/bulk` | Templated |
| `POST` | `/api/emails/send-bulk` | Flat alias |
| `POST` | `/api/practices/{id}/send/score` | Dry-run |
| `POST` | `/api/practices/{id}/send-test` | Simulator / quality |
| `POST` | `/api/practices/{id}/validate-emails` | |
| `GET` | `/api/practices/{id}/messages/search` | |
| `GET` | `/api/practices/{id}/messages/{messageID}` | |
| `GET` | `/api/practices/{id}/messages/{messageID}/timeline` | |
| `GET` | `/api/practices/{id}/messages/{messageID}/events` | |
| `GET` | `/api/practices/{id}/events` | Workspace stream |
| `GET` | `/api/practices/{id}/audit` | |

## Projects, routes, tokens

| Method | Path | Notes |
| --- | --- | --- |
| `GET\|POST` | `/api/practices/{id}/projects` | |
| `PATCH` | `/api/practices/{id}/projects/{projectID}` | |
| `GET\|POST` | `/api/practices/{id}/routes` | |
| `PATCH` | `/api/practices/{id}/routes/{routeID}` | UUID or slug |
| `POST` | `/api/practices/{id}/routes/{routeID}/default` | |
| `POST` | `/api/practices/{id}/api-tokens` | Team / project token |
| `GET` | `/api/practices/{id}/roles` | |
| `PATCH` | `/api/practices/{id}/members/{memberID}` | |

## Webhooks, alerts, inbound

| Method | Path | Notes |
| --- | --- | --- |
| `GET\|POST` | `/api/practices/{id}/webhooks` | |
| `DELETE` | `/api/practices/{id}/webhooks/{webhookID}` | |
| `POST` | `/api/practices/{id}/webhooks/{webhookID}/test` | |
| `GET` | `/api/practices/{id}/webhooks/{webhookID}/deliveries` | |
| `GET\|POST` | `/api/practices/{id}/alerts` | Slack / Teams |
| `GET\|POST` | `/api/practices/{id}/inbound` | enable/disable/check/apply-dns |
| `GET` | `/api/practices/{id}/inbound/messages` | |
| `GET\|POST` | `/api/practices/{id}/inbound-hooks` | |
| `POST` | `/api/public/hooks/{id}/send` | Public + hook secret |

## Suppressions, lists, campaigns

| Method | Path | Notes |
| --- | --- | --- |
| `GET\|POST` | `/api/practices/{id}/suppression` | |
| `POST` | `/api/practices/{id}/suppression/import` | CSV |
| `GET` | `/api/practices/{id}/suppression/export` | |
| `POST` | `/api/practices/{id}/suppression/bulk-delete` | ≤1000 |
| `DELETE` | `/api/practices/{id}/suppression/{suppressionID}` | |
| `GET\|POST` | `/api/practices/{id}/lists` | |
| `GET\|POST` | `/api/practices/{id}/lists/{listID}/subscribers` | |
| `POST` | `/api/practices/{id}/campaigns` | Broadcast job |

## Public compliance

| Method | Path | Auth |
| --- | --- | --- |
| `GET\|POST` | `/api/public/unsub` | signed query |
| `GET` | `/api/public/confirm` | token |
| `GET\|POST` | `/api/public/abuse` | public |
| `POST` | `/api/public/subscribe` | public DOI |
| `POST` | `/api/public/waitlist` | public |

## Integrations

| Method | Path | Notes |
| --- | --- | --- |
| `POST` | `/api/practices/{id}/integrations/ics-preview` | |
| `POST` | `/api/practices/{id}/integrations/test-termin` | |

Topic guides: [Send](/api/send), [Messages](/api/messages), [Webhooks](/api/webhooks), [Suppressions](/api/suppressions), [Inbound](/api/inbound), [Errors](/api/errors).
