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…",
    "recommended_action": "Evaluate our messaging…",
    "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",
            "recommended_action": "Review positioning",
            "category": "pricing",
            "direction": "retreated",
            "delta": { "added": 0, "removed": 0, "modified": 1 },
            "review_status": "unreviewed",
            "created_at": "2026-07-18T19:00:00.000Z"
          }
        ]
      },
      "this_week": {
        "bullets": [{ "text": "Example Co cut Pro pricing", "change_ids": [901] }]
      },
      "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
this_week.bullets First buffered summaries OpenAI synthesis (or DB fallback)
whos_moving.sparkline Single-bucket count Daily buckets over period

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
No credit card required. 100 free checks included.