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

# Fill a new vector space for past articles

> Fill an embedding model or evaluation plugin on past articles before making search use it.

A new embedding model enters as an evaluation space of your ingestion plugin. Articles ingested from then on get vectors in it, but the ones you already have do not. A backfill fills that space for a Corpus's past articles, at low priority, and a promotion then makes search use it. You can also backfill a second plugin selected in `ingestion.evaluation`; it creates its own segments from each article's canonical Parts. See [Configuration](/reference/configuration#ingestion-routing) for the selection and [Upgrade or switch a plugin](/run-quivr/upgrade-a-plugin#promote-an-evaluation-plugin) for promotion.

## Prerequisites

* A version of your ingestion plugin whose manifest declares the new space, activated with it as `evaluation` and the current one as `served` (see [Upgrade or switch a plugin](/run-quivr/upgrade-a-plugin)). For another model of the same plugin, it must preserve that plugin's stored segments. A second evaluation plugin can use different cuts.
* A key of the Organization that owns the Corpus with the actions `plugins:admin`, `operations:read` (to follow the backfill) and `operations:write` (to pause, resume or cancel it), in `QUIVR_OPERATOR_KEY`, and the Corpus id in `CORPUS_ID`.
* Optional: an `input_price` on the new space in the manifest (`usd_per_million_tokens`), so the estimate shows a cost. Without it the cost is unknown.

## Steps

<Steps>
  <Step title="Estimate it">
    A dry run is required. It counts the articles to fill and estimates how long it takes and what it costs:

    ```bash theme={null}
    curl -s -X POST "$QUIVR_API_URL/v0/admin/backfills" -H "Authorization: Bearer $QUIVR_OPERATOR_KEY" \
      -H "Content-Type: application/json" -d @- <<EOF | jq
    {"idempotency_key": "large-model-2026", "corpus_id": "$CORPUS_ID", "dry_run": true}
    EOF
    ```

    ```json theme={null}
    {"registration_id": "plugin_registration_…", "spaces": ["acme.embedder.large@1"], "versions": 12000, "segments": 48000,
     "input_tokens": 9600000, "estimated_seconds": 6000, "duration_basis": "rate", "estimated_cost_usd": 0.2, "confirmation_required": true}
    ```

    `registration_id` selects any ingestion registration in the active plan; omit it to use the default ingestion plugin. `spaces` defaults to that plugin's evaluation spaces; name them in `spaces` to choose. For a second plugin, articles of the source formats assigned to it in the pinned plan are in scope, including articles without that plugin's segments. Existing segments of that owner are reused. Name its primary space explicitly when preparing an owner switch. If the owner has no segments yet, the dry run estimates segments and tokens from the current served projection; completion counters use the new owner's actual cuts. To fill only part of the Corpus, add `accepted_after` or `accepted_before`: the time Quivr accepted each article. `estimated_seconds` uses the deployment's `backfill.rate`, or the recent throughput when it is slower (`duration_basis`).
  </Step>

  <Step title="Start it">
    Send the same body with `dry_run` set to `false`. When `confirmation_required` is `true`, because the cost exceeds `backfill.max_cost_without_confirmation`, add `"confirm_cost": true`:

    ```bash theme={null}
    curl -s -X POST "$QUIVR_API_URL/v0/admin/backfills" -H "Authorization: Bearer $QUIVR_OPERATOR_KEY" \
      -H "Content-Type: application/json" -d @- <<EOF | jq '{operation_id, state}'
    {"idempotency_key": "large-model-2026", "corpus_id": "$CORPUS_ID", "dry_run": false, "confirm_cost": true}
    EOF
    ```

    ```json theme={null}
    {"operation_id": "operation_…", "state": "queued"}
    ```

    The answer is an Operation, Quivr's record of a long-running administrative task. The same key and body return the same Operation. Keep its id:

    ```bash theme={null}
    export OPERATION_ID=<the operation_id above>
    ```
  </Step>

  <Step title="Follow it">
    ```bash theme={null}
    curl -s "$QUIVR_API_URL/v0/operations/$OPERATION_ID" -H "Authorization: Bearer $QUIVR_OPERATOR_KEY" \
      | jq '{state, counters, checkpoint: .backfill.checkpoint}'
    ```

    `counters` shows `versions_in_scope`, `versions_done`, `versions_skipped` and `segments`. One backfill of a Corpus runs at a time: another one is refused with `409 backfill_in_progress` until it ends. Pause, resume or cancel it with `POST /v0/operations/{id}/pause`, `/resume` or `/cancel`, each with an `idempotency_key` body. A paused or restarted backfill continues after its `checkpoint` and never fills an article twice.
  </Step>

  <Step title="Promote the space">
    When the Operation is `succeeded`, make search use the new space:

    ```bash theme={null}
    curl -s -X POST "$QUIVR_API_URL/v0/admin/spaces/acme.embedder.large@1/promote" -H "Authorization: Bearer $QUIVR_OPERATOR_KEY" \
      -H "Content-Type: application/json" -d '{}' | jq
    ```

    ```json theme={null}
    {"served_space_id": "acme.embedder.large@1", "previous_space_id": "acme.embedder.base@1", "generations_switched": 3, "corpora_incomplete": 0, "segments_missing": 0}
    ```

    A promotion applies to this space's owner across the deployment. Other plugins keep their served spaces. It is refused with `409 coverage_incomplete` while some Corpus has current articles segmented by this owner without a vector in the space: backfill those Corpora first. `{"force": true}` promotes anyway, and the articles without a vector drop out of semantic results until a backfill fills them.
  </Step>
</Steps>

## Check it worked

```bash theme={null}
curl -s "$QUIVR_API_URL/v0/corpora/$CORPUS_ID/vector-spaces" -H "Authorization: Bearer $QUIVR_API_KEY" \
  | jq '{segments, spaces: [.items[] | {vector_space_id, role, covered: .coverage.segments}]}'
```

The new space is `served` and covers every segment produced by its owner. Semantic hits name it in `vector_space_id`.

## Fill and promote another plugin

To change the plugin that produces segments and vectors, follow [Try a vector model and switch to it](/run-quivr/switch-a-vector-model). That guide owns the second-plugin setup, deployment-wide fill, comparison, registration activation, alert migration and rollback. The space promotion above changes models within one owner; it does not select a different plugin for a format.

## Go back to the previous model

Promote the former space the same way. It stayed an evaluation space and kept its vectors, so search uses it again at once. A promotion survives restarts, activations and rollbacks while the ingestion plugin enables both spaces. Once a plugin version stops declaring one of them, its own `spaces` settings decide again.

## How a backfill runs

* It runs on its own worker queue, with at most `backfill.concurrency` Versions in flight per step (default four), paced at `backfill.rate` Versions per second (see [Configuration](/reference/configuration#backfill)), so live ingestion keeps its capacity.
* It is pinned to the Pipeline Plan active when it started, like other work, and fails with `pinned_plugin_unavailable` or `pinned_plan_stopped` rather than moving to another plugin version.
* On its first step, the Corpus's search index starts carrying the new space, so articles ingested from then on get their vectors from live ingestion. The backfill fills only the articles that already had theirs.
* It creates no new Version and no content event in the change feed. Articles outside its window are not touched.
* If this plugin already has stored segments, an article it now cuts differently is skipped and counted as `skipped_segmentation_differs`: rebuild the Corpus to re-cut it (see [Write an ingestion plugin](/plugins/write-an-ingestion-plugin)). A refusal by the plugin is counted as `skipped_ingestion_refused`, and an article the plugin cannot embed within its deadline as `skipped_plugin_deadline`. Rerun the backfill to try skipped articles again.

## Troubleshooting

| Answer | Cause | Fix |
| - | - | - |
| `409 dry_run_required` | No dry run with this key and body | Send it with `"dry_run": true` first |
| `409 cost_confirmation_required` | The estimate exceeds `backfill.max_cost_without_confirmation` | Add `"confirm_cost": true` |
| `409 idempotency_conflict` | The key was used with another scope | Use a new key |
| `409 backfill_in_progress` | Another backfill of the Corpus has not finished | Wait for it, or cancel it |
| `409 registration_not_active` | `registration_id` is not an ingestion member of the active plan | Omit it, or activate that registration |
| `409 rebuild_required` | The Corpus's index predates named vector spaces | Rebuild the Corpus first |
| `422 invalid_backfill` | A space the plugin does not declare or the deployment does not enable, or an empty window | Check `spaces` and the window |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.