SoftStop

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

install
# Pressure rises fast. SoftStop decides if it should.
$ npm i softstop

JS/TS client from npm. Point it at a self-hosted SoftStop API (pnpm devhttp://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.

usage
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
check →
SMS
check →
Push
check →
Popup
check →
Ads
check →
Agent
check →
email · urgencyallow
sms · reminderallow
push · urgencyblock
popup · discountblock
Allow
record landed
Block
record blocked
01
Any system

Email, SMS, push, modal, ads, agent — each about to raise pressure.

02
check()

Ask SoftStop with userId, actionType, surface.

03
Shared journal

Decision uses the same per-customer history — not a silo per tool.

04
record()

Always write the outcome — including blocked — so the next system isn’t blind.

LandedOFF or allowed

Touch hits the user. Journal entry lands. SoftStop OFF lands everything.

BlockedON + over limit

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.

urgency
time pressure
discount
price / promo
interruption
modal / popup
reminder
soft nudge
Allow
landed
Block
blocked

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.

Pressure
Relaxed 0
😵

Death by a Thousand Touches

This is what multi-channel automation looks like without coordination.

Illustrative demo outcomes — not measured LTV or brand metrics

Without SoftStop
User churned
Marked as spam
$0 lifetime value
Damaged brand reputation
With SoftStop
User retained
Healthy engagement
$247 lifetime value
Positive brand sentiment
0
Touches
0
Blocked
0
Peak
8
Channels
Try again with SoftStop ON

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

Growth
urgency blast
CRM
discount drip
Support
reminder ping
Ads
retarget
Product
interruption
Agents
outreach
cust_1042
cust_881
cust_2207
cust_559
cust_3301
cust_774
cust_4156

With SoftStop · one journal

Growth
urgency blast
CRM
discount drip
Support
reminder ping
Ads
retarget
Product
interruption
Agents
outreach
cust_1042
cust_881
cust_2207
cust_559
cust_3301
cust_774
cust_4156

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.