> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI

> Manage Telnyx SQL databases from the terminal with the telnyx-edge CLI — create, list, get, and delete databases, run SQL with execute --remote, apply migrations, and generate binding types.

Manage SQL databases using the `telnyx-edge` CLI. Every command in this family reaches a
database through the same primary as the `env` binding, so the CLI is not a second writer —
SQL run from a terminal and SQL run from a deployed function are serialized together.

<Note>
  The `storage sqldb` family requires **CLI v0.3.0 or newer** — the first release to carry any SQL
  surface. On v0.2.5 and earlier `telnyx-edge storage sqldb` fails as an unknown command. Check
  with `telnyx-edge --version`.
</Note>

## Database Management

```bash theme={null}
# List databases
telnyx-edge storage sqldb list

# Create a database (name: lowercase letters, numbers, hyphens)
telnyx-edge storage sqldb create --name links-db

# Get one database by id
telnyx-edge storage sqldb get <database-id>

# Delete a database by id — no confirmation prompt
telnyx-edge storage sqldb delete <database-id>
```

`create` returns immediately with `status: pending`; provisioning finishes in the background,
typically in 2 to 11 seconds:

```
✓ SQL database 'links-db' created — provisioning runs in the background
FIELD          VALUE
------------   ------------------------------
SQLDB ID       550e8400-e29b-41d4-a716-446655440000
Name           links-db
Status         pending
Created At     Jul 29, 2026, 14:52

Use 'telnyx-edge storage sqldb get 550e8400-e29b-41d4-a716-446655440000' to check when it is ready.
```

Poll `get` until the status is `provision_ok`:

```
FIELD          VALUE
------------   ------------------------------
SQLDB ID       550e8400-e29b-41d4-a716-446655440000
Name           links-db
Status         provision_ok
Created At     Jul 29, 2026, 14:52
Updated At     Jul 29, 2026, 14:52
```

`list` paginates, newest first:

```
SQLDB ID                               NAME                   STATUS            CREATED AT
------------------------------------   --------------------   ---------------   -----------------
7c9e6679-7425-40de-944b-e07fc1f90ae7   analytics              provision_ok      Jul 29, 2026, 15:11
550e8400-e29b-41d4-a716-446655440000   links-db               provision_ok      Jul 29, 2026, 14:52

Page 1 of 1 (showing 2 of 2 total SQL databases)
```

`delete` takes effect immediately and asks nothing first — there is no confirmation prompt
and no `--yes` flag:

```
✓ SQL database '550e8400-e29b-41d4-a716-446655440000' deletion started
```

The record disappears shortly afterwards: `get` and any query against that id return `404`.
Delete removes the database record and makes the data unreachable through every path; it is
not a guaranteed erase of the stored bytes, so it does not satisfy a data-destruction
requirement on its own. It also does not touch functions that still declare the id in a
`[storage.sqldb.<NAME>]` block: those functions stay deployed and their queries start failing.
Remove the block and redeploy them.

### `list` Flags

| Flag          | Description                              |
| ------------- | ---------------------------------------- |
| `--page`      | Page number, 1-based (default `1`)       |
| `--page-size` | Items per page, `1`–`250` (default `20`) |

<Note>
  `get` and `delete` take the database **id** only. `execute` and the `migrations` subcommands
  accept either the id or the database name — see [Addressing a Database](#addressing-a-database).
</Note>

## Running SQL

`execute` runs SQL against a database without deploying anything — to create a schema, load
seed data, or inspect what a function wrote.

```bash theme={null}
# One statement, inline
telnyx-edge storage sqldb execute links-db --remote \
  --command "CREATE TABLE links (id INTEGER PRIMARY KEY, slug TEXT UNIQUE, url TEXT NOT NULL)"

# A whole file — submitted as one multi-statement script
telnyx-edge storage sqldb execute links-db --remote --file ./schema.sql

# Read rows back
telnyx-edge storage sqldb execute links-db --remote \
  --command "SELECT id, slug, url FROM links ORDER BY id"
```

A statement that returns no rows prints a confirmation:

```
✓ Statement executed — no rows returned
```

A read prints a table and a row count:

```
id    slug   url
---   ----   ---
1     docs   https://developers.telnyx.com
2     home   https://telnyx.com

2 row(s)
```

Columns are ordered alphabetically rather than by their order in the `SELECT` list, and a SQL
`NULL` renders as `NULL`. To learn a new row's id, or
to count the rows a write touched, add `RETURNING` to the statement and count the rows it
prints. `SELECT changes()` in a separate call reads a counter on a connection shared with every
other caller of the database, so another write can land in between.

`--json` prints the raw result object instead of a table — on **stderr** in the current CLI, so
scripts should capture it with `2>&1 >/dev/null` rather than piping stdout. `results` holds the
rows and is the only field the service returns — a statement that produces no rows prints `{}`:

```bash theme={null}
telnyx-edge storage sqldb execute links-db --remote \
  --command "SELECT slug FROM links ORDER BY id" --json
```

```json theme={null}
{
  "results": [
    {
      "slug": "docs"
    },
    {
      "slug": "home"
    }
  ]
}
```

`BLOB` columns cross this boundary as a JSON array of byte values — a three-byte blob comes back
as `[9, 8, 7]`, not base64 and not an object. Decode it as bytes on the client. Writing binary
this way means a SQL hex literal (`X'090807'`), since this path has no parameter binding; from a
function, bind an `ArrayBuffer` instead.

### `execute` Flags

| Flag              | Description                                                                             |
| ----------------- | --------------------------------------------------------------------------------------- |
| `--command`, `-c` | SQL to run inline. Mutually exclusive with `--file`; exactly one of the two is required |
| `--file`, `-f`    | Path to a `.sql` file, submitted verbatim as a single multi-statement script            |
| `--remote`        | **Required.** Runs against the remote database — local execution is not supported       |
| `--json`          | Print the raw result object instead of a table                                          |

<Note>
  `--remote` is mandatory. Omitting it fails with `--remote is required` before any network
  call: there is no local SQLite emulation to run against, and no `--local` flag.
</Note>

The SQL text is submitted verbatim — the CLI never splits statements itself. A script may hold
several `;`-separated statements; all of them run in order, and the rows returned are the
**last** statement's rows. The whole script must fit under the \~4 MiB script ceiling
(see [Limits](/docs/edge-compute/sqldb/limits)).

A database that has not finished provisioning rejects `execute` with
`409` — `Database is not ready (status: pending)`. Wait for `provision_ok`.

## Migrations

Versioned schema changes live in numbered `.sql` files and are tracked in a
`sqldb_migrations` table inside the database itself.

```bash theme={null}
# Create a numbered migration file (offline — no auth, no network)
telnyx-edge storage sqldb migrations create links-db create_links

# Show which files are applied and which are pending
telnyx-edge storage sqldb migrations list links-db --remote

# Apply everything outstanding, in order
telnyx-edge storage sqldb migrations apply links-db --remote
```

```
✓ Applied 0001_create_links.sql
✓ Applied 0002_add_click_count.sql

2 migration(s) applied
```

### `migrations` Flags

| Flag               | Description                                                                                                                                                                                           |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--migrations-dir` | Directory holding the migration files (default `migrations/<database>`, with every run of non-alphanumeric characters in the database argument collapsed to `_` and leading or trailing runs dropped) |
| `--remote`         | **Required** on `list` and `apply`; `create` is offline and does not accept it                                                                                                                        |
| `--json`           | Machine-readable output — `apply` emits `{"applied": [...]}`; `list` accepts it too                                                                                                                   |

The full workflow — file naming, the ledger table, and what happens when a migration fails
midway — is covered in [Migrations](/docs/edge-compute/sqldb/migrations).

## Generating Types

`telnyx-edge types` reads the manifest and writes `telnyx-env.d.ts`, so `env.<BINDING>` is
typed at compile time. It runs fully offline — no authentication, no network.

```toml theme={null}
[storage.sqldb.DB]
id = "550e8400-e29b-41d4-a716-446655440000"
```

```bash theme={null}
telnyx-edge types
```

```
✓ Generated binding types for 1 binding(s) at telnyx-env.d.ts
    env.DB → SqlDatabase
```

Two things are checked before anything is written: the `id` must be a well-formed UUID
(inline creation and binding by name are rejected), and `@telnyx/edge-runtime` in
`package.json` must be **0.9.0 or newer** — the first version that exports `SqlDatabase`.
An older pin fails with
`@telnyx/edge-runtime <declared version> does not export SqlDatabase (requires >= 0.9.0)`,
quoting the range exactly as `package.json` declares it.

## Addressing a Database

`execute`, `migrations list`, and `migrations apply` take a `<database>` argument that
resolves either way:

* An exact **id** match always wins.
* A **name** resolves when exactly one database in the organization carries it.
* A name shared by several databases errors with `Multiple SQL databases are named "..."` —
  pass the id instead.
* No match errors with `No SQL database found matching "..."`.

Names are unique within an organization, so an ambiguous match should not occur in practice —
the CLI guards against it anyway. `get` and `delete` do not resolve names; they take the id.

## Related

* [Quick Start](/docs/edge-compute/sqldb/quick-start) — create a database, bind it, query it
* [Migrations](/docs/edge-compute/sqldb/migrations) — versioned schema changes and the `sqldb_migrations` ledger
* [Limits](/docs/edge-compute/sqldb/limits) — request body size, query duration, and what is not enforced
* [`SqlDatabase`](/docs/edge-compute/sqldb/reference/sql-database) — the `env` binding surface
* [CLI Reference](/docs/edge-compute/reference/cli) — every `telnyx-edge` command
