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

# Core concepts

> Sources, templates, migrations, runs, and the managed agent that turns messy files into clean records.

The Migration API has a small vocabulary. Once these few ideas click, the
endpoints are obvious.

## Source — the system you migrate from

A **source** is one system you migrate customers off of (Salesforce, a legacy
CRM, a pile of spreadsheets). You set it up **once**, in the Vern UI. Over the
API a source is addressed by its **name** — Vern never exposes a source ID. You
can [list your sources](/migration-api/list-sources) to let a customer pick which
system they're migrating from, then pass that name as `source` when you
[create a migration](/migration-api/create-a-migration).

A source is **optional**. A migration that doesn't name one is "sourceless" — the
agent works purely from the uploaded files. A source is **not** a frozen import
plan, either: there's nothing to hand-author and publish. The import logic is
produced by the [managed agent](#the-managed-agent) at run time, and reused across
customers as an optimization.

## Template — the object

A **template** is the target schema for one importable object — a sheet like
Contacts or Companies — with its columns and validation rules (which columns
exist, which are required, which must be unique, how they link to other objects).

Templates are scoped to your **account**, not to a single source: the same set of
objects is available to every source you migrate from. Over the API a template is
addressed by its **slug** — a stable snake\_case identifier derived from its name
(e.g. `contacts`). When you create a migration, each template you select becomes
one **sheet** on its workbook. You can
[list your templates](/migration-api/list-templates) over the API — columns and
all — to show a customer what they can import and let them choose.

## Migration — the instance

A **migration** is one customer's migration — their isolated workspace. You
create it with the first API call and it holds a **workbook**: their data, with
one **sheet** per template. The `id` returned at creation is the **migration ID**
you pass to every later call.

A migration is created against a source (or none), but **nothing is bound to
it** — there's no frozen plan to replay. **Your run and export calls only ever
reference the migration**; you never pass plan or recipe IDs. The agent decides
what logic to run each time.

A migration moves through a small set of overall states, derived from what's
happened so far:

* **`awaiting_files`** — created, with no data source yet: no files uploaded and
  no [live source connected](#live-sources).
* **`ready`** — a data source is in place (files uploaded or a source connected),
  no preview generated yet.
* **`awaiting_approval`** — a preview has been generated and is waiting for you to
  execute or refine it.
* **`completed`** — an execute run has imported the data.

### Live sources

A migration's data can come from **uploaded files**, a **live API source**, or
both. A live source is a connector the agent reads from directly at run time — you
attach one with [`POST /source-connection`](/migration-api/source-connection).
Once a connection exists, the migration leaves `awaiting_files` even with nothing
uploaded, and a `generate` run can extract from it (see
[`extract_via_api`](/migration-api/start-a-run#request-body)). If the connector
needs secrets, the run pauses for [credentials](/migration-api/submit-credentials).

## The managed agent

Every run executes on **one durable, tool-using agent** — the same agent that
powers Vern's interactive import UI, driven headlessly. Across a migration's runs
the agent:

1. **Builds.** On a `generate` run it **reuses-or-authors** the import logic — a
   portable mapping + transform recipe — and verifies it in a sandbox before
   touching real data, stopping at a **preview**.
2. **Self-heals.** When a customer's file drifts from what the recipe expected,
   it edits and re-verifies until it passes.
3. **Asks, when it has to.** If a mapping is genuinely ambiguous and neither the
   data nor your templates settle it, the run pauses at
   [`blocked`](#runs) with a plain-language question. You answer through the run's
   [messages](/migration-api/answer-questions) sub-resource and it resumes.
4. **Executes.** On an `execute` run it runs the real import, then self-heals
   invalid cells. Surviving invalid cells are **reported, not parked** — the run
   completes and the bad cells show up in the report.

The migration is a managed agent, **not a frozen-recipe replay**. Robustness
comes from the agent's self-heal and its ability to ask — not from a pinned
artifact.

### Reuse and pinning

By default the agent **reuses the source's most-recent working recipe** as a
starting point, adapting only what each customer's upload changed. Because every
successful generate writes a fresh recipe, "latest" naturally **drifts** as
different end-customers import slightly different shapes.

If you'd rather isolate that drift, you can **pin** one known-good run's recipe
for a source. Pinned runs read it as a starting point and don't promote their
adapted recipe back to the shared "latest", so one customer's run can't change the
starting point other runs draw from. See [Pin a source](/migration-api/pin-a-source).

## Runs

Anything the agent does is a **run**. You start a run with
[`POST /migrations/{id}/runs`](/migration-api/start-a-run) and a `kind`:

* **`generate`** — author the recipe and produce a preview.
* **`update`** — edit the recipe per a message, producing a new preview.
* **`clarify`** — a read-only question about the current preview (changes
  nothing).
* **`execute`** — run the approved preview for real.

**One run at a time per migration** — starting a second while one is in flight
returns `409`. A `generate` opens a preview run and `update`/`clarify`/`execute`
**resume that same run**, so a single `run_id` stays pollable from preview through
the real import. Each run you poll until it reaches a terminal state:

```
queued → running → awaiting_approval → (execute) → completed
                 ↘ blocked → (answer) → running → …
                 ↘ failed
                 ↘ canceled
```

* **`queued`** — accepted, not started yet.
* **`running`** — the agent is working; the poll response carries a
  human-readable `message`.
* **`blocked`** — the agent is waiting on you. `blocked_reason` says which kind:
  `"question"` (an ambiguous decision; the poll carries `questions`, answered via
  the run's [messages](/migration-api/answer-questions) sub-resource) or
  `"credentials"` (a live source needs secrets; the poll carries a
  `credential_request`, answered via
  [credentials](/migration-api/submit-credentials)).
* **`awaiting_approval`** — a `generate`/`update` run finished and produced a
  preview. Fetch it from [`GET /preview`](/migration-api/get-the-preview), then
  refine or [execute](/migration-api/start-a-run).
* **`completed`** — finished. An `execute` run carries a `report` (rows inserted,
  per-sheet counts, how many rows still carry an invalid cell). A `clarify` run
  completes with no report.
* **`failed`** — the agent couldn't produce a working result even after
  self-healing. For an execute, nothing was inserted.
* **`canceled`** — the run was stopped before it finished (see
  [cancel a run](/migration-api/poll-a-run#cancel-a-run)).

CSV download is **not** a run — it's a synchronous stream of the validated data
that already exists in the migration.

## Identifiers

* **Migrations** and **runs** are identified by a **UUID** — store them as opaque
  strings; don't assume a typed prefix.
* **Sources** are addressed by their **name**, **templates** by their **slug** —
  there are no public source or template IDs.
* The **`external_id`** you set on a migration is your own string, echoed back
  untouched, and makes creation idempotent.

## Next

* [Quickstart](/migration-api/quickstart) — see all of this in one flow.
* [Create a migration](/migration-api/create-a-migration) — the first call.
