Open source · Circuit breaker
The Circuit Breaker
for agents and outreach.
Prevent rogue agents, growth loops, and background jobs from spamming your users—across every surface. Shared permit. One journal.
Quick Start
JS/TS client from npm. Point it at a self-hosted SoftStop API (pnpm dev → http://localhost:3000).
Alternates: curl -fsSL https://softstop.vercel.app/install.sh | bash
or npm i https://softstop.vercel.app/softstop.tgz
Self-host for production. softstop.vercel.app is the live demo and SDK CDN — not a production SoftStop host.
Platform / lifecycle eng runs the SoftStop API. Growth, CRM, product, and agents call check/record at send time.
import { SoftStop } from 'softstop'
const ss = new SoftStop({ url: 'http://localhost:3000' })
const d = await ss.check({ userId: 'u1', actionType: 'urgency' })
if (!d.allowed) {
// d.reason, d.explanation — optional d.suggestedActionType ("reminder" for urgency/interruption)
await ss.record({
decisionId: d.decisionId,
userId: 'u1',
actionType: 'urgency',
outcome: 'blocked',
blockReason: d.reason
})
return
}
How it works
Docs →Many systems. One shared journal per customer.
SoftStop is authorize-only: every pressure surface asks the same permit with check, then writes the outcome with record. Action types stay honest — urgency, discount, interruption, reminder. check is read-only for pressure; state advances on record — so two concurrent allows can both pass before either records.
Email, SMS, push, modal, ads, agent — each about to raise pressure.
Ask SoftStop with userId, actionType, surface.
Decision uses the same per-customer history — not a silo per tool.
Always write the outcome — including blocked — so the next system isn’t blind.
Touch hits the user. Journal entry lands. SoftStop OFF lands everything.
Touch does not fire. Still record blocked so other systems see it.
Action types
Four ways pressure shows up
Same permit path for every type — pressure kind → check → shared journal → allow or block.
Live example
7 days, many channels, one journal
Stacked escalations across email, SMS, push, and in-app — same customer, same SoftStop journal. SoftStop isn’t the messenger; it’s the shared permit every channel asks before it fires.
OFF → every touch LANDED (journal stays blind between systems). ON → excess shows BLOCKED because the journal already knows.
Death by a Thousand Touches
This is what multi-channel automation looks like without coordination.
Illustrative demo outcomes — not measured LTV or brand metrics
Why SoftStop
GitHub →Thousands of customers. Many departments. One journal — or none.
Growth, CRM, Support, Ads, Product, and Agents each fire urgency, discount, interruption, and reminder without seeing what the others already did. Pressure climbs per person — Happy → Annoyed → Frustrated → Angry → Churned. SoftStop doesn’t become your CDP — a CDP stores identity and journeys; SoftStop only gates whether an actor may raise pressure right now. Every system checks and records into one per-customer journal so landings stay capped.
Without SoftStop · blind silos
With SoftStop · one journal
Cross-system journal
Every record lands in one per-customer history — Growth isn’t blind to CRM.
Authorize only
Doesn’t send mail or write copy. Your stack owns the message — SoftStop owns the permit.
Honest adoption
verify + health catch orphans. Partial wiring isn’t false confidence.


