Webhooks

Competiflow can POST structured JSON to your HTTPS endpoint when a competitor change is detected or when a digest window closes. Configure a webhook URL in Workspace settings → Notifications.

See also Notifications for email and Slack delivery.

Envelope

Every delivery uses the same top-level shape:

{
  "schema_version": "v1",
  "id": "wh_123",
  "idempotency_key": "…",
  "event": "change.detected",
  "sent_at": "2026-07-18T19:58:44.000Z",
  "data": {}
}
Field Description
schema_version Contract version (v1)
id Unique delivery id — store this to dedupe retries
idempotency_key Stable key for the logical delivery (same across retries)
event Event type (see below)
sent_at UTC timestamp when Competiflow sent the webhook
data Event-specific payload

Events

Event When
change.detected Realtime route fired for a single change
digest.ready Digest window flushed (Slack/webhook digest mode)
test.ping You clicked Test in notification settings

Signing

If you set a signing secret, Competiflow sends:

X-Competiflow-Timestamp: 1721335124
X-Competiflow-Signature: sha256=<hex>

The signed payload is "{timestamp}.{raw_json_body}" using HMAC-SHA256 and your secret.

expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{raw_body}")
valid = Rack::Utils.secure_compare("sha256=#{expected}", signature_header)

Reject requests when the timestamp is older than five minutes.

Dedupe and retries

Failed deliveries retry up to five times. Treat duplicate id or idempotency_key values as already processed and respond 2xx without side effects.

change.detected

{
  "schema_version": "v1",
  "id": "wh_456",
  "idempotency_key": "abc…",
  "event": "change.detected",
  "sent_at": "2026-07-18T19:58:44.000Z",
  "data": {
    "id": 133489,
    "workspace_id": 12,
    "workspace_name": "Acme",
    "competitor_id": 24,
    "competitor": "apify.com",
    "monitor_id": 88,
    "monitor_type": "homepage",
    "severity": "medium",
    "summary": "The competitor updated their headline…",
    "why_it_matters": "Signals a reposition toward mid-market buyers comparing tiers side by side.",
    "review_status": "unreviewed",
    "category": "positioning",
    "direction": "neutral",
    "delta": { "added": 0, "removed": 0, "modified": 2 },
    "field_diffs": [
      {
        "op": "modified",
        "path": "headline",
        "tier": "alertable",
        "change_type": "headline_changed",
        "old": "53,432 tools for your AI",
        "new": "53,502 tools to automate your business"
      }
    ],
    "evidence": {
      "captured_at": "2026-07-18T19:58:41Z",
      "previous_captured_at": "2026-07-18T16:47:41Z",
      "diffs": [
        { "op": "modified", "path": "headline", "tier": "alertable", "change_type": "headline_changed" }
      ]
    },
    "source_url": "https://apify.com",
    "links": { "self": "https://api.competiflow.com/v1/changes/133489" },
    "raw_diff_available": true,
    "created_at": "2026-07-18T19:58:41.982Z"
  }
}

Field locations

  • Full before/after values: data.field_diffs
  • Evidence index (no duplicate values): data.evidence.diffs
  • Fetch more via data.links.self or GET /v1/changes/:id?include=raw_diff

digest.ready

{
  "schema_version": "v1",
  "id": "wh_a1b2…",
  "idempotency_key": "digest-7-12-1d-1721332800",
  "event": "digest.ready",
  "sent_at": "2026-07-18T20:00:00.000Z",
  "data": {
    "workspace_id": 12,
    "workspace_name": "Acme",
    "digest": {
      "period": "1d",
      "generated_at": "2026-07-18T20:00:00.000Z",
      "needs_review": {
        "count": 3,
        "max_severity": "high",
        "items": [
          {
            "change_id": 901,
            "competitor_id": 7,
            "competitor": "Example Co",
            "homepage_url": "https://example.com",
            "logo_url": "https://…",
            "monitor_type": "pricing",
            "source_url": "https://example.com/pricing",
            "summary": "Pro plan dropped from $99 to $79/mo",
            "severity": "high",
            "why_it_matters": "Mid-market buyers comparing tiers side by side will see a lower entry price.",
            "category": "pricing",
            "direction": "retreated",
            "delta": { "added": 0, "removed": 0, "modified": 1 },
            "review_status": "unreviewed",
            "created_at": "2026-07-18T19:00:00.000Z"
          }
        ]
      },
      "whos_moving": [],
      "top_opportunities": [],
      "coverage": { "monitors_total": 18, "monitors_healthy": 17, "monitors_failing": 0, "failing_monitor_ids": [] },
      "consumption": { "pending": 3, "reviewed_in_period": 1 }
    }
  }
}

Notification digest vs API digest

Both use the same needs_review.items[] shape (see Digest). They differ in scope:

Aspect digest.ready webhook / email digest GET /v1/workspaces/:id/digest
Changes included Buffered in the notification flush window Full workspace period
needs_review filter All unreviewed in buffer Unreviewed with severity ≥ high
whos_moving.sparkline Daily buckets over the digest period Daily buckets over period
top_opportunities Top buffered changes with opportunity_score > 0 (same item shape as Overview) Top period changes with opportunity_score > 0

test.ping

Sent when you click Test in notification settings. Each test uses a unique wh_test_… delivery id so repeated tests are not deduped by your receiver.

{
  "schema_version": "v1",
  "id": "wh_test_a1b2c3d4e5f67890",
  "idempotency_key": "wh_test_a1b2c3d4e5f67890",
  "event": "test.ping",
  "sent_at": "2026-07-18T20:00:00.000Z",
  "data": { "message": "Competiflow webhook test" }
}

Find out before your customers do

Point Competiflow at your competitors and get the moves that matter, with the before and after attached. Skip the dashboard nobody opens and the alert flood nobody reads.

  • It diffs real fields, not pixels
  • Every change shows its receipts
  • One quiet digest, your cadence
Start free

No credit card required. 100 free checks included.