Complete Guide
10 automation recipes with code. From Slack alerts to AI-generated executive briefings, build the competitive intelligence workflows your team actually needs.
Manual competitive monitoring is a process that every team starts and few maintain. The pattern is predictable: someone volunteers to track competitors, does it diligently for a few weeks, then gets pulled into a launch, a reorg, or a fire drill. Two months later, nobody can remember what the competitor's pricing looked like last quarter.
Automation solves this by making competitive intelligence a system rather than a task. Systems run whether people are busy or not. The question is not whether you should automate — it is which parts to automate first.
What automation solves
What still needs a human
The best competitive intelligence programmes automate the collection and distribution layers completely, then give humans the time and data to focus on analysis and action. That is the approach this guide takes.
Every automation in this guide uses one or more layers of RivalCheck's platform. Understanding the stack helps you choose the right tool for each recipe.
DETECTION
RivalCheck monitors pages
NOTIFICATION
Webhooks + digests
INTEGRATION
REST API + MCP
INTELLIGENCE
AI analysis + cards
RivalCheck visits competitor pages on a schedule (6-hour to 24-hour intervals depending on plan), takes screenshots, and uses AI to detect meaningful changes. Changes are categorised (pricing, features, messaging, content, company) and severity-rated. This layer runs automatically — no integration needed.
Webhooks push change.detected events to your endpoint in real time. Email digests (daily or weekly) summarise changes for your team. These are the triggers for most automations in this guide.
The REST API provides endpoints for competitors, changes, battle cards, and the competitive landscape. The MCP server wraps these in a conversational interface for AI agents. Pull data on demand or in response to webhook events.
AI generates strategic analysis for every change, produces and maintains battle cards, and powers the competitive landscape view. Use this layer's outputs directly or as inputs to your own AI workflows.
# Webhook event payload — the trigger for most automations
{
"event": "change.detected",
"timestamp": "2026-03-24T09:15:00Z",
"data": {
"id": 891,
"competitor": { "id": 42, "name": "Acme Corp" },
"page_url": "https://acmecorp.com/pricing",
"category": "pricing",
"severity": "high",
"title": "Enterprise plan price increased from $299 to $349/mo",
"summary": "Acme Corp raised their Enterprise tier by 17%...",
"strategic_analysis": "This price increase signals confidence...",
"detected_at": "2026-03-24T09:15:00Z"
}
}
The most popular automation: push every competitive change into a dedicated Slack channel so the whole team sees it in real time. High-severity changes get highlighted.
Option A: Direct webhook to Slack (zero code)
If you just want raw notifications, point your RivalCheck webhook directly at a Slack incoming webhook URL. Every change event gets posted as a JSON block. This works but is not pretty.
Option B: Formatted Slack messages (recommended)
A small webhook handler formats change events into rich Slack messages with colour-coded severity, competitor name, and strategic analysis:
#!/usr/bin/env python3
"""
RivalCheck -> Slack notification service
Receives webhook events and posts formatted messages to Slack.
"""
from flask import Flask, request
import requests
import os
app = Flask(__name__)
SLACK_WEBHOOK = os.environ["SLACK_WEBHOOK_URL"]
SEVERITY_COLORS = {
"high": "#dc2626", # red
"medium": "#f59e0b", # amber
"low": "#6b7280" # gray
}
CATEGORY_EMOJI = {
"pricing": "Price change",
"features": "Feature update",
"messaging": "Messaging shift",
"content": "Content update",
"company": "Company update"
}
@app.route("/webhooks/rivalcheck", methods=["POST"])
def handle():
event = request.json
if event["event"] != "change.detected":
return "OK", 200
change = event["data"]
severity = change["severity"]
category = change["category"]
payload = {
"attachments": [{
"color": SEVERITY_COLORS.get(severity, "#6b7280"),
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": (
f"*{change['competitor']['name']}* "
f"| {CATEGORY_EMOJI.get(category, category)} "
f"| Severity: {severity.upper()}\n\n"
f"*{change['title']}*\n\n"
f"{change['summary']}"
)
}
},
{
"type": "context",
"elements": [{
"type": "mrkdwn",
"text": (
f"Page: <{change['page_url']}|{change['page_url']}> "
f"| Detected: {change['detected_at'][:16]}"
)
}]
}
]
}]
}
# Add strategic analysis for high-severity changes
if severity == "high" and change.get("strategic_analysis"):
payload["attachments"][0]["blocks"].insert(1, {
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*Strategic Analysis:*\n>{change['strategic_analysis']}"
}
})
requests.post(SLACK_WEBHOOK, json=payload)
return "OK", 200
if __name__ == "__main__":
app.run(port=8080)
Pro tip: Filter by channel
Create separate Slack channels for different severities or categories. Route pricing changes to #competitive-pricing (sales team) and feature changes to #competitive-product (product team). Just add conditional logic to the handler.
RivalCheck includes a built-in email digest system. No code needed — configure it in your Settings page.
Configuration options:
Each digest includes AI-composed summaries that highlight what changed, why it matters, and suggested actions. It is not a raw data dump — it reads like a briefing written by a competitive analyst.
For custom email formatting or routing to external lists, use the API to build your own digest. See the weekly report example in the CI API guide.
Maintain a living Google Sheet that tracks all pricing changes across competitors. This is popular with finance teams and executives who want a simple view of competitive pricing trends over time.
// Google Apps Script — runs on a daily trigger
// Pulls pricing changes from RivalCheck and appends to a sheet
const API_KEY = PropertiesService.getScriptProperties()
.getProperty("RIVALCHECK_API_KEY");
const API_BASE = "https://app.rivalcheck.com/api/v1";
function fetchPricingChanges() {
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Pricing Changes");
// Get the date of the last entry to avoid duplicates
const lastRow = sheet.getLastRow();
const lastDate = lastRow > 1
? sheet.getRange(lastRow, 5).getValue()
: "2026-01-01";
// Fetch pricing changes since last entry
const url = `${API_BASE}/changes?category=pricing&since=${lastDate}`;
const options = {
headers: { "Authorization": `Bearer ${API_KEY}` },
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
if (!data.changes || data.changes.length === 0) {
Logger.log("No new pricing changes found.");
return;
}
// Append each change as a row
const rows = data.changes.map(change => [
change.competitor.name,
change.title,
change.severity,
change.summary,
change.detected_at,
change.page_url,
change.strategic_analysis || ""
]);
const startRow = sheet.getLastRow() + 1;
sheet.getRange(startRow, 1, rows.length, 7).setValues(rows);
// Format severity column with conditional colours
const severityRange = sheet.getRange(startRow, 3, rows.length, 1);
const rules = severityRange.getValues().map((row, i) => {
const cell = sheet.getRange(startRow + i, 3);
if (row[0] === "high") {
cell.setBackground("#fecaca");
} else if (row[0] === "medium") {
cell.setBackground("#fef3c7");
}
return row;
});
Logger.log(`Added ${rows.length} pricing changes.`);
}
// Set up daily trigger
function createDailyTrigger() {
ScriptApp.newTrigger("fetchPricingChanges")
.timeBased()
.everyDays(1)
.atHour(7)
.create();
}
Set up the sheet with these column headers: Competitor, Change, Severity, Summary, Detected, Page URL, Analysis. Then paste the Apps Script into Extensions > Apps Script and run createDailyTrigger() once.
When a competitor changes their pricing or ships a new feature, automatically flag open deals where that competitor is listed. This ensures sales reps see competitive updates in context — on the opportunity record, not buried in a Slack channel.
# HubSpot integration — update deal records on competitive changes
import os
import requests
from flask import Flask, request
app = Flask(__name__)
HUBSPOT_TOKEN = os.environ["HUBSPOT_ACCESS_TOKEN"]
@app.route("/webhooks/rivalcheck", methods=["POST"])
def handle_change():
event = request.json
if event["event"] != "change.detected":
return "OK", 200
change = event["data"]
competitor_name = change["competitor"]["name"]
# Only update CRM for high-severity changes
if change["severity"] != "high":
return "OK", 200
# Search for open deals with this competitor
search_url = "https://api.hubapi.com/crm/v3/objects/deals/search"
headers = {
"Authorization": f"Bearer {HUBSPOT_TOKEN}",
"Content-Type": "application/json"
}
search_body = {
"filterGroups": [{
"filters": [
{
"propertyName": "competitor",
"operator": "CONTAINS_TOKEN",
"value": competitor_name
},
{
"propertyName": "dealstage",
"operator": "NOT_IN",
"values": ["closedwon", "closedlost"]
}
]
}],
"properties": ["dealname", "hubspot_owner_id", "competitor"]
}
deals = requests.post(search_url, json=search_body, headers=headers)
results = deals.json().get("results", [])
# Create a note on each deal
for deal in results:
note_body = {
"properties": {
"hs_note_body": (
f"Competitive Alert: {competitor_name}"
f"
{change['title']}"
f"
Analysis: {change['strategic_analysis']}"
f"
Auto-generated by RivalCheck"
),
"hs_timestamp": change["detected_at"]
},
"associations": [{
"to": {"id": deal["id"]},
"types": [{"associationCategory": "HUBSPOT_DEFINED",
"associationTypeId": 214}]
}]
}
requests.post(
"https://api.hubapi.com/crm/v3/objects/notes",
json=note_body,
headers=headers
)
return "OK", 200
For Salesforce, see the full example in the CI API guide. The pattern is the same: receive webhook, find affected deals, create tasks or notes.
Use the RivalCheck MCP server with Claude to generate executive-ready competitive briefings. This replaces the 2-3 hours a PMM typically spends compiling weekly updates.
The approach: connect Claude to RivalCheck via MCP, then use a structured prompt to generate the briefing. You can run this manually each week or automate it with the Claude API.
# Prompt for Claude with RivalCheck MCP tools available
Using the RivalCheck tools, generate a weekly competitive briefing for our leadership team. Follow this structure: 1. EXECUTIVE SUMMARY (3 sentences max) - The single most important competitive development this week - Overall competitive landscape temperature (heating up / stable / cooling) 2. TOP COMPETITIVE MOVES (ranked by strategic impact) For each: competitor name, what changed, why it matters, recommended response 3. PRICING INTELLIGENCE - Any pricing changes detected this week - How our pricing compares (pull the landscape view) - Pricing opportunities or risks 4. BATTLE CARD STATUS - Which battle cards were updated and why - Any cards flagged as stale 5. RECOMMENDED ACTIONS - For Product: features to prioritize or deprioritize - For Sales: talking points to update, deals to flag - For Marketing: positioning adjustments, content opportunities Keep the tone executive-friendly. No jargon. Each section should be scannable in under 30 seconds.
To automate this weekly, use the Claude API with MCP tool use:
import anthropic
import json
client = anthropic.Anthropic()
# Define the RivalCheck MCP tools Claude can use
tools = [
{
"name": "rivalcheck_get_changes",
"description": "Get recent competitor changes from RivalCheck",
"input_schema": {
"type": "object",
"properties": {
"since": {"type": "string", "description": "ISO date"},
"category": {"type": "string"},
"severity": {"type": "string"}
}
}
},
{
"name": "rivalcheck_get_landscape",
"description": "Get the competitive landscape overview",
"input_schema": {"type": "object", "properties": {}}
},
{
"name": "rivalcheck_get_battle_card",
"description": "Get a competitor's battle card",
"input_schema": {
"type": "object",
"properties": {
"competitor_id": {"type": "integer"}
},
"required": ["competitor_id"]
}
}
]
# Generate the briefing
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4000,
tools=tools,
messages=[{
"role": "user",
"content": EXECUTIVE_BRIEFING_PROMPT # from above
}]
)
# Handle tool use and follow-up (simplified)
# In production, implement the full tool-use loop
briefing = response.content[-1].text
# Email the briefing
send_email(
to="leadership@yourcompany.com",
subject=f"Weekly Competitive Briefing — {datetime.now().strftime('%B %d')}",
body=briefing
)
When a high-severity change is detected for a competitor, automatically regenerate their battle card so it reflects the latest intelligence. This is the simplest way to keep battle cards perpetually fresh.
// Node.js webhook handler (Express)
const express = require("express");
const app = express();
app.use(express.json());
const API_KEY = process.env.RIVALCHECK_API_KEY;
const API_BASE = "https://app.rivalcheck.com/api/v1";
app.post("/webhooks/rivalcheck", async (req, res) => {
const event = req.body;
if (event.event !== "change.detected") {
return res.sendStatus(200);
}
const { severity, competitor } = event.data;
// Only regenerate for significant changes
if (severity !== "high" && severity !== "medium") {
return res.sendStatus(200);
}
console.log(
`Regenerating battle card for ${competitor.name} ` +
`(${severity} severity change)`
);
try {
const response = await fetch(
`${API_BASE}/competitors/${competitor.id}/battle_card/generate`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ force: true })
}
);
const result = await response.json();
console.log(`Battle card regeneration started: ${result.status}`);
} catch (error) {
console.error(`Failed to regenerate: ${error.message}`);
}
res.sendStatus(200);
});
app.listen(8080, () => console.log("Listening on :8080"));
For the full battle card automation workflow including Slack notifications and CRM sync, see the Battle Card API guide.
Build a live dashboard that shows competitive activity alongside your own metrics. Use the API's changes endpoint with time-range filtering to populate charts and activity feeds.
// Dashboard data fetcher — call on page load and every 5 minutes
async function fetchDashboardData() {
const headers = { "Authorization": `Bearer ${API_KEY}` };
const since = new Date(Date.now() - 30 * 86400000)
.toISOString().split("T")[0];
// Parallel requests for dashboard sections
const [competitors, changes, landscape] = await Promise.all([
fetch(`${API_BASE}/competitors`, { headers }).then(r => r.json()),
fetch(`${API_BASE}/changes?since=${since}`, { headers }).then(r => r.json()),
fetch(`${API_BASE}/landscape`, { headers }).then(r => r.json())
]);
// Calculate activity metrics
const changesByWeek = groupByWeek(changes.changes);
const changesByCompetitor = groupBy(changes.changes, "competitor.name");
const changesByCategory = groupBy(changes.changes, "category");
return {
totalCompetitors: competitors.meta.total,
totalChanges30d: changes.changes.length,
highSeverity: changes.changes.filter(c => c.severity === "high").length,
activityTrend: changesByWeek,
mostActive: Object.entries(changesByCompetitor)
.sort((a, b) => b[1].length - a[1].length)[0],
categoryBreakdown: changesByCategory,
landscape: landscape
};
}
Pair this with a charting library like Chart.js or Recharts to visualise trends. The landscape endpoint provides competitive positioning data that maps well to quadrant or radar charts.
Generate a formatted PDF or slide deck that summarises the competitive landscape for board meetings. Pull data from the landscape endpoint and changes feed, format it with a reporting library.
# Generate a competitive landscape PDF using ReportLab
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Table, Spacer
from reportlab.lib.styles import getSampleStyleSheet
import requests
from datetime import datetime, timedelta
def generate_board_report():
headers = {"Authorization": f"Bearer {API_KEY}"}
since = (datetime.utcnow() - timedelta(days=90)).strftime("%Y-%m-%d")
# Fetch data
competitors = requests.get(
f"{API_BASE}/competitors", headers=headers
).json()["competitors"]
changes = requests.get(
f"{API_BASE}/changes?since={since}", headers=headers
).json()["changes"]
landscape = requests.get(
f"{API_BASE}/landscape", headers=headers
).json()
# Build PDF
doc = SimpleDocTemplate("competitive_report.pdf", pagesize=letter)
styles = getSampleStyleSheet()
elements = []
elements.append(Paragraph(
"Quarterly Competitive Landscape Report", styles["Title"]
))
elements.append(Paragraph(
f"Period: {since} to {datetime.utcnow().strftime('%Y-%m-%d')}",
styles["Normal"]
))
elements.append(Spacer(1, 24))
# Executive summary
high_severity = [c for c in changes if c["severity"] == "high"]
elements.append(Paragraph("Executive Summary", styles["Heading2"]))
elements.append(Paragraph(
f"Monitored {len(competitors)} competitors across "
f"{sum(c['monitored_pages'] for c in competitors)} pages. "
f"Detected {len(changes)} changes, "
f"{len(high_severity)} of high strategic significance.",
styles["Normal"]
))
# ... (add competitor details, charts, battle card summaries)
doc.build(elements)
return "competitive_report.pdf"
Automatically create issues in Linear, Jira, or Shortcut when competitors ship features in areas relevant to your roadmap. This ensures product decisions are informed by competitive reality.
@app.route("/webhooks/rivalcheck", methods=["POST"])
def create_product_insight():
event = request.json
if event["event"] != "change.detected":
return "OK", 200
change = event["data"]
# Only create issues for feature and pricing changes
if change["category"] not in ("features", "pricing"):
return "OK", 200
# Create Linear issue via GraphQL
mutation = """
mutation($input: IssueCreateInput!) {
issueCreate(input: $input) {
issue { id identifier url }
}
}
"""
variables = {
"input": {
"teamId": PRODUCT_TEAM_ID,
"projectId": COMPETITIVE_INSIGHTS_PROJECT,
"title": (
f"[Competitive] {change['competitor']['name']}: "
f"{change['title']}"
),
"description": (
f"## Competitive Intelligence\n\n"
f"**Source:** RivalCheck automated detection\n"
f"**Competitor:** {change['competitor']['name']}\n"
f"**Category:** {change['category']}\n"
f"**Severity:** {change['severity']}\n\n"
f"### What Changed\n{change['summary']}\n\n"
f"### Strategic Analysis\n{change['strategic_analysis']}\n\n"
f"### Action Required\n"
f"Review whether this change impacts our roadmap "
f"priorities or competitive positioning.\n\n"
f"---\n*Auto-created by RivalCheck*"
),
"priority": 2 if change["severity"] == "high" else 3,
"labelIds": [COMPETITIVE_LABEL_ID]
}
}
requests.post(
"https://api.linear.app/graphql",
json={"query": mutation, "variables": variables},
headers={"Authorization": LINEAR_API_KEY}
)
return "OK", 200
Combine RivalCheck's competitive change data with your CRM's win/loss data to identify patterns. Do you win more deals after a competitor raises prices? Do you lose more when they launch a specific feature? This recipe builds a dataset for that analysis.
import pandas as pd
import requests
from datetime import datetime, timedelta
def build_competitive_correlation_dataset():
"""
Combine RivalCheck change data with CRM win/loss data
to identify competitive patterns.
"""
headers = {"Authorization": f"Bearer {API_KEY}"}
# Get 6 months of competitive changes
since = (datetime.utcnow() - timedelta(days=180)).strftime("%Y-%m-%d")
changes = []
page = 1
while True:
resp = requests.get(
f"{API_BASE}/changes?since={since}&page={page}&limit=100",
headers=headers
).json()
changes.extend(resp["changes"])
if len(changes) >= resp["meta"]["total"]:
break
page += 1
# Create change events dataframe
change_df = pd.DataFrame([{
"date": c["detected_at"][:10],
"competitor": c["competitor"]["name"],
"category": c["category"],
"severity": c["severity"],
"title": c["title"]
} for c in changes])
# Load CRM deal data (export from your CRM)
deals_df = pd.read_csv("crm_deals_export.csv")
deals_df["close_date"] = pd.to_datetime(deals_df["close_date"])
# For each closed deal, count competitor changes in the
# 30 days before close
results = []
for _, deal in deals_df.iterrows():
window_start = deal["close_date"] - timedelta(days=30)
competitor = deal.get("competitor_name", "")
relevant_changes = change_df[
(change_df["competitor"] == competitor) &
(change_df["date"] >= window_start.strftime("%Y-%m-%d")) &
(change_df["date"] <= deal["close_date"].strftime("%Y-%m-%d"))
]
results.append({
"deal_id": deal["deal_id"],
"outcome": deal["outcome"], # "won" or "lost"
"competitor": competitor,
"changes_30d": len(relevant_changes),
"pricing_changes_30d": len(relevant_changes[
relevant_changes["category"] == "pricing"
]),
"high_severity_30d": len(relevant_changes[
relevant_changes["severity"] == "high"
])
})
correlation_df = pd.DataFrame(results)
# Analyse correlations
print("Win rate by competitor change activity:")
print(correlation_df.groupby(
pd.cut(correlation_df["changes_30d"], bins=[0,1,3,5,100])
)["outcome"].value_counts(normalize=True))
correlation_df.to_csv("competitive_correlation.csv", index=False)
return correlation_df
You do not need to write code to build powerful competitive intelligence automations. RivalCheck's webhooks work with any platform that can receive HTTP POST requests.
Use Zapier's "Webhooks by Zapier" trigger to receive RivalCheck events, then connect to any of Zapier's 6,000+ app integrations.
Example Zaps:
Setup steps:
Make provides more sophisticated data transformation than Zapier, making it ideal for complex routing. Use the "Webhooks" module as the trigger and the "HTTP" module to call the RivalCheck API for additional data.
Advanced Make scenario:
Webhook trigger (RivalCheck event) │ ├─ Router │ ├─ Route 1: severity = "high" │ │ ├─ HTTP: Fetch battle card from RivalCheck API │ │ ├─ Slack: Post to #competitive-alerts │ │ └─ Salesforce: Create task on matching opportunities │ │ │ ├─ Route 2: category = "pricing" │ │ ├─ Google Sheets: Add row to pricing tracker │ │ └─ Email: Notify finance team │ │ │ └─ Route 3: all other changes │ └─ Notion: Add to competitive changelog database
If you prefer self-hosted automation, n8n is an excellent open-source option. It supports webhook triggers, HTTP requests, and has nodes for Slack, Google Sheets, various CRMs, and AI services.
// n8n workflow JSON (import via the workflow editor)
{
"nodes": [
{
"name": "RivalCheck Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "rivalcheck",
"httpMethod": "POST"
}
},
{
"name": "Filter High Severity",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"string": [{
"value1": "={{ $json.data.severity }}",
"operation": "equals",
"value2": "high"
}]
}
}
},
{
"name": "Post to Slack",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#competitive-alerts",
"text": "={{ $json.data.competitor.name }}: {{ $json.data.title }}",
"attachments": [{
"color": "#dc2626",
"text": "{{ $json.data.strategic_analysis }}"
}]
}
},
{
"name": "Regenerate Battle Card",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "=https://app.rivalcheck.com/api/v1/competitors/{{ $json.data.competitor.id }}/battle_card/generate",
"method": "POST",
"authentication": "genericCredentialType",
"headers": {
"Authorization": "Bearer {{ $credentials.rivalcheckApi.apiKey }}"
},
"body": { "force": true }
}
}
]
}
For maximum flexibility, build your automations with code. The RivalCheck API is a standard REST API that works with any HTTP client. Here are starter templates in both Node.js and Python.
const express = require("express");
const app = express();
app.use(express.json());
const API_KEY = process.env.RIVALCHECK_API_KEY;
const API_BASE = "https://app.rivalcheck.com/api/v1";
// Helper: make authenticated API requests
async function rivalcheck(endpoint) {
const resp = await fetch(`${API_BASE}${endpoint}`, {
headers: { "Authorization": `Bearer ${API_KEY}` }
});
return resp.json();
}
// Webhook endpoint
app.post("/webhooks/rivalcheck", async (req, res) => {
const { event, data } = req.body;
switch (event) {
case "change.detected":
await handleChange(data);
break;
case "competitor.discovered":
await handleDiscovery(data);
break;
default:
console.log(`Unhandled event: ${event}`);
}
res.sendStatus(200);
});
async function handleChange(change) {
console.log(
`[${change.severity.toUpperCase()}] ` +
`${change.competitor.name}: ${change.title}`
);
// Add your automation logic here:
// - Post to Slack
// - Update CRM
// - Regenerate battle card
// - Create Linear issue
// - Log to database
if (change.severity === "high") {
// Fetch the full battle card for additional context
const card = await rivalcheck(
`/competitors/${change.competitor.id}/battle_card`
);
console.log("Battle card stale:", card.battle_card.stale);
}
}
async function handleDiscovery(data) {
console.log(`New competitor discovered: ${data.name}`);
}
app.listen(process.env.PORT || 8080);
"""
RivalCheck API Client — reusable wrapper for common operations.
"""
import os
import requests
from datetime import datetime, timedelta
class RivalCheckClient:
def __init__(self, api_key=None):
self.api_key = api_key or os.environ["RIVALCHECK_API_KEY"]
self.base_url = "https://app.rivalcheck.com/api/v1"
self.session = requests.Session()
self.session.headers["Authorization"] = f"Bearer {self.api_key}"
def get_competitors(self, status=None):
params = {}
if status:
params["status"] = status
resp = self.session.get(
f"{self.base_url}/competitors", params=params
)
resp.raise_for_status()
return resp.json()["competitors"]
def get_changes(self, since=None, category=None,
severity=None, competitor_id=None):
params = {}
if since:
params["since"] = since
if category:
params["category"] = category
if severity:
params["severity"] = severity
if competitor_id:
url = f"{self.base_url}/competitors/{competitor_id}/changes"
else:
url = f"{self.base_url}/changes"
all_changes = []
params["page"] = 1
params["limit"] = 100
while True:
resp = self.session.get(url, params=params)
resp.raise_for_status()
data = resp.json()
all_changes.extend(data["changes"])
if len(all_changes) >= data["meta"]["total"]:
break
params["page"] += 1
return all_changes
def get_battle_card(self, competitor_id, format="json"):
resp = self.session.get(
f"{self.base_url}/competitors/{competitor_id}/battle_card",
params={"format": format}
)
if resp.status_code == 404:
return None
resp.raise_for_status()
return resp.json()["battle_card"]
def regenerate_battle_card(self, competitor_id, force=False):
resp = self.session.post(
f"{self.base_url}/competitors/{competitor_id}"
f"/battle_card/generate",
json={"force": force}
)
resp.raise_for_status()
return resp.json()
def get_landscape(self):
resp = self.session.get(f"{self.base_url}/landscape")
resp.raise_for_status()
return resp.json()
def get_recent_high_severity(self, days=7):
since = (datetime.utcnow() - timedelta(days=days)).strftime("%Y-%m-%d")
return self.get_changes(since=since, severity="high")
# Usage
client = RivalCheckClient()
competitors = client.get_competitors(status="active")
changes = client.get_recent_high_severity(days=7)
card = client.get_battle_card(competitors[0]["id"])
The RivalCheck MCP server is a different kind of automation. Instead of building fixed workflows, it gives AI agents — Claude, Cursor, or any MCP-compatible client — direct access to your competitive data through natural conversation.
This is powerful because competitive intelligence questions are often ad hoc and context-dependent:
Things you can ask Claude with MCP access to RivalCheck:
"I have a sales call against Nexus Software in 30 minutes. Give me the top 3 things I need to know."
"Have any competitors changed their pricing in the last 2 weeks? How does it affect our positioning?"
"Write a competitive analysis paragraph for our board deck focusing on market pricing trends."
"Compare Acme Corp and Nexus Software. Which is the bigger competitive threat right now and why?"
"Draft an email to our sales team about the Acme Corp pricing change with talk tracks they can use."
MCP setup takes under 5 minutes. See the MCP documentation for configuration steps with Claude Desktop, Cursor, and other MCP clients.
Automations fail silently. A broken Slack webhook or expired API key means your team stops getting competitive intelligence without anyone noticing. Here is how to prevent that.
Monitor webhook delivery
RivalCheck logs webhook delivery attempts in your Organisation Settings. Check for failed deliveries weekly. Common issues: endpoint URL changed, server down, SSL certificate expired.
Set up a heartbeat check
Create a simple health check that verifies your API key works and your webhook endpoint is reachable. Run it daily. If it fails, alert your ops team before anyone notices missing competitive updates.
Rotate API keys on a schedule
Generate a new API key quarterly and update your integrations. RivalCheck supports having two active keys simultaneously so you can rotate without downtime.
Review and prune automations quarterly
Are people actually reading the Slack alerts? Is the Google Sheet being used? Automations that nobody pays attention to are just noise. Audit your workflows quarterly and remove or adjust ones that are not driving decisions.
# Health check script — run daily via cron
#!/bin/bash
# Verify RivalCheck API connectivity
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $RIVALCHECK_API_KEY" \
"https://app.rivalcheck.com/api/v1/account")
if [ "$RESPONSE" != "200" ]; then
# Send alert — API key may have expired or been revoked
curl -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{"text":"RivalCheck API health check failed (HTTP '$RESPONSE'). Check API key and integrations."}'
exit 1
fi
echo "RivalCheck API: healthy"
Start with Recipe 1 (Slack alerts) or Recipe 2 (email digests) — they take minutes to set up and deliver immediate value. Then layer on additional automations as your CI programme matures.
Recommended rollout order:
Start building:
We use essential cookies to keep the site working. With your consent, we may also use analytics cookies to improve the experience. Privacy policy.