> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# Managing Datasets

A [dataset](/get-started/concepts/datasets) is a versioned, namespaced collection of tables. Managing one takes two
steps: you **register** it, which publishes its definition, its [manifest](/datasets/manifests), under a name and
version, and you **deploy** it, which starts the jobs that fill its tables with data. This page covers both, and
everything after: following a deployment, stopping it, listing and inspecting datasets, restoring them from storage,
and removing them.

You manage datasets with [`ampctl dataset`](/clis/ampctl/dataset) or the
[Admin API](/managing-amp/admin-api/datasets), the controller's HTTP API. This page shows both.

## Prerequisites

* Amp running, with its Admin API reachable. [Solo mode](/running-amp/solo-mode) serves it at
  `http://localhost:1610`, where `ampctl` looks by default. For another Amp, set its URL, and a token if it needs one:

  ```bash
  export AMP_ADMIN_URL=https://amp.example.com:1610
  export AMP_AUTH_TOKEN=your-token
  ```

* `ampctl` and `ampsql` installed (see [Installation](/running-amp/installation)).

* For the examples, which follow the [Quickstart](/get-started/quickstart): an EVM RPC provider named `mainnet_rpc`
  (see [Managing Providers](/managing-amp/providers)), and its manifest in `eth_mainnet.json`, from
  [`ampctl manifest generate`](/managing-amp/manifests#generate-a-manifest):

  ```bash
  ampctl manifest generate --kind evm-rpc --network mainnet --start-block 24000000 --out eth_mainnet.json
  ```

## Dataset references

Commands name a dataset as `namespace/name`, and one of its revisions as `namespace/name@revision`, like
`acme/eth_mainnet@1.0.0`. A revision is one of:

* A version you tagged, like `1.0.0`.
* `latest`, the highest version.
* `dev`, the most recently registered manifest, tagged or not.
* A manifest hash, the 64 hexadecimal characters that identify a manifest's content.

`latest` and `dev` move as you register, so a command that uses them acts on whatever they point to at the time. See
[Versioning](/datasets/versioning).

## Register a dataset

Registering stores the manifest, links it to the dataset's name, and tags it. It doesn't extract anything yet.

:::code-group
```bash [ampctl]
ampctl dataset register acme/eth_mainnet ./eth_mainnet.json --tag 1.0.0
```

```bash [Admin API]
curl -X POST http://localhost:1610/datasets \
  -H 'Content-Type: application/json' \
  -d "{\"namespace\": \"acme\", \"name\": \"eth_mainnet\", \"version\": \"1.0.0\", \"manifest\": $(cat eth_mainnet.json)}"
```
:::

`ampctl` prints the registration:

```
✓ Dataset registered successfully
→ Namespace: acme
→ Name: eth_mainnet
→ Version: 1.0.0
→ Hash: b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b
→ Kind: evm-rpc
→ Start block: 24000000
→ Finalized only: false
→ Tables: blocks, logs, transactions
```

The Admin API answers `201 Created` with the same fields as JSON:

```json
{
  "namespace": "acme",
  "name": "eth_mainnet",
  "version": "1.0.0",
  "manifest_hash": "b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b",
  "kind": "evm-rpc",
  "start_block": 24000000,
  "finalized_blocks_only": false,
  "tables": ["blocks", "logs", "transactions"]
}
```

What registering does:

* **Stores the manifest** by its content hash, if it isn't stored yet. The same content always has the same hash, so
  registering it again reuses it. See [Manifest Storage](/managing-amp/manifests).
* **Tags it.** With `--tag`, it gets that version, and becomes `latest` if that's the highest version. Either way, it
  becomes `dev`. Without `--tag`, only `dev` moves.
* **Validates it.** A derived dataset's SQL must plan against its dependencies, which must already be registered.

| Argument or option | Type               | Default  | Description                                                                                                                                    |
| ------------------ | ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `FQN`              | `namespace/name`   | required | The dataset's name, like `acme/eth_mainnet`.                                                                                                   |
| `FILE_OR_HASH`     | path, URL, or hash | required | The manifest: a local path, a `file://`, `s3://`, `gs://`, or `az://` URL, or the hash of a manifest already stored.                           |
| `--tag`, `-t`      | semantic version   | none     | The version to tag it with, like `--tag 1.0.0`. `latest` and `dev` are managed by Amp and can't be given.                                      |
| `--deploy`         | flag               | off      | Deploy the registered revision right away, with the default deploy settings (see [Register and deploy at once](/managing-amp/datasets#register-and-deploy-at-once)). |

Something that parses as 64 hexadecimal characters is read as a hash. To register a file named like one, prefix it with
`./`.

To tag a manifest that's already stored as a new version, pass its hash instead of a file:

```bash
ampctl dataset register acme/eth_mainnet b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b --tag 1.0.1
```

Reading a manifest from object storage uses that store's usual credentials from the environment, like
`AWS_ACCESS_KEY_ID`, `GOOGLE_APPLICATION_CREDENTIALS`, or `AZURE_STORAGE_ACCOUNT_NAME`.

### Register a manifest under a second name

Amp keeps a dataset's data by its manifest, not by its name. So a second name that registers a manifest another dataset
uses shares the data already materialized for it: its queries work at once, with nothing to deploy or extract. If
nothing has materialized the manifest yet, there's nothing to share, and queries fail with
[`has no synced data`](/clis/ampsql#errors).

This example starts from a completed [Quickstart](/get-started/quickstart), with `acme/eth_mainnet@1.0.0` still the
manifest it deployed, and its 100 blocks. Get the manifest from Amp, and register it under a second name:

:::terminal
```bash
ampctl dataset manifest acme/eth_mainnet@1.0.0 -o eth_copy.json
ampctl dataset register acme/eth_copy ./eth_copy.json --tag 1.0.0
ampsql 'SELECT count(*) AS blocks FROM "acme/eth_copy@1.0.0".blocks'
```

```
✓ Manifest written to eth_copy.json
✓ Dataset registered successfully
→ Namespace: acme
→ Name: eth_copy
→ Version: 1.0.0
→ Hash: b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b
→ Kind: evm-rpc
→ Start block: 24000000
→ Finalized only: false
→ Tables: blocks, logs, transactions

+--------+
| blocks |
+--------+
| 100    |
+--------+
```
:::

Both names read the same files, so:

* **Blocks that a deployment of one name writes show up in the other.** Deploying the second name continues from where
  the data ends, and writes nothing if it's already there.
* **Removing one name leaves the data.** The other name still queries it.
* **[`ampctl dataset status`](/clis/ampctl/dataset#ampctl-dataset-status) only reports progress for the name deployed
  last.** Until the second name is deployed, its status fails with `materialized dataset not found`. Once it is, the
  first name reports nothing written, even though its data is still there and its queries still work:

:::terminal
```bash
ampctl dataset deploy acme/eth_copy@1.0.0 --end-block 24000099
ampctl dataset status acme/eth_mainnet@1.0.0
```

```
✓ Dataset deployed successfully
→ Job ID: 4

ID:              1
Status:          COMPLETED
Kind:            (unknown)
Dataset:         acme/eth_mainnet@1.0.0
Progress:        (nothing written yet)
```
:::

Deploying the first name again moves its progress back to it, and the second name then reports nothing written.

### Register and deploy at once

`--deploy` deploys the revision it just registered: the `--tag` version, or else the manifest hash. It uses the
default settings only (continuous, no verification, the default retry strategy). Use
[`deploy`](/managing-amp/datasets#deploy-a-dataset) for anything else.

```bash
ampctl dataset register acme/eth_mainnet ./eth_mainnet.json --tag 1.0.0 --deploy
```

## Deploy a dataset

Deploying a revision **materializes** it: Amp extracts its data, or for a derived dataset, runs its SQL, and writes
the results to its tables. Deploying schedules three jobs, which [workers](/managing-amp/workers) run:

* The **materialization**, which writes the tables.
* **Garbage collection**, which runs periodically to delete the files that compaction and reorgs leave behind (see
  [Garbage Collection](/running-amp/storage/garbage-collection)).
* A **parent** that groups the two. Its ID identifies the deployment, and its status is the deployment's.

:::code-group
```bash [ampctl]
ampctl dataset deploy acme/eth_mainnet@1.0.0 --end-block 24000099
```

```bash [Admin API]
curl -X POST http://localhost:1610/materialized-datasets/acme/eth_mainnet/versions/1.0.0 \
  -H 'Content-Type: application/json' \
  -d '{"end_block": "24000099"}'
```
:::

`ampctl` prints the deployment's ID:

```
✓ Dataset deployed successfully
→ Job ID: 1
```

The Admin API answers `202 Accepted` with `{"id": 1}`. To follow the deployment, use its dataset reference with
[`ampctl dataset status`](/managing-amp/dataset-status) and [`ampctl dataset events`](/managing-amp/datasets#see-a-deployments-events).

Without an end block, a deployment keeps materializing new blocks as the chain grows.

### Deploy options

Each option has an Admin API field of the same name in the request body, except `--parallelism`, which is
`concurrency`, and the `--retry-*` options, which form one nested `retry_strategy` object (see
[Retries](/managing-amp/jobs/retries#with-the-admin-api)).

| Option                                            | Type                                              | Default                | Description                                                                                                            |
| ------------------------------------------------- | ------------------------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `--end-block <END_BLOCK>`                         | integer                                           | none (continuous)      | The block the deployment stops at, like `--end-block 5000000`. The Admin API takes it as a string: `"5000000"`.        |
| `--parallelism <PARALLELISM>`                     | integer, at least 1                               | `1`                    | How many extractions run at once, like `--parallelism 4`. Solana and Phaser datasets only. `--concurrency` also works. |
| `--worker-id <WORKER_ID>`                         | worker ID or glob pattern                         | the controller's       | The worker to run on, like `--worker-id worker` or `--worker-id 'eth-*'`.                                              |
| `--verify <ENCODING>`                             | `ethereum` or `solana`                            | none (off)             | Verify each block while extracting it, like `--verify ethereum`. Raw datasets other than Phaser.                       |
| `--provider <PROVIDER>`                           | provider name                                     | none (rotates)         | Extract from this provider only, like `--provider mainnet_rpc`. Raw datasets other than Phaser.                        |
| `--backfill-strategy <STRATEGY>`                  | `archive-then-rpc` or `rpc-only`                  | `archive-then-rpc`     | How past blocks are extracted, like `--backfill-strategy rpc-only`. Raw datasets other than Phaser.                    |
| `--retry-strategy <RETRY_STRATEGY>`               | `none`, `bounded`, `unless_stopped`               | the server's           | How failed jobs are retried, like `--retry-strategy bounded`. See [Retries](/managing-amp/jobs/retries).               |
| `--retry-max-attempts <RETRY_MAX_ATTEMPTS>`       | integer, at least 1                               | required for `bounded` | The failures allowed in the window, like `--retry-max-attempts 5`.                                                     |
| `--retry-window-secs <RETRY_WINDOW_SECS>`         | integer, 1 to 604800                              | `3600`                 | The window failures are counted over, like `--retry-window-secs 86400`. `bounded` only.                                |
| `--retry-backoff <RETRY_BACKOFF>`                 | `fixed`, `exponential`, `exponential_with_jitter` | `exponential`          | How the delay between retries grows, like `--retry-backoff fixed`.                                                     |
| `--retry-base-delay-secs <RETRY_BASE_DELAY_SECS>` | integer, at least 1                               | `1`                    | The first delay, or every delay for `fixed`, like `--retry-base-delay-secs 5`.                                         |
| `--retry-multiplier <RETRY_MULTIPLIER>`           | positive number                                   | `2.0`                  | What each exponential delay is multiplied by, like `--retry-multiplier 1.5`.                                           |
| `--retry-max-delay-secs <RETRY_MAX_DELAY_SECS>`   | integer, at least the base delay                  | none                   | The longest exponential delay, like `--retry-max-delay-secs 300`.                                                      |

`--verify`, `--provider`, and `--backfill-strategy` are refused with `UNSUPPORTED_OPTION` for derived and Phaser
datasets, rather than ignored. `--parallelism` is ignored where it doesn't apply.

#### `--end-block`

Without it, the deployment follows the chain head. With it, the deployment completes once it has materialized that
block:

```bash
ampctl dataset deploy acme/eth_mainnet@1.0.0 --end-block 5000000
```

#### `--parallelism`

For a raw dataset from the [Solana](/providers/solana) provider, how many blocks are extracted at once. For a
[Phaser](/providers/phaser) dataset, how many tables are written at once. Other datasets, raw or derived, ignore it. A
Phaser dataset takes its segment size from its provider's `segment_size`, not from a deploy option.

#### `--worker-id`

Without it, the controller picks the worker set by its `controller.default_worker_id` setting (see the
[Configuration Reference](/running-amp/configuration-reference#controllerdefault_worker_id)), or else a random active
worker. A glob pattern picks a random active worker among those that match. The worker must be active, meaning it sent
a heartbeat in the last 5 seconds, or deploying fails with `WORKER_NOT_AVAILABLE`. Solo mode has one worker, named
`worker`.

#### `--verify`

Checks each block against the chain's own hashes, or each Solana transaction against its signatures, before writing it.
`ethereum` needs the dataset's `blocks`, `transactions`, and `logs` tables. A block that fails verification fails the
job with an error that's retried. See [Verification](/managing-amp/verification).

#### `--provider`

Without it, the materialization uses every provider registered for the dataset's kind and network, moving to the next
one each time it retries. With it, it only uses the provider you name. If no provider of that name is registered for
the dataset's kind and network, the attempt fails with `PINNED_PROVIDER_NOT_FOUND` instead of falling back. That
failure is retried, so registering the provider afterwards lets the next attempt start.

#### `--backfill-strategy`

How the blocks before the chain head are extracted, for providers that have an archive:

* `archive-then-rpc` reads what the provider's archive covers first, then the rest over RPC. The archive always lags
  the chain head, so the backfill always ends over RPC.
* `rpc-only` skips the archive.

Once caught up, a deployment follows the head over RPC either way. Only the [Solana](/providers/solana) provider has an
archive; the others extract over RPC and ignore the option.

### Deploying again

Deploying is idempotent: while a revision's deployment is active, deploying it again returns the same ID and ignores
the options you pass. A materialization also reads its provider's configuration only once, when it starts. To change a
deployment's settings, or to pick up a changed provider, [stop](/managing-amp/datasets#stop-a-deployment) the deployment, wait until its
status is `STOPPED`, then deploy it again with the options you want. A deployment that has already finished, `COMPLETED`
or `FATAL`, needs no stopping: deploy it again right away. The data it already wrote stays, and it continues from where
it was.

## See a deployment's events

A deployment's events record each status its jobs went through, in order, across the parent, the materialization, and
garbage collection.

:::code-group
```bash [ampctl]
ampctl dataset events acme/eth_mainnet@1.0.0
```

```bash [Admin API]
curl http://localhost:1610/materialized-datasets/acme/eth_mainnet/versions/1.0.0/events
```
:::

`ampctl` lists the jobs, with their kind and idempotency key, then the events. Each event shows its ID, time, status,
job, and the node that recorded it: `controller` for the parent, the worker's ID for the others.

```
Materialization events for dataset: acme/eth_mainnet@1.0.0

Jobs:
  7  composite        7c1979eb622264f65e9380f31356352660a8a2b1334e6a9bd8ff71c7c0028e3d
  8  materialize-raw  9d172701920e8de1e6d26d9fa7edc6d87742d624cd5770f792f34bc25c1d901a
  9  gc               7e84c6c0514cd4152184c35c2a24128582075ec30df78b0e17c11f87ea600660

Events:
  [41] 2026-10-30T14:02:11.418223Z - SCHEDULED (job: 7/composite, node: controller)
  [42] 2026-10-30T14:02:11.418223Z - SCHEDULED (job: 8/materialize-raw, node: worker)
  [43] 2026-10-30T14:02:11.418223Z - SCHEDULED (job: 9/gc, node: worker)
  [44] 2026-10-30T14:02:12.006517Z - RUNNING (job: 8/materialize-raw, node: worker)
  [45] 2026-10-30T14:02:12.009845Z - RUNNING (job: 9/gc, node: worker)
  [46] 2026-10-30T14:02:12.351902Z - COMPLETED (job: 9/gc, node: worker)
```

The statuses are those of the [job lifecycle](/managing-amp/jobs/lifecycle). To look at one job, use its ID with
[`ampctl job`](/managing-amp/jobs).

## Stop a deployment

Stopping asks the deployment's jobs to stop. It returns as soon as the request is accepted; each job's worker then stops
it, so the deployment may still be winding down. The tables it wrote stay, and you can still query them.

:::code-group
```bash [ampctl]
ampctl dataset stop acme/eth_mainnet@1.0.0
```

```bash [Admin API]
curl -X DELETE http://localhost:1610/materialized-datasets/acme/eth_mainnet/versions/1.0.0
```
:::

The Admin API answers `202 Accepted`. Stopping is idempotent: stopping a deployment whose jobs have all finished, or a
revision that was never deployed, succeeds, and leaves its status as it is: the Quickstart's deployment stays
`COMPLETED`. Its [garbage collection](/running-amp/storage/garbage-collection) job keeps running every hour, though: to
stop it, stop the job itself, with [`ampctl job stop`](/clis/ampctl/job#ampctl-job-stop) and the
`garbage_collection.job_id` of the [dataset's status](/managing-amp/dataset-status). To check that an active deployment
stopped, run [`ampctl dataset status`](/managing-amp/dataset-status) until its status is `STOPPED`.

## List datasets

List every registered dataset, with how many versions each has:

:::code-group
```bash [ampctl]
ampctl dataset list
```

```bash [Admin API]
curl http://localhost:1610/datasets
```
:::

```
Datasets:
  acme/eth_mainnet - 2 version(s)
  acme/token_analytics - 1 version(s)
```

`ampctl dataset ls` is an alias. The Admin API returns each dataset's `namespace`, `name`, `latest_version`, and
`versions`, highest first.

### List deployments

`ampctl dataset list materialized` lists deployments across every dataset, and `ampctl dataset versions materialized`
the deployments of one dataset. By default, they only list active deployments, the ones that haven't finished.

:::code-group
```bash [ampctl]
ampctl dataset list materialized
ampctl dataset versions materialized acme/eth_mainnet
```

```bash [Admin API]
curl http://localhost:1610/materialized-datasets
curl http://localhost:1610/materialized-datasets/acme/eth_mainnet
```
:::

```
Materialized Datasets
────────────────────────────────────
Job ID        : 8
Dataset       : acme/eth_mainnet
Manifest Hash : b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b
Status        : RUNNING
Node          : worker
End Block     : none
Kind          : materialize-raw
Created At    : 2026-10-30 14:02 UTC
Updated At    : 2026-10-30 14:02 UTC
────────────────────────────────────
```

Each entry is a deployment's materialization job. With no active deployment, the command prints
`No active materializations found.`

| Option                | Type                                             | Default  | Description                                                                      |
| --------------------- | ------------------------------------------------ | -------- | -------------------------------------------------------------------------------- |
| `--status`, `-s`      | `active`, `all`, or statuses separated by commas | `active` | Which deployments to list, like `--status all` or `--status scheduled,running`.  |
| `--limit`, `-l`       | integer                                          | `50`     | The most entries to return: up to 1000 across datasets, and 100 for one dataset. |
| `--last-id <LAST_ID>` | integer                                          | none     | List the page after this job ID, like `--last-id 1234`.                          |

To get the next page of `versions materialized`, pass the `Next Cursor` it prints after a full page to `--last-id`.
`list materialized` doesn't print one: pass the last entry's `Job ID` instead. With the Admin API, the parameters are
`status`, `limit`, and `last_id`, and a full page includes `next_cursor`.

## Inspect a dataset

See a revision's kind, manifest hash, start block, and tables. Without a revision, `ampctl` uses `latest`.

:::code-group
```bash [ampctl]
ampctl dataset inspect acme/eth_mainnet@1.0.0
```

```bash [Admin API]
curl http://localhost:1610/datasets/acme/eth_mainnet/versions/1.0.0
```
:::

```
Namespace:             acme
Name:                  eth_mainnet
Revision:              1.0.0, latest, dev
Kind:                  evm-rpc
Manifest Hash:         b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b
Start Block:           0
Finalized Blocks Only: false
Tables:                blocks, logs, transactions
```

`Revision` lists every tag that points at this manifest. `ampctl dataset get` is an alias.

## List a dataset's versions

:::code-group
```bash [ampctl]
ampctl dataset versions acme/eth_mainnet
```

```bash [Admin API]
curl http://localhost:1610/datasets/acme/eth_mainnet/versions
```
:::

```
Dataset: acme/eth_mainnet

Latest version: 1.0.1
Dev hash: b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b

Versions:
  1.0.1 (b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b)
    Created: 2026-10-30T14:20:45.201337Z
    Updated: 2026-10-30T14:20:45.201337Z
  1.0.0 (b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b)
    Created: 2026-10-30T14:01:58.927104Z
    Updated: 2026-10-30T14:01:58.927104Z
```

## Get a manifest

Print the manifest a revision points to, as JSON. Without a revision, `ampctl` uses `latest`.

:::code-group
```bash [ampctl]
ampctl dataset manifest acme/eth_mainnet@1.0.0
```

```bash [Admin API]
curl http://localhost:1610/datasets/acme/eth_mainnet/versions/1.0.0/manifest
```
:::

`--output <OUTPUT>` (`-o`) writes it to a file instead. To get a manifest by its hash, see
[Manifest Storage](/managing-amp/manifests#inspect-a-manifest).

## Restore a dataset from storage

Amp keeps a dataset's data as Parquet files in [storage](/running-amp/storage), and what it knows about those files in
its [metadata database](/running-amp/metadata-database). Restoring reads the files back into the metadata database:
after losing the database, when starting a new Amp over existing data, or after restoring storage from a backup.

:::code-group
```bash [ampctl]
ampctl dataset restore acme/eth_mainnet@1.0.0
```

```bash [Admin API]
curl -X POST http://localhost:1610/datasets/acme/eth_mainnet/versions/1.0.0/restore
```
:::

```
✓ Dataset restored successfully
→ Restored 3 table(s):
  • blocks (location_id: 1, url: file:///home/you/amp/.amp/data/acme/eth_mainnet/blocks/0199f0c4-6f21-7b1e-9a3d-2c4e8b7f1a05/)
  • logs (location_id: 2, url: file:///home/you/amp/.amp/data/acme/eth_mainnet/logs/0199f0c4-6f22-73a8-b1c5-5e7f9d2a4c16/)
  • transactions (location_id: 3, url: file:///home/you/amp/.amp/data/acme/eth_mainnet/transactions/0199f0c4-6f22-7d0b-8e4f-1a3c5e7b9d27/)
```

For each table, Amp finds the newest copy of its files in storage, indexes them, and makes that copy the one queries
read. Each copy is a [table revision](/managing-amp/table-revisions), identified by its location ID. If a table has no
files in storage, restoring fails with `TABLE_NOT_FOUND`.

### Restore one table

`--table` restores one table. With `--location-id`, it makes that existing table revision the active one instead of
looking in storage:

```bash
ampctl dataset restore acme/eth_mainnet@1.0.0 --table blocks --location-id 42
```

| Option               | Type    | Default | Description                                                                         |
| -------------------- | ------- | ------- | ----------------------------------------------------------------------------------- |
| `--table <TABLE>`    | string  | none    | Restore this table only, like `--table blocks`.                                     |
| `--location-id <ID>` | integer | none    | Activate this table revision instead of finding one in storage. Requires `--table`. |

The Admin API endpoint is `POST /datasets/acme/eth_mainnet/versions/1.0.0/tables/blocks/restore`, with an optional
`{"location_id": 42}` body.

### Restore from another storage path (In development)

When a table's files aren't where Amp would put them, after a migration or when importing data, register each one as a
table revision, index its files, then activate it:

```bash
# Register the manifest, if it isn't registered yet
ampctl dataset register acme/eth_mainnet ./eth_mainnet.json --tag 1.0.0

# Register each table's path, relative to the storage root, and note its location ID
ampctl table register acme/eth_mainnet@1.0.0 blocks imported/eth_mainnet/blocks
ampctl table register acme/eth_mainnet@1.0.0 transactions imported/eth_mainnet/transactions

# Index the files of each revision, by location ID
ampctl table restore 42
ampctl table restore 43

# Make each revision the active one
ampctl dataset restore acme/eth_mainnet@1.0.0 --table blocks --location-id 42
ampctl dataset restore acme/eth_mainnet@1.0.0 --table transactions --location-id 43
```

See [Table Revisions](/managing-amp/table-revisions) for the `ampctl table` commands.

## Remove a dataset

Removing a dataset deletes its registration: its version tags, and the manifests no other dataset uses. Its data in
storage stays. Add `@version` to remove only that version tag.

:::code-group
```bash [ampctl]
ampctl dataset remove acme/eth_mainnet
```

```bash [Admin API]
curl -X DELETE http://localhost:1610/datasets/acme/eth_mainnet
```
:::

`ampctl` describes what it's about to delete and asks first:

```
⚠ This permanently deletes the registration of dataset acme/eth_mainnet, all its version tags, and any of its manifests not shared with other datasets (materialized tables in object storage are kept).
Remove dataset acme/eth_mainnet? [y/N] y
✓ Dataset deleted successfully
```

| Argument or option | Type                       | Default  | Description                                                         |
| ------------------ | -------------------------- | -------- | ------------------------------------------------------------------- |
| `REFERENCE`        | `namespace/name[@version]` | required | The dataset, or one of its versions, like `acme/eth_mainnet@1.0.0`. |
| `--force`, `-f`    | flag                       | off      | Don't ask first, for scripts.                                       |

`ampctl dataset rm` is an alias. With the Admin API, remove a version with
`DELETE /datasets/acme/eth_mainnet/versions/1.0.0`. Both answer `204 No Content`, even when there was nothing to
remove, so you can safely run them again.

* **Stop deployments first.** A dataset with deployments that haven't finished can't be removed (`409 Conflict`).
  [Stop](/managing-amp/datasets#stop-a-deployment) them, and wait until they've stopped.
* **Only plain versions can be removed one at a time.** `@latest`, `@dev`, and hashes are refused, and so is the version
  `latest` points to (`CANNOT_DELETE_LATEST_VERSION`): tag a higher version first.

## Authorization

When the Admin API sits behind a gateway that checks who's calling, the gateway passes each caller's permissions in the
`amp-capabilities` header (see [Admin API](/managing-amp/admin-api)). Permissions are per namespace: `namespace.read`
or `namespace.write`. For datasets:

* **Changing a dataset** (registering, deploying, stopping, removing) needs `namespace.write` on its namespace.
* **Restoring** isn't checked at all: with a header that grants another namespace, the restore endpoints still restore
  a dataset's tables. Only let operators reach them.
* **A derived dataset's dependencies** also need `namespace.read` on their namespaces, to register, deploy, or infer
  the schema of the derived dataset. A dependency you can't read is reported as missing (`DEPENDENCY_NOT_FOUND`), like
  one that doesn't exist, so other namespaces stay invisible. A dependency given by manifest hash must also be
  published under the namespace and name it declares.
* **Listings** (`GET /datasets` and `GET /manifests`) only return what's linked to a namespace you can read. An empty
  set of permissions lists nothing.
* **A request without the header** isn't restricted. That's how trusted callers inside your infrastructure, like
  `ampctl` talking to solo mode, reach the API.

## Errors

| Error                                                             | Cause                                                                                   | Fix                                                                          |
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `dataset not found: acme/eth_mainnet@1.0.0` (`DATASET_NOT_FOUND`) | The dataset or revision isn't registered.                                               | Check the name and version with `ampctl dataset versions acme/eth_mainnet`.  |
| `DEPENDENCY_NOT_FOUND`                                            | A derived dataset depends on a dataset that isn't registered, or that you can't read.   | Register the dependency first, or get read access to its namespace.          |
| `UNSUPPORTED_OPTION`                                              | A deploy option the dataset's kind doesn't support, like `--provider` on a derived one. | Drop the option.                                                             |
| `WORKER_NOT_AVAILABLE`                                            | `--worker-id` matches no active worker.                                                 | Check `ampctl worker list` (see [Workers](/managing-amp/workers)).           |
| `STATIC_DATASET_NOT_MATERIALIZABLE`                               | The dataset is [static](/datasets/static-datasets): it has nothing to materialize.      | Query it directly; there's nothing to deploy.                                |
| `PINNED_PROVIDER_NOT_FOUND`                                       | The `--provider` isn't registered for the dataset's kind and network. The job retries.  | Register the provider, or stop the deployment and deploy it without the pin. |
| `materialized dataset not found: …`                               | The revision exists but was never deployed.                                             | Deploy it.                                                                   |

The [Admin API reference](/managing-amp/admin-api/datasets) lists every error each endpoint returns.
