HTTP API reference

Base URL is your deployment origin (for example http://localhost:8080). JSON endpoints expect Content-Type: application/json unless noted otherwise.

Public

GET /

HTML landing page.

GET /healthz

Liveness probe. Returns ok or 503 if MySQL health check fails when MySQL is configured for the app.

Gmail ingestion (Google Cloud)

POST /pubsub/gmail

Pub/Sub push delivery endpoint for Gmail watch notifications. Body is the standard Pub/Sub JSON envelope with base64-encoded Gmail notification in message.data. Returns 204 on success.

Payment provider webhooks

Public ingress that accepts raw provider webhooks (currently rapyd, stripe, trust_payments) and forwards them to one or more internal endpoints registered as provider routes. Signature is verified on the raw body using the access/secret stored on the matching ingress. Each matched route receives its own POST signed with X-Mailhub-Signature: sha256=<hex> using the route's HMAC secret.

POST /webhooks/providers/:provider/:ingress_key

Raw webhook ingress. Body must be the exact bytes sent by the provider (do not re-serialize). Returns 204 on accepted, 200 on duplicate or unrouted, 401 on invalid signature, 404 on unknown ingress.

Authentication

Token mode (MAILHUB_ADMIN_AUTH=token): set ADMIN_TOKEN. Sign in via POST /api/admin/login with { "token": "…" } to receive an HTTP-only session cookie mailhub_session, or send Authorization: Bearer <token> or header X-Mailhub-Admin-Token on each request.

Database mode (MAILHUB_ADMIN_AUTH=database): use POST /api/auth/login with email and password; session cookie mailhub_admin_session. If ADMIN_TOKEN is also set, the same Bearer / X-Mailhub-Admin-Token headers are accepted and treated as superadmin for automation.

Auth routes (database mode)

GET /api/auth/bootstrap

Returns { openRegistration: boolean }true only while no admin users exist.

POST /api/auth/login

Body: { "email": "…", "password": "…" } (password min 10 characters). Sets session cookie; response { ok, user, role, expiresAt }.

POST /api/auth/register

Body: same shape as login. Allowed only when there are zero admin users; creates the first superadmin. Returns 201 and session on success.

Legacy admin login (token mode)

POST /api/admin/login

Body: { "token": "…" }. Returns mailhub_session cookie.

POST /api/admin/logout

Clears admin session cookies. Body can be empty JSON.

Admin API

All routes below require an authenticated admin session or Bearer / X-Mailhub-Admin-Token when configured.

GET /api/admin/session

Returns authentication state: configured, authenticated, and in database mode authMode plus user when signed in.

GET /api/admin/status

Summary: alias route count, feature flags, mysqlConfigured, adminConfigured.

GET /api/admin/activity?limit=50

Recent monitoring events (limit 1–500, default from server config).

GET /api/admin/provider-webhook-events?limit=100&kind=ingress&provider=rapyd&ingress_key=site-a-prod

Provider webhook audit log: inbound ingress results and outbound forwards. List entries omit raw bodies; each row includes hasPayloadBody when bodies are captured (PROVIDER_WEBHOOK_CAPTURE_BODIES). Filters: kind ingress | forward, provider, ingress_key.

GET /api/admin/provider-webhook-events/:eventId

Single event including payloadBody when it was captured at ingress time, plus verificationSnapshot (signature headers and signed URL path) for inbound events.

POST /api/admin/provider-webhook-events/:eventId/replay

/api/admin/provider-webhook-events/:eventId/revalidate

Re-deliver a captured inbound webhook to a backend route. JSON body optional { "route_id": "…" }; if omitted and the ingress event matched exactly one route, that route is used. Returns { delivery } from the forwarder.

Alias routes (HTTP JSON)

Same behaviour on /api/admin/alias-routes and shorter /api/alias-routes (GET list, POST upsert, DELETE by alias). Use a normal admin session or Authorization: Bearer / X-Mailhub-Admin-Token with ADMIN_TOKEN. If MAILHUB_ALIAS_ROUTES_TOKEN is set, you may also send that value as Authorization: Bearer … or header X-Mailhub-Alias-Routes-Token — only for these alias-route endpoints (useful for CI or narrow automation).

GET /api/admin/alias-routes

List configured alias routes (secrets omitted). Same as GET /api/alias-routes.

POST /api/admin/alias-routes

Create or update a route. Body: aliasAddress, webhookUrl, optional webhookHmacSecret, description, active. Same as POST /api/alias-routes. Returns 201 and { route }.

DELETE /api/admin/alias-routes/:aliasAddress

Remove a route by alias email (URL-encoded). Same as DELETE /api/alias-routes/:aliasAddress.

Provider ingresses and routes

Authentication: normal admin session, Authorization: Bearer <ADMIN_TOKEN>, or — when set — Authorization: Bearer <MAILHUB_API_TOKEN>. Error responses follow { "message": "…", "code": "…" }. verify_secret and webhook_hmac_secret are write-only and never returned on read.

GET /api/provider-ingresses

List provider ingresses (without secrets). Returns { ingresses: [...] }.

GET /api/supported-providers

List supported provider names with form-field hints for the admin UI. Returns { providers: [{ name, requiresVerifySecret, fields }] }.

POST /api/provider-ingresses

Upsert by (provider, key). Body: provider (rapyd | stripe | trust_payments | nikulipe | shift4 | paypal), key, display_name, verify_access_key, verify_secret (required on create except pass-through providers), active. verify_secret is updated only when provided non-empty. Returns { ingress }.

DELETE /api/provider-ingresses/:provider/:key

Delete an ingress and (cascade) its routes. Returns 204 or 404.

GET /api/provider-routes?ingress_key=site-a-prod

List provider routes (without secrets). Optional filter by ingress_key (+ ingress_provider, default rapyd). ingress_provider may be rapyd, stripe, trust_payments, nikulipe, shift4, or paypal.

POST /api/provider-routes

Upsert by (ingress_id, description). Body: ingress_provider, ingress_key, description, matcher_jsonpath, matcher_regex, webhook_url, webhook_hmac_secret (required on create), priority, active. Returns { route }.

DELETE /api/provider-routes/:id

Remove a route by id. Returns 204 or 404.

GET /api/provider-metrics

Snapshot of provider webhook counters and forward attempt histogram.

POST /api/admin/gmail-watch

Start or renew a watch. Body: emailAddress, optional topicName, labelIds, labelFilterAction.

GET /api/admin/gmail-watch

List saved Gmail watch states.

POST /api/admin/gmail-watch/renew

Body: optional { "emailAddress": "…" } to renew one mailbox, or omit to renew all saved watches.

Administrators (database mode only)

GET /api/admin/users

List admin users (email, role, createdAt).

POST /api/admin/users

Create an admin. Body: email, password (min 10 chars), role admin | superadmin. Requires superadmin session (or Bearer token when ADMIN_TOKEN is set).

HTML UI (browser)

Home Admin console