Enrichment & images
When a company or contact is created, the agent is queued to fill in what can be observed about it. None of it is required — every outside source is optional, and the product is designed to run with none of them (Configuration).
Company data
Given a domain, the agent fills in the real name, industry, location and social links — so a company arrives as itself rather than as a grey square with its initials in it. A written brief (the Research button) is a separate, heavier pass that summarizes the whole account onto its timeline.
Pictures are copied, never linked
A logo and a profile photo both arrive as somebody else's URL, and neither is stored as one. A LinkedIn CDN URL is signed with an expiry roughly three weeks out; a brand's own CDN becomes a rate limit on a page drawing forty logos. Either way the failure is the worst shape it can take — a picture that works today and is broken next month, with nothing connecting the two.
So the bytes are copied into blob storage and the record points at our copy. The storage key carries a hash of the bytes, which makes re-running idempotent: unchanged artwork lands on the same URL, and a redesigned logo gets a new one rather than a month of stale cache.
| Picture | Source |
|---|---|
| A company's logo and brand artwork | The brand's own site / a web search |
| A company's favicon | The icon the domain's own site serves — needs no vendor at all |
| A contact's portrait | Only ever a source already tied to that person — their LinkedIn profile, their GitHub account, or their employer's team page, in that order of certainty |
| A signed-in user's avatar | Their Google account |
There is no image search by name
It's the obvious next step and the one that breaks everything else. A search for a name returns three different confident faces — and unlike a wrong name, which a rep can read and smell, nobody audits a face. The rule is: guess where to look, never what you'll find. A team page is a guess about a URL; the name printed beside the photo is the answer.
Without blob storage configured, portraits are skipped entirely (a signed CDN link that expires is not worth storing), while logos and favicons keep the origin's URL.
Catching up what was missed
Anything created before a capability existed never got it. Two backfill routes run on sign-in — the one moment nothing else in the app can see:
| Sweep | Covers | Cost |
|---|---|---|
| Automatic enrichment sweep | Records never successfully looked up | One web lookup per company |
| Image mirror sweep | Rows whose picture is still served from someone else's origin | Nothing |
| Favicon sweep | Companies with no favicon yet | Nothing — runs every pass |
Each is capped per table per sweep, so a backlog drains over several sign-ins rather than in one burst that looks like a scrape. A finished portrait attempt stands a contact down for thirty days; a finished workspace-profile attempt stands the sweep down for seven. There is deliberately no "sweep now" button — a control whose correct usage is "press it whenever you notice blanks" is a chore dressed as a feature.
Every session knows who we are
A research agent that knows everything about a person and nothing about the company employing it writes a dossier, not a briefing. So a tiny Who we are block — 320 characters of narrative plus one line each for what we sell, who we sell to, and what we're picked over — rides in front of every question. It's derived from the website set during onboarding, refreshed when that website changes, and it dies with the website rather than describing the company we used to be.