Skip to content
PostMTA Hosted
StatusSign inOpen panelRequest access
Open menu

Docs

Open in panel → /app · fbl

FBL Ingest

PostMTA Hosted accepts FBL (complaint) feedback via a single shared-secret endpoint and (Release B) drives suppressions from it.

Endpoint

POST /v1/fbl/ingest requires X-FBL-Secret header matching FBL_INGEST_SECRET.

{
  "provider": "postmark",
  "feedback_type": "abuse",
  "recipient_hash": "sha256:redacted@example.com",
  "raw": { /* free-form provider payload */ }
}

Returns 200 { ok: true, id } or 401 { error: 'unauthorized' }.

Storage

Rows land in fbl_events with the panel Realtime channel (when enabled) so the FBL panel section refreshes without polling.

Release B — FBL → Suppressions (M55)

scripts/fbl-poll.ts (or the in-process poller if RUN_FBL_POLLER=1) walks fbl_events created in the last FBL_POLL_WINDOW_HOURS (default 24) and upserts suppressions rows. Idempotent on (workspace_id, address, reason). The dry-run path prints what it would upsert; --apply hits Supabase.

# dry-run, prints candidate suppressions
node --experimental-strip-types scripts/fbl-poll.ts

# apply (requires SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY)
node --experimental-strip-types scripts/fbl-poll.ts --apply

Release B — Admission reject (M56)

runSend and the SMTP path call checkSuppressed at the top of every send. If any recipient address is on the suppression list, the call short-circuits:

Path Behaviour on suppressed address
POST /v1/send / POST /v1/panel/send 422 SUPPRESSED with { suppressed: [...], reasons: [...] }
SMTP DATA 550 5.7.1 Recipient suppressed: <addr> (fbl,bounce)

Mock provider honours MOCK_SUPPRESS_ADDRESS to keep the smoke suite deterministic. Live provider uses the partial index btree(workspace_id, address) for sub-ms admission.

Operational notes

Last verified: Sat Jul 11 2026 17:00:00 GMT-0700 (Pacific Daylight Time). Cross-link maintained byscripts/check-docs-drift.sh (M54).

← All docs