Research
Research a target account before your first outreach
Read the company's own site, recent coverage, and what they post publicly, then write an account brief with recent moves, likely priorities, conversation starters, and honest unknowns.
Copy the prompt
Replace the bracketed fields, then paste it into Claude Code, Codex, or any AI with terminal access. It installs the Felo skill, asks before spending credits, and runs the workflow.
Build an account brief for [company name or domain] before [outreach / discovery call / renewal]. Our offer: [product or service]. Buyer we care about: [role]. Time window for recent moves: [last 90 days / last 12 months]. Read their own site, public coverage, and public posts. Produce a Markdown brief with: 1. What they do, in one paragraph, with sources. 2. Recent moves: launches, funding, hiring, leadership, partnerships — with dates and links. 3. Likely priorities this quarter, clearly marked as inference. 4. Conversation starters: three specific observations, each with its source. 5. Unknowns: what you could not verify from public sources. 6. A full source list. Rules: - Never invent a contact name, email, phone number, headcount, revenue, or funding amount. - Public sources only: no private data, no login-only pages, no scraped directories. - Do not write to a CRM or any other system. The brief is a file a person reviews. - Mark every inference as inference; keep facts with their links and dates. - Do not state the buyer's intent as fact. - Before spending credits, show the API calls you plan to make and wait for my approval. - Do not send, schedule, publish, or message anyone. - Write the result to ./account-brief.md and summarize it in chat.
The copy also includes the install steps and safety rules.
What you get
Example output from a run. Not a live generation.
Account brief — [company], checked 2026-10-30
What they do
Builds warehouse picking robots for mid-size logistics providers. Source: https://example.com/about (checked 2026-10-30).
Recent moves
- Opened a second assembly site — https://example.com/news/expansion (2026-10-24)
- Hiring for a "deployment operations" team — https://example.com/careers (checked 2026-10-30)
- A post from their account on a partner integration — @example_company (2026-10-21)
Likely priorities (inference)
Faster deployments across more sites. Inference from the expansion post and the new deployment role — not a stated plan, and not confirmed anywhere public.
Conversation starters
- Their second site is new: ask how the deployment playbook changes when sites differ. Source: https://example.com/news/expansion
- A dedicated deployment operations role suggests a handoff between sales and install. Source: https://example.com/careers
Unknowns
- Who owns deployment operations today: Not available from public sources.
- Fleet size and contract values: Not available.
Sources
- https://example.com/about
- https://example.com/news/expansion
- https://example.com/careers
The exact calls
For developers: the same workflow as copy-paste code, in the order it runs.
Show the exact API calls
Get the sourced company overview
Chat returns an answer plus the pages it used. Start with a narrow question — what the company does, who buys from it, and what changed recently — and keep the links: the overview orients you, but every claim in the brief needs the page behind it. The helper below sends the key once and throws on a non-2xx response.
account.ts const BASE = "https://openapi.felo.ai"; const headers = { Authorization: `Bearer ${process.env.FELO_API_KEY}`, "Content-Type": "application/json", }; async function felo<T>(path: string, body?: unknown): Promise<T> { const res = await fetch(`${BASE}${path}`, { method: body ? "POST" : "GET", headers, body: body ? JSON.stringify(body) : undefined, }); if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`); return res.json() as Promise<T>; } type ChatData = { data: { answer: string; resources: { link: string; title: string; snippet: string }[]; }; }; const company = "Example Robotics"; const domain = "example.com"; const buyerRole = "Head of Operations"; const overview = await felo<ChatData>("/v2/chat", { query: `What does ${company} do, who buys from it, and what changed in the last 90 days?`, }); const seedSources = overview.data.resources.slice(0, 5);Example output
{ "answer": "The company builds …", "sources": [ { "link": "https://example.com/about", "title": "…", "snippet": "…" } ] }Read their own pages
The about, pricing, and careers pages are the cheapest signal about a company's direction: what they sell, how they price it, and what work they are hiring for. Extract each as Markdown with readability enabled, and timestamp the capture so the brief can say how current it is.
account.ts type ExtractData = { data: { content: string } }; const companyPages = [ { url: `https://${domain}/about`, title: "About" }, { url: `https://${domain}/pricing`, title: "Pricing" }, { url: `https://${domain}/careers`, title: "Careers" }, ]; const pages = await Promise.all( companyPages.map(async (page) => { const res = await felo<ExtractData>("/v2/web/extract", { url: page.url, output_format: "markdown", with_readability: true, with_cache: true, }); const checkedAt = new Date().toISOString().slice(0, 10); return { ...page, checkedAt, content: res.data.content }; }), );Example output
{ "url": "https://example.com/careers", "checkedAt": "2026-10-30", "content": "# Careers\n\nOpen roles: deployment operations…" }Check what they post publicly
Recent posts show what the company is telling the market: launches, partnerships, and the roles they are proud of. Search the last 90 days, keep the handle and date with each post, and treat the result as signal — a company's own posts are marketing, not independent evidence.
account.ts type XData = { data: { tweets: { content: string; author: { username: string }; created_at: string; metrics: { retweet_count: number; favorite_count: number }; }[]; }; }; const since = Math.floor((Date.now() - 90 * 24 * 60 * 60 * 1000) / 1000); const posts = await felo<XData>("/v2/x/tweet/search", { query: `"${company}" (hiring OR launch OR partnership OR expansion)`, limit: 20, since_time: since, }); const recent = posts.data.tweets.slice(0, 5);Example output
{ "data": { "tweets": [ { "content": "We just opened our second assembly site", "author": { "username": "example_company" }, "created_at": "2026-10-21T14:02:00Z", "metrics": { "retweet_count": 4, "favorite_count": 31 } } ] } }Compose the account brief
This step is plain code: assemble the overview, the recent posts, and the captured pages into one Markdown string with explicit sections. Anything you can only infer goes under likely priorities with the word inference; anything the sources do not cover goes under unknowns rather than being filled in.
account.ts const today = new Date().toISOString().slice(0, 10); const accountBrief = [ `# Account brief — ${company}`, `Checked ${today}. Buyer role: ${buyerRole}.`, "", "## What they do", overview.data.answer, "", "## Recent moves", ...recent.map( (post) => `- ${post.created_at.slice(0, 10)}: ${post.content} (from @${post.author.username})`, ), ...pages.map((page) => `- ${page.title} page captured ${page.checkedAt}: ${page.url}`), "", "## Likely priorities (inference)", "<!-- One or two sentences from the moves above. Mark them as inference, never a stated plan. Delete this comment when filled. -->", "", "## Conversation starters", ...recent.slice(0, 3).map( (post) => `- Ask about: ${post.content.slice(0, 100)} (dated ${post.created_at.slice(0, 10)})`, ), "", "## Unknowns", "- Who owns the workflow we sell into: Not available from public sources.", "- Contract values and team size: Not available.", "", "## Sources", ...pages.map((page) => `- ${page.title}: ${page.url}`), ...seedSources.map((source) => `- ${source.title}: ${source.link}`), ].join("\n"); console.log(accountBrief);Example output
# Account brief — Example Robotics
Keep the evidence in a LiveDoc
Create one LiveDoc per account, add the captured pages and the Chat sources as URL resources, then add the brief as a document resource. The next call, renewal, or expansion check reuses the same archive, and re-adding a URL is deduplicated rather than duplicated.
account.ts type LiveDocData = { data: { short_id: string } }; const accountDoc = await felo<LiveDocData>("/v2/livedocs", { name: `Account: ${company}`, description: `Briefs and sources checked ${today}`, }); await felo(`/v2/livedocs/${accountDoc.data.short_id}/resources/urls`, { urls: [ ...pages.map((page) => ({ url: page.url, title: page.title })), ...seedSources.map((source) => ({ url: source.link, title: source.title })), ], }); await felo(`/v2/livedocs/${accountDoc.data.short_id}/resources/doc`, { title: `Account brief ${today}`, content: accountBrief, }); console.log(accountDoc.data.short_id); // the next call reuses this LiveDocExample output
{ "short_id": "PvyKouzJirXjFdst4uKRK3" }
What's included
- A sourced company overview from Chat API, plus the pages behind it
- The company's about, pricing, and careers pages extracted as Markdown
- Recent public posts with handles and dates, labeled as signal
- An account brief with recent moves, inference, starters, and unknowns
- The evidence saved in a LiveDoc for the next call or the next account
What it doesn't do
- No contact emails, phone numbers, or personal profiles
- No private data, and no pages behind a login or paywall
- No CRM writes; the brief is a file a person reviews first
- Headcount, revenue, and funding are reported only when a source states them
- Priorities and buyer intent are marked as inference, never as facts
Questions
Does it find contact emails or phone numbers?
No. The workflow reads public company sources and never returns emails, phone numbers, or personal profiles. Contact data belongs in a consented source you already manage, not in a research brief.
Can I research a private company?
Yes, as far as its public footprint goes. If there is no public source for revenue, headcount, or funding, the brief writes "Not available" instead of estimating it.
Does it write to my CRM?
No. It produces a Markdown brief and a LiveDoc. You review both, then copy across whatever you decide is accurate and appropriate for the CRM.
How recent is "recent"?
The time window is a placeholder you resolve to explicit dates. X search takes a Unix timestamp, so the window is enforced in the request; every item in the brief carries its own date.
Can I use it for a renewal or expansion account?
Yes. Ask what changed since the last renewal, and keep the same LiveDoc per account so the previous check stays beside the new one instead of being overwritten.
How much does an account brief cost?
Each chat, extraction, and X search call spends usage-based credits; current rates are on the pricing page. The prompt asks for approval before it spends anything.
Related workflows
Turn a market question into a go-to-market brief
Ask the market a precise question, read the category and pricing pages behind the answer, collect how buyers describe the problem on X, then save a GTM brief with sourced claims and open questions.
Open workflowResearchBuild a competitor teardown your sales team can use
Collect a competitor's own pages, the coverage around them, and what buyers say on X, then assemble a teardown where every claim carries a source, a date, and a status.
Open workflowContentTurn a product brief into a landing page
Research the audience, keep the brief in a LiveDoc, then create a Landing Page task from that context and poll until the hosted page is ready.
Open workflowMake it yours
The same recipe, adapted to the way you actually ship.
Run it for a whole territory
Loop over the domains on your list and reuse the same functions, one LiveDoc per account. Name each LiveDoc from the domain so the archive stays navigable, and review the briefs in the order you plan to work them.
Refresh it right before the call
Re-run the workflow against the same LiveDoc when the call is booked. Each run adds a dated brief, so the previous check stays beside the new one and you can see what changed without rereading both.
Turn the unknowns into discovery questions
Take the unknowns section into the call as written questions instead of guessing answers beforehand. That keeps the brief honest and gives you something concrete to confirm with the buyer.
Keep going
The reference pages behind this recipe, and where to go next.