Documentation

Wire events into your stack.

Every endpoint returns the same event shape: named buyer, named seller, named competitor, 0.95+ confidence, refreshed daily. Pick the surface that matches your workflow — Clay, Claude Code via MCP, REST, webhook, or CRM — and have your first event response in your stack on the next business day.

Quickstart

Three steps to your first event response:

  • 01 Get an API key from your account page (or by replying to the welcome email).
  • 02 Connect your CRM (Salesforce or HubSpot) so we can sync your account list, and send us your competitor list.
  • 03 Call /competitive_activity. On the next business day after your list ingests, you'll have the last 30 days of verified events at your accounts.
# Your first call — last 24h of competitive activity
curl -X GET https://api.dealintelligence.io/v1/competitive_activity \
  -H "Authorization: Bearer $DI_API_KEY" \
  -G --data-urlencode "timeframe=24h"

# Response: 200 OK
{
  "events": [
    {
      "event_id": "evt_8H3K9PqR2nL",
      "event_type": "open_opportunity",
      "timestamp": "2026-05-19T14:22:08Z",
      "confidence": 0.99,
      "buyer": { "name": "M. Eisenberg", "company": "Workday", ... },
      "competitor": { "company": "[redacted]", "seller": {...} }
    },
    ...
  ],
  "count": 7,
  "next_cursor": "cur_aB42xZ..."
}

Authentication

All requests authenticate via a Bearer token in the Authorization header. API keys are issued on signup and rotate-able from your account page. Test keys (di_test_*) and live keys (di_live_*) are separate; test keys hit a deterministic event fixture so your CI doesn't flap.

Authorization: Bearer di_live_xxxxxxxxxxxx

# Test mode — deterministic fixtures, no rate limit, no billing
Authorization: Bearer di_test_xxxxxxxxxxxx

Your first event

Once your account list is ingested (typically within 24 hours of upload), events refresh on a daily cadence — new observations land each morning UTC. You can also pull historical events from the prior 90 days via any endpoint. Most customers start with a 30-day pilot, then wire the daily refresh into Clay or Claude Code.

Clay integration

Deal Intelligence is a native Clay enrichment. Add it as a column in any Clay table and the column returns the latest competitive events at the row's account.

Add as a column

In your Clay table, click Add Enrichment, search for Deal Intelligence, and connect with your API key. Pick the endpoint, the input column (typically company_domain or account_id), and the time window.

// Clay enrichment configuration
{
  "provider": "deal_intelligence",
  "endpoint": "account_activity",
  "input": "{{ company_domain }}",
  "window": "90d",
  "output": "events_json"
}

Trigger Clay workflows from events

Wire a Clay workflow trigger to a Deal Intelligence webhook. New events fire a row insert in the target table, where downstream enrichments (territory routing, AE assignment, sequence enrollment) execute automatically.

// Webhook → Clay table trigger
POST https://api.clay.com/v1/tables/{table_id}/rows
{
  "trigger": "deal_intelligence.event",
  "filter": { "event_type": "closed_lost_revival" },
  "actions": ["enrich_account", "assign_owner", "send_to_sequence"]
}

Claude Code (MCP)

Native MCP server. Configure it in your ~/.claude/mcp.json (or your team's shared config) the same way you'd configure any MCP server. Every endpoint becomes a tool call your agent can use to query events, slice by competitor / industry / owner, build account-level rollups, or join against CRM pipeline state.

// ~/.claude/mcp.json
{
  "mcpServers": {
    "deal_intelligence": {
      "command": "npx",
      "args": ["@deal-intelligence/mcp@latest"],
      "env": { "DI_API_KEY": "di_live_xxxxxxxxxxxx" }
    }
  }
}

Restart Claude Code. The agent gets the full tool surface — five primary event-stream tools plus aggregation, slice, CRM-cross, and discovery helpers. Have your agent call whoami() first; it returns your tenant's filter vocabulary so the model can suggest realistic competitor / industry / owner values without guessing.

// Primary — verified event streams
deal_intelligence.competitive_activity({ timeframe, event_type, competitor,
                                         industry, region, employee_band,
                                         job_function, seniority, owner, ... })
deal_intelligence.closed_lost_revival({ window })
deal_intelligence.churn_risk({ window })
deal_intelligence.account_activity({ account, window })
deal_intelligence.buyer_activity({ name, company, title, window })

// Aggregations — "where should I look first?"
deal_intelligence.top_accounts({ window, by, account_status, limit })
deal_intelligence.multi_competitor_accounts({ window, min_competitors })
deal_intelligence.account_momentum({ window, baseline_window })
deal_intelligence.alert_inbox({ window, limit_per_group })
deal_intelligence.account_timeline({ account, window })

// Slices — scope to one dimension
deal_intelligence.competitor_summary({ competitor, window })
deal_intelligence.owner_book({ owner, window })
deal_intelligence.industry_pulse({ industry, window })

// CRM-cross — events joined with pipeline state
deal_intelligence.pipeline_at_risk({ window, stage_filter, min_amount })
deal_intelligence.renewals_at_risk({ days_to_renewal, window })
deal_intelligence.revival_candidates({ window })

// Discovery — call this first
deal_intelligence.whoami({ discovery_window })

Example agent prompt

❯ Find every customer account in EMEA approaching renewal in the next
  60 days that's getting hit by Walnut. For each, draft a save-play
  note for the CSM that includes the buyer name, the renewal amount,
  and the last three Slack threads in #cs-saves about that account.

→ deal_intelligence.whoami()
→ deal_intelligence.renewals_at_risk({ days_to_renewal: 60, window: "30d" })
→ deal_intelligence.competitor_summary({ competitor: "Walnut", window: "30d" })
→ slack.search_threads({ channel: "#cs-saves", accounts: [...] })

Found 4 renewals at risk in EMEA touched by Walnut. Drafting save notes...

Webhooks

Events deliver to any HTTPS endpoint within minutes of observation. Each webhook delivery is signed with HMAC-SHA256. Verify the signature before acting on the payload.

// Configure a webhook in your account settings
POST https://api.dealintelligence.io/v1/webhooks
{
  "url": "https://your-app.com/webhooks/di",
  "events": ["competitive_activity", "churn_risk"],
  "filter": { "confidence_min": 0.97 }
}

// Delivery includes a signature header
X-DI-Signature: t=1716156128,v1=5257a869e7ecebeda32affa62cdca3fa...
X-DI-Event-Id:   evt_8H3K9PqR2nL
X-DI-Delivery:   d_29a8b3...

// Verify with HMAC-SHA256 over the raw body
const sig = req.headers['x-di-signature'];
const computed = crypto
  .createHmac('sha256', WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');
if (computed !== sig.split('v1=')[1]) throw new Error('invalid');

Retries: failed deliveries (non-2xx response) retry with exponential backoff for 24 hours. After 24 hours the event is moved to a dead-letter queue, queryable via /webhooks/dead_letter. Replay any event via /webhooks/replay/{event_id}.

Slack routing

Events route to Slack channels by territory, segment, or account owner. The Slack app configures channel mappings once; thereafter every event with matching attributes posts to the right channel — threaded by account so the full activity history travels with the event.

// Routing rules (configured once, applies to all events)
{
  "rules": [
    { "if": { "territory": "NAMER-ENT" },     "channel": "#deals-namer-ent" },
    { "if": { "event_type": "churn_risk" }, "channel": "#cs-saves" },
    { "if": { "owner": "@rory" },           "channel": "@rory" }
  ]
}

CRM sync

Salesforce and HubSpot supported natively. Events write to the opportunity or account record as a custom activity entry, with optional custom fields on the Opportunity object for trigger-ready signals.

Salesforce object  → Custom_DI_Event__c (extends Task)
HubSpot object     → deal_intelligence_event (custom timeline event)
Custom fields      → di_last_event_at, di_last_competitor, di_event_count_90d

API reference

Base URL: https://api.dealintelligence.io/v1. All responses are JSON. All endpoints support cursor-based pagination via cursor and limit parameters.

GET /competitive_activity

The workhorse tool. Verified connection events across every account in your book — every event arrives shaped with a priority_score (0–100), a multi_competitor flag (same buyer reached by 2+ competitors in the window), and an inline headline_opportunity block when there's an open deal on the account. Default sort is priority_score desc, so the LLM/agent gets the most actionable events first without extra prompting.

Every slice filter is multi-value (lists are OR'd within the dimension; dimensions AND together). The two highest-leverage shortcut filters are multi_competitor_only=true and has_open_opp_only=true.

GET /v1/competitive_activity
  ?timeframe=24h                # 1h, 24h, 7d, 14d, 30d, 60d, 90d, 180d, 365d
  # Slice filters - all multi-value, all AND'd across dimensions:
  &event_type=Churn+Risk&event_type=Renewal+Risk
  &competitor=Walnut&competitor=1mind          # list of name substrings
  &industry=Fintech&industry=Insurance
  ®ion=EMEA                  # N America | EMEA | APAC | LATAM
  &employee_band=Mid-Market     # Startup | SMB | Mid-Market | Enterprise | Global Enterprise
  &job_function=Sales           # buyer role bucket - see /whoami
  &seniority=C-Level&seniority=VP
  &owner=Natalie+Smith          # CRM owner exact match (case-insensitive)
  &in_crm_only=true             # default true; flip to include prospects
  # Signal thresholds (the noise filters):
  &min_priority_score=60        # 0..100; 60+ = "queue-worthy"
  &multi_competitor_only=true
  &has_open_opp_only=true
  # Output shaping:
  &dedupe_within_days=7         # collapse repeats; default 0 = no dedupe
  &view=raw                     # raw | summary | grouped_by_account
  &limit=100                    # 1-200, default 100

GET /account_activity

Everything you need about ONE account in a single response. Smart resolver: pass a name ("Workday"), a domain ("workday.com"), or a Salesforce Account.Id ("001Qn00000GMUT0IAP") — the detector picks the right strategy.

Returns a structured response, not a flat event list:

  • account: display block (name, owner, status, region, industries, employee_band, crm_link).
  • headline_opportunity: largest open opp on the account with days_to_close inline. null when no open opp.
  • summary: counts (events, buyers, competitors), date range, alert-type breakdown.
  • buying_committee: one row per distinct buyer reached, with their job_function, seniority, last competitor that hit them. The unit reps actually think in.
  • competitors: per-competitor breakdown (event_count, buyer_count, first/last seen).
  • timeline: chronological events, newest first.
  • recommended_followups: next tool calls suggested for this account.
GET /v1/account_activity
  ?account=workday.com          # name | domain | SF Account.Id (auto-detected)
  &window=90d
  &view=all                     # all | overview | committee | timeline
  &timeline_limit=50            # 1-200, default 50
  &in_crm_only=true

GET /buyer_activity

Everything about ONE buyer — across every employer they've worked at. The response splits events by employer (so a buyer who changed jobs is shown with separate sections, newest-employer first) and ships pre-computed personal_signals: decision-maker likelihood (0–100), distinct competitor breadth, and how long competitor interest has been sustained.

Two equally-supported identifier modes:

  • linkedin_url: most reliable when known. Bypasses the name fuzzy match entirely.
  • name (+ optional company, title): the typical LLM caller path. Pass company whenever you have it — common names are not unique on their own.
GET /v1/buyer_activity
  ?name=Daniel+Vorderstrasse    # OR pass linkedin_url; name+company recommended
  &company=SoundHound
  &title=Director+of+Sales      # optional disambiguator
  &linkedin_url=https://linkedin.com/in/ACwAA...
  &window=180d                  # default 180d (cross-employer history matters)
  &limit=100                    # events per employer section

GET /closed_lost_revival

Closed-lost accounts where competitors are back in the door. Account-rollup-first: returns one row per closed-lost account with revived activity, each carrying a save_score (0–100) so the LLM can rank "warmest revivals" without re-deriving heuristics. The save_score blends competitor breadth, top seniority reached, recency, and the number of buyers being approached by 2+ competitors at once.

Each row includes a recent-events tail (priority-ordered) for context plus a recommended_followups block.

GET /v1/closed_lost_revival
  ?window=90d                   # reachout lookback
  &owner=Natalie+Smith&owner=Jacob+Fleisher  # multi-value owner scope
  &min_save_score=60            # 0..100; 60+ = "queue-worthy"
  &include_events=true          # include per-account event tail
  &events_per_account=5
  &limit=25                     # account rows; max 100

GET /churn_risk

Customer accounts where competitors are landing — the renewal early-warning. Account-rollup-first: one row per customer account with competitive activity in the window. Each row carries a risk_score (0–100) and an inline renewal block (close_date, amount, days_to_renewal, owner_id) so the LLM doesn't need a follow-up renewals_at_risk call. Includes a momentum block (current vs. baseline window delta) and the standard recommended_followups.

GET /v1/churn_risk
  ?window=7d                    # default 7d (weekly review cadence)
  &severity_min=low             # low | med | high  (med drops Churn Risk Low)
  &min_risk_score=60            # 0..100 composite floor
  &owner=Sarah+Park             # multi-value owner scope
  &customers_only=true
  &include_renewal_only=false   # true = only rows with renewal opp closing within 90d
  &include_events=true
  &events_per_account=5
  &limit=25                     # account rows; max 100

Aggregations

Account-level rollups over the same event stream. Designed for the "where should I look first?" pass — return one row per account with the activity already summarized, instead of every underlying event.

GET /top_accounts

Rank accounts in your book by competitive activity. Sort by total reachout volume, distinct competitor breadth, distinct buyers reached, recency, or top seniority of the buyers contacted.

GET /v1/top_accounts
  ?window=30d                 # 1h, 24h, 7d, 14d, 30d, 60d, 90d, 180d, 365d
  &by=competitor_breadth      # reachout_count | competitor_breadth | buyer_count | recency | seniority
  &account_status=customer    # substring filter on CRM Account.Type
  &in_crm_only=false
  &limit=25                   # max 100

GET /multi_competitor_accounts

Accounts being approached by 2+ distinct competitors in the same window — the strongest signal in the product. When multiple competitors arrive at the same buyer in a tight window, it's almost never coincidence.

GET /v1/multi_competitor_accounts
  ?window=30d
  &min_competitors=2          # bump to 3+ for the highest-signal cohort
  &in_crm_only=false
  &limit=25

GET /account_momentum

Per-account week/week (or window/window) delta in reachout volume. Compares the current window against an immediately-prior baseline window of the same length, so you can see which accounts are accelerating versus quieting down. Brand-new accounts in the current window appear with delta_pct: null.

GET /v1/account_momentum
  ?window=7d                  # current window
  &baseline_window=7d         # comparison window (default: same length, immediately prior)
  &in_crm_only=false
  &limit=25

GET /alert_inbox

Events grouped by alert type into a Slack-notification-style inbox. One response your rep — or your agent — can act on directly: groups are emitted in priority order (Churn Risk first, New Business last) with per-group account rollups.

GET /v1/alert_inbox
  ?window=7d
  &limit_per_group=10         # max 50 accounts per alert_type group

GET /account_timeline

Chronological per-buyer feed for one named account. Multi-competitor reachouts on the same buyer expand into one entry per competitor, mirroring the structure of the manual Slack notifications the team builds today.

GET /v1/account_timeline
  ?account=Workday
  &window=180d
  &limit=100                  # max 200 timeline entries

Slices

Scope the event stream to one dimension and return account-level rollups plus a top-line summary. The "tell me about X" companions to /top_accounts.

GET /competitor_summary

Footprint of one competitor across your book. Returns top accounts the named competitor reached into, plus aggregate counts: total reachouts, distinct accounts, distinct buyers, and an alert-type breakdown.

GET /v1/competitor_summary
  ?competitor=Walnut          # substring; matches single + multi-competitor events
  &window=30d
  &in_crm_only=false
  &limit=25

GET /owner_book

Per-account competitive activity scoped to one CRM owner's book. "What's Natalie's book seeing this week?" — returns rollups for every account whose Salesforce Owner.Name matches (case-insensitive exact match).

GET /v1/owner_book
  ?owner=Natalie+Smith        # exact CRM owner name; call /whoami for the list
  &window=30d
  &limit=25

GET /industry_pulse

Competitive activity across one industry slice of your book. Returns per-account rollups for every account whose firmographic industry list contains the query, plus the distinct competitor set hitting that vertical.

GET /v1/industry_pulse
  ?industry=Fintech           # substring against buyer company industries
  &window=30d
  &in_crm_only=false
  &limit=25

CRM-cross

Events joined against your CRM's opportunity and account state. Each row pairs one open opportunity with the recent reachout activity on that account — the "deal in flight + competitor in the room" patterns.

GET /pipeline_at_risk

Open opportunities whose accounts have had competitive reachouts in the window. Returns per-account rows with the headline opportunity (largest open opp on that account by amount) plus a summary of recent reachout activity. Sorted by reachout count, then opportunity amount.

GET /v1/pipeline_at_risk
  ?window=30d
  &stage_filter=Negotiation   # substring on Opportunity.StageName
  &min_amount=50000           # USD floor
  &limit=25

GET /renewals_at_risk

Customers approaching renewal with recent competitive activity. Cross of: opportunities closing within days_to_renewal AND reachout events in the lookback window. The strongest early-warning signal for churn we have — competitor activity often shows up weeks before formal renewal discussions begin.

GET /v1/renewals_at_risk
  ?days_to_renewal=90         # max days from today to opportunity CloseDate
  &window=30d                 # reachout lookback window
  &limit=25

GET /revival_candidates

Closed-lost accounts where competitor activity has returned. Account-shaped companion to /closed_lost_revival — designed for save-play prospecting: which dead deals have momentum again? Sorted by competitor breadth first (multiple competitors back on a closed-lost = strongest revival signal), then total reachouts.

GET /v1/revival_candidates
  ?window=90d
  &limit=25

Discovery

Schema and tenant-vocabulary lookup. Designed to be called first by an agent so it can suggest realistic filter values without guessing.

GET /whoami

Tenant identity, filter vocabulary, and the full tool catalog. Returns the static enum values for filters (region, employee_band, job_function, seniority, account_status, alert_type) plus the dynamic vocabulary discovered from your recent events (competitors, industries, owners, account types, event date range).

GET /v1/whoami
  ?discovery_window=90d       # how far back to scan for distinct values

Error codes

CodeMeaningWhat to do
200OKProcess the event payload.
202Accepted (account list ingesting)Wait 24 hours. The response includes an ETA.
400Bad requestInspect error.field for the offending parameter.
401UnauthorizedRotate your API key. Test keys can't access live data.
403Tier limit exceededUp-tier or wait for the next billing cycle.
404Account or buyer not foundThe entity isn't on your ingested list yet.
429Rate limitedBackoff with the Retry-After header.
5xxServer errorAutomatic retry from our side; webhook delivery is idempotent.

Rate limits

All endpoints share a single rate limit budget per API key: 600 requests / minute (10/sec) on Basic and Pro, 3,000 requests / minute on Custom. Webhook deliveries do not count against your rate limit budget.

Event schema

Every endpoint returns the same event shape. The full JSON schema is published at /schema/event.json with semver-stable releases. Breaking changes ship as new endpoint versions (/v2/...); existing versions are supported for a minimum of 24 months after a successor ships.

Deal Intelligence

Subscribe to your competition.

The best way to know what your competitors are doing inside your accounts.