Skip to Content
We are live but in Staging 🎉
Data EnginesSQLgRPC & HTTP APIOverview

gRPC & HTTP API

Endpoints

EndpointServes
table-rpc.uk-lon-1.dodil.io:443gRPC — the whole dodil.tables.v1 surface, TLS
https://table.uk-lon-1.dodil.io/v1/databases/{db}/sql/executeHTTP — one SQL statement, JSON in and out
https://table.uk-lon-1.dodil.io/v1/databases/{db}/sql/queryHTTP — read-only twin of execute
POST /v1/sql/execute + header x-db-id: {db}Legacy HTTP form; still served, prefer the path form

{db} is the bare bucket name. The gateway qualifies it to the tenant-scoped storage id from your credential; never build an org-prefixed name yourself.

Credentials ride as gRPC metadata or an HTTP header:

CredentialgRPC metadataHTTP header
API keyapi-key: dk_<id>:<secret>Authorization: Basic base64(dk_<id>:<secret>)
Service accountapi-key: <client_id>:<client_secret>Authorization: Basic base64(client_id:client_secret)
Bearer JWTauthorization: Bearer <jwt>Authorization: Bearer <jwt>

The RPC surface

GroupRPCsPage
SQLExecuteExecute
ReadsQuery · QueryStream · GetRow · BatchGetRows · InferParamsReads
WritesUpsert · Delete · WriteStream · CommitWrites
TablesCreateTable · AlterTable · DropTableTables & maintenance
MaintenanceOptimizeTable · VacuumTable · CompactTables & maintenance
Database catalogCreateDatabase · DropDatabase · ListDatabases · DescribeDatabase · GetDatabaseStats · UpdateDatabaseS3CredentialsTables & maintenance
Control-plane authoredSetReservation · DeleteReservation · ReleaseDatabase · AdoptDatabaseNot tenant-callable — see Capacity

Sibling services in the same package: TablesResidency (Load / Release / GetLoadState), TablesObserve (NodeStatus / Capacity / Health), TablesVector and TablesGraph — see Vector and Graph.

There is no Insert, Merge, Update, DeleteRows, ListTables, GetTable, DescribeTable, History or Restore RPC. Those are SQL statements through Execute; the dodil data table commands with those names build the statement for you. Templates for pipeline-bound tables are a control-plane call — Templates.

Wire conventions

gRPCHTTP
Addressdb_id request field{db} path segment
Requestproto field names{ "sql": "…" }
RowsRowSet — TypedRows (default) or an Arrow IPC streamJSON objects keyed by column name
int64native int64_valueJSON number; pass values beyond ±2⁵³−1 as strings
NUMERIC / TIMESTAMP / DATE / UUIDcanonical strings inside TypedRowsstrings

TypedRows column type codes follow the storage vocabulary: INT16, INT32 and INT64 for short, int and long, UUID for uuid, NUMERIC, TIMESTAMP, DATE, JSON, BYTES, BOOL, FLOAT32, FLOAT64, STRING, plus array, struct and vector. The full list with pg wire OIDs is on Data types.

Arrow input is validated before use: size caps are enforced first, the decoded schema must match the table’s declared schema, and dictionary-encoded or compressed IPC is rejected.

ExecuteResponse is a oneof: rows for SELECT / SHOW / DESCRIBE / EXPLAIN, rows_affected for DML, ddl for DDL — plus statement_kind, served_by, warnings and max_wal_ulid. Over HTTP that flattens to {"kind":"query"|"write"|"ddl", …}.

Read-your-writes across calls

On the Postgres wire the adapter carries this for you. Over gRPC and HTTP every request is independent, so the watermark is the session:

  1. Every write returns a ULID — WriteAck.wal_ulid, WriteStreamAck.max_ulid, CommitResponse.max_wal_ulid or ExecuteResponse.max_wal_ulid.
  2. Pass it as min_ulid on the next Query, GetRow or Execute.
  3. That read observes at least that write, or fails rather than answering from a stale view.

You rarely need it within one process: reads default to the strongest consistency the plane can prove, and a read issued after a write’s acknowledgement sees it. The watermark matters when a different process must see a write it did not make. The HTTP door takes only {"sql": …} — no min_ulid, no parameters; use gRPC or the Postgres wire for those.

Next: Execute for the SQL entry point, or Reads and Writes for the typed RPCs.