Catching churn risk when a competitor appears in your customer's stack
By the time a customer's usage declines, the decision has usually been made. By the time they open the renewal conversation, it has definitely been made. Customer Success teams spend enormous effort on leading indicators for exactly this reason, and most of the ones available — login frequency, support ticket sentiment, champion departure — are indirect.
A competitor appearing in a customer's published vendor list is not indirect. It means the company has begun routing data through a system that competes with yours, and has said so in writing. That is about as unambiguous as a leading indicator gets.
The catch, which this post will not soft-pedal, is timing. Read to the end before you build anything.
The mechanism: snapshot, then diff
There is no push notification here. You build the signal by taking periodic snapshots of your customer base and comparing them.
# monthly: snapshot every customer domain
for domain in $(cat customers.txt); do
curl -sS "https://api.vendorstacks.com/v1/company/$domain" \
-H "Authorization: Bearer $VENDORSTACKS_KEY" \
| jq -c '{
domain,
found,
stack: .vendor_stack,
evidence: .subprocessor_urls,
scanned_at
}' >> "snapshots/$(date +%Y-%m).jsonl"
sleep 1.1
doneTwo fields carry the weight. vendor_stack is the state you diff. scanned_at tells you when the underlying data was actually gathered — which is not the same as when you fetched it, and is the field that determines how stale your signal is.
Use /v1/company for the sweep. It reads the index and never triggers a live scan, so a 400-customer run takes minutes and can't stall. Same 1 credit per lookup.
The diff logic
Compare this month's snapshot against last month's per domain, and classify each change:
{
"domain": "meridian-fleet.com",
"change": "vendor_added",
"category": "sms_messaging",
"vendor": "sinch",
"previous": ["twilio"],
"current": ["twilio", "sinch"],
"evidence": "https://meridian-fleet.com/legal/subprocessors",
"prev_scanned_at": "2026-06-02T10:11:04Z",
"curr_scanned_at": "2026-07-04T07:52:31Z",
"severity": "high"
}Four change types, in descending order of how much they should interrupt someone's day:
- A direct competitor added to your category. The signal you built this for. Someone is running a pilot, a bake-off, or a migration. Alert immediately.
- Your own vendor entry disappeared. Ambiguous but urgent. It can mean removal, or it can mean the page was restructured. Verify against the evidence URL before anyone panics — the source is right there.
- An adjacent-category vendor added. A platform that bundles your category, or a tool that implies a broader re-architecture. Worth a note on the account, not an alert.
- Stack churn generally. A customer replacing three vendors in a quarter is reorganizing something. Good context for the next QBR.
Alert design
The failure mode of every automated CS alert is volume. Three rules keep this one credible:
Only alert on high severity. Competitor-in-category and your-vendor-removed go to a channel. Everything else lands in a weekly digest or on the account record. If the channel produces more than a handful of alerts a month for a normal-sized base, your severity rules are too loose.
Every alert carries the evidence URL. The first thing a CSM will do is ask whether it's real. Give them the page. An alert they can verify in ten seconds gets acted on; one they have to investigate gets ignored by the third occurrence.
Never alert on a miss. If this month's lookup returns found: false and last month's returned a stack, that is not a vendor removal. It means no public evidence was located this time — the page may have moved, or the format may have changed. Suppress the diff entirely when either side is a miss. Getting this wrong will generate a wave of false alarms in your first month and destroy trust in the signal permanently.
The honest part: detection lag
Here is what this signal cannot do. It cannot tell you a competitor entered the account the day it happened.
There are two lags stacked on each other. The first is the customer's own publication lag: a company adds a vendor, then updates its published list at whatever cadence its legal or compliance process runs — which might be same-week or might be next quarter. The second is refresh lag: the corpus is continuously refreshed, but any given entity is re-crawled on a schedule, and scanned_at tells you exactly when. Your own sweep cadence adds a third.
Practically: this is a signal that arrives weeks to months after the fact, not hours. That still beats most alternatives — a renewal conversation is the alternative — but it means you should design for it. Sweep monthly rather than daily; a daily sweep spends credits to re-read data that hasn't moved. For a small set of highest-value accounts where today's truth matters, use /v1/check?url=DOMAIN instead, which will live-scan a domain the index hasn't refreshed recently. Budget 15–90 seconds per call and a 120-second timeout.
And do not sell this internally as real-time monitoring. It is a periodic diff over a continuously refreshed corpus of public evidence. Described accurately it is a genuinely valuable signal; described as something it isn't, it will be judged against a standard it was never going to meet.
Cost
A 400-customer base swept monthly is at most 400 credits a month — less, because misses cost nothing. That's under $4 at the $10/1,100-credit pack, or about $3.30 at the $50 pack rate. See pricing for the packs. For a base of that size, one prevented churn pays for a decade of it.
The API reference has the full field list, and the GTM plays post covers the expansion-side version of the same diff — customers adding vendors that pair with your premium tier.
Get a key with 25 free credits and snapshot your 25 largest accounts today. Next month you'll have a diff, and you'll know within one cycle whether this signal is worth automating for your base.