---
slug: vectors/restore
title: Restoring a collection
kind: howto
surface: vectors
summary: How to get an Aetherfy collection back from a restore point, a collection that still exists or one that was deleted, into a new collection. What a restore point is, a worked example, what comes back, the monthly allowance, the routes, afy commands, the dashboard and the error codes.
sources:
  - vectordb:backend/docs/COLLECTION_BACKUP_RESTORE.md
  - vectordb:backend/routes/restorePointsMount.js
  - vectordb:backend/routes/collectionRestorePoints.js
  - vectordb:backend/services/restorePoints/service.js
  - vectordb:backend/services/restorePoints/ids.js
  - vectordb:backend/services/qdrantBackup/layout.js
  - vectordb:backend/services/qdrantBackup/run.js
  - vectordb:backend/services/qdrantBackup/shardQueue.js
  - vectordb:backend/services/collectionRecovery/restore.js
  - vectordb:backend/services/collectionRecovery/service.js
  - dashboard/migrations/194_collection_backup_restore.sql
  - dashboard/src/lib/billing/collection-recovery-limits.js
  - aetherfy-cli:cmd/collection_restore_points.go
---

# Restoring an Aetherfy collection

An Aetherfy restore brings a collection back from a **restore point** into a **new
collection**. It works the same whether the collection still exists (its points are wrong)
or was deleted (you want it back). The collection a restore point is a copy of is never
changed: you compare the new collection with it and keep whichever is right.

## What an Aetherfy restore point is

A restore point is a copy of a collection that you can restore from. A collection has up to
two kinds:

- **The copy taken when it was deleted.** Deleting a collection, whoever does it, a person or
  an agent, frees its name, its slot and its storage at once, but Aetherfy backs it up first,
  before any of its data is dropped, and keeps that copy for your plan's window, counted from
  when the copy was ready. Every plan has it. If the copy cannot be
  taken, the data is kept and Aetherfy looks at it: a delete is never turned into a loss.
- **A daily copy.** Aetherfy backs up every collection once a day, and each day's copy is a
  restore point for as many days back as your plan reaches. A plan with no daily copies
  (Free) has only the copy taken at a deletion.

How many days each plan keeps and reaches, and how many restores a calendar month allows, is
on the [platform limits](/platform/limits#aetherfy-plan-limits-by-plan) page and nowhere
else; this page names the limits and does not copy them.

A restore point has an **id**. A list gives it and a restore takes it back; treat it as
opaque. A copy taken at a deletion has a UUID; a daily copy has
`day_<collection id>_<date>`. It is never a name: two deleted collections can have had the
same one. An id that is another account's, malformed, outside your plan's days or with no
copy behind it is the same `404 COLLECTION_BACKUP_NOT_FOUND`.

## A worked example of an Aetherfy restore

Your agents read the collection `products` through the alias `products-live`. On 30 October
a script overwrites half of its payloads. The daily copy of 27 October is still good.

List the restore points of `products`:

```bash
afy collections restore-points products --workspace shop
```

Restore the copy of 27 October. `products` is live, so its old name is taken and the new
collection is called `products-2026-10-27`:

```bash
afy collections restore day_3f2b0c1e-0c61-4c0e-9b53-2f9f6a7a4d10_2026-10-27 --workspace shop --wait
```

Look at `products-2026-10-27`: its points, a few payloads. When it is right, move the alias
to it in one batch, so your agents change collection with no downtime:

```bash
curl -s -X POST https://vectors.aetherfy.com/api/v1/workspaces/shop/aliases \
  -H "Authorization: Bearer $AETHERFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"actions":[{"create_alias":{"alias_name":"products-live","collection_name":"products-2026-10-27"}}]}'
```

`products` is untouched, so you can compare, or move the alias back. Delete it when you no
longer need it.

**Why a new collection.** A restore never goes over a live collection, because overwriting
would destroy today's data, including what was written correctly since that day. A name that
a collection or an alias already has in the workspace is refused with
`COLLECTION_NAME_TAKEN_FOR_RESTORE` and nothing is overwritten.

## Restoring a deleted Aetherfy collection

A deleted collection has the copy taken at its deletion, and its daily copies stay restorable
for the days your plan reaches. List the workspace's deleted collections, with their ids:

```bash
afy collections restore-points --workspace shop
```

Restoring the copy taken at the deletion brings the collection back under its old name when
that is free (otherwise as `<name>-<date>`), with its payload schema and its aliases:

```bash
afy collections restore 3f2b0c1e-0c61-4c0e-9b53-2f9f6a7a4d10 --wait
```

**The gap.** The copy taken at a deletion is the collection as it was when it was deleted. If
it was damaged before that, the copy is the damaged state. The daily copies from before the
damage are the way back, and a deleted collection has them too. List both kinds by the
collection's id, which the list of deleted collections shows:

```bash
afy collections restore-points --collection-id 3f2b0c1e-0c61-4c0e-9b53-2f9f6a7a4d10
```

A daily copy is as of the day it was taken, so writes after it, up to the deletion, are only
in the copy taken at the deletion. A collection created and deleted between two daily copies
has only the copy taken at its deletion. When both matter, restore both under different names
and compare.

## What comes back from an Aetherfy restore

Both kinds bring back the points (ids, vectors and payloads), the payload indexes, the
description, the vector size and distance, and the regions the collection had, in the same
workspace. The workspace must still cover those regions.

- **The payload schema** comes back as it was. For a daily copy it is the schema the
  collection had on that day, even when it has changed since; a collection that had none that
  day comes back with none.
- **Aliases** are not copied from a daily copy. An alias names exactly one collection and the
  live collection still holds its own; copying them would make two collections answer to one
  alias, and moving them would silently send live traffic to old data. Move them yourself
  after you have checked the restore, as in the example above. The copy taken at a deletion
  does bring its aliases back, because nothing else holds them.
- **The restored collection is a full collection.** It is on your collections list, counts
  toward your plan's collection limit and storage like any other, and is backed up from the
  next daily pass.

## The Aetherfy restore allowance is one

A calendar month (UTC) allows a number of restores and a volume restored, at most the plan's
storage size. There is one count and one volume for both kinds of restore point and all your
collections: a restore of a daily copy and a restore of a deleted collection draw on the
same ones. A restore that fails is not counted. A restore is also a create, so the plan's
collection limit and storage limit apply. Every list tells what is left this month in its
`allowance`, and the message of a refusal gives the date a monthly limit resets.

A plan limit is the vector API's one body, with `error.code` `PLAN_LIMIT_EXCEEDED`, and
`error.limit` names which: `collection_backup_restore_days` (a daily copy on a plan with no
daily copies), `collection_restores_per_month`, `max_storage_bytes` or `max_collections`.

## A daily Aetherfy copy is as of a range of times

A collection's shards are copied one after another, so the points of one day's copy were read
at different times. Each daily restore point shows when its first and its last shard were
taken (`taken_from` and `taken_to`). A small collection is taken within seconds; a large one
can take hours, and the restored collection mixes points from across that range. Read the
range before you restore.

Aetherfy takes the shards of the daily pass in the order of collection, then shard, with no
copy slot idle, which narrows the range of a large collection. That comes from a simulation
of the copy pace, not from a measurement on the live fleet, and the real range depends on
how fast the nodes snapshot. Making the range only the snapshot time, by snapshotting every
shard of a collection before uploading any, is not built: it needs room on a collection's
nodes for all of its snapshots at once.

## Aetherfy restore routes

These routes are for people. An Aetherfy agent's key is refused with
`403 AUTH_AGENT_KEY_OUT_OF_SCOPE`. A restore point is addressed by its own id, and a
collection by its id, never by name, except the list of a live collection under its
workspace:

| Method | Path | Purpose |
|---|---|---|
| GET | `/api/v1/restore-points?collection_id={id}` | The restore points of one collection, live or deleted, newest first, with this month's allowance |
| GET | `/api/v1/workspaces/{ws}/collections/{name}/restore-points` | The same list for a live collection, by its name |
| GET | `/api/v1/restore-points?deleted=true` | The account's deleted collections, each as the copy taken at its deletion. `&workspace_id=` narrows it |
| GET | `/api/v1/restore-points/{point_id}` | One restore point |
| POST | `/api/v1/restore-points/{point_id}/restore` | Restore it as a new collection. Body `{"as": "new-name"}` is optional |
| GET | `/api/v1/restore-points/restores/{restore_id}` | A restore's state and its regions ready out of total |

Each restore point has its `kind` (`daily` or `deletion`), the `collection` it is of (its
`id`, `name` and whether it is `deleted`), when it was `taken_at`, its `points`, `bytes`,
`regions` and `status`. A daily copy also has its `day`, `taken_from` and `taken_to`. A copy
taken at a deletion also has `deleted_by` (`{"kind": "person"}`, or an agent by its id) and
`expires_at`. The `status` is `ready`, `backing_up` (a copy taken at a deletion that is still
being taken), `unavailable` (it could not be completed; its data is kept and support can
retry it) or `restoring`.

A restore answers `202` at once and runs in the background, region by region. The new
collection is available when every region it lives in (one on a Free or Starter plan) has its
copy; `regions_ready` out of `regions_total` shows the progress, and the `restore_id` of the `202` is the id to follow. A
restore that fails removes the new collection and leaves the restore point and the live
collection as they were; it can be restored again. Until it finishes, creating a collection
under the name it holds answers `409 COLLECTION_BEING_RESTORED`. Without `as` the name is the
first free of the collection's old name and the old name with the point's date
(`<name>-<date>`).

```bash
curl -s "https://vectors.aetherfy.com/api/v1/restore-points?collection_id=$COLLECTION_ID" \
  -H "Authorization: Bearer $AETHERFY_API_KEY"

curl -s -X POST https://vectors.aetherfy.com/api/v1/restore-points/$POINT_ID/restore \
  -H "Authorization: Bearer $AETHERFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"as": "products-2026-10-27"}'
```

## Aetherfy restore errors

| Status | Code | When |
|---|---|---|
| 403 | `PLAN_LIMIT_EXCEEDED` | the plan has no daily copies, or this month's restores or restored volume are used, or the collection limit is reached; `error.limit` says which |
| 404 | `COLLECTION_NOT_FOUND` | no collection with that id on the account |
| 404 | `COLLECTION_BACKUP_NOT_FOUND` | no restore point with that id on the account: restored already, expired, a day your plan does not reach or with no daily copy, malformed, or another account's |
| 400 | `VALIDATION_ERROR` | a list names neither a collection nor `deleted=true`, or both; or `as` is not a string; `error.field` names it |
| 400 | `INVALID_COLLECTION_NAME` | `as` is not a valid collection name |
| 409 | `COLLECTION_NAME_TAKEN_FOR_RESTORE` | the name is used by a collection or an alias in the workspace; restore it under another with `as` |
| 409 | `COLLECTION_BACKUP_NOT_READY` | the copy taken at the deletion is still being taken |
| 409 | `COLLECTION_BACKUP_UNAVAILABLE` | the copy taken at the deletion could not be completed; its data is kept and support can retry it |
| 409 | `COLLECTION_RESTORE_IN_PROGRESS` | the restore point is already being restored |
| 409 | `COLLECTION_RESTORE_REGIONS_UNAVAILABLE` | the workspace no longer covers a region the collection had; `missing_regions` names them |
| 409 | `COLLECTION_RESTORE_KEEPS_FAILING` | this restore point failed to restore three times in the last 24 hours; it is held until someone looks at why |
| 410 | `COLLECTION_BACKUP_EXPIRED` | a copy taken at a deletion past your plan's window; it is deleted |

## Restoring an Aetherfy collection from the CLI

```bash
afy collections restore-points products --workspace shop
afy collections restore-points --workspace shop
afy collections restore-points --collection-id 3f2b0c1e-0c61-4c0e-9b53-2f9f6a7a4d10
afy collections restore day_3f2b0c1e-0c61-4c0e-9b53-2f9f6a7a4d10_2026-10-27 --as products-before --workspace shop --wait
```

`afy collections restore-points` lists, in one of three ways: a live collection by name,
the workspace's deleted collections (no argument), or one collection by its id
(`--collection-id`), live or deleted. `afy collections restore` takes a restore point's id
and starts the restore; `--as` names the new collection and `--wait` follows it ([the CLI
reference](/cli/vectors)).

## Restoring an Aetherfy collection from the dashboard

The `RESTORE` tab of a collection lists its restore points, each with the range it was taken
in. **RESTORE** opens a dialog that says what comes back and asks for the name of the new
collection ([the database page](/dashboard/database)). On Free the tab says the plan has no
daily copies. A workspace's **Recently deleted** list shows its deleted collections with the
same dialog, and **DAILY COPIES** on a row lists that collection's daily copies from before
it was deleted ([workspaces](/dashboard/workspaces)).
