# Enrich

Tiered enrichment for a company, person, or topic - a cached brief instantly, first-party evidence on a miss, and the full fused brief on demand

`POST /enrich/company` / `/enrich/person` / `/enrich/topic` return intelligence on one subject **fast by default**: a fresh cached brief comes back instantly; on a cache miss you get your own first-party conversation evidence in a bounded read while the full web + LinkedIn + Crunchbase fusion rebuilds in the background (`refresh_enqueued: true`). Pass `mode: "full"` when you would rather block for the complete fused brief — including the market-vs-customers divergence map — inline.

Reach for it for call prep, outreach personalization, account research, topic scoping — any "tell me about X" where you want *something grounded now* and the deep brief soon.

| Subject | Operation | REST | MCP (`enrich` tool) | Scope |
| --- | --- | --- | --- | --- |
| Company (by domain) | `enrich.company` | `POST /enrich/company` | action `company` | `data:read` (+ `external_search:execute` for the paid tiers) |
| Person (by LinkedIn URL or email) | `enrich.person` | `POST /enrich/person` | action `person` | same |
| Topic (by term) | `enrich.topic` | `POST /enrich/topic` | action `topic` | same |

## The tier ladder

- **Tier `cached`** — a brief you (or a background refresh) already paid for, served from the store. Company briefs stay fresh for ~7 days, person and topic briefs ~14; the response carries `captured_at` so you can judge staleness yourself, plus the `artifact_id` backing it.
- **Tier `first_party`** — on a miss, a bounded read over your **own** corpus: who has talked to this account, what they said, what your customers say about this topic. It answers in seconds because it never leaves your tenant — and it is evidence generic enrichment tools do not have.
- **The background refresh** — on that same miss, the full composite is enqueued behind the scenes (`refresh_enqueued: true`), so a later call for the same subject lands on tier `cached`. First call seeds; second call feasts.
- **`mode: "full"`** — run the synchronous fused composite inline and return its brief (`tier: "full"`). This is the deep, slow call — use it when you are willing to wait.

The paid tiers (the background refresh and `mode: "full"`) require the `external_search:execute` scope and **degrade instead of failing**: a key without it still gets cache + first-party evidence, with `refresh_omitted: "missing_scope"` telling you why no refresh was queued.

## Company — by domain

```bash
curl -X POST "https://app.amdahl.co/api/platform/v1/enrich/company" \
  -H "X-API-Key: $AMDAHL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "stripe.com", "name": "Stripe" }'
```

Over MCP, the `enrich` tool's `company` action:

```json
{ "action": "company", "domain": "stripe.com", "name": "Stripe" }
```

`domain` is required (it is the cache key); `name` is optional and sharpens both the evidence read and the full fan-out.

**Cache hit** — the stored brief, instantly:

```json
{
  "success": true,
  "tier": "cached",
  "brief": { "summary": "…", "sections": { "positioning": "…", "recent_signals": "…" }, "citations": ["…"], "divergence_map": { "internal_only": ["…"], "market_only": ["…"] } },
  "artifact_id": "a91f…",
  "captured_at": "2026-07-18T02:11:40Z",
  "refresh_enqueued": false
}
```

**Cache miss** — your first-party evidence now, the rebuild queued:

```json
{
  "success": true,
  "tier": "first_party",
  "evidence": {
    "matched_count": 23,
    "utterances": [
      { "text": "we're consolidating vendors this quarter…", "speaker_type": "external", "interaction_id": "int_4812…", "relevance": 0.86 }
    ],
    "narrative_facts": [],
    "entity_lifts": [],
    "call_fingerprints": [],
    "aggregates": { "…": "…" }
  },
  "refresh_enqueued": true
}
```

`evidence` is the bounded internal-evidence envelope — ranked `utterances` plus, when your corpus supports them, `narrative_facts`, `entity_lifts`, and per-call `call_fingerprints`. An account with no history returns the empty envelope (`matched_count: 0`), not an error.

