> ## Documentation Index
> Fetch the complete documentation index at: https://docs.minerva.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflows — `mc.workflows.*`

> Encoded best-practice multi-call recipes. Start with `resolve_then_enrich` — the faster path for going from names/emails to full profiles.

The shortest path between "I have a list of names/emails" and "I have full
profiles" isn't a single call to `enrich`. It's a two-step pattern:

1. **`/resolve`** — match your records to Minerva PIDs.
2. **`/enrich`** — fetch profiles, keyed *only* by the PIDs from step 1.

The second call skips our match pipeline entirely (we already know who they
are), so it's faster and cheaper than re-running the whole enrich-by-PII
flow. For pipelines that re-process the same person list periodically
(monthly hygiene refresh, segment re-materialization, customer data
activation), step 1 only needs to run once — cache the PIDs and call
`enrich` directly on subsequent runs.

`mc.workflows.resolve_then_enrich` is the encoded version of step 1 + 2:

```python theme={null}
from minerva import Minerva

mc = Minerva()
result = mc.workflows.resolve_then_enrich(
    records=[
        {"record_id": "u1", "emails": ["a@example.com"]},
        {"record_id": "u2", "emails": ["b@example.com"]},
        {"record_id": "u3", "full_name": "Some Person"},
    ],
)

result.resolved              # list[ResolveResult] — PIDs (or None for unmatched)
result.enriched              # list[EnrichResult]  — full profiles, keyed by PID
result.unmatched_record_ids  # list[str]           — record_ids that didn't match
```

The async sibling is `await mc.workflows.resolve_then_enrich(...)` on an
`AsyncMinerva` client; same shape.

## What it does for you

* Validates the resolve request locally (raises `MinervaValidationError` before
  any HTTP call).
* Splits the resolve results into "matched" + "unmatched" so you don't have
  to filter manually.
* Builds the enrich payload from the matched PIDs only — your original
  fields (emails, names, etc.) don't get sent on the second call, and the
  server takes the fast path.
* Skips the enrich call entirely if no records resolved (saves a round-trip
  * your daily quota).

## When to use it vs. `mc.api.enrich` directly

| You have…                                          | Use                                                         |
| -------------------------------------------------- | ----------------------------------------------------------- |
| Names/emails/phones, want profiles                 | `mc.workflows.resolve_then_enrich(records)`                 |
| Already-known Minerva PIDs (e.g. from a prior run) | `mc.api.enrich(records)` with `minerva_pid` per record      |
| LinkedIn URLs                                      | `mc.api.enrich(records)` — direct lookup, no resolve needed |

## Caching the resolved PIDs

If your workflow repeats (e.g. nightly), cache the `result.resolved` list and
short-circuit step 1 on the next run:

```python theme={null}
# First run: full pipeline
result = mc.workflows.resolve_then_enrich(records)
cache.save({r.record_id: r.minerva_pid for r in result.resolved if r.minerva_pid})

# Subsequent runs: enrich by PID directly
known_pids = cache.load()
enrich_resp = mc.api.enrich(
    [{"record_id": rid, "minerva_pid": pid} for rid, pid in known_pids.items()]
)
```

That second pattern is what the server is most optimized for — single round
trip, no match pipeline, just a profile lookup.
