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

The `AdminApi` service calls Amp's [Admin API](/managing-amp/admin-api), which the controller serves: the same API
[`ampctl`](/clis/ampctl) uses. Use it to register datasets and inspect them, and to watch and manage the jobs that
extract and transform their data, from your own code: a deployment script, a dashboard, a test.

It covers part of the Admin API: datasets, manifests, jobs, workers, providers, and schema analysis. It can't deploy a
dataset, register a provider, or manage config maps and table revisions. For those, use `ampctl`, or send
the requests [the Admin API documents](/managing-amp/admin-api) yourself.

## Prerequisites

* The SDK, [installed](/sdks/typescript#install-the-sdk).
* An Admin API to connect to (see [Connecting](/sdks/typescript/connecting)). The examples use the
  [Quickstart](/get-started/quickstart)'s Amp, whose Admin API is on port 1610, and its `eth_mainnet.json` manifest.
* On a hosted Amp, a token that allows what you do: reading needs read access to a namespace, and registering, write
  access.

## Set up the service

`AdminApi` and its `layer` are in `@edgeandnode/amp/admin/service`. The layer takes the Admin API's URL, and a token in
`jwt` for a hosted Amp, and needs Effect's HTTP client:

```ts
import { AdminApi, layer } from "@edgeandnode/amp/admin/service"
import { Layer } from "effect"
import { FetchHttpClient } from "effect/http"

const AdminLayer = layer({ url: "http://localhost:1610" }).pipe(Layer.provide(FetchHttpClient.layer))
```

Most methods take a dataset's namespace, name, and revision as [branded
types](/sdks/typescript/effect#schemas-and-branded-types): create them with `Domain.DatasetNamespace.make("acme")`,
`Domain.DatasetName.make("eth_mainnet")`, and `Domain.DatasetVersion.make("1.0.0")`. A revision can also be a manifest
hash, `"latest"`, or `"dev"` (see [`DatasetRevision`](/sdks/typescript/domain#datasetrevision)).

## Register a dataset

Registering a dataset stores its [manifest](/datasets/manifests) in Amp, under a name and a version: it doesn't extract
anything yet. This program registers the Quickstart's manifest as `acme/eth_blocks`, version 1.0.0, then lists the
dataset's versions:

```ts [register.ts]
import { Domain } from "@edgeandnode/amp"
import { AdminApi, layer } from "@edgeandnode/amp/admin/service"
import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/http"
import { readFileSync } from "node:fs"

const program = Effect.gen(function* () {
  const admin = yield* AdminApi

  // Store the manifest: Amp returns its hash
  const manifest = JSON.parse(readFileSync("eth_mainnet.json", "utf8"))
  const { hash } = yield* admin.registerManifest(manifest)

  // Register it as version 1.0.0 of acme/eth_blocks
  const registered = yield* admin.registerDataset(
    Domain.DatasetNamespace.make("acme"),
    Domain.DatasetName.make("eth_blocks"),
    hash,
    Domain.DatasetVersion.make("1.0.0"),
  )
  console.log(registered)

  const { versions, specialTags } = yield* admin.getDatasetVersions(registered.namespace, registered.name)
  console.log(
    versions.map((v) => v.version),
    specialTags,
  )
})

const AdminLayer = layer({ url: "http://localhost:1610" }).pipe(Layer.provide(FetchHttpClient.layer))

await Effect.runPromise(program.pipe(Effect.provide(AdminLayer)))
```

:::terminal
```bash
node register.ts
```

```
{
  namespace: 'acme',
  name: 'eth_blocks',
  version: '1.0.0',
  manifestHash: 'b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b',
  kind: 'evm-rpc',
  startBlock: 24000000,
  finalizedBlocksOnly: false,
  tables: [ 'blocks', 'logs', 'transactions' ]
}
[ '1.0.0' ] {
  latest: '1.0.0',
  dev: 'b9e2ebd92c0752ba8a61a233f9cc5d3f4f8b601b1b789a882a5dd8ece9bca56b'
}
```
:::

`registerManifest` takes the manifest as it is in its file. `registerDataset` takes either its hash or the manifest
itself, decoded into a [`DatasetManifest`](/sdks/typescript/domain#datasetmanifest):
`Schema.decodeUnknownSync(Domain.DatasetManifest)(json)`. Without a version, `registerDataset` only points the
dataset's `dev` tag at the manifest.

To start extracting, deploy the dataset with [`ampctl dataset deploy`](/clis/ampctl/dataset): the SDK can't.

## List jobs

A job extracts or transforms a dataset's data (see [Jobs](/managing-amp/jobs)). `getJobs` lists them, newest first, a
page at a time. Pass the last page's `nextCursor` as `lastJobId` to get the next page:

```ts [jobs.ts]
import { AdminApi, layer } from "@edgeandnode/amp/admin/service"
import { DateTime, Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/http"

const program = Effect.gen(function* () {
  const admin = yield* AdminApi
  let lastJobId: number | undefined
  while (true) {
    const page = yield* admin.getJobs({ status: "all", limit: 10, lastJobId })
    for (const job of page.jobs) {
      console.log(job.id, job.status, job.descriptor.kind, DateTime.formatIso(job.createdAt))
    }
    if (page.jobs.length < 10 || page.nextCursor === undefined) break
    lastJobId = page.nextCursor
  }
})

const AdminLayer = layer({ url: "http://localhost:1610" }).pipe(Layer.provide(FetchHttpClient.layer))

await Effect.runPromise(program.pipe(Effect.provide(AdminLayer)))
```

:::terminal
```bash
node jobs.ts
```

```
3 COMPLETED gc 2026-10-30T20:58:19.199Z
2 COMPLETED materialize-raw 2026-10-30T20:58:19.199Z
1 COMPLETED composite 2026-10-30T20:58:19.199Z
```
:::

Deploying the Quickstart's dataset created these three jobs: `composite` groups the others, `materialize-raw`
extracts the blocks, and `gc` removes files the dataset no longer needs. Yours have their own dates.

Without `status`, `getJobs` lists only the jobs that haven't finished. Dates, like `createdAt`, are Effect
`DateTime.Utc` values: `DateTime.formatIso` formats one, and `DateTime.toDate` turns it into a `Date`.

`stopJob(id)` asks a job to stop, and `deleteJob(id)` deletes a job that's stopped or finished. Deleting a running job
fails with a `JobConflictError`:
`job '7' cannot be deleted from current state: RUNNING`.

## Errors

Each method fails with the errors its endpoint returns, typed: `getDatasetVersion` can fail with a
`DatasetNotFoundError`, for example. Each has a `_tag`, a stable `code`, and a `message`:

```ts
{ _tag: "DatasetNotFoundError", code: "DATASET_NOT_FOUND", message: "Dataset 'acme/eth_testnet' at revision '1.0.0' not found" }
```

Handle them with `Effect.catchTag`, as in [Effect Basics](/sdks/typescript/effect#errors). [Error
Codes](/sdks/typescript/error-codes#admin-api) lists every one, with the methods that return it.

A request that fails without one of these errors, like when the Admin API can't be reached or a hosted Amp rejects your
token, makes the program fail with a [defect](/sdks/typescript/errors#defects), an `HttpClientError`, instead of an
error you can catch with `catchTag`. So does a response the SDK can't decode.

## Reference

### `AdminApi`

```ts
import { AdminApi } from "@edgeandnode/amp/admin/service"

class AdminApi extends Context.Service<AdminApi, { … }>
```

The service that calls the Admin API. Get it with `yield* AdminApi`. Its methods are below. Each fails with
[`HttpError`](/sdks/typescript/admin-api#httperror), and with the method's own errors, its endpoint's error type below: the
[Admin API errors](/sdks/typescript/error-codes#admin-api) its endpoint returns.

#### `registerDataset`

```ts
registerDataset(
  namespace: DatasetNamespace,
  name: DatasetName,
  manifest: DatasetManifest | DatasetHash,
  version?: DatasetRevision,
): Effect<RegisterDatasetResponse, HttpError | RegisterDatasetError>
```

Registers a dataset: links `namespace/name` to a manifest, given in full or by the hash of one already stored, and tags
it with `version`. Without `version`, only the `dev` tag points at it. Registering the same manifest again changes
nothing.

Returns a [`RegisterDatasetResponse`](/sdks/typescript/admin-api#response-types): the dataset's namespace, name, version, manifest hash, kind,
start block, whether it only extracts finalized blocks, and its tables. See [Register a dataset](/sdks/typescript/admin-api#register-a-dataset).

#### `getDatasets`

```ts
getDatasets: Effect<GetDatasetsResponse, HttpError | GetDatasetsError>
```

Lists every registered dataset: `{ datasets }`, each with its `namespace`, `name`, `versions`, and `latestVersion`. An
Effect, not a function: `yield* admin.getDatasets`.

#### `getDatasetVersions`

```ts
getDatasetVersions(namespace: DatasetNamespace, name: DatasetName): Effect<GetDatasetVersionsResponse, HttpError | GetDatasetVersionsError>
```

Lists a dataset's versions: `versions`, each with its `version`, `manifestHash`, `createdAt`, and `updatedAt`; and
`specialTags`, the version `latest` points to and the manifest hash `dev` points to.

#### `getDatasetVersion`

```ts
getDatasetVersion(namespace: DatasetNamespace, name: DatasetName, revision: DatasetRevision): Effect<GetDatasetVersionResponse, HttpError | GetDatasetVersionError>
```

Gets a version of a dataset: its `kind`, `revision`, `manifestHash`, `startBlock`, `finalizedBlocksOnly`, `tables`,
and `tags`, the tags that point at its manifest (versions first, then `latest`, then `dev`).

```ts
const version = yield* admin.getDatasetVersion(namespace, name, Domain.DatasetVersion.make("1.0.0"))
// { kind: "evm-rpc", revision: "1.0.0", tables: ["blocks", "logs", "transactions"], tags: ["1.0.0", "latest", "dev"], … }
```

#### `getDatasetManifest`

```ts
getDatasetManifest(namespace: DatasetNamespace, name: DatasetName, revision: DatasetRevision): Effect<DatasetManifest, HttpError | GetDatasetManifestError>
```

Gets a version's [manifest](/sdks/typescript/domain#datasetmanifest), decoded: its keys are camelCase, like
`startBlock`, where the manifest file has `start_block`. To store it again with `registerManifest`, which takes the
file's form, encode it first: `Schema.encodeSync(Domain.DatasetManifest)(manifest)`.

#### `getDatasetLineage`

```ts
getDatasetLineage(
  namespace: DatasetNamespace,
  name: DatasetName,
  revision: DatasetRevision,
  options?: { direction?: "upstream" | "downstream" | "both"; maxDepth?: number; maxNodes?: number },
): Effect<GetDatasetLineageResponse, HttpError | GetDatasetLineageError>
```

Gets a dataset's [lineage](/managing-amp/lineage): the datasets it reads from (upstream), the ones that read from it
(downstream), or both.

| Option      | Type                                   | Default      | Description                                     |
| ----------- | -------------------------------------- | ------------ | ----------------------------------------------- |
| `direction` | `"upstream"`, `"downstream"`, `"both"` | `"upstream"` | Which way to walk from the dataset.             |
| `maxDepth`  | `number`                               | Unlimited    | How many steps to walk, like `{ maxDepth: 1 }`. |
| `maxNodes`  | `number`                               | Unlimited    | The most datasets to return in each direction.  |

Returns the graph: `anchor`, the dataset you asked about; `nodes`, each dataset reached, with its `kind`, `status`
(`"ok"`, or `"missing"` when it isn't registered), `resolvedHash`, and `depth`; `edges`, from each upstream dataset
(`source`) to the one that reads it (`target`), with the `alias` it reads it by; `depth`, the longest path; `truncated`,
whether `maxDepth` cut it short; and `cycles`, the datasets in a dependency cycle.

```ts
const lineage = yield* admin.getDatasetLineage(namespace, name, revision, { direction: "both" })
// { anchor: { namespace: "acme", name: "eth_mainnet", revision: "1.0.0" }, nodes: [{ … depth: 0 }], edges: [], depth: 0, truncated: false, cycles: [] }
```

#### `getJobs`

```ts
getJobs(options?: { limit?: number; lastJobId?: number; status?: string }): Effect<GetJobsResponse, HttpError | GetJobsError>
```

Lists jobs, newest first.

| Option      | Type     | Default    | Description                                                                                                        |
| ----------- | -------- | ---------- | ------------------------------------------------------------------------------------------------------------------ |
| `limit`     | `number` | `50`       | How many jobs to return, up to 1,000.                                                                              |
| `lastJobId` | `number` | None       | Returns the jobs after this one: the previous page's `nextCursor`.                                                 |
| `status`    | `string` | `"active"` | `"active"` for the jobs that haven't finished, `"all"`, or statuses separated by commas, like `"COMPLETED,ERROR"`. |

Returns `{ jobs, nextCursor }`: the jobs, as [`JobInfo`](/sdks/typescript/domain#jobinfo), and the ID to pass as
`lastJobId` for the next page. See [List jobs](/sdks/typescript/admin-api#list-jobs).

#### `getJobById`

```ts
getJobById(jobId: number): Effect<JobInfo, HttpError | GetJobByIdError>
```

Gets a job: its `id`, `status`, `nodeId` (the worker running it), `createdAt`, `updatedAt`, and `descriptor`, what the
job does, like `{ kind: "materialize-raw", dataset_name: "eth_mainnet", end_block: "24000099", … }`.

#### `stopJob`

```ts
stopJob(jobId: number): Effect<void, HttpError | StopJobError>
```

Asks a job to stop. Its status becomes `STOP_REQUESTED`, then `STOPPING`, then `STOPPED`. Fails with a
`JobNotFoundError` when there's no such job.

#### `deleteJob`

```ts
deleteJob(jobId: number): Effect<void, HttpError | DeleteJobError>
```

Deletes a job that has stopped or finished. Fails with a `JobConflictError` when it's still running.

#### `getWorkers`

```ts
getWorkers: Effect<GetWorkersResponse, HttpError | GetWorkersError | SchedulerListWorkersError>
```

Lists the [workers](/managing-amp/workers): `{ workers }`, each with its `nodeId` and `heartbeatAt`, when it last
reported in, as an ISO 8601 string. An Effect, not a function.

```ts
const { workers } = yield* admin.getWorkers
// [{ nodeId: "worker", heartbeatAt: "2026-10-30T21:07:42.866074+00:00" }]
```

#### `getProviders`

```ts
getProviders: Effect<GetProvidersResponse, HttpError>
```

Lists the [providers](/providers): `{ providers }`, each with its `name`, `kind`, and the rest of its configuration,
like `network` and `url`. Secrets in the configuration come back as you registered them, like `${RPC_URL}`. An Effect,
not a function.

#### `registerManifest`

```ts
registerManifest(manifest: unknown): Effect<RegisterManifestResponse, HttpError | RegisterManifestError>
```

Stores a manifest without registering a dataset, and returns `{ hash }`, the hash to pass to `registerDataset`. Takes
the manifest as JSON, as in its file, with snake_case keys: a decoded `DatasetManifest` gets stored as a different
manifest, with a different hash.

#### `getOutputSchema`

```ts
getOutputSchema(request: GetOutputSchemaPayload): Effect<GetOutputSchemaResponse, HttpError | GetOutputSchemaError>
```

Works out the tables a [derived dataset](/datasets/derived-datasets) would have, without registering it: the
columns each table's SQL produces, and their types. Use it to check a derived dataset's SQL, and to write its
manifest's `schema`.

| Field          | Type                                 | Description                                                                                                                               |
| -------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `tables`       | `Record<string, string>`             | Each table's SQL, by table name.                                                                                                          |
| `dependencies` | `Record<string, DatasetReference>`   | The datasets the SQL reads, by the alias it uses for them, like `{ eth: { namespace: "acme", name: "eth_mainnet", revision: "1.0.0" } }`. |
| `functions`    | `Record<string, FunctionDefinition>` | [JavaScript functions](/querying/functions/javascript) the SQL calls, by name.                                                            |

```ts
const { schemas } =
  yield* admin.getOutputSchema({
    dependencies: { eth: { namespace, name, revision } },
    tables: { big_blocks: "SELECT block_num, gas_used FROM eth.blocks WHERE gas_used > 30000000" },
  })
// schemas.big_blocks.arrow.fields: _block_num (UInt64), _ts (Timestamp), block_num (UInt64), gas_used (UInt64)
```

A table that reads one network gets `_block_num`, and `_ts` if the tables it reads have it. One that reads several gets
`_ts`, and `_block_num` only if its SQL selects it.

SQL that Amp can't run incrementally, like with `ORDER BY` or `LIMIT`, fails with a `NonIncrementalQueryError`:
`Table 't' contains non-incremental SQL: query contains non-incremental operation: Limit`.

### `layer`

```ts
function layer(options: MakeOptions): Layer<AdminApi, never, HttpClient>
```

Builds the `AdminApi` service. Needs an `HttpClient`: `FetchHttpClient.layer`, from `effect/http`, uses `fetch`.

### `layerAuth`

```ts
function layerAuth(options: MakeOptions): Layer<AdminApi, never, HttpClient | KeyValueStore>
```

Like `layer`, and also builds the [`Auth`](/sdks/typescript/authentication#auth) service, so requests carry your saved
sign-in. Needs a `KeyValueStore` to read it from. See [Sign in](/sdks/typescript/authentication#sign-in).

### `MakeOptions`

```ts
interface MakeOptions {
  readonly url: string | URL
  readonly jwt?: string | null | undefined
}
```

| Option | Type                             | Default  | Description                                                                                                               |
| ------ | -------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `url`  | `string` or `URL`                | Required | The Admin API's URL, like `"http://localhost:1610"`.                                                                      |
| `jwt`  | `string`, `null`, or `undefined` | None     | A token to send as a bearer token with every request. When set, it's used even if the `Auth` service has a saved sign-in. |

### `HttpError`

```ts
type HttpError = HttpApiError.Forbidden | HttpApiError.Unauthorized | HttpClientError.HttpClientError
```

The HTTP errors every method declares. In practice, the service turns `HttpClientError`s into defects (see
[Errors](/sdks/typescript/admin-api#errors)).

### The Admin API's definition

The root's `AdminApi` namespace, also `@edgeandnode/amp/admin/api`, is the Admin API's definition, from which the
service builds its client: Effect's `HttpApi`, with its endpoints, their parameters, responses, and errors. You only
need it to build a client of your own, with Effect's `HttpApiClient`.

| Export                                                                                                                                                                                                                                                                                                             | What it is                                                                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `Api`                                                                                                                                                                                                                                                                                                              | The whole API, `AmpAdminApi`.                                                                                   |
| `DatasetGroup`, `JobGroup`, `WorkerGroup`, `SchemaGroup`, `ManifestGroup`, `ProviderGroup`                                                                                                                                                                                                                         | Its groups of endpoints.                                                                                        |
| `GetDatasetsError`, `RegisterDatasetError`, `GetDatasetVersionsError`, `GetDatasetVersionError`, `GetDatasetManifestError`, `GetDatasetLineageError`, `GetJobsError`, `GetJobByIdError`, `StopJobError`, `DeleteJobError`, `GetWorkersError`, `GetOutputSchemaError`, `RegisterManifestError`, `GetProvidersError` | The union of the errors each endpoint returns, listed in [Error Codes](/sdks/typescript/error-codes#admin-api). |

### Response types

`@edgeandnode/amp/admin/domain` has the schemas of the requests and responses. Each is a schema and a type, of the same
name:

| Export                       | What it is                                                                                                        |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `GetDatasetsResponse`        | What `getDatasets` returns.                                                                                       |
| `RegisterDatasetPayload`     | The body `registerDataset` sends: `namespace`, `name`, `version`, and `manifest`, a manifest or its hash.         |
| `RegisterDatasetResponse`    | What `registerDataset` returns.                                                                                   |
| `GetDatasetVersionResponse`  | What `getDatasetVersion` returns.                                                                                 |
| `GetDatasetVersionsResponse` | What `getDatasetVersions` returns.                                                                                |
| `DatasetVersionInfo`         | A version in `GetDatasetVersionsResponse`.                                                                        |
| `DatasetSpecialTags`         | `GetDatasetVersionsResponse`'s `specialTags`: `latest` and `dev`.                                                 |
| `GetDatasetLineageResponse`  | What `getDatasetLineage` returns.                                                                                 |
| `LineageNode`                | A dataset in a lineage graph.                                                                                     |
| `LineageEdge`                | A dependency in a lineage graph, from `source` to `target`, with its `alias` and `resolvedSource`.                |
| `LineageDirection`           | `"upstream"`, `"downstream"`, or `"both"`.                                                                        |
| `LineageNodeStatus`          | `"ok"` or `"missing"`.                                                                                            |
| `LineageQueryParams`         | `getDatasetLineage`'s options, as the request's query parameters.                                                 |
| `NonNegativeIntFromString`   | A non-negative integer, read from a string: the type of `maxDepth` and `maxNodes` in the query.                   |
| `GetJobsResponse`            | What `getJobs` returns.                                                                                           |
| `GetOutputSchemaPayload`     | What `getOutputSchema` takes.                                                                                     |
| `GetOutputSchemaResponse`    | What `getOutputSchema` returns: `schemas`, a [`TableSchema`](/sdks/typescript/domain#tableschema) for each table. |
| `WorkerInfo`                 | A worker in `GetWorkersResponse`.                                                                                 |
| `GetWorkersResponse`         | What `getWorkers` returns.                                                                                        |
| `RegisterManifestResponse`   | What `registerManifest` returns.                                                                                  |
| `ProviderInfo`               | A provider in `GetProvidersResponse`: `name`, `kind`, and any other properties.                                   |
| `GetProvidersResponse`       | What `getProviders` returns.                                                                                      |

### Errors

The errors, from `@edgeandnode/amp/admin/error`, are in [Error Codes](/sdks/typescript/error-codes#admin-api).