`refresh_enqueued: true` means the full composite is rebuilding in the background — re-call the same subject later and you will land on `tier: "cached"`. There is no promised completion time; treat it as "soon", and poll by simply re-calling.

## Person — by LinkedIn URL or email

A **bare name is refused** (`invalid_argument`) — a name cannot be resolved to one person, and a name-keyed cache would poison lookups. Anchor on `linkedin_url` (preferred — it is the cache key) or `email`:

```bash
curl -X POST "https://app.amdahl.co/api/platform/v1/enrich/person" \
  -H "X-API-Key: $AMDAHL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "linkedin_url": "https://www.linkedin.com/in/example-person/", "name": "Jordan Lee" }'
```

```json
{ "action": "person", "linkedin_url": "https://www.linkedin.com/in/example-person/", "name": "Jordan Lee" }
```

An **email-only** call works, but skips the cache tier by construction (the person-brief cache keys on `linkedin_url`) — it goes straight to first-party evidence (your calls and threads that mention them), with the deep-profile refresh queued behind it.

## Topic — by term

The topic brief keys on the verbatim term; the first-party tier reads what **your customers** say about it (external speakers by construction), so even the fast answer carries the internal half of the divergence story:

```bash
curl -X POST "https://app.amdahl.co/api/platform/v1/enrich/topic" \
  -H "X-API-Key: $AMDAHL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "topic": "AI SDRs" }'
```

```json
{ "action": "topic", "topic": "AI SDRs" }
```

`mode: "full"` on a topic runs the whole divergence-map synthesis inline — market story vs. your customers' story, with the disagreements called out — the long call worth making when the topic is the deliverable.

## A prompt to hand an agent

```
Tiered enrichment, fast path first — use the enrich tool.

Enrich: {company {domain} | person {linkedin_url or email} | topic "{term}"}.

Start in fast mode. Tell me which tier answered (cached / first_party), and:
- if cached: summarize the brief and give me captured_at so I know how fresh it is;
- if first_party: give me the strongest first-party evidence verbatim, confirm
  whether refresh_enqueued is true, and re-check the cache before we finish.

Only use mode "full" if I explicitly say I'm willing to wait for the deep brief.
```

## Variations

- **Block for the deep brief**: `mode: "full"` returns `tier: "full"` with the complete fused brief inline. Requires `external_search:execute`; without it the call degrades to the fast path with `refresh_omitted: "missing_scope"` instead of erroring.
- **Warm the cache ahead of a meeting block**: fast-enrich tomorrow's accounts this afternoon; each miss queues a refresh, so by meeting time the same calls land on `tier: "cached"`.
- **Read-only key**: everything still works on `data:read` alone — you get cache hits and first-party evidence; `refresh_omitted: "missing_scope"` just tells you no background rebuild was queued (so a cold subject stays on the first-party tier until a scoped caller warms it).
- **Batch politely**: enriching a list (the [expansion motion](../endpoints.md#how-the-endpoints-chain)) is exactly what fast mode is for — N quick tiered calls now, the deep briefs backfilling behind you.

## Tips

- **Branch on `tier`, render accordingly.** `cached` → a brief with `captured_at`; `first_party` → verbatim evidence plus a "full brief refreshing" note; `full` → the fused brief. Same envelope every time.
- **`refresh_enqueued: false` on a miss means look at `refresh_omitted`.** `missing_scope` is the usual reason; it is a degradation, not a failure.
- **Anchor people on identifiers, not names.** `linkedin_url` gets you the cache tier; `email` gets first-party resolution; a bare name gets a typed refusal by design.
- **The first-party tier is not a consolation prize.** "What this account said to *us*" is the half of the brief no external tool can produce — often it is the more useful half for a rep.

## See also

- [Lookalike](lookalike.md) — find *which* accounts to enrich (nearest neighbours to your best wins).
- [Endpoints overview](../endpoints.md) — the whole surface, prerequisites, and the expansion motion.
- [Chat](../chat.md) — when the brief needs judgment on top of retrieval.
