How to Build an AI Company Research Agent with Claude Code and ClickUp
Build an agent that wakes up when you add a company name to ClickUp, researches it autonomously, and delivers a structured competitive brief in minutes.
Why Manual Company Research Is a Tax on Your Sales Team
When a rep wants background on a prospect, they typically spend 20 to 40 minutes opening tabs: LinkedIn, Crunchbase, Google News, the company’s website, maybe G2. They copy notes into a doc, format it, and send it. That’s manageable for one company. For a pipeline of 50 or 100, it’s an impossible workload.
Building an AI company research agent solves this at the source. This guide walks through how to build one using Claude Code and ClickUp: your team adds a company name to ClickUp, and minutes later a structured competitive brief appears — funding history, headcount, products, competitors, and recent news — without anyone lifting a finger.
Here’s exactly how to do it.
How the Agent Works
The system has four moving parts:
- ClickUp — the trigger point. When someone creates a task with a company name, ClickUp fires a webhook.
- A lightweight server — receives the webhook, extracts the company name, and kicks off the research chain.
- Claude — the reasoning layer. It searches for information, reads sources, and synthesizes everything into a structured brief.
- ClickUp API — writes the finished brief back to the original task as a comment or description update.
The workflow fits naturally into how teams already operate. No one changes their habits — they add a company name to ClickUp the same way they always have, and the research shows up.
What the agent researches
By default, each brief covers:
- Company overview (founding year, headquarters, headcount, industry)
- Funding history and key investors
- Core products or services
- Primary competitors
- Recent news from the last 30 to 90 days
- Potential buying signals or pain points
You can customize this list — the Claude prompt is easy to modify, and we’ll cover that in Step 3.
Prerequisites
Before building, make sure you have:
- A ClickUp account — any paid plan to access webhook functionality
- An Anthropic API key — for calling Claude
- A search API key — Tavily is well-suited for grounded web search; Serper.dev is a solid alternative
- Node.js 18+ installed locally
- Claude Code — install it with
npm install -g @anthropic-ai/claude-code - A server host — Railway, Fly.io, or a basic VPS all work
If Claude Code is new to you: it’s Anthropic’s command-line coding agent. You describe what you want built, and it writes, edits, and runs the code for you — including installing dependencies, fixing errors, and wiring files together. It significantly reduces the time from idea to working prototype.
Building the Agent Step by Step
Step 1: Scaffold the Project with Claude Code
Create a new directory and launch Claude Code:
mkdir company-research-agent && cd company-research-agent
claude
Give it this prompt to generate the project structure:
Build a Node.js Express server that:
1. Receives ClickUp webhooks on POST /webhook
2. Validates the webhook signature from the X-Signature header
3. Extracts a company name from the task name when a new task is created in a specific list
4. Calls an async research function with that company name
5. Posts the research result back to ClickUp as a task comment
6. Uses environment variables: CLICKUP_API_KEY, CLICKUP_LIST_ID,
WEBHOOK_SECRET, ANTHROPIC_API_KEY, TAVILY_API_KEY
Claude Code will scaffold the entire project: package.json, Express setup, environment variable handling, and the base webhook handler. On a first pass, it’s usually 80–90% complete. You’ll iterate on a few details, but the bulk of the boilerplate is done.
Step 2: Configure the ClickUp Webhook
ClickUp webhooks let you subscribe to events in your workspace. Register one via the ClickUp API:
curl -X POST "https://api.clickup.com/api/v2/team/{team_id}/webhook" \
-H "Authorization: YOUR_CLICKUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"endpoint": "https://your-server.com/webhook",
"events": ["taskCreated"],
"list_id": "YOUR_LIST_ID"
}'
Replace {team_id} with your ClickUp workspace ID (found under Settings → Workspace) and YOUR_LIST_ID with the ID of the list where your team will add company names.
ClickUp returns a webhook_id and a secret. Store that secret as WEBHOOK_SECRET — you’ll use it to verify that incoming requests actually come from ClickUp.
A few practical notes:
taskCreatedfires as soon as a task is added to the list.- If your team uses one list for multiple purposes, use
taskUpdatedwith a custom field filter instead — more on that in the extensions section below. - ClickUp delivers webhooks within a few seconds of the event.
Step 3: Build the Research Function
This is the core of the agent. Ask Claude Code to write a research orchestration function:
Write an async function researchCompany(companyName) that:
1. Uses Tavily to search for "[companyName] company overview funding headcount"
2. Uses Tavily to search for "[companyName] top competitors alternatives"
3. Uses Tavily to search for "[companyName] news announcements 2025"
4. Runs all three searches in parallel using Promise.allSettled
5. Passes the compiled results to Claude claude-sonnet-4-5 via the Anthropic SDK
6. Returns a structured markdown brief with sections: Overview, Funding & Investors,
Products & Services, Key Competitors, Recent News, Buying Signals
7. Handles errors gracefully and returns a partial brief if any searches fail
Claude Code will produce something close to this:
const Anthropic = require("@anthropic-ai/sdk");
const { tavily } = require("@tavily/core");
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const tavilyClient = tavily({ apiKey: process.env.TAVILY_API_KEY });
async function researchCompany(companyName) {
const [overviewResults, competitorResults, newsResults] = await Promise.allSettled([
tavilyClient.search(`${companyName} company overview funding headcount employees`, { maxResults: 5 }),
tavilyClient.search(`${companyName} top competitors alternatives`, { maxResults: 5 }),
tavilyClient.search(`${companyName} news announcements 2025`, { maxResults: 5 }),
]);
const searchContext = [
formatResults("Company Overview", overviewResults),
formatResults("Competitors", competitorResults),
formatResults("Recent News", newsResults),
].join("\n\n");
const message = await anthropic.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 2048,
messages: [
{
role: "user",
content: `Based on the following search results, write a structured research brief for "${companyName}".
Format the brief in markdown with these exact sections:
## Overview
## Funding & Investors
## Products & Services
## Key Competitors
## Recent News (last 90 days)
## Buying Signals
Be specific and factual. Note any uncertainties. Keep each section to 3–5 bullet points.
Search Results:
${searchContext}`,
},
],
});
return message.content[0].text;
}
The parallel search calls matter. Running three searches concurrently instead of sequentially cuts total research time from 30+ seconds to under 15 for the search phase. Combined with Claude’s synthesis time, most briefs complete in 45–90 seconds.
Step 4: Write the Brief Back to ClickUp
Once research is complete, post the brief as a task comment:
async function postToClickUp(taskId, brief, companyName) {
const response = await fetch(`https://api.clickup.com/api/v2/task/${taskId}/comment`, {
method: "POST",
headers: {
Authorization: process.env.CLICKUP_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
comment_text: `## Research Brief: ${companyName}\n\n${brief}\n\n---\n*Generated by Company Research Agent*`,
notify_all: false,
}),
});
if (!response.ok) {
throw new Error(`ClickUp API error: ${response.status}`);
}
return await response.json();
}
A few decisions to make here:
- Comments vs. description — Comments preserve the original task and create a searchable audit trail. For automated content, this is usually the right choice.
- Custom fields — If you have structured ClickUp custom fields for research data, use the
/field/{field_id}/valueendpoint to write there instead. - notify_all — Set to
trueif task assignees should get a notification when the brief lands.
Step 5: Wire the Webhook Handler
The handler ties everything together:
app.post("/webhook", async (req, res) => {
const signature = req.headers["x-signature"];
if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: "Invalid signature" });
}
const { event, task } = req.body;
if (event !== "taskCreated") {
return res.status(200).json({ ok: true });
}
if (task.list.id !== process.env.CLICKUP_LIST_ID) {
return res.status(200).json({ ok: true });
}
// Respond immediately — ClickUp times out after a few seconds
res.status(200).json({ ok: true });
// Research runs asynchronously after the response
const companyName = task.name;
try {
const brief = await researchCompany(companyName);
await postToClickUp(task.id, brief, companyName);
} catch (error) {
console.error(`Research failed for ${companyName}:`, error);
}
});
The critical pattern: respond to ClickUp immediately, then run research asynchronously. ClickUp expects a response within a few seconds. If your server waits for Claude to finish synthesizing before replying, ClickUp marks the delivery as failed and may retry.
Step 6: Deploy and Test
Railway is the fastest deployment option for this kind of small server:
npm install -g @railway/cli
railway login
railway init
railway up
After deploying, copy the public URL from your Railway dashboard and update your ClickUp webhook to point to it.
To test end-to-end:
- Go to your designated ClickUp list and create a task named something like “HubSpot”
- Check your server logs — the webhook should arrive within seconds
- Wait 45–90 seconds for research to complete
- Open the task in ClickUp — the brief should appear as a comment
If nothing shows up, check: server logs for API errors, that all environment variables are set correctly in Railway, and that the ClickUp list ID in your environment matches the webhook configuration.
Useful Extensions
Once the core version is running, these additions significantly increase the agent’s value.
Add a confidence rating
Add a line to your Claude prompt asking it to rate confidence (1–5) in each section and flag any claims it couldn’t verify. Sales reps find this useful — a low score tells them to do additional digging before a first call.
Trigger on a custom field instead of task creation
If your team uses the same ClickUp list for purposes beyond research, every new task triggers the agent — which gets noisy fast. Add a ClickUp custom field called “Research Requested” with a checkbox or dropdown. Subscribe to taskUpdated events and only kick off research when that field changes.
Enrich with CRM data
Before generating the brief, pull any existing deal history from HubSpot or Salesforce. Pass your internal context to Claude alongside the web search results. The resulting brief is considerably more useful than a purely external research summary.
Schedule recurring research
For companies already in your pipeline, re-run research every 30 days to catch funding rounds, leadership changes, or new product launches. A simple cron job that reads a list of tracked companies and calls your research function handles this without any additional infrastructure. If you want to build this as a scheduled background agent without managing the cron infrastructure yourself, MindStudio’s autonomous background agent capability handles the scheduling layer.
Where MindStudio Fits Into This Stack
Claude Code handles the code generation efficiently, but there are two points in this stack where MindStudio adds real utility.
For developers extending the agent: MindStudio publishes an npm SDK called the Agent Skills Plugin (@mindstudio-ai/agent). It exposes 120+ typed capabilities — including agent.searchGoogle(), agent.runWorkflow(), and agent.sendEmail() — as simple method calls your Claude Code agent can use directly.
Instead of wiring up Tavily, handling rate limiting, and managing retries yourself, you can call web search in a single line:
const { MindStudio } = require("@mindstudio-ai/agent");
const ms = new MindStudio();
const results = await ms.worker.searchGoogle({ query: `${companyName} funding 2025` });
MindStudio handles the infrastructure layer — auth, rate limiting, retries — so your research logic stays clean.
For teams that don’t want to manage a server at all: MindStudio’s no-code builder can wire together the same ClickUp trigger, Claude reasoning, and web search workflow in a visual canvas. No Node.js, no deployment, no infrastructure to maintain. The same research agent this guide builds in code can be assembled in the visual editor in under an hour.
For sales and marketing teams that want this kind of AI workflow for sales intelligence without a dedicated engineering resource, this is worth exploring. You can try MindStudio free at mindstudio.ai.
Common Mistakes to Avoid
Not responding immediately to the webhook. This is the most common failure mode. ClickUp expects a response in a few seconds — if your handler waits for research to finish, you’ll see timeout errors and missed triggers. Always respond immediately and run research asynchronously.
Using unformatted task names for search. Task names like “SFDC,” “salesforce.com,” or “SF - CRM tool” produce poor search results. Add a normalization step — ask Claude to clean up the name before searching: "Normalize this company name for web search: ${rawName}".
No deduplication. If someone creates the same task twice, your agent fires twice. Keep a short-lived cache of recently processed task IDs — an in-memory Map with a TTL is enough to prevent duplicates without adding Redis to your stack.
Silent failures. When a research job fails, there’s no visibility unless you build it in. At minimum, post a comment to the ClickUp task indicating research failed, with a timestamp. If you want alerting, webhook-triggered notification agents can route failure events to Slack or email without custom code.
Ignoring rate limits. Tavily, Anthropic, and ClickUp all have rate limits. If you process a batch of companies at once, you can hit them quickly. Add a queue (BullMQ works cleanly with Node.js) to control concurrency.
Frequently Asked Questions
How long does the agent take to research each company?
With parallel search calls, most research completes in 45–90 seconds. The main variables are Tavily API response time and how long Claude takes to synthesize. For user experience, this is fine — the brief appears before the person who added it has moved on to their next task.
Can I use a different project management tool instead of ClickUp?
Yes. The webhook-receiver-plus-write-back architecture works with any tool that supports outbound webhooks and has an API for writing data back. Notion, Linear, Asana, and Airtable all follow this pattern. The ClickUp-specific code in Steps 2 and 4 gets swapped out; the research logic in Step 3 stays exactly the same.
Which Claude model should I use for synthesis?
Claude Sonnet 3.5 or Sonnet 4.5 is the right balance — fast, cost-effective, and the brief quality is strong for most use cases. Claude Opus produces more detailed output but is meaningfully slower and more expensive per call. Start with Sonnet and upgrade only if you find the output quality lacking for your specific research needs.
How accurate is the research output?
Reasonably accurate for most companies with a reasonable web presence, but not perfect. The agent searches public web sources, so accuracy depends on what’s indexed. For a funding round announced this week, accuracy is high. For a small private company with limited public information, the brief will reflect that gap. The confidence rating extension helps flag lower-quality briefs automatically.
How do I limit research to only certain tasks, not everything in the list?
The cleanest approach is to scope the ClickUp webhook to a specific list ID. Only tasks created in that list trigger the agent. If you need finer control — say, only tasks with a specific tag or naming convention — add a filter in the webhook handler before kicking off research. A simple if (!task.name.startsWith("Research:")) check is enough.
What does it cost to run?
At current API pricing, a typical research run costs roughly:
- Tavily — $0.01–0.02 for three searches
- Anthropic (Claude Sonnet 3.5) — $0.02–0.05 for synthesis
- Railway hosting — approximately $5/month for a small server
Total per company: well under $0.10. For a team running 100 research requests a month, total API costs land around $3–$7, plus hosting.
Key Takeaways
- The core architecture is simple: ClickUp webhook → Node.js server → parallel web searches → Claude synthesis → ClickUp write-back.
- Claude Code handles the scaffolding and code generation, reducing development time from days to hours.
- Always respond to webhooks immediately and process research asynchronously — this prevents delivery failures.
- Parallel search calls are the key latency optimization; running three searches concurrently instead of sequentially is the difference between 45 seconds and 2+ minutes.
- Extensions like CRM enrichment, custom field triggers, and scheduled re-research add compounding value on top of the same infrastructure.
- If you’d rather build this without managing a server, MindStudio lets you wire the same ClickUp + Claude + web search workflow visually — no code required, and it’s free to start.