> **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.

# Admin Reference

The admin client is in the `amp.admin` package. It sends requests to Amp's [Admin API](/managing-amp/admin-api); each
method below names the work it asks for, and [Admin Client](/sdks/python/admin) shows them in use.

Its methods are grouped in sub-clients, like `datasets` and `jobs`, which you reach through an `AdminClient`, or through
a `Client` created with `admin_url`. Models are [Pydantic](https://docs.pydantic.dev) models: read their fields as
attributes, and turn them into dicts with `model_dump()`.

## `AdminClient`

```python
from amp.admin import AdminClient

AdminClient(base_url: str, auth_token: str | None = None, auth: bool = False)
```

A client for one Amp's Admin API.

| Parameter    | Type    | Default | Description                                                                                               |
| ------------ | ------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `base_url`   | string  | none    | The Admin API's URL, like `'http://localhost:1610'`, solo mode's.                                         |
| `auth_token` | string  | `None`  | A token to send with every request.                                                                       |
| `auth`       | boolean | `False` | Send the saved token, and refresh it when it expires (see [Authentication](/sdks/python/authentication)). |

Without `auth_token`, the client sends the `AMP_AUTH_TOKEN` environment variable when it's set, then the saved token
when `auth=True`. Passing both `auth_token` and `auth=True` raises a `ValueError`. Requests time out after 30 seconds.

| Property     | Type                                    |
| ------------ | --------------------------------------- |
| `datasets`   | [`DatasetsClient`](/sdks/python/reference/admin#datasetsclient)     |
| `jobs`       | [`JobsClient`](/sdks/python/reference/admin#jobsclient)             |
| `schema`     | [`SchemaClient`](/sdks/python/reference/admin#schemaclient)         |
| `configmaps` | [`ConfigMapsClient`](/sdks/python/reference/admin#configmapsclient) |
| `revisions`  | [`RevisionsClient`](/sdks/python/reference/admin#revisionsclient)   |

A `Client` created with `admin_url` has `datasets`, `jobs`, and `schema`, from an `AdminClient` that uses the `Client`'s
token, or with `auth=True`, `AMP_AUTH_TOKEN` or else the saved login (see
[Authentication](/sdks/python/authentication)). Without `admin_url`, they raise
`ValueError: Admin API not configured. Provide admin_url parameter to Client() …`.

### `list_manifests()`

```python
list_manifests() -> ManifestsResponse
```

Lists the manifests the Amp stores, each with its hash, its kind, and how many datasets use it. Returns a
[`ManifestsResponse`](/sdks/python/reference/admin#manifestsresponse).

### `close()`

```python
close() -> None
```

Closes the client's connections. An `AdminClient` is also a context manager, which closes them at the end of its `with`
block.

## `DatasetsClient`

`client.datasets`: registers, deploys, explores, and removes datasets. Methods that take a `revision` accept a version,
like `'1.0.0'`, `'latest'`, `'dev'`, or a manifest hash (see
[Dataset references](/managing-amp/datasets#dataset-references)).

`QueryBuilder`'s `with_dependency()`, `to_manifest()`, and `register_as()`, which build a manifest from a query, are in
the [Client reference](/sdks/python/reference/client).

### `datasets.register()`

```python
register(namespace: str, name: str, version: str, manifest: dict) -> None
```

Registers a [manifest](/datasets/manifests) as a version of a dataset. Registering a version that exists replaces its
manifest, unless it's the same.

| Parameter   | Type   | Description                                               |
| ----------- | ------ | --------------------------------------------------------- |
| `namespace` | string | The dataset's namespace, like `'acme'`.                   |
| `name`      | string | The dataset's name, like `'gas_usage'`.                   |
| `version`   | string | The version to register, like `'1.0.0'`.                  |
| `manifest`  | dict   | The manifest, like one from `QueryBuilder.to_manifest()`. |

Raises an [`AdminAPIError`](/sdks/python/reference/admin#errors) when Amp rejects the manifest, like `DEPENDENCY_NOT_FOUND` for a dependency it
doesn't have.

### `datasets.register_as()`

```python
register_as(
    namespace: str,
    name: str,
    version: str,
    tables: dict[str, QueryBuilder],
    network: str = 'mainnet',
) -> Dataset
```

Builds a derived dataset's manifest from queries, one per table, and registers it. It asks Amp for each query's output
columns, then plans each query on the query server, so it fails before registering when a query can't run. Queries refer
to their dependencies by [dataset aliases](/sdks/python/querying#dataset-aliases), which it adds to the manifest's
dependencies.

| Parameter   | Type                              | Default     | Description                                                     |
| ----------- | --------------------------------- | ----------- | --------------------------------------------------------------- |
| `namespace` | string                            | none        | The dataset's namespace.                                        |
| `name`      | string                            | none        | The dataset's name.                                             |
| `version`   | string                            | none        | The version to register, like `'1.0.0'`.                        |
| `tables`    | dict of strings to `QueryBuilder` | none        | Each table's name and query, from `client.sql()`. At least one. |
| `network`   | string                            | `'mainnet'` | The chain the tables' rows come from.                           |

Returns a [`Dataset`](/sdks/python/reference/admin#dataset) for the new version. Raises `ValueError` when `tables` is empty, when two tables use an
alias for different datasets, or when a query fails to plan (`SQL validation failed for … table(s) …`). Example:
[Register several tables](/sdks/python/admin#register-several-tables).

### `datasets.add_tables()`

```python
add_tables(
    namespace: str,
    name: str,
    from_version: str,
    new_version: str,
    tables: dict[str, QueryBuilder],
    network: str = 'mainnet',
) -> Dataset
```

Registers `new_version` with the tables of `from_version` plus `tables`, checked like `register_as()`'s. It never
replaces a table. `network` applies to the new tables only.

Returns a [`Dataset`](/sdks/python/reference/admin#dataset) for the new version. Raises `ValueError` when `tables` is empty, when a table exists in
`from_version` (`table 'transfers' already exists in acme/token_activity@1.1.0. add_tables is strictly additive …`),
when an alias conflicts with the existing dependencies, or when a query fails to plan. Example: [Add tables in a new
version](/sdks/python/admin#add-tables-in-a-new-version).

### `datasets.get()`

```python
get(namespace: str, name: str, version: str = 'latest') -> Dataset
```

Returns a [`Dataset`](/sdks/python/reference/admin#dataset) for a version, with its manifest. Raises `DatasetNotFoundError` when the dataset or
version doesn't exist.

### `datasets.deploy()`

```python
deploy(
    namespace: str,
    name: str,
    revision: str,
    end_block: str | None = None,
    concurrency: int | None = None,
    retry_strategy: dict | None = None,
    verify: str | None = None,
    worker_id: str | None = None,
) -> DeployResponse
```

Schedules the jobs that fill a version's tables, and returns without waiting for them. While the version's jobs are
still running, it returns their deployment instead of scheduling another.

| Parameter        | Type    | Default       | Description                                                                                                           |
| ---------------- | ------- | ------------- | --------------------------------------------------------------------------------------------------------------------- |
| `end_block`      | string  | `None`        | The block to stop at, like `'24000099'`. `None` keeps processing new blocks.                                          |
| `concurrency`    | integer | `1`           | How many extractions to run at once, for providers that support it.                                                   |
| `retry_strategy` | dict    | Amp's default | How to retry failures, like `{'strategy': 'bounded', 'max_attempts': 3}` (see [Retries](/managing-amp/jobs/retries)). |
| `verify`         | string  | `None`        | `'ethereum'` or `'solana'`: check extracted data against the chain's hashes. Raw datasets only.                       |
| `worker_id`      | string  | Any worker    | The worker to run on, by ID or a pattern like `'worker-eth-*'`.                                                       |

`parallelism`, the former name of `concurrency`, still works, with a `DeprecationWarning`.

Returns a `DeployResponse`, whose `job_id` is the deployment's job. The deployment runs its work in two other jobs,
which [`datasets.list_jobs()`](/sdks/python/reference/admin#datasetslist_jobs) returns. Raises `DatasetNotFoundError` when the version doesn't
exist, and `SchedulerError` when Amp can't schedule the jobs.

### `datasets.list_all()`

```python
list_all() -> DatasetsResponse
```

Lists every dataset, in every namespace, with its versions. Returns a [`DatasetsResponse`](/sdks/python/reference/admin#datasetsresponse).

### `datasets.get_versions()`

```python
get_versions(namespace: str, name: str) -> VersionsResponse
```

Lists a dataset's versions, newest first, and what its `latest` and `dev` tags point to. Returns a
[`VersionsResponse`](/sdks/python/reference/admin#versionsresponse).

### `datasets.get_version()`

```python
get_version(namespace: str, name: str, revision: str) -> DatasetInfo
```

Returns a version's details: its kind, tables, start block, and manifest hash, as a [`DatasetInfo`](/sdks/python/reference/admin#datasetinfo).
Raises `DatasetNotFoundError` when it doesn't exist.

### `datasets.get_manifest()`

```python
get_manifest(namespace: str, name: str, revision: str) -> dict
```

Returns a version's manifest, as a dict.

### `datasets.describe()`

```python
describe(namespace: str, name: str, revision: str = 'latest') -> dict[str, list[dict]]
```

Returns a version's tables, from its manifest. Each key is a table's name, and its value lists the table's columns, each
a dict with `name`, `type` (a string, like `'FixedSizeBinary(20)'`), and `nullable` (a boolean). See
[Inspecting Datasets](/sdks/python/inspecting-datasets).

### `datasets.inspect()`

```python
inspect(namespace: str, name: str, revision: str = 'latest') -> None
```

Prints a version's kind, tables, and columns. See [Inspecting Datasets](/sdks/python/inspecting-datasets).

### `datasets.stats()`

```python
stats(namespace: str, name: str, revision: str = 'latest') -> DatasetStatsResponse
```

Returns how far each table of a deployed version has synced, as a [`DatasetStatsResponse`](/sdks/python/reference/admin#datasetstatsresponse).
Raises `DatasetStatsUnavailableError` when the version was never deployed, and `DatasetNotFoundError` when it doesn't
exist.

### `datasets.list_jobs()`

```python
list_jobs(namespace: str, name: str, revision: str) -> JobsResponse
```

Returns a deployed version's two jobs, the one that fills its tables and its hourly garbage collection, as a
[`JobsResponse`](/sdks/python/reference/admin#jobsresponse). Its `jobs` list is empty when the version was never deployed.

### `datasets.lineage()`

```python
lineage(
    namespace: str,
    name: str,
    revision: str = 'latest',
    direction: str = 'upstream',
    max_depth: int | None = None,
) -> LineageResponse
```

Returns the graph of datasets that a version depends on, or that depend on it (see [Lineage](/managing-amp/lineage)).

| Parameter   | Type    | Default      | Description                                                                             |
| ----------- | ------- | ------------ | --------------------------------------------------------------------------------------- |
| `direction` | string  | `'upstream'` | `'upstream'` follows dependencies, `'downstream'` finds dependents, `'both'` does both. |
| `max_depth` | integer | Amp's limit  | How many levels to follow, like `max_depth=1` for direct dependencies only.             |

Returns a [`LineageResponse`](/sdks/python/reference/admin#lineageresponse).

### `datasets.restore()`

```python
restore(namespace: str, name: str, revision: str) -> RestoreResponse
```

Restores every table of a version from the files in storage (see [Managing Datasets](/managing-amp/datasets)). Returns a
`RestoreResponse`, whose `total_files` is the number of files restored.

### `datasets.restore_table()`

```python
restore_table(namespace: str, name: str, revision: str, table_name: str, location_id: int | None = None) -> None
```

Restores one table. `location_id` picks which of its [revisions](/managing-amp/table-revisions) to activate.

### `datasets.delete_version()`

```python
delete_version(namespace: str, name: str, version: str) -> None
```

Removes a version. Raises `AdminAPIError` with `CANNOT_DELETE_LATEST_VERSION` for the version `latest` points to.

### `datasets.delete()`

```python
delete(namespace: str, name: str) -> None
```

Removes a dataset, with all its versions, and the manifests no other dataset uses. It succeeds when the dataset doesn't
exist.

## `Dataset`

A handle on one version of a dataset, with its manifest. `datasets.get()`, `datasets.register_as()`,
`datasets.add_tables()`, and `QueryBuilder.register_as()` return one.

| Property    | Type   | Description                                              |
| ----------- | ------ | -------------------------------------------------------- |
| `namespace` | string | The dataset's namespace.                                 |
| `name`      | string | The dataset's name.                                      |
| `version`   | string | The version, as you gave it.                             |
| `manifest`  | dict   | The version's manifest.                                  |
| `reference` | string | `namespace/name@version`, like `'acme/gas_usage@1.0.0'`. |

### `Dataset.deploy()`

```python
deploy(
    end_block: str | None = None,
    concurrency: int | None = None,
    retry_strategy: dict | None = None,
    verify: str | None = None,
    worker_id: str | None = None,
    wait: bool = False,
) -> JobInfo
```

Deploys the version, with the options of [`datasets.deploy()`](/sdks/python/reference/admin#datasetsdeploy). Returns the deployment job's
[`JobInfo`](/sdks/python/reference/admin#jobinfo). With `wait=True`, it calls [`jobs.wait_for_completion()`](/sdks/python/reference/admin#jobswait_for_completion) first, which
never returns: check [`status()`](/sdks/python/reference/admin#datasetstatus) instead.

### `Dataset.status()`

```python
status() -> JobStatusResponse | None
```

Returns the status of the job that fills the version's tables, as a [`JobStatusResponse`](/sdks/python/reference/admin#jobstatusresponse), or
`None` when the version was never deployed.

### `Dataset.jobs()`

```python
jobs() -> JobsResponse
```

Same as [`datasets.list_jobs()`](/sdks/python/reference/admin#datasetslist_jobs) for this version.

### `Dataset.add_tables()`

```python
add_tables(tables: dict[str, QueryBuilder], new_version: str, network: str = 'mainnet') -> Dataset
```

Same as [`datasets.add_tables()`](/sdks/python/reference/admin#datasetsadd_tables), from this version's manifest.

### `Dataset.describe()` and `Dataset.inspect()`

```python
describe() -> dict[str, list[dict]]
inspect() -> None
```

Same as [`datasets.describe()`](/sdks/python/reference/admin#datasetsdescribe) and [`datasets.inspect()`](/sdks/python/reference/admin#datasetsinspect), from the manifest the
handle has, without a request.

## `JobsClient`

`client.jobs`: follows and controls [jobs](/managing-amp/jobs). Job statuses are in capitals, like `RUNNING` and
`COMPLETED` (see [Job Lifecycle](/managing-amp/jobs/lifecycle)).

### `jobs.get()`

```python
get(job_id: int) -> JobInfo
```

Returns a job, as a [`JobInfo`](/sdks/python/reference/admin#jobinfo). Raises `JobNotFoundError` when it doesn't exist.

### `jobs.list()`

```python
list(limit: int = 50, last_job_id: int | None = None) -> JobsResponse
```

Lists the jobs that haven't ended, a page at a time. `limit` is at most 1,000. For the next page, pass the response's
`next_cursor` as `last_job_id`; it's `None` on the last page. Returns a [`JobsResponse`](/sdks/python/reference/admin#jobsresponse).

### `jobs.get_status()`

```python
get_status(job_id: int) -> JobStatusResponse
```

Returns a job's status, with each table's progress, as a [`JobStatusResponse`](/sdks/python/reference/admin#jobstatusresponse).

### `jobs.get_events()`

```python
get_events(job_id: int) -> JobEventsResponse
```

Lists the changes in a job's status, oldest first, as a `JobEventsResponse`, whose `events` each have an `id`, an
`event_type` (the new status), `created_at`, and the `node_id` of the worker that recorded it.

### `jobs.get_event()`

```python
get_event(job_id: int, event_id: int) -> JobEventDetailResponse
```

Returns one event, with its `detail`, like an error's cause. Raises `EventNotFoundError` when it doesn't exist.

### `jobs.stop()`

```python
stop(job_id: int) -> None
```

Asks a job to stop, and returns: the job then moves through `STOP_REQUESTED` and `STOPPING` to `STOPPED`. Raises
`JobStopError` when the job can't be stopped.

### `jobs.delete()`

```python
delete(job_id: int) -> None
```

Deletes a job that ended. Raises `JobDeleteError` for a job that hasn't.

### `jobs.delete_by_status()`

```python
delete_by_status(status: str) -> None
```

Deletes every job that ended a certain way: `'terminal'` for all of them, or `'completed'`, `'stopped'`, or `'error'`.

### `jobs.wait_for_completion()`

```python
wait_for_completion(job_id: int, poll_interval: int = 5, timeout: int | None = None) -> JobInfo
```

Gets the job every `poll_interval` seconds until its status is `Completed`, `Failed`, or `Stopped`, then returns it.
Amp reports statuses in capitals, like `COMPLETED`, so it never sees the job end: it raises `TimeoutError` after
`timeout` seconds, or never returns without one. Check [`jobs.get()`](/sdks/python/reference/admin#jobsget)'s `status` yourself instead.

### `jobs.create()`

```python
create(request: CreateJobRequest) -> CreateJobResponse
```

Schedules a job from a request model in `amp.admin.models`. The models don't have the fields that say which dataset the
job is for, so Amp rejects them with an `InvalidPayloadError` (`[INVALID_BODY] invalid request body: …`) that names the
missing `dataset_namespace` field. Use [`datasets.deploy()`](/sdks/python/reference/admin#datasetsdeploy) to schedule a dataset's jobs.

## `SchemaClient`

`client.schema`: works out the columns queries return, without running them.

### `schema.analyze()`

```python
analyze(
    dependencies: dict[str, str] | None = None,
    functions: dict | None = None,
    tables: dict[str, str] | None = None,
) -> SchemaResponse
```

| Parameter      | Type                       | Description                                                                                                    |
| -------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `dependencies` | dict of strings to strings | Each alias the queries use, and the dataset it stands for, like `{'eth': 'acme/eth_mainnet@1.0.0'}`.           |
| `functions`    | dict                       | [JavaScript functions](/querying/functions/javascript) the queries call, by name, as a manifest declares them. |
| `tables`       | dict of strings to strings | Each table's name and SQL query.                                                                               |

Returns a `SchemaResponse`, whose `schemas` has each table's columns, in a manifest's format:

```python
response = client.schema.analyze(
    dependencies={'eth': 'acme/eth_mainnet@1.0.0'},
    tables={'block_gas': 'SELECT block_num, gas_used FROM eth.blocks'},
)
print(response.schemas['block_gas']['arrow']['fields'][2])
# {'name': 'block_num', 'type': 'UInt64', 'nullable': False}
```

Raises `AdminAPIError` when a query can't be planned, like `[INVALID_PLAN] Failed to plan SQL for table 'block_gas': …`.

## `ConfigMapsClient`

`admin.configmaps`, on `AdminClient` only: stores [config maps](/managing-amp/config-maps). Each write creates a new
generation of the config map, with its own `id`.

### `configmaps.get()`

```python
get(namespace: str, name: str) -> ConfigMap
```

Returns a config map's current generation, as a [`ConfigMap`](/sdks/python/reference/admin#configmap). Raises `ConfigMapNotFoundError` when it
doesn't exist.

### `configmaps.list()`

```python
list(namespace: str) -> ConfigMapsResponse
```

Lists a namespace's config maps, by name, without their documents. Loop over the response for its summaries, each with
`namespace`, `name`, `id`, `kind`, `created_at`, `created_by`, `updated_at`, and `updated_by`.

### `configmaps.create()`

```python
create(namespace: str, name: str, kind: str, data: dict, if_match: str | None = None) -> ConfigMap
```

Creates a config map, or replaces the one with that name.

| Parameter  | Type   | Default | Description                                                                                   |
| ---------- | ------ | ------- | --------------------------------------------------------------------------------------------- |
| `kind`     | string | none    | What the document configures, like `'evm-rpc'`. Amp stores it beside the document, unchecked. |
| `data`     | dict   | none    | The document, any JSON object.                                                                |
| `if_match` | string | `None`  | A generation's `id`: replace the config map only if it's still at that generation.            |

Returns a [`ConfigMap`](/sdks/python/reference/admin#configmap) for the new generation. Raises `ConfigMapPreconditionFailedError` when `if_match`
doesn't match, `ConfigMapTooLargeError` when the document is too large, and `InvalidConfigMapDataError` when it isn't a
JSON object.

### `configmaps.update()`

```python
update(namespace: str, name: str, data: dict, kind: str | None = None) -> ConfigMap
```

Reads a config map, then replaces its document if nobody wrote to it in between. `kind` keeps the current one by
default. Raises `ConfigMapNotFoundError` when it doesn't exist, and `ConfigMapPreconditionFailedError` when someone
wrote to it in between.

### `configmaps.delete()`

```python
delete(namespace: str, name: str) -> None
```

Deletes a config map. It succeeds when the config map doesn't exist, and doesn't check whether anything still reads it.

## `ConfigMap`

One generation of a config map, which `configmaps.get()`, `configmaps.create()`, and `configmaps.update()` return. It
doesn't change when the config map does.

| Property    | Type   | Description                                                      |
| ----------- | ------ | ---------------------------------------------------------------- |
| `namespace` | string | The config map's namespace.                                      |
| `name`      | string | Its name.                                                        |
| `id`        | string | This generation's ID.                                            |
| `kind`      | string | What it configures.                                              |
| `data`      | dict   | The document, as stored, with `${{ env.VAR }}` references as is. |
| `reference` | string | `namespace/name`.                                                |

### `ConfigMap.update()`

```python
update(data: dict, kind: str | None = None) -> ConfigMap
```

Replaces the document, if the config map is still at this generation. Returns a `ConfigMap` for the new generation.
Raises `ConfigMapPreconditionFailedError` (`config map is not bound to the expected generation`) when someone wrote to
it since: get it again, and apply your change to that.

### `ConfigMap.delete()`

```python
delete() -> None
```

Deletes the config map, whatever its current generation.

## `RevisionsClient`

`admin.revisions`, on `AdminClient` only: manages [table revisions](/managing-amp/table-revisions), the storage
locations of tables' files. Revisions are identified by their ID, which Amp calls a location ID. Methods that take a
`dataset` take a reference like `'acme/eth_mainnet@1.0.0'`.

| Method                                                                                         | Returns                                 | Description                                                                                                                                                      |
| ---------------------------------------------------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list(active: bool \| None = None, limit: int \| None = None, last_id: int \| None = None)`    | list of [`RevisionInfo`](/sdks/python/reference/admin#revisioninfo) | Lists revisions, 100 at a time unless you pass `limit`. `active` keeps only the active ones, or the inactive ones; `last_id` continues after that revision's ID. |
| `get(revision_id: int)`                                                                        | [`RevisionInfo`](/sdks/python/reference/admin#revisioninfo)         | One revision.                                                                                                                                                    |
| `create(dataset: str, table_name: str, storage_path: str)`                                     | `CreateRevisionResponse`                | Registers a storage location as a new revision of a table. Its `location_id` is the new revision's ID.                                                           |
| `activate(revision_id: int, dataset: str, table_name: str)`                                    | `None`                                  | Makes a revision the one the table's queries read.                                                                                                               |
| `deactivate(dataset: str, table_name: str)`                                                    | `None`                                  | Deactivates a table's active revision.                                                                                                                           |
| `prune(revision_id: int, before_block: int \| None = None, gc_delay_secs: int \| None = None)` | `PruneResponse`                         | Schedules the revision's non-canonical files for deletion, only before `before_block` if given, after `gc_delay_secs`. Its `files_scheduled` counts them.        |
| `truncate(revision_id: int, concurrency: int \| None = None)`                                  | `TruncateResponse`                      | Deletes all of the revision's files now. Its `files_deleted` counts them.                                                                                        |
| `restore(revision_id: int)`                                                                    | `RestoreResponse`                       | Restores the revision's files from storage. Its `total_files` counts them.                                                                                       |
| `delete(revision_id: int)`                                                                     | `None`                                  | Deletes a revision.                                                                                                                                              |

They raise `RevisionNotFoundError` for a revision that doesn't exist.

## Models

The models in `amp.admin.models`, and in `amp.admin` for the dataset statistics, that the methods return. Times are
[RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) strings, like `'2026-10-30T21:04:40.394300+00:00'`.

### `DatasetsResponse`

| Field      | Type                     | Description                                                                                      |
| ---------- | ------------------------ | ------------------------------------------------------------------------------------------------ |
| `datasets` | list of `DatasetSummary` | Each dataset's `namespace`, `name`, `versions` (newest first), and `latest_version` (or `None`). |

### `VersionsResponse`

| Field          | Type                  | Description                                                                                            |
| -------------- | --------------------- | ------------------------------------------------------------------------------------------------------ |
| `namespace`    | string                | The dataset's namespace.                                                                               |
| `name`         | string                | Its name.                                                                                              |
| `versions`     | list of `VersionInfo` | Each version's `version`, `manifest_hash`, `created_at`, and `updated_at`, newest first.               |
| `special_tags` | `SpecialTags`         | `latest`, the newest version, and `dev`, the hash of the manifest registered last. Each can be `None`. |

### `DatasetInfo`

| Field                   | Type            | Description                                                                           |
| ----------------------- | --------------- | ------------------------------------------------------------------------------------- |
| `namespace`             | string          | The dataset's namespace.                                                              |
| `name`                  | string          | Its name.                                                                             |
| `revision`              | string          | The revision you asked for.                                                           |
| `kind`                  | string          | `manifest` for a derived dataset, or a raw dataset's kind, like `evm-rpc`.            |
| `manifest_hash`         | string          | The manifest's hash.                                                                  |
| `tables`                | list of strings | Its tables' names.                                                                    |
| `start_block`           | integer         | The block it starts at.                                                               |
| `finalized_blocks_only` | boolean         | Whether it only takes finalized blocks.                                               |
| `tags`                  | list of strings | The versions and tags that point to this manifest, like `['1.0.0', 'latest', 'dev']`. |

### `JobInfo`

| Field             | Type    | Description                                                                                       |
| ----------------- | ------- | ------------------------------------------------------------------------------------------------- |
| `id`              | integer | The job's ID.                                                                                     |
| `status`          | string  | Its status, like `RUNNING` or `COMPLETED`.                                                        |
| `descriptor`      | dict    | What the job does: its `kind` (like `materialize-derived` or `gc`), its dataset, and its options. |
| `node_id`         | string  | The worker it runs on.                                                                            |
| `created_at`      | string  | When it was created.                                                                              |
| `updated_at`      | string  | When it last changed.                                                                             |
| `idempotency_key` | string  | The key Amp uses to avoid scheduling the same job twice.                                          |

### `JobsResponse`

| Field         | Type              | Description                                                       |
| ------------- | ----------------- | ----------------------------------------------------------------- |
| `jobs`        | list of `JobInfo` | The jobs.                                                         |
| `next_cursor` | integer           | For `jobs.list()`: the `last_job_id` of the next page, or `None`. |

### `JobStatusResponse`

| Field        | Type                             | Description                                                                  |
| ------------ | -------------------------------- | ---------------------------------------------------------------------------- |
| `job_id`     | integer                          | The job's ID.                                                                |
| `job_status` | string                           | Its status.                                                                  |
| `tables`     | dict of strings to `TableStatus` | Each table it writes, or `None`.                                             |
| `detail`     | dict                             | When the job failed: the error's `error_code`, `error_message`, and context. |

Each `TableStatus` has `files_count`, `total_size_bytes`, `extracted_block_count`, `latest_segment_delay_seconds` (how
long ago its last block was produced), and `synced_ranges`: dicts with the `network`, the block `numbers` (`start` and
`end`), and the last block's `hash` and `timestamp`.

### `DatasetStatsResponse`

| Field        | Type                                  | Description                      |
| ------------ | ------------------------------------- | -------------------------------- |
| `job_id`     | integer                               | The job the statistics are from. |
| `job_status` | string                                | Its status.                      |
| `tables`     | dict of strings to `PublicTableStats` | Each table's statistics.         |

Each `PublicTableStats` has `data_synced_through` (the time of the last block synced), `data_lag_seconds` (how long ago
that was), `row_count` and `min_timestamp` (when Amp reports them, `None` otherwise), and `synced_ranges` (each with its
`network` and block `numbers`).

### `LineageResponse`

| Field       | Type                  | Description                                                                                                                                                  |
| ----------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `anchor`    | string                | The dataset you asked about.                                                                                                                                 |
| `nodes`     | list of `LineageNode` | Each dataset found: its reference (`dataset`), `depth` from the anchor, `kind`, `resolved_hash`, and `status` (`ok`, or `missing` when it isn't registered). |
| `edges`     | list of `LineageEdge` | Each dependency: the `source` dataset, the `target` that reads it, the `alias` it uses, and `resolved_source`.                                               |
| `depth`     | integer               | The longest path from the anchor.                                                                                                                            |
| `truncated` | boolean               | Whether `max_depth` cut the graph short.                                                                                                                     |
| `cycles`    | list of strings       | The datasets in a dependency cycle, if any.                                                                                                                  |

### `ManifestsResponse`

`manifests`: a list of `ManifestInfo`, each with the manifest's `hash`, its `kind`, and `dataset_count`, how many
datasets use it.

### `RevisionInfo`

| Field      | Type                   | Description                                                                 |
| ---------- | ---------------------- | --------------------------------------------------------------------------- |
| `id`       | integer                | The revision's ID.                                                          |
| `path`     | string                 | Its location in storage, relative to Amp's data directory.                  |
| `active`   | boolean                | Whether queries read it.                                                    |
| `writer`   | integer                | The job that writes it, or `None`.                                          |
| `metadata` | `RevisionMetadataInfo` | Its `dataset_namespace`, `dataset_name`, `manifest_hash`, and `table_name`. |

## Errors

Every error the Admin API returns raises an `AdminAPIError`, from `amp.admin`, or one of its subclasses:

| Attribute     | Type    | Description                                             |
| ------------- | ------- | ------------------------------------------------------- |
| `error_code`  | string  | The API's code for the error, like `DATASET_NOT_FOUND`. |
| `message`     | string  | What went wrong.                                        |
| `status_code` | integer | The HTTP status, like `404`.                            |

Its text is `[error_code] message`, like `[DATASET_NOT_FOUND] Dataset 'acme/nope' at revision '1.0.0' not found`. The
subclasses for the errors you're most likely to handle:

| Class                                                 | Raised when                                                                |
| ----------------------------------------------------- | -------------------------------------------------------------------------- |
| `DatasetNotFoundError`                                | A dataset or version doesn't exist.                                        |
| `DatasetStatsUnavailableError`                        | `datasets.stats()` was called on a version that was never deployed.        |
| `InvalidManifestError`                                | A manifest isn't valid.                                                    |
| `DependencyValidationError`                           | A manifest's queries use a dependency it doesn't declare.                  |
| `ManifestRegistrationError`                           | Amp couldn't store a manifest.                                             |
| `SchedulerError`                                      | Amp couldn't schedule a job.                                               |
| `JobNotFoundError`                                    | A job doesn't exist.                                                       |
| `JobStopError`, `JobDeleteError`, `JobsDeleteError`   | A job couldn't be stopped or deleted, like deleting one that hasn't ended. |
| `EventNotFoundError`                                  | A job event doesn't exist.                                                 |
| `ConfigMapNotFoundError`                              | A config map doesn't exist.                                                |
| `ConfigMapPreconditionFailedError`                    | A config map changed since the generation you wrote against.               |
| `ConfigMapTooLargeError`, `InvalidConfigMapDataError` | A config map's document is too large, or isn't a JSON object.              |
| `NamespaceNotFoundError`                              | A namespace doesn't exist, or your token doesn't give you access to it.    |
| `RevisionNotFoundError`                               | A table revision doesn't exist.                                            |
| `InvalidPayloadError`                                 | A request's body or parameters aren't valid.                               |
| `InvalidPathError`                                    | A namespace, name, or other part of a request's URL isn't valid.           |
| `DatabaseError`, `InternalServerError`                | Amp failed while handling the request.                                     |

Errors without a subclass of their own, like `DEPENDENCY_NOT_FOUND`, `INVALID_PLAN`, `NON_INCREMENTAL_QUERY`, and
`CANNOT_DELETE_LATEST_VERSION`, raise `AdminAPIError` itself: check its `error_code`. When the API can't be reached, the
SDK raises the HTTP library's error instead, like `httpx.ConnectError`.
