Docs / Guides

Complete Guide

Competitor Monitoring Automation: Build Real-Time Competitive Intelligence Workflows

10 automation recipes with code. From Slack alerts to AI-generated executive briefings, build the competitive intelligence workflows your team actually needs.

Updated March 2026 · 30 min read

Why automate competitor monitoring?

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

  • + Consistent coverage regardless of team bandwidth
  • + Real-time detection instead of weekly spot-checks
  • + Distribution to every stakeholder, every time
  • + Complete historical record of all competitor changes
  • + Frees up analyst time for strategic thinking

What still needs a human

  • - Deciding which competitors matter most
  • - Interpreting changes in business context
  • - Making strategic decisions from the data
  • - Validating AI analysis with market knowledge
  • - Choosing which changes to act on

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.

The competitive intelligence automation stack

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

DETECT

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.

NOTIFY

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.

INTEGRATE

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.

ANALYZE

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"
  }
}
RECIPE 1

Slack channel for competitive updates

Difficulty: Easy
Stack: Webhook + Slack
Time: 15 minutes

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.

RECIPE 2

Weekly email digest

Difficulty: None (built-in)
Stack: RivalCheck settings
Time: 2 minutes

RivalCheck includes a built-in email digest system. No code needed — configure it in your Settings page.

Configuration options:

1. Frequency: Choose daily (delivered at 8 AM) or weekly (delivered Monday morning). Weekly is recommended for most teams — daily can feel noisy unless you are in an active competitive evaluation.
2. Severity threshold: Receive all changes, or only medium and high severity. High-only means you will only get emails when something strategically important happens.
3. Recipients: All team members receive digests by default. Individual users can opt out or adjust their own frequency in personal settings.

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.

RECIPE 3

Competitor pricing tracker in Google Sheets

Difficulty: Medium
Stack: API + Apps Script
Time: 30 minutes

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.

RECIPE 4

CRM competitive field updates

Difficulty: Medium
Stack: Webhook + CRM API
Time: 1 hour

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.

RECIPE 5

AI-powered weekly competitive briefing

Difficulty: Medium
Stack: MCP + Claude
Time: 20 minutes

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
)
RECIPE 6

Auto-regenerate battle cards on major changes

Difficulty: Easy
Stack: Webhook + API
Time: 15 minutes

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.

Recipes 7-10: Dashboard, reports, roadmap input, and win/loss

RECIPE 7

Competitor activity dashboard

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.

RECIPE 8

Board-ready competitive landscape report

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"
RECIPE 9

Product roadmap competitive input

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
RECIPE 10

Win/loss competitive correlation

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

Building with no-code tools: Zapier, Make, and n8n

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.

Zapier

Use Zapier's "Webhooks by Zapier" trigger to receive RivalCheck events, then connect to any of Zapier's 6,000+ app integrations.

Example Zaps:

1. RivalCheck webhook -> Filter (high severity) -> Slack message — Formatted Slack alerts for important changes only
2. RivalCheck webhook -> Filter (pricing) -> Google Sheets row — Auto-log pricing changes to a spreadsheet
3. RivalCheck webhook -> Salesforce task — Create follow-up tasks for deal owners when competitors change
4. RivalCheck webhook -> Notion page — Maintain a competitive changelog in your team wiki

Setup steps:

  1. 1. Create a new Zap with "Webhooks by Zapier" as the trigger (choose "Catch Hook")
  2. 2. Copy the webhook URL Zapier provides
  3. 3. Paste it into your RivalCheck Organisation Settings > Webhook URL
  4. 4. Send a test webhook from RivalCheck to configure the Zap's data mapping
  5. 5. Add your action steps (Slack, Sheets, CRM, etc.) and publish

Make (formerly Integromat)

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

n8n (open-source alternative)

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 }
      }
    }
  ]
}

Building with custom code: Node.js and Python

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.

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";

// 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);

Python API client

"""
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"])

Using MCP for conversational competitive intelligence

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.

Monitoring and maintaining your automations

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"

Getting started with RivalCheck automation

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:

Week 1 Email digests + Slack alerts. Zero to low code. Gets competitive intelligence flowing to your team immediately.
Week 2 Battle card auto-regeneration. Ensures battle cards stay current as changes are detected. Sales team immediately benefits.
Week 3 CRM integration. Competitive insights appear on deal records. Sales reps see relevant intelligence without leaving their workflow.
Week 4 MCP + AI briefings. Give your team conversational access to competitive data. Replace manual briefing creation with AI-generated reports.

Start building:

  1. 1. Create your free account and add your first competitors
  2. 2. Configure email digests in Settings (2 minutes, zero code)
  3. 3. Set up a webhook endpoint for real-time notifications
  4. 4. Generate an API key and try the recipes in this guide
  5. 5. Connect the MCP server for conversational access to your competitive data