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

# ampctl provider

`ampctl provider` manages [providers](/providers): named connections to the sources Amp extracts data from, like an
Ethereum JSON-RPC endpoint. A [raw dataset](/datasets/raw-datasets) doesn't name its provider. When its job starts, Amp
looks for the providers registered with the dataset's kind and network, like `evm-rpc` and `mainnet`, and extracts
through all of them. So you register a provider before you deploy a dataset that needs it, and you can change or add
endpoints without changing any dataset. [Managing Providers](/managing-amp/providers) covers the task; this page covers
the commands.

Amp stores each provider as a TOML file, named after the provider, in the directory its `providers_dir` setting names
([Configuration Reference](/running-amp/configuration-reference#providers_dir)). Use these commands to change them
rather than editing the files: `register` checks [ADBC providers](/managing-amp/pipelines/destinations), and `rm`
removes Amp's other records of a provider.

`ampctl providers` is the same command.

## Prerequisites

* A running Amp, like the one the [Quickstart](/get-started/quickstart) starts with
  [`ampd solo`](/running-amp/solo-mode).
* [`ampctl` installed](/clis/ampctl#install) and [connected to it](/clis/ampctl#connect-to-amp).
* An endpoint for the chain you extract, like an Ethereum RPC endpoint. The examples read its URL from the `RPC_URL`
  environment variable, which must be set where `ampd` runs, before it starts:

  ```bash
  export RPC_URL=https://ethereum-rpc.example.com
  ampd solo
  ```

Every command also takes `ampctl`'s [global flags](/clis/ampctl#global-flags): `--admin-url`, `--auth-token`, and
`--json`, which prints [JSON](/clis/ampctl#json-output) instead of text.

## Names

A provider's name has lowercase letters, digits, and underscores, and starts with a letter or an underscore, like
`mainnet_rpc`. Any other name fails before anything is sent, including names with hyphens, like `mainnet-rpc`.

Names are unique: registering a name that exists replaces that provider.

## `ampctl provider register`

Registers a provider from a TOML file, or replaces the provider registered under that name.

```
ampctl provider register [OPTIONS] PROVIDER_NAME FILE
```

| Argument        | Type        | Default  | Description                                                                                                                       |
| --------------- | ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `PROVIDER_NAME` | string      | required | The provider's [name](/clis/ampctl/provider#names), like `mainnet_rpc`. A `name` key in the file is ignored.                                           |
| `FILE`          | path or URL | required | The provider's configuration, in TOML: a local path, or a `file://`, `s3://`, `gs://`, or `az://` URL. See [The file](/clis/ampctl/provider#the-file). |

For a file in object storage, `ampctl` reads it itself, with the credentials in its environment, like
`AWS_ACCESS_KEY_ID`, `GOOGLE_APPLICATION_CREDENTIALS`, or `AZURE_STORAGE_ACCOUNT_NAME`.

```toml [mainnet.toml]
kind = "evm-rpc"
network = "mainnet"
url = "${RPC_URL}"
```

```bash
ampctl provider register mainnet_rpc ./mainnet.toml
```

With `--json`, it prints the name: `{"name":"mainnet_rpc"}`.

Registering a name that exists replaces its configuration whole. Nothing is merged, so the file must hold every field
you want to keep. Jobs that are running keep the configuration they loaded, and jobs that start afterwards load the new
one. A file identical to the current configuration changes nothing.

### The file

A provider's file is TOML, with these keys:

* `kind`: the provider kind, like `evm-rpc`. Required.
* `network`: the network the provider serves, like `mainnet`. Raw datasets with the same kind and network use it.
* The kind's own fields, like `url` for EVM RPC. Each kind's page lists them: [EVM RPC](/providers/evm-rpc),
  [Solana](/providers/solana), [Tempo](/providers/tempo), [Bittensor](/providers/bittensor),
  [Bitcoin RPC](/providers/bitcoin-rpc), [Ethereum Beacon](/providers/eth-beacon), [Stellar](/providers/stellar),
  [Phaser](/providers/phaser), [Static](/providers/static), and, for the ADBC kinds,
  [PostgreSQL](/managing-amp/pipelines/postgresql) and [Snowflake](/managing-amp/pipelines/snowflake).

`register` reads the file as TOML whatever its extension, so a JSON file fails:

```
✗ failed to parse provider TOML
  → TOML parse error at line 1, column 1
  |
1 | {"kind": "evm-rpc", "network": "mainnet", "url": "${RPC_URL}"}
  | ^
invalid key-value pair, expected key
```

To register a provider from JSON, send it to the [Admin API](/managing-amp/admin-api), with its name in the body. It
stores the provider as `register` does:

```bash
curl -X POST http://localhost:1610/providers \
  -H "Content-Type: application/json" \
  -d '{"name": "mainnet_rpc", "kind": "evm-rpc", "network": "mainnet", "url": "${RPC_URL}"}'
```

For any kind but ADBC, `register` only checks that the file has a `kind`. It doesn't check the other fields, or the
[references](/clis/ampctl/provider#environment-variables) in them: a provider with a misspelled kind, a missing `url`, or a value of the
wrong type is registered. When a job looks for providers, it skips one it can't read, and logs a warning in `ampd`'s
log. A job with no other provider for its kind and network then fails with
`No provider found for kind 'evm-rpc' and network 'mainnet'`. Check what you registered with
[`ampctl provider inspect`](/clis/ampctl/provider#ampctl-provider-inspect).

### Environment variables

Write `${NAME}` in any string value to read it from an environment variable, like `url = "${RPC_URL}"` or
`auth_token = "${RPC_TOKEN}"`. Amp stores the reference, not the value, and replaces it with the variable's value from
`ampd`'s environment each time a job loads the provider. So your credentials never reach Amp's storage, and the
variable must be set where `ampd` runs, not where you run `ampctl`. A variable name has letters, digits, and
underscores.

If the variable isn't set where `ampd` runs, or a `${` has no closing `}`, jobs skip the provider, as they skip one they
can't read. `ampd`'s log says why:

```
WARN … environment variable substitution failed for provider, skipping provider_name=mainnet_rpc provider_kind=evm-rpc provider_network=mainnet error=Variable not found: RPC_URL …
```

Set it, and restart `ampd`.

### Errors

* `error: invalid value './mainet.toml' for '<FILE>': invalid provider file path: ./mainet.toml`: the local file
  doesn't exist. Check the path.
* `✗ failed to parse provider TOML`: the file isn't valid TOML. The lines after it show where.
* `✗ provider registration failed`, then `→ invalid request body`, then a line with ``missing field `kind` ``: the file
  has no `kind` key.
* `→ unexpected response (status 409): {"error_code":"PROVIDER_KIND_CONFLICT",…}`: an
  [ADBC provider](/managing-amp/pipelines/destinations) already has the name. Remove it first, or pick another name.
* `→ unexpected response (status 400)`, with an `error_code` starting with `ADBC_` or `INVALID_ADBC_CONFIG`: the ADBC
  provider's configuration isn't valid. The `error_message` says which fields, and
  [Destinations](/managing-amp/pipelines/destinations#errors) explains each code.

## `ampctl provider inspect`

Prints a provider's configuration, as Amp stores it.

```
ampctl provider inspect [OPTIONS] NAME
```

| Argument | Type   | Default  | Description                                        |
| -------- | ------ | -------- | -------------------------------------------------- |
| `NAME`   | string | required | The provider's [name](/clis/ampctl/provider#names), like `mainnet_rpc`. |

:::terminal
```bash
ampctl provider inspect mainnet_rpc
```

```json
{
  "name": "mainnet_rpc",
  "kind": "evm-rpc",
  "network": "mainnet",
  "url": "${RPC_URL}"
}
```
:::

References stay as they're stored, since Amp only replaces them when a job loads the provider. Nothing is hidden: a
token written as a literal value is printed. With `--json`, `inspect` prints the same object on one line, so `jq` reads
a field from either:

:::terminal
```bash
ampctl provider inspect mainnet_rpc --json | jq -r .url
```

```
${RPC_URL}
```
:::

A name that doesn't exist fails, with exit code 1.

## `ampctl provider list`

Lists every provider, sorted by name, with its kind.

```
ampctl provider list [OPTIONS]
```

It takes no arguments. `ampctl provider ls` is the same command.

:::terminal
```bash
ampctl provider list
```

```
Providers:
  base_rpc (evm-rpc)
  mainnet_rpc (evm-rpc)
```
:::

With no providers registered, it prints `No providers found`, or `{"providers":[]}` with `--json`.

With `--json`, each provider has its whole configuration, as [`inspect`](/clis/ampctl/provider#ampctl-provider-inspect) prints it, so the
output includes any credential written as a literal value. Use it to find the providers of a kind or network:

:::terminal
```bash
ampctl provider list --json | jq -r '.providers[] | select(.network == "mainnet") | .name'
```

```
mainnet_rpc
```
:::

```json
{
  "providers": [
    { "name": "base_rpc", "kind": "evm-rpc", "url": "${BASE_RPC_URL}", "network": "base" },
    { "name": "mainnet_rpc", "kind": "evm-rpc", "url": "${RPC_URL}", "network": "mainnet" }
  ]
}
```

## `ampctl provider rm`

Removes a provider.

```
ampctl provider rm [OPTIONS] NAME
```

| Argument | Type   | Default  | Description                                     |
| -------- | ------ | -------- | ----------------------------------------------- |
| `NAME`   | string | required | The provider's [name](/clis/ampctl/provider#names), like `base_rpc`. |

`ampctl provider remove` is the same command. It removes without asking first.

```bash
ampctl provider rm base_rpc
```

Removing a provider that doesn't exist succeeds too, so you can run it again safely. With `--json`, it prints the name:
`{"name":"base_rpc"}`.

`rm` deletes the provider's file, and doesn't check whether any dataset needs it. Jobs that are running keep the
provider they loaded. A job that starts afterwards and has no other provider for its kind and network fails with
`No provider found for kind 'evm-rpc' and network 'mainnet'`. To replace an endpoint, register the new provider before
you remove the old one, or [register](/clis/ampctl/provider#ampctl-provider-register) the new configuration under the same name.

An [ADBC provider](/managing-amp/pipelines/destinations#removing-a-destination) can't be removed while other providers
reference it through `using`, or while a job holds connections to it. `rm` then fails with status 409, and an
`error_code` of `PROVIDER_REFERENCED`, with the providers that reference it, or `PROVIDER_IN_USE`.
