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_closeinline.nullwhen 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. Passcompanywhenever 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
| Code | Meaning | What to do |
|---|---|---|
| 200 | OK | Process the event payload. |
| 202 | Accepted (account list ingesting) | Wait 24 hours. The response includes an ETA. |
| 400 | Bad request | Inspect error.field for the offending parameter. |
| 401 | Unauthorized | Rotate your API key. Test keys can't access live data. |
| 403 | Tier limit exceeded | Up-tier or wait for the next billing cycle. |
| 404 | Account or buyer not found | The entity isn't on your ingested list yet. |
| 429 | Rate limited | Backoff with the Retry-After header. |
| 5xx | Server error | Automatic 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.