# HTTP JSON API

> Status: Available. Run a statement, an atomic batch or an interactive session over HTTPS with a bearer token.

| | |
|---|---|
| **Works** | POST /db/<name>/sql, POST /db/<name>/tx for an atomic list of statements, interactive sessions, GET /db/<name>/txn/<txid> for outcome lookup, GET /db/<name>/ready. |
| **Differs** | This is Gauntlet's own JSON shape. It is not the libSQL (Hrana) protocol. |
| **Missing** | A published OpenAPI schema, a libSQL-compatible endpoint, CORS for browsers. |
| **Operational impact** | /sql, /tx and sessions activate the database. /ready does not. HTTP requests are not counted against the PostgreSQL connection limit. |
| **Evidence** | Covered by the project's gateway tests. The request and response shapes may still change in pre-beta. |

All routes are under `/db/<name>/` on any node's HTTP listener (port 7101 in the examples). Send the [database token](https://gauntlet-db.stavros.xyz/docs/concepts/databases.md) as a bearer token over TLS.

## One statement

```sh
curl --cacert ca/ca.crt \
  -H "Authorization: Bearer $GAUNTLET_DATABASE_TOKEN" \
  -H 'Content-Type: application/json' \
  https://n3.example.net:7101/db/app/sql \
  -d '{"sql": "SELECT name FROM items WHERE id = $1", "params": [1]}'
```

The body is `{"sql": "…", "params": […]}`; `params` is optional. A result has `columns`, `rows` (an array of arrays) and `rows_affected`.

## An atomic batch

`/tx` runs a list of statements as one transaction. Either all commit or none do.

```sh
curl --cacert ca/ca.crt \
  -H "Authorization: Bearer $GAUNTLET_DATABASE_TOKEN" \
  -H 'Content-Type: application/json' \
  https://n3.example.net:7101/db/app/tx \
  -d '{"statements": [
    {"sql": "UPDATE accounts SET balance = balance - 5 WHERE id = $1", "params": [1]},
    {"sql": "UPDATE accounts SET balance = balance + 5 WHERE id = $1", "params": [2]}
  ]}'
```

The response has one entry in `results` per statement.

## Sessions

`POST /db/<name>/session` opens an interactive session and returns its ID; pass `"session": <id>` in later `/sql` bodies to run `BEGIN … COMMIT` across requests. `DELETE /db/<name>/session/<id>` closes it. Prefer `/tx` when the statements are known up front: it has no session to leak.

## Errors

An error is a JSON body with an `error` object that carries the same SQLSTATE code as PostgreSQL wire.

| SQLSTATE | HTTP status | Do |
|---|---|---|
| `40001` | 409 | Retry the whole transaction |
| `57P03` | 503 | Retry with backoff |
| `40003` | 504 | [Look the outcome up](https://gauntlet-db.stavros.xyz/docs/concepts/transactions.md#40003-the-commit-outcome-is-unknown). Do not retry |
| `XX…` | 500 | Report it |
| Any other | 400 | Fix the request |

## Outcome lookup

```sh
curl --cacert ca/ca.crt -H "Authorization: Bearer $GAUNTLET_DATABASE_TOKEN" \
  https://n1.example.net:7101/db/app/txn/<txid>
```

Returns `{"txid": "…", "status": "committed" | "aborted" | "unknown" | "quarantined"}`, or HTTP 503 with `lookup_failed`.

## Readiness without waking

`GET /db/<name>/ready` with the database token reports that database's readiness **without activating it**. Use it for health checks; never use `SELECT 1` for a fleet-wide probe.

## Values

Integers and floats are JSON numbers, text is a string, a BLOB is `{"$blob": "<hex>"}`, and infinite floats are `{"$float": "Infinity"}` or `{"$float": "-Infinity"}`.

## The CLI

```sh
GAUNTLET_CLIENT_TOKEN=$TOKEN gauntlet sql --url https://n3.example.net:7101/db/app 'SELECT * FROM items'
```

---
Source: https://gauntlet-db.stavros.xyz/docs/clients/http
