Skip to content

Add webhook-based email analytics/suppression ingestion path to unblock provider adapters (blocking #29553) #29828

Description

@wakqasahmed

Context

PR #29553 adds the minimal email-provider adapter seam (AdapterManager resolving EmailServiceWrapper's provider) discussed in #25367, with ghost-ses-email-adapter as the first intended consumer. It's currently blocked:

  • @acburdine on Added email provider adapter wiring #29553 (comment): "the main blocker to a standardized email adapter pattern isn't the email sending bits itself, it's the analytics/suppression bits... Ghost polls Mailgun's Events API for that analytics/suppression data, and we'd need to refactor some stuff in Ghost internally to allow for webhooks to be utilized instead in order to unblock full adapter support."
  • @9larsons on Added email provider adapter wiring #29553 (comment): applied the blocked label — "the core team will likely take this over or adapt contributions."
  • @9larsons earlier on Add email adapter pattern for pluggable email providers #28247 (comment) gave the fuller diagnosis: email-analytics-provider-mailgun.js is coupled to Mailgun's Events polling API, while every other candidate provider (SES, Postmark, SendGrid, etc.) is webhook-based. An adapter can send fine but analytics (opens/clicks/delivered/bounced) and suppression silently go dark, because the poll loop has nothing to poll and the bounce/complaint suppression list never fills — so Ghost keeps emailing dead addresses.

Both threads point at the same structural fix: ghost/core/core/server/services/email-analytics/ needs a webhook ingestion path alongside (or instead of) the polling path, so a non-Mailgun adapter has somewhere to deliver events.

Proposal

Open this as its own tracked issue, decoupled from any specific adapter PR, so the scope is:

  1. Add a webhook ingestion entry point to email-analytics-service.js / email-analytics-service-wrapper.js that accepts normalized delivery/open/click/bounce/complaint events from an adapter, independent of email-analytics-provider-mailgun.js's poll loop.
  2. Feed suppression (bounce/complaint) events from that path into the same suppression list Mailgun polling currently populates, so adapters don't silently leave it empty.
  3. Leave the Mailgun poll path untouched — this is additive, not a replacement — matching @9larsons' framing on Add email adapter pattern for pluggable email providers #28247 of "send-only seam, edges documented, scheduler untouched" as an achievable first slice.
  4. Document (in the adapter interface docs, not just code comments) which events an adapter is expected to push and in what shape, so AdapterManager-based adapters have a contract to implement against.

Why file this separately

#29553 is blocked specifically because this refactor doesn't exist yet, and the fix isn't scoped to that PR's diff — it's core analytics plumbing. Tracking it here gives the core team (or contributors, per @9larsons' "adapt contributions" note) a concrete, reviewable unit of work, and gives #29553 (and any future adapter PR) a real merge path instead of an open-ended "we're looking into it."

Happy to help scope or prototype the webhook ingestion path against ghost-ses-email-adapter as a concrete test case if that's useful — SES already delivers bounce/complaint/delivery events via SNS webhooks, so it's a reasonably representative first adapter to validate the contract against.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    affects:serverIssues relating to the server or core of Ghost

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions