# AI contact research

> AI contact research reads the public web about a person and their company, saves cited facts and openers, and writes first lines in bulk within a budget.

Source: https://docs.ayinbox.com/guides/ai-contact-research/

AI contact research reads the public web for a specific person and their company, then saves a small set of accurate, cited facts you can use to write a genuinely personal first email. It is grounded in your workspace voice profile, so what it saves is judged by what you actually sell and who you sell to.

It gathers raw material and one suggested first line; it never writes or sends the email. You stay in control of the outreach.

Running research [#running-research]

Open a contact and go to the **Research** tab. Add an optional focus (for example "recent funding" or "hiring") and press **Run research**. The agent searches the web, reads the most promising pages, and saves what it found. A run costs 2 AI credits (see [AI credits](/guides/ai-credits/)).

To research many contacts at once, select them in the contacts list and choose **Research** from the selection bar. You are asked to confirm the credit cost first. Bulk research runs in the background: results fill in on each contact's Research tab as they finish, and the contacts list updates live.

Company website, news and LinkedIn posts [#company-website-news-and-linkedin-posts]

When the instance's operator turns on Looot research (`AYINBOX_LOOOT_RESEARCH`, see [configuration](/development/configuration/)), research also gets three company lookups through [Looot](https://looot.ai/):

* **Company website**: reads a page of the company's site as text, including pages a plain fetch cannot render. Long pages are cut to the same length as any other page research reads.
* **Company news**: recent articles about the company, found by its domain or its name. An article is kept only when its title, summary or text mentions the company's name or domain, or its link is on the company's own domain. A news provider can ignore the company it was asked about and answer with unrelated headlines, so everything else is dropped rather than shown.
* **Company LinkedIn posts**: the company's recent posts from its LinkedIn page (`linkedin.com/company/...`), each with its date and link. Only a company page is accepted, never a person's profile, and research is told to use a page from the contact's record or one it found on a page it read rather than a guessed one.

These lookups are dated, which is what a first line needs. Each run makes at most two website reads, one news lookup and one LinkedIn lookup. They are paid from the instance's Looot balance, and each one has a small cost cap. On an instance that bills with credits, the workspace pays for each lookup's settled spend in [credits](/guides/ai-credits/#looot-verification-lead-search-and-research). Before a lookup runs, AY Inbox checks that the workspace could pay its cost cap in credits within its balance, its spend limits and your own member limit. When it could not, the lookup is skipped without calling Looot, and research carries on with the other sources. A lookup your workspace already made today is reused instead of paid for again, and needs no credits. [Aya](/guides/ai-assistant/) can use the same lookups when you ask it about a company. They are not on the [REST tool surface](/api/agent-tools/) or the [MCP server](/api/mcp/): only research and the assistant run them.

Everything a page, article or post says is treated as data, never as instructions to the agent.

What it saves [#what-it-saves]

Every finding is cited. The agent only keeps facts it saw on a real page it read, and it records how confident it is in each one:

* **First line**: one sentence under 25 words that opens a cold email with one concrete fact from the last 90 days, shown with the fact's date and a link to its source. The source has to be a page, article or post research actually read in that run, and the date has to fall within the last 90 days.
* **Company**: a short summary, industry, rough size, who they sell to, and any technology signals.
* **Signals**: up to five specific, dated, cited facts worth mentioning (a hire, a launch, a funding round, a post). Each shows a **high**, **medium**, or **low** confidence chip and links to its source.
* **Openers**: up to three plain-language opening lines built on those signals, each with a copy button so you can drop it straight into an email.
* **Notes**: a short honest summary of what it found and what it could not.

When no fact from the last 90 days can be confirmed, the first line is `NO_FACT`. That means "send this email with no personalization": the Research tab says so, and anything that reads the result through the API should leave the opener out rather than fall back to an older or vaguer fact. A run that finds nothing solid always has a `NO_FACT` first line. Runs saved before first lines existed have no `first_line` at all.

If the agent searches and genuinely finds nothing solid, it says so. That is still a completed run and still costs its credits, because you paid for the search, not the result.

<Callout type="info" title="Sources and honesty">
  The agent is built to cite or stay quiet. It will not guess a job title, invent a connection, or fill a field to look thorough. A finding with no source is never saved. Treat low-confidence signals as leads to verify, not facts.
</Callout>

The first line on the contact [#the-first-line-on-the-contact]

Every finished run also stores its first line on the contact, as three custom fields:

| Field                   | What it holds                                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `first_line`            | The sentence, ready to drop into an email. Empty when the run found no fact from the last 90 days (`NO_FACT`) |
| `first_line_source`     | The page, article or post the sentence cites. Empty with `NO_FACT`                                            |
| `first_line_checked_at` | When the line was checked, in UTC (RFC 3339). Set for `NO_FACT` too                                           |

A later run replaces all three, unless the contact holds a first line research did not write: a non-empty `first_line` with no `first_line_source`, from an import or typed by hand. That line is yours, so research never replaces it, not with a new line and not with `NO_FACT`, and the run's result stays on the Research tab. Clear `first_line` to get a researched one again. A run that fails before it saves anything leaves them as they were. Put the line in a sequence email with `{{.first_line}}` (or `{{first_line}}`): a contact without one gets nothing at that spot, never a raw tag. See [AI variables](/guides/ai-variables/#stored-first-lines) for fallbacks. The fields travel with the contact like any other custom field, so a [workspace export](/guides/workspace-export-import/) carries them.

Write first lines in bulk [#write-first-lines-in-bulk]

To give many contacts a first line at once, select them in the contacts list (or open a segment and select its members) and choose **First lines** from the selection bar. Set a budget in dollars and start the job. It runs in the background: the **First lines** chip at the top of the contacts list shows its progress, and the panel it opens lists every job with what it wrote, what it skipped and what it spent.

This needs the operator to turn on first-line jobs (`AYINBOX_LOOOT_FIRST_LINES`, see [configuration](/development/configuration/)), an AI provider on the instance, and the **Manage contacts** and **Use AI** permissions. A job takes up to 5,000 contacts.

A job is built to be cheap at scale:

* **One lookup per company.** Contacts are grouped by company (their `company_domain` or `website` field, otherwise the domain of their address when it is not a consumer mail service such as Gmail). Each company's website, recent news and, when a contact's record names its LinkedIn company page (`company_linkedin_url`, `linkedin_company_url`, or a company page in `linkedin_url`), its LinkedIn posts are read once through Looot and shared by every contact there.
* **One model call per contact.** The model writes the line from the company research and the contact's name, company and title, under the same rule as research: one sentence under 25 words, one fact from the last 90 days, the fact's date and source taken from the research. A line that cites a source the research did not return, or a fact older than 90 days, is stored as `NO_FACT`. A company whose lookups found nothing gets `NO_FACT` with no model call at all.
* **Your own lines are kept.** A contact that already holds a first line research did not write is counted as **Kept** at no cost, and nothing on it changes.
* **Fresh lines are skipped.** A contact whose `first_line_checked_at` is less than 30 days old is skipped at no cost, `NO_FACT` included, so running the same selection twice does not pay twice. Contacts with no company domain are skipped too, and nothing is written to them.
* **A budget checked before every contact.** Before each contact the job adds that contact's worst case to what it has spent so far: the company's lookup caps when the company is new to the job, plus the model call's estimate. When that would pass the budget, the job stops and shows **Budget reached**. A Looot lookup whose cost Looot does not report counts at its cap. The model side is a count, not a bill: see [how the model call is counted](#how-the-model-call-is-counted) for when the real bill can differ.
* **Resumable.** Progress is saved after every contact. A job that stops mid-way (a restart, a lookup still running) is picked up again within a few minutes, and the company it was in replays its Looot lookups under the same keys, which Looot does not bill again. Each contact's company and LinkedIn page are fixed when the job starts, so editing a contact during the job does not send a new lookup. A line the model has already written is kept across a retry, so it is not paid for twice.

How the model call is counted [#how-the-model-call-is-counted]

The job does not see what the AI provider bills, so it counts each call from the tokens the provider reports. For the models AY Inbox defaults to on Anthropic, it uses their list prices (`claude-haiku-4-5`: $1 per million input tokens and $5 per million output tokens; `claude-sonnet-4-6`: $3 and $15). For any other model it uses $5 and $25, so one call's estimate is about **$0.045**. That is above most list prices but not all of them: a model priced higher is really billed more than the job counts, so set a lower budget if your instance runs one. Only the two models above are counted at their real rate. Up to 800 of a call's tokens, its output limit, are priced at the output rate, and a call whose usage the provider does not report counts at the estimate. The estimate assumes about three characters to a token, which holds for English and European text. A site in Chinese, Japanese or Korean can use more, and the count then follows the tokens the provider reports, so one contact can pass its estimate. A free or local model (`AI_FREE`) counts nothing. The job panel shows which of these applied.

What a job costs [#what-a-job-costs]

The dialog shows the most a job can spend: the smaller of the budget and $0.07 a contact, the worst case of a contact alone at its company with the model call at its cap. Real jobs spend far less, because contacts share their company's lookups, misses cost nothing at Looot and fresh lines are skipped. Company lookups come from the instance's Looot balance.

On an instance that bills with credits, the workspace pays for:

* each line's model call: its tokens at the [AI credit rate](/guides/ai-credits/#what-each-action-costs), at least 1 credit, shown as **First lines**;
* each company's Looot spend at one credit per $0.01, rounded up (the operator can change the price), shown as **First lines company research**. A lookup whose cost Looot does not report is charged at its cap, because a job has no later step that could settle it, and that charge is not given back.

The job checks the balance before each contact and stops with an error when the workspace cannot pay for the next one, or when a [spend limit](/guides/ai-credits/#usage-and-spend-controls) is reached. Each charge is made once, so a resumed job never pays for the same contact or company twice. A self-hosted instance that does not bill with credits charges none of this.

Cost and limits [#cost-and-limits]

* Each run costs 2 AI credits, whether or not it finds something.
* A run is bounded (a small number of searches and page reads) so it stays fast and cheap.
* With Looot research on, a run also makes at most four company lookups, paid from the instance's Looot balance and, on an instance that bills with credits, charged to the workspace at the operator's price per credit (one credit per $0.01 of settled spend by default), rounded up.
* A single bulk request covers up to 500 contacts. AY Inbox checks you have credits before it starts and skips work for contacts it cannot fund.
* A first-line job covers up to 5,000 contacts inside a budget of $0.05 to $100 (default $5). See [Write first lines in bulk](#write-first-lines-in-bulk).

Privacy [#privacy]

Research only reads the public web. It does not contact the person, and it never sends email. The facts it saves live on the contact for your team to use; running research is recorded in your workspace activity like any other change, and so is starting a first-line job.
