Skip to content

Scope new-account pacing to the risk: don't throttle a customer's own ESP by account age #122

Closed

Description

@clbotdev

Background

#73 (merged in #91) paces every marketing send of a hosted organization younger than newAccountDays (default 30): its daily allowance (the marketing ramp's, or the monthly cap / 30) is split into hourly slices (apps/api/src/billing/entitlements.ts:526-561, apps/api/src/abuse/pacing.ts). This applies whatever the delivery source is, including the team's own ESP.

Per ADR 0005 the pacing itself is in scope (it's app-level scheduling, not sending infrastructure). The problem is who it protects:

  • Through the customer's own ESP, the sending reputation is theirs and the ESP already enforces its own limits (SES quota, max send rate, sandbox). A beehiiv switcher with a warmed SES account and a 50k list is slowed down for 30 days. The only way out is an operator running abuse.ts pacing-exempt. These are the users beehiiv switch-ready: everything needed before inviting beehiiv switchers #79 targets.
  • What SendLit actually risks on those sends is its own assets: the tracking/view-in-browser domain (api.sendlit.app) that every email links to, its payment account and its standing with providers. Age-based pacing only delays spam; it doesn't stop it.
  • Through an organization's shared mailbox, one team can burn an account other teams depend on, so pacing new accounts still makes sense there.

Proposed fix

  1. Shared mailbox (pin.type === "organization"): keep new-account pacing as it is.
  2. Team's own ESP: no age-based pacing. Instead:
  3. Warm-up for a cold domain stays opt-in for the customer (Day-one warm-up answer: warm-up guide and self-serve daily send caps for new domains / SES accounts #67 caps, Warmup scheduler #5 scheduler), not enforced by SendLit.
  4. Decide together with Custom tracking domain and custom return-path (bounce) domain per team #50 (custom tracking domain per team). Once a team's links use its own domain, most of the risk to api.sendlit.app goes away, and so does most of the reason to gate own-ESP sends.

Open question: should the plan's marketing ramp (advanceMarketingRamp, fair-use plans) also skip own-ESP sends? It's a billing/fair-use control rather than abuse, so it's out of scope here unless we decide otherwise.

Acceptance criteria

  • New-account pacing applies only to sends through a shared mailbox
  • A new account's first large broadcast through its own ESP is held for review; threshold configurable; hosted only, off for self-host
  • Held broadcasts show in the dashboard with a clear status, and in abuse.ts queue
  • Docs, REST/MCP descriptions and pacing status (broadcastPacing) updated
  • Abuse events sent to PostHog (src/observability)

Related: #73, #75, #79, #50, #74, #72, #25, #67.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions