API endpoint reference
Base: https://werkmail.eu. {id} on /api/practices/{id} is the workspace UUID.
This table is the full live router. /openapi.yaml has the same paths and methods, plus request and response field names. A CI check fails if the router, this page, and the OpenAPI disagree. Rows marked Planned are not implemented.
GET /openapi.json returns JSON (application/json). GET /openapi.yaml returns YAML. GET /api/reference on the product origin is public, the same catalog as this page.
How an agent should call the API
- Send
Authorization: Bearer $TOKENon every route whose Auth column isbearerorplatform admin. Public routes do not take a bearer. - Prefer a project token (
wm_proj_…) or team token (wm_team_…).GET /api/projects/melistsscopes(team tokens includeaudit.viewandconsent.view). - Branch on the JSON
codefield. Retry only whenx-error-codesmarks that coderetryable: true, and sendIdempotency-Key(orX-Idempotency-Key) on the retry. - Treat a send as accepted only when the status is 202 and the body has
message_idandid. Then pollGET /api/emails/{id}. - Do not call provider or billing callbacks (
/api/public/ses-events, the inbound provider callback,POST /api/public/creem-webhook).POST /api/public/unsubscribeis 410.
| Job | Call |
|---|---|
| Send one message | POST /api/emails or POST /api/practices/{id}/send |
| Send status | GET /api/emails/{id} |
| List messages | GET /api/emails (array; next page is X-Next-Cursor) |
| Counters | GET /api/emails/stats |
| Suppress an address | POST /api/practices/{id}/suppression or POST /api/suppressions |
| Webhook | POST /api/practices/{id}/webhooks |
Topic guides: Send, Messages, Drop-in, Webhooks, Suppressions, Inbound, Lists and campaigns, Errors.
Meta and documents
| Method | Path | Auth | Notes |
|---|---|---|---|
GET |
/healthz |
public | Liveness |
GET |
/readyz |
public | Readiness: database, migrations and worker heartbeat. 503 lists the failing checks. |
GET |
/metrics |
bearer | Prometheus metrics. Requires the ops/admin bearer or the dedicated metrics token. |
GET |
/openapi.yaml |
public | This OpenAPI document |
GET|HEAD |
/bimi-logo.svg |
public | Default BIMI logo · Default BIMI logo headers |
GET |
/openapi.json |
public | This OpenAPI document as JSON. Content-Type is application/json. |
GET |
/api/reference |
public | Public endpoint reference (this page). No bearer. |
GET |
/api/reference.md |
public | This page as Markdown. No bearer. |
GET|POST |
/unsubscribe |
public | Hosted unsubscribe page · Hosted unsubscribe form |
Auth, meta, regions
| Method | Path | Auth | Notes |
|---|---|---|---|
GET |
/api/regions |
bearer | List supported European cloud regions. Returns the selectable European data processing regions (Frankfurt, Ireland, Paris, Stockholm, Milan, Zurich). London (eu-west-2) is not offered because the United Kingdom is outside the EU. |
POST |
/api/templates/render |
bearer | Compile MJML or Markdown/HTML into responsive email markup |
GET |
/api/meta |
bearer | Runtime flags, attachment policy, safe mode, and compliance gates |
POST |
/api/auth/request-otp |
public | Email a sign-in code — Always returns a generic 200 so the endpoint cannot be used to enumerate accounts. |
POST |
/api/auth/verify-otp |
public | Exchange a sign-in code for a session bearer |
POST |
/api/auth/login-password |
public | Password login (when enabled) |
POST |
/api/auth/totp/verify |
public | Complete a sign-in with the second factor |
GET |
/api/auth/sso/start |
public | Start single sign-on in the browser |
GET |
/api/auth/sso/callback |
public | Identity provider callback |
GET |
/api/auth/totp |
bearer | Second factor status of the signed-in user |
POST |
/api/auth/totp/enroll |
bearer | Start authenticator setup |
POST |
/api/auth/totp/confirm |
bearer | Confirm the first code and turn the second factor on |
POST |
/api/auth/totp/disable |
bearer | Turn the second factor off (needs a valid code) |
POST |
/api/auth/totp/recovery-codes |
bearer | Replace the recovery codes (needs a valid code) |
POST |
/api/auth/logout |
bearer | Revoke the current session bearer |
GET |
/api/auth/me |
bearer | Current session identity |
GET |
/api/rbac/permissions |
bearer | List permission strings used by roles and tokens |
Workspaces, projects, access
| Method | Path | Auth | Notes |
|---|---|---|---|
POST |
/api/practices/{id}/integrations/ics-preview |
bearer | Preview ICS without sending |
POST |
/api/practices/{id}/integrations/test-termin |
bearer | Send test Termin with ICS attachment |
GET|PUT |
/api/practices/{id}/regions |
bearer | Get workspace primary and fallback European cloud regions · Update workspace primary and fallback European cloud regions |
GET |
/api/practices/{id}/audit-export |
bearer | Export DPO compliance audit report (CSV or JSON) |
GET |
/api/practices/{id}/avv |
bearer | Get Data Processing Agreement (AVV) template |
POST |
/api/practices/{id}/avv/sign |
bearer | Electronically sign Data Processing Agreement (AVV) |
GET |
/api/practices/{id}/consent-audit/export |
bearer | Workspace-wide consent ledger audit export (JSON or CSV). Requires consent.view (team tokens include it). Project tokens get 403 forbidden; the hint names consent.view. |
GET |
/api/practices/{id}/reports/recipients |
bearer | One row per recipient and send (message_id, to, subject, status, delivered_at, opened, clicked, bounced, complained, last_event). Empty workspace returns []. |
GET |
/api/practices/{id}/reports/recipients/export |
bearer | CSV with the same columns as the recipient send list |
GET |
/api/practices/{id}/reports/recipients/{email} |
bearer | Same rows as the recipient send list, for one address. Unknown address is 200 []. The body is never null. |
GET |
/api/projects/me |
bearer | Token self-introspection. Returns the workspace, projects, and scopes. Team tokens include audit.view and consent.view. Project tokens do not. Requires a practice token (wm_proj_... / wm_team_...). |
GET|POST |
/api/practices |
bearer | List workspaces visible to the caller · Create a workspace (alias of POST /api/projects) |
GET |
/api/practices/{id} |
bearer | Get one workspace |
POST |
/api/practices/{id}/pause |
bearer | Pause sending for a workspace |
POST |
/api/practices/{id}/resume |
bearer | Resume sending for a workspace |
POST |
/api/practices/{id}/plan |
platform admin | Set plan and monthly send cap (platform admin) |
GET |
/api/practices/{id}/overview |
bearer | Dashboard overview (plan usage, warm-up, reputation) |
GET |
/api/practices/{id}/onboarding |
bearer | Onboarding wizard status |
GET |
/api/practices/{id}/cutover |
bearer | Go-live checklist |
GET |
/api/practices/{id}/billing |
bearer | Billing summary for a workspace |
GET |
/api/practices/{id}/sandbox |
bearer | Sandbox checklist, progress and verified addresses |
GET|PUT|DELETE |
/api/practices/{id}/sso |
bearer | Single sign-on configuration, domains and SCIM tokens · Save the OpenID Connect connection · Remove the single sign-on connection |
POST |
/api/practices/{id}/sso/domains |
bearer | Claim an email domain and get the DNS TXT proof |
POST |
/api/practices/{id}/sso/domains/verify |
bearer | Check the DNS TXT record and verify the domain |
DELETE |
/api/practices/{id}/sso/domains/{domain} |
bearer | Remove a claimed domain |
POST |
/api/practices/{id}/scim-tokens |
bearer | Create a SCIM token (plaintext shown once) |
DELETE |
/api/practices/{id}/scim-tokens/{tokenID} |
bearer | Revoke a SCIM token |
GET|POST |
/api/practices/{id}/verified-recipients |
bearer | List test addresses and whether they are confirmed · Add a test address and send the confirmation link |
POST |
/api/practices/{id}/first-send |
bearer | Send a short test message to a confirmed address (guided first send) |
DELETE |
/api/practices/{id}/verified-recipients/{recipientID} |
bearer | Remove a test address |
POST |
/api/practices/{id}/production-access |
bearer | Request production access after the checklist is complete |
GET |
/api/practices/{id}/plan-grant/certificate |
bearer | Printable certificate for a time-limited plan |
POST |
/api/practices/{id}/billing/checkout |
bearer | Create a checkout session |
POST |
/api/practices/{id}/billing/portal |
bearer | Open the customer billing portal |
GET|PATCH |
/api/practices/{id}/compliance |
bearer | Read compliance settings (fields plus impressum_ok and impressum_error) · Update Impressum, from_name, legal sender fields, and compliance toggles, including unsubscribe_footer_enabled (visible unsubscribe line in the footer, default false; List-Unsubscribe headers are always sent). Requires settings.edit (team token or workspace role). Project send tokens (wm_proj_…) cannot call this endpoint. |
GET |
/api/practices/{id}/events |
bearer | Recent workspace event stream |
GET |
/api/practices/{id}/audit |
bearer | Workspace audit log. Requires audit.view (team tokens include it). Project tokens get 403 forbidden; the hint names audit.view. |
GET|POST |
/api/practices/{id}/projects |
bearer | List projects (route groups) in a workspace · Create a project (route group) |
PATCH |
/api/practices/{id}/projects/{projectID} |
bearer | Update a project |
GET|POST |
/api/practices/{id}/routes |
bearer | List sending routes · Create a sending route |
PATCH|DELETE |
/api/practices/{id}/routes/{routeID} |
bearer | Update route tracking settings · Delete a sending route (409 for the default, built-in, or in-use routes) |
POST |
/api/practices/{id}/routes/{routeID}/default |
bearer | Set the default route |
GET|POST |
/api/practices/{id}/api-tokens |
bearer | List practice API tokens (secrets never returned). Requires team.manage. · Create a team or project API token |
GET|POST |
/api/practices/{id}/roles |
bearer | List workspace roles. Requires team.manage. · Create a custom role (roles.manage) |
PATCH|DELETE |
/api/practices/{id}/members/{memberID} |
bearer | Update a member's role and project access · Remove a workspace member. DELETE requires team.manage. |
POST |
/api/practices/{id}/demo/seed |
bearer | Seed demo contacts and messages on one workspace |
POST |
/api/projects |
bearer | Create a workspace — Alias of POST /api/practices. Omit domain to start on the managed host. |
PATCH |
/api/practices/{id}/workspace |
bearer | Update workspace name and slug |
POST |
/api/practices/{id}/presets/seed |
bearer | Seed default routes and starter content |
GET |
/api/practices/{id}/cockpit |
bearer | Workspace health snapshot for the dashboard |
GET |
/api/practices/{id}/bounce-reasons |
bearer | Grouped bounce reasons |
GET |
/api/practices/{id}/content-scores |
bearer | Recent content-gate scores |
GET |
/api/practices/{id}/exports/bi |
bearer | BI CSV export — Query kind is stats, campaigns, bounces, scores, or audit. |
GET |
/api/practices/{id}/practice-semantics |
bearer | Practice mail semantics for the workspace |
POST |
/api/practices/{id}/practice-mail/check |
bearer | Check a practice-mail address |
GET |
/api/practices/{id}/analytics |
bearer | Workspace analytics |
GET |
/api/practices/{id}/stats |
bearer | Workspace send counters |
GET |
/api/practices/{id}/rates |
bearer | Daily send rates for one workspace |
GET |
/api/practices/{id}/unsub-feedback |
bearer | Unsubscribe reasons collected from the signed link |
GET|PUT |
/api/practices/{id}/weekly-report |
bearer | Read the weekly report schedule. Requires team.manage. · Update the weekly report schedule |
GET|POST |
/api/practices/{id}/members |
bearer | List workspace members · Invite a workspace member |
PATCH|DELETE |
/api/practices/{id}/roles/{roleID} |
bearer | Update a custom role · Delete a custom role |
DELETE |
/api/practices/{id}/api-tokens/{tokenID} |
bearer | Revoke an API token |
POST |
/api/practices/{id}/api-tokens/{tokenID}/rotate |
bearer | Rotate an API token. Requires team.manage. The replacement is returned once; the old token is revoked now or after a grace period of up to 7 days. |
POST |
/api/practices/{id}/projects/{projectID}/client-portal-tokens |
bearer | Create a client-portal link token — The raw token is returned once. |
SCIM provisioning
SCIM 2.0 for identity providers. Authenticate with a workspace SCIM token (wm_scim_…) as bearer. Needs the Compliance plan or higher. See Single sign-on and SCIM.
| Method | Path | Auth | Notes |
|---|---|---|---|
GET |
/scim/v2/ServiceProviderConfig |
SCIM token | SCIM service provider configuration |
GET |
/scim/v2/ResourceTypes |
SCIM token | SCIM resource types |
GET|POST |
/scim/v2/Users |
SCIM token | List members. Supports filter=userName eq "value" · Create a member with the default role |
GET|PUT|PATCH|DELETE |
/scim/v2/Users/{id} |
SCIM token | Get a member · Replace a member (active=false removes the membership) · Patch a member (active, displayName) · Remove a member from the workspace |
GET|POST |
/scim/v2/Groups |
SCIM token | List workspace roles as groups · Not supported: roles are managed in the dashboard |
GET|PATCH|PUT|DELETE |
/scim/v2/Groups/{id} |
SCIM token | Get a role as a group · Add or remove group members (assigns the role) · Not supported: roles are managed in the dashboard |
DNS and deliverability
| Method | Path | Auth | Notes |
|---|---|---|---|
GET |
/api/practices/{id}/dns-records |
bearer | DKIM / MAIL FROM / DMARC records to publish |
POST |
/api/practices/{id}/check-dns |
bearer | Verify DKIM / MAIL FROM DNS live |
GET |
/api/practices/{id}/dns-records/cloudflare-zone |
bearer | Download a BIND zone file for the workspace records |
GET|POST |
/api/practices/{id}/domain-connect/cloudflare |
bearer | One-click Domain Connect apply URL. GET and POST return the same apply_url and redirect_uri. |
GET |
/api/practices/{id}/deliverability |
bearer | SPF / DKIM / DMARC / BIMI posture and channel mix |
GET |
/api/practices/{id}/channel-mix |
bearer | Transactional vs broadcast send mix |
GET |
/api/practices/{id}/placement |
bearer | Subject-line placement / spam risk hints |
POST |
/api/practices/{id}/custom-domain |
bearer | Attach a custom sending domain |
POST |
/api/practices/{id}/restore-managed-domain |
bearer | Restore the managed sending domain |
POST |
/api/projects/{id}/custom-domain |
bearer | Attach a custom sending domain — Flat alias of POST /api/practices/{id}/custom-domain. Body field domain. |
POST |
/api/projects/{id}/restore-managed-domain |
bearer | Restore the managed sending host — Flat alias of POST /api/practices/{id}/restore-managed-domain. |
GET|PUT|DELETE |
/api/practices/{id}/bimi-logo |
bearer | Read the workspace BIMI logo · Upload the workspace BIMI logo · Remove the workspace BIMI logo |
Send and messages
| Method | Path | Auth | Notes |
|---|---|---|---|
POST |
/api/practices/{id}/send |
bearer | Send transactional or broadcast email |
GET|PUT |
/api/practices/{id}/sending-profile |
bearer | Get the workspace sending lane (managed or BYOK) and comparison view · Set the workspace sending lane (managed or BYOK) |
POST |
/api/practices/{id}/sending-profile/migrate |
bearer | Migrate a workspace between managed and BYOK with a cutover checklist |
POST |
/api/practices/{id}/send/batch |
bearer | Send a batch of up to 500 messages |
POST |
/api/practices/{id}/send/bulk |
bearer | Templated bulk send with per-recipient variables |
POST |
/api/practices/{id}/send/score |
bearer | Dry-run the content and consent gates. Does not send. Returns the score, reasons, and warnings a real send would see. from must be a bare address (invalid_from, retryable false). from_name sets the display name. |
POST |
/api/practices/{id}/send-test |
bearer | Send a test through the mailbox simulator or a real quality address. kind real returns 422 code suppressed when the address is suppressed, same as POST /api/practices/{id}/send. |
POST |
/api/practices/{id}/validate-emails |
bearer | Validate recipient addresses (syntax, disposable, risk) |
GET |
/api/practices/{id}/messages |
bearer | List messages. Same filters as /messages/search (q, status, to, route_id, project_id, days, limit, offset). Empty filters return the recent list. |
GET |
/api/practices/{id}/messages/search |
bearer | Search messages |
GET |
/api/practices/{id}/messages/{messageID} |
bearer | Get one message. messageID is the Werkmail UUID (id from the send). The provider message id returns 400 bad_id (retryable false). |
GET |
/api/practices/{id}/messages/{messageID}/timeline |
bearer | Message plus its delivery / engagement event timeline. messageID is the Werkmail UUID, not the provider message id. |
GET |
/api/practices/{id}/messages/{messageID}/events |
bearer | Delivery / engagement events for one message. messageID is the Werkmail UUID, not the provider message id. |
Drop-in send
| Method | Path | Auth | Notes |
|---|---|---|---|
POST |
/api/compat/resend/emails |
bearer | Resend-compatible send |
POST |
/api/compat/postmark/email |
bearer | Postmark-compatible send |
POST |
/api/compat/lettermint/send |
bearer | Lettermint-compatible send |
POST |
/api/compat/lettermint/emails |
bearer | Lettermint-compatible send via /emails |
POST |
/api/compat/eu/send |
bearer | Alias of Lettermint-compatible send |
POST |
/api/compat/resend/emails/batch |
bearer | Resend-compatible batch send (max 100) |
GET |
/api/compat/resend/emails/{id} |
bearer | Resend-compatible retrieve one email |
POST |
/api/compat/postmark/email/batch |
bearer | Postmark-compatible batch send (1-500) |
POST |
/api/compat/lettermint/send/batch |
bearer | Lettermint-compatible batch send (max 100) |
POST |
/api/compat/eu/send/batch |
bearer | Alias of the Lettermint-compatible batch send — Maximum 100 messages. |
POST |
/api/compat/lettermind/send |
bearer | Typo alias of the Lettermint-compatible send — Prefer /api/compat/lettermint/send. |
POST |
/api/compat/lettermind/emails |
bearer | Typo alias of the Lettermint-compatible emails send — Prefer /api/compat/lettermint/emails. |
POST |
/api/compat/lettermind/send/batch |
bearer | Typo alias of the Lettermint-compatible batch send — Prefer /api/compat/lettermint/send/batch. |
Flat messages and account stream
| Method | Path | Auth | Notes |
|---|---|---|---|
GET|POST |
/api/emails |
bearer | List recent messages for the token's own workspace / project — Newest first. The body is a JSON array (empty => [], never 404). When more pages exist the opaque cursor is returned in the X-Next-Cursor response header; pass it back as cursor. A project token sees only its own project. · Send with workspace from the token |
POST |
/api/emails/send-bulk |
bearer | Templated bulk send (flat alias) |
GET |
/api/emails/stats |
bearer | Lightweight counters for the token's own workspace / project — Sent, delivered, bounced, complained, opened, and clicked over a window (max 90 days). |
GET |
/api/emails/{id} |
bearer | Status of one message for the token's own project — Poll-based status check after a 202 send, so webhooks are optional. Status transitions through queued, processed, delivered, opened, clicked, soft_bounced, hard_bounced, complained, unsubscribed, or suppressed. |
GET |
/api/events |
platform admin | Account-wide recent delivery events |
GET |
/api/rates |
platform admin | Account-wide daily send rates |
Suppressions
| Method | Path | Auth | Notes |
|---|---|---|---|
GET|POST |
/api/practices/{id}/suppression |
bearer | List suppressed addresses · Add suppressed addresses (team / project / route scope) |
POST |
/api/practices/{id}/suppression/import |
bearer | Import suppressions from CSV |
GET |
/api/practices/{id}/suppression/export |
bearer | Export suppressions as CSV |
POST |
/api/practices/{id}/suppression/bulk-delete |
bearer | Bulk-delete suppressions (max 1000) |
DELETE |
/api/practices/{id}/suppression/{suppressionID} |
bearer | Delete one suppression |
GET|POST |
/api/suppressions |
bearer | List suppressions for a workspace — Query project_id is the workspace id. · Add a suppression — Body or query project_id is the workspace id. |
DELETE |
/api/suppressions/{suppressionID} |
bearer | Delete one suppression — Query project_id is the workspace id. |
Contacts, lists, campaigns, journeys
| Method | Path | Auth | Notes |
|---|---|---|---|
GET|POST |
/api/practices/{id}/topics |
bearer | List marketing topics for a workspace · Create a marketing topic |
PUT |
/api/practices/{id}/topics/order |
bearer | Set the topic display order atomically. Body {"topic_ids":[...]}; listed topics come first, the rest keep their relative order. Unknown or duplicate ids return 400 |
GET|PATCH|DELETE |
/api/practices/{id}/topics/{topicID} |
bearer | Read a single topic · Update topic name/description/visibility (default_subscription immutable). Partial update: omitted fields keep their value, an explicit empty description clears it. Sending default_subscription returns 422 code immutable_field. The response always includes description (empty string when unset). Topics and preference-center writes require projects.manage, contacts.manage, or settings.edit, not a project send token. Compliance/legal stays settings.edit only. · Delete a topic |
GET |
/api/practices/{id}/contacts/lookup |
bearer | Look up a contact by email and return the signed preference link — Returns preference_url plus signed p, e, t for GET/POST /api/public/portal and the /preferences SPA. Scoped to the token's workspace. |
GET|PUT |
/api/practices/{id}/contacts/{contactID}/topics |
bearer | List a contact's topic subscriptions · Replace a contact's topic subscriptions atomically |
GET|PUT|PATCH |
/api/practices/{id}/preference-center |
bearer | Get the workspace Preference Center branding · Replace the branding (full replacement, omitted fields are cleared) · Update single fields (partial, omitted fields keep their value). channels configures which consent channels contacts see on the public page, their order, label and description. Colours must be hex (#rgb, #rrggbb, #rrggbbaa), the logo must be an https URL; empty string clears a field |
GET|PUT |
/api/practices/{id}/segments/{segmentID}/members |
bearer | List a segment's explicit members (static membership) · Replace a segment's explicit membership atomically (marks segment static) |
GET |
/api/practices/{id}/reports/campaigns/{campaignID} |
bearer | Campaign aggregate report (sent/delivered/open/click + rates) |
GET |
/api/practices/{id}/contacts/{contactID}/consent |
bearer | Per-contact consent ledger |
GET |
/api/practices/{id}/contacts/{contactID}/consent-history |
bearer | Alias of GET .../consent |
POST |
/api/practices/{id}/dsar-export |
bearer | DSAR export for one contact (body contact_id). 202 when ready. |
GET |
/api/practices/{id}/contacts/{contactID}/export |
bearer | DSAR-ready contact export |
GET|PATCH|DELETE |
/api/practices/{id}/contacts/{contactID} |
bearer | Get one contact · Update a contact · Erase a contact (DSAR) |
GET|POST |
/api/practices/{id}/lists |
bearer | List contact lists · Create a contact list |
GET |
/api/practices/{id}/lists/stats |
bearer | Subscriber and active counts per list |
GET|POST |
/api/practices/{id}/lists/{listID}/subscribers |
bearer | List subscribers of a list · Add a subscriber — Without status=active, the response includes confirm_token for GET /api/public/confirm?token=. status=active or skip_doi=true skips DOI and writes a newsletter-channel consent grant (source active_import). The public subscribe API does not return a confirm token. |
GET|POST |
/api/practices/{id}/campaigns |
bearer | List broadcast campaigns · Create, schedule, or queue a broadcast campaign |
GET |
/api/practices/{id}/contacts |
bearer | List contacts |
GET|POST |
/api/practices/{id}/fields |
bearer | List custom contact fields · Create a custom contact field |
DELETE |
/api/practices/{id}/fields/{fieldID} |
bearer | Delete a custom contact field |
GET|POST |
/api/practices/{id}/segments |
bearer | List segments · Create a segment |
GET|PATCH|DELETE |
/api/practices/{id}/segments/{segmentID} |
bearer | Read a single segment · Update a segment · Delete a segment |
GET |
/api/practices/{id}/segments/{segmentID}/preview |
bearer | Preview which contacts match a segment |
GET|POST |
/api/practices/{id}/templates |
bearer | List message templates · Create a message template |
GET|PATCH|DELETE |
/api/practices/{id}/templates/{templateID} |
bearer | Read a single message template · Update a message template · Delete a message template |
DELETE |
/api/practices/{id}/lists/{listID} |
bearer | Delete a subscriber list |
POST |
/api/practices/{id}/lists/{listID}/subscribers/import |
bearer | Import subscribers — JSON emails array, or a CSV upload. |
DELETE |
/api/practices/{id}/lists/{listID}/subscribers/{subID} |
bearer | Remove a subscriber |
GET|PATCH |
/api/practices/{id}/campaigns/{campaignID} |
bearer | Get one campaign · Update a draft campaign |
POST |
/api/practices/{id}/campaigns/{campaignID}/start |
bearer | Queue a campaign now |
POST |
/api/practices/{id}/campaigns/{campaignID}/schedule |
bearer | Schedule a campaign — Body field scheduled_at (RFC3339). |
POST |
/api/practices/{id}/campaigns/{campaignID}/cancel |
bearer | Cancel a campaign |
POST |
/api/practices/{id}/campaigns/{campaignID}/test |
bearer | Send a campaign test |
GET |
/api/practices/{id}/campaigns/{campaignID}/stats |
bearer | Campaign delivery stats |
GET |
/api/practices/{id}/campaigns/{campaignID}/links |
bearer | Campaign link click stats |
GET|PUT |
/api/practices/{id}/campaigns/{campaignID}/variants |
bearer | A/B variant stats · Replace A/B variants |
POST |
/api/practices/{id}/campaigns/{campaignID}/ab-winner |
bearer | Pick the A/B winner and send the remainder |
GET|POST |
/api/practices/{id}/journeys |
bearer | List journeys · Create a journey |
POST |
/api/practices/{id}/journeys/seed-termin-recall |
bearer | Seed the termin-to-recall journey |
GET|PATCH|DELETE |
/api/practices/{id}/journeys/{journeyID} |
bearer | Get one journey · Update a journey · Delete a journey |
Webhooks, alerts, inbound
| Method | Path | Auth | Notes |
|---|---|---|---|
GET|PATCH |
/api/practices/{id}/inbound |
bearer | Inbound receiving status and MX checklist · Update inbound webhook URL |
POST |
/api/practices/{id}/inbound/enable |
bearer | Enable inbound receiving for in. |
GET|POST |
/api/practices/{id}/inbound-hooks |
bearer | List inbound send hooks · Create inbound send hook |
GET|POST |
/api/practices/{id}/alerts |
bearer | List Slack/Teams alert endpoints · Create Slack or Teams alert endpoint |
GET|POST |
/api/practices/{id}/webhooks |
bearer | List signed outbound webhooks · Create signed outbound webhook |
DELETE |
/api/practices/{id}/webhooks/{webhookID} |
bearer | Delete a webhook |
POST |
/api/practices/{id}/webhooks/{webhookID}/test |
bearer | Send a test webhook delivery |
GET |
/api/practices/{id}/webhooks/{webhookID}/deliveries |
bearer | Webhook delivery log (paginated, latest first) |
POST |
/api/practices/{id}/webhooks/{webhookID}/deliveries/{deliveryID}/replay |
bearer | Replay a delivery |
POST |
/api/practices/{id}/webhooks/{webhookID}/enable |
bearer | Enable a webhook |
POST |
/api/practices/{id}/webhooks/{webhookID}/rotate-secret |
bearer | Rotate the webhook signing secret |
GET |
/api/practices/{id}/inbound/messages |
bearer | Recently received inbound messages |
DELETE |
/api/practices/{id}/alerts/{alertID} |
bearer | Delete a Slack or Teams alert |
POST |
/api/practices/{id}/alerts/{alertID}/test |
bearer | Send a test alert |
POST |
/api/practices/{id}/inbound/disable |
bearer | Disable inbound receiving |
POST |
/api/practices/{id}/inbound/check |
bearer | Re-check inbound DNS |
POST |
/api/practices/{id}/inbound/apply-dns |
bearer | Publish inbound MX when DNS apply is enabled |
GET |
/api/practices/{id}/inbound/messages/{messageID} |
bearer | Get one inbound message |
DELETE |
/api/practices/{id}/inbound-hooks/{hookID} |
bearer | Delete an inbound send hook |
POST |
/api/practices/{id}/inbound-hooks/{hookID}/rotate |
bearer | Rotate an inbound hook secret — The new secret is returned once. |
POST |
/api/practices/{id}/inbound-hooks/{hookID}/test |
bearer | Send a test message through an inbound hook |
Public
| Method | Path | Auth | Notes |
|---|---|---|---|
POST |
/api/public/hooks/{id}/send |
public | Inbound send webhook (Zapier/Make/n8n) — Authenticate with header X-Werkmail-Hook-Secret. |
Same body as /api/practices/{id}/send. |
|||
POST |
/api/public/ses-inbound |
public | Inbound receiving event notifications |
GET|POST |
/api/public/portal |
public | Unified preference center payload — Two auth modes, mutually exclusive. (1) token — long-lived contact portal token from the contact profile (?token=). (2) signed link — p (practice UUID), e (email), t (HMAC from PreferenceCenterURL / contacts/lookup). Errors use {code,error,hint}: bad_auth (400), contact_not_found (404). · Save preference center changes (JSON) — Same auth as GET (token or p/e/t in the body or query). |
GET|POST |
/api/public/preferences |
public | Branded preference center page (signed link) — Redirects to /preferences SPA; prefer /api/public/portal JSON. · Save topic checkboxes or unsubscribe from all (signed link) |
GET |
/api/public/domain-connect/template |
public | Domain Connect template for the provider |
GET |
/api/public/domain-connect/meta |
public | Domain Connect discovery metadata |
GET|POST |
/api/public/unsub |
public | One-click unsubscribe (signed link) · One-click unsubscribe (RFC 8058 List-Unsubscribe=One-Click) |
GET |
/api/public/confirm |
public | Double opt-in confirmation |
GET|POST |
/api/public/abuse |
public | Abuse desk information · Submit an abuse report |
POST |
/api/public/subscribe |
public | Public list subscribe (double opt-in) |
POST |
/api/public/waitlist |
public | Join the product waitlist |
GET |
/api/public/landing |
public | Public landing content for a workspace host |
GET |
/api/public/site |
public | Public site catalog and legal pointers |
GET |
/api/public/client-portal |
public | Client portal dashboard — Query token is the client-portal token. |
GET |
/api/public/bimi/{id} |
public | Public BIMI logo. Path id is {workspaceUUID}.svg. 200 SVG or 404 bimi_logo_not_hosted. |
POST |
/api/public/creem-webhook |
public | Billing provider webhook — Not a send API. Signed with the creem-signature header. |
GET |
/api/public/creem-return |
public | Billing checkout return URL — Not a send API. |
POST |
/api/public/ses-events |
public | Provider delivery-event callback — Not a customer send API. Signature-checked provider notifications. |
POST |
/api/public/unsubscribe |
public | Retired unsubscribe endpoint — Always 410. Use the signed /api/public/unsub link. |
GET |
/api/public/waitlist/admin-action |
public | Signed waitlist admin link from email — Not a bearer API. |
GET|POST |
/api/public/waitlist/join |
public | Preview a waitlist invite · Complete a waitlist invite |
POST |
/api/public/access/redeem |
public | Redeem an access code for an account invitation |
GET |
/api/public/status |
public | Platform status, 90 day uptime and incidents |
GET |
/api/public/verified-recipients/confirm |
public | Confirm a sandbox test address from the emailed link |
Operator routes
Authenticated routes for operators. Rows marked platform admin reject team and project tokens.
| Method | Path | Auth | Notes |
|---|---|---|---|
GET |
/api/ops/digest |
platform admin | Ops digest (abuse + cheap deliverability snapshot, platform admin) |
GET |
/api/ops/jobs |
platform admin | List background jobs by status and kind (default failed), cursor paginated, no payloads |
POST |
/api/ops/jobs/{id}/retry |
platform admin | Requeue a failed job |
POST |
/api/ops/jobs/{id}/discard |
platform admin | Discard a failed or pending job with a reason |
GET |
/api/ops/vdm |
platform admin | Cheap deliverability snapshot (platform admin). Advisor + ISP metrics. Never subscribes to Global / $1,250 dashboard. |
POST |
/api/practices/wire-event-destinations |
platform admin | Attach delivery-event destinations on every workspace sending profile |
GET|PUT |
/api/admin/user-entitlements |
platform admin | Read a user's workspace entitlement · Set a user's workspace entitlement |
GET|POST |
/api/admin/access-codes |
platform admin | List access codes · Create an access code (plaintext returned once) |
POST |
/api/admin/access-codes/{id}/disable |
platform admin | Disable an access code |
POST |
/api/admin/access-codes/{id}/provision |
platform admin | Bulk provision addresses against an access code |
POST |
/api/admin/prize-grants |
platform admin | Give winners a time-limited plan |
GET|POST |
/api/admin/incidents |
platform admin | List status page incidents · Open a status page incident |
POST |
/api/admin/incidents/{id}/updates |
platform admin | Post an incident update and change its status |
GET |
/api/admin/production-access |
platform admin | List production access requests |
POST |
/api/admin/production-access/{id}/decide |
platform admin | Approve or reject a production access request |
POST |
/api/admin/practices/{id}/approve-production |
platform admin | Move a workspace to production without a request |
GET |
/api/abuse |
platform admin | List abuse-desk reports. Requires platform.admin. |
GET|PATCH |
/api/abuse/{reportID} |
platform admin | Get one abuse report · Update an abuse report |
POST |
/api/abuse/{reportID}/messages |
platform admin | Reply on an abuse report |
GET |
/api/abuse/{reportID}/attachments/{attachmentID} |
platform admin | Download an abuse-report attachment |
GET |
/api/waitlist |
platform admin | List waitlist signups |
POST |
/api/waitlist/{id}/accept |
platform admin | Accept a waitlist signup |
POST |
/api/waitlist/{id}/reject |
platform admin | Reject a waitlist signup |
GET|PUT |
/api/smtp |
platform admin | Read the optional SMTP relay settings — The password is never returned. · Update the optional SMTP relay settings — Omit password to keep the stored secret. |
POST |
/api/smtp/enabled |
platform admin | Enable or disable the SMTP relay — Body field enabled (boolean). |
POST |
/api/smtp/probe |
platform admin | Check SMTP authentication and TLS without sending |
POST |
/api/smtp/test |
platform admin | Send one SMTP test message — Body field to. |
POST |
/api/demo/seed |
bearer | Seed the showcase workspace |