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

# API reference

> BlockDB gRPC services and methods, expressed as protobuf, JSON payloads, and grpcurl invocations.

# API reference

BlockDB exposes a set of gRPC services. All services are pure gRPC (no REST gateway), and **gRPC server reflection is enabled**, so you can inspect and call every method with `grpcurl` without preparing proto files yourself. The default service address is `localhost:19002`.

## JSON mapping conventions

Before writing requests, it helps to know how proto3 messages map to JSON:

* **`Value` is a `oneof`**: in JSON only the set field appears, using its camelCase name — `"stringValue"`, `"intValue"`, `"doubleValue"`, `"boolValue"`. NULL is represented by omitting `kind` (an empty `{}`).

  ```json theme={null}
  { "stringValue": "0xabc..." }   // STRING / HASH / ADDRESS / TXID / ...
  { "intValue": "18000000" }      // BIGINT / BLOCKHEIGHT
  { "doubleValue": 3.14 }          // DOUBLE
  { "boolValue": true }            // BOOLEAN
  ```

* **`int64` fields are encoded as JSON strings** (per proto3). For example `height`, `limit`, and `affected_rows` appear as strings.

* **Field names**: proto3 JSON *emits* lowerCamelCase (`tableId`, not `table_id`), but *accepts* both camelCase and the original snake\_case on input.

* **Read responses** use a **schema + positional array** shape: `ResultSet { columns: ColumnMeta[], rows: Row[] }`, where each `Row.values` is a positional `Value[]` aligned with `columns`.

* **Write requests** use a **self-describing map**: `WriteRow.fields` is a `map<string, Value>`.

```
ResultSet: {
  "columns": [ { "name": "id", "logicalType": "ADDRESS" }, ... ],
  "rows":    [ { "values": [ { "stringValue": "0x..." }, ... ] } ]
}
```

The canonical definitions live in `proto/blockdb/v1/` (`common.proto`, `table.proto`, `block.proto`, `time.proto`, `batch.proto`).

## Regular table services

### `TableReadService`

| Method | Description |
| - | - |
| `GetRow` | Read a single row by primary key |
| `BatchGetRows` | Read multiple primary keys |
| `FilterRows` | Filter rows by condition, with sorting and pagination |

#### GetRow

Read a single row.

**Proto**

```protobuf theme={null}
message GetRowRequest {
  string table_id = 1;
  Value  row_id   = 2;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "demo.event_log",
  "rowId":   { "stringValue": "evt_001" }
}' localhost:19002 blockdb.v1.TableReadService/GetRow
```

**Response**

```json theme={null}
{
  "result": {
    "columns": [ { "name": "id", "logicalType": "STRING" }, { "name": "value", "logicalType": "INT" } ],
    "rows": [ { "values": [ { "stringValue": "evt_001" }, { "intValue": "1" } ] } ]
  }
}
```

#### BatchGetRows

Read several primary keys in one call.

**Proto**

```protobuf theme={null}
message BatchGetRowsRequest {
  string         table_id = 1;
  repeated Value row_ids  = 2;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "demo.event_log",
  "rowIds":  [ { "stringValue": "evt_001" }, { "stringValue": "evt_002" } ]
}' localhost:19002 blockdb.v1.TableReadService/BatchGetRows
```

#### FilterRows

Filter by a condition string, with sorting and pagination.

**Proto**

```protobuf theme={null}
message FilterRowsRequest {
  string table_id = 1;
  string filters  = 2;
  string order_by = 3;
  int64  limit    = 4;
  int64  offset   = 5;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "demo.event_log",
  "filters": "value > 0",
  "orderBy": "value desc",
  "limit":   "100"
}' localhost:19002 blockdb.v1.TableReadService/FilterRows
```

### `TableWriteService`

| Method | Description |
| - | - |
| `UpsertRows` | Insert or update in batch; supports conditional and partial updates |
| `DeleteRows` | Delete in batch |

#### UpsertRows

The request uses self-describing `WriteRow` maps, so only the columns present are updated (partial update). The optional `condition` supports `UPDATE_IF_SMALLER` / `UPDATE_IF_LARGER`.

**Proto**

```protobuf theme={null}
message UpsertRowsRequest {
  string                    table_id  = 1;
  repeated WriteRow         rows      = 2;
  optional UpsertCondition  condition = 3;
  bool                      sync      = 4;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "demo.event_log",
  "rows": [
    {
      "fields": {
        "id":    { "stringValue": "evt_001" },
        "name":  { "stringValue": "login" },
        "value": { "intValue": "1" }
      }
    }
  ]
}' localhost:19002 blockdb.v1.TableWriteService/UpsertRows
```

**Response**

```json theme={null}
{ "result": { "affectedRows": "1", "jobId": "..." } }
```

#### DeleteRows

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "demo.event_log",
  "rowIds": [ { "stringValue": "evt_001" } ]
}' localhost:19002 blockdb.v1.TableWriteService/DeleteRows
```

### `TableScanService`

#### Scan (server-streaming)

**Proto**

```protobuf theme={null}
message ScanRequest { string sql = 1; }
message ScanResponse { ResultSet result = 1; }
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{"sql": "SELECT * FROM demo.event_log WHERE value > 0"}' \
  localhost:19002 blockdb.v1.TableScanService/Scan
```

## Block table services

### `BlockReadService`

| Method | Description |
| - | - |
| `GetEvent` | Read an event anchored at a block |
| `GetState` | Read a state snapshot anchored at a block |
| `GetConsensusHeight` | Get the table's consensus height |
| `GetBlockRows` | Get all rows at a block height (`L2BlockReadService`) |

#### GetState

Read a state snapshot at a block. `current_block` injects `height ≤ target height`; if omitted it reads the latest.

**Proto**

```protobuf theme={null}
message Block {
  string block_id = 1;
  int64  height   = 2;
  string timestamp = 3;
}

message GetStateRequest {
  string table_id      = 1;
  Value  state_id      = 2;
  Block  current_block = 3;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "token.token.eth",
  "stateId": { "stringValue": "acct_1" },
  "currentBlock": {
    "blockId": "0x0c76...",
    "height":  "18000000",
    "timestamp": "2024-06-08T00:00:00Z"
  }
}' localhost:19002 blockdb.v1.BlockReadService/GetState
```

### `BlockWriteService`

| Method | Description |
| - | - |
| `UpsertBlock` | Atomic write at block granularity |

#### UpsertBlock

**Proto**

```protobuf theme={null}
message UpsertBlockRequest {
  string           table_id = 1;
  Block            block    = 2;
  repeated WriteRow rows     = 3;
  optional bool    sync     = 5;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "token.token.eth",
  "block": {
    "blockId": "0x0c76...",
    "height":  "18000000",
    "timestamp": "2024-06-08T00:00:00Z"
  },
  "rows": [
    { "fields": { "id": { "stringValue": "acct_1" }, "balance": { "intValue": "1000" } } }
  ]
}' localhost:19002 blockdb.v1.BlockWriteService/UpsertBlock
```

### `BlockSubscribeService`

#### Subscribe (server-streaming)

**Proto**

```protobuf theme={null}
message BlockSubscribeRequest {
  repeated string table_ids      = 1;
  optional int64  start_timestamp = 2;
}
message BlockSubscribeEvent {
  string table_id  = 1;
  Block  block     = 2;
  int64  created_at = 3;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{"tableIds": ["token.token.eth"]}' \
  localhost:19002 blockdb.v1.BlockSubscribeService/Subscribe
```

## Time table services

### `TimeReadService` — `GetValue`

**Proto**

```protobuf theme={null}
message GetValueRequest {
  string table_id = 1;
  string row_id   = 2;
  string time_at  = 3;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "token_time_demo",
  "rowId":   "0xabc...",
  "timeAt":  "2026-05-18 17:04:00"
}' localhost:19002 blockdb.v1.TimeReadService/GetValue
```

### `TimeWriteService` — `UpsertTimeRows`

**Proto**

```protobuf theme={null}
message UpsertTimeRowsRequest {
  string           table_id = 1;
  string           time_at  = 2;
  repeated WriteRow rows     = 3;
  optional bool    sync     = 5;
}
```

**Call**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "token_time_demo",
  "timeAt":  "2026-05-18 17:04:00",
  "rows": [ { "fields": { "id": { "stringValue": "0xabc..." }, "value": { "doubleValue": 2543.12 } } } ]
}' localhost:19002 blockdb.v1.TimeWriteService/UpsertTimeRows
```

The time bucket alignment (rounding `17:04:35` up to the `17:05:00` bucket) is done on the SDK side, so a raw gRPC call receives whatever `time_at` you pass.

## Batch write service — `BatchWriteService`

| Method | Description |
| - | - |
| `InitWriteJob` | Initialize a write job; returns an S3 presigned upload URL and job ID |
| `CommitWriteJob` | Commit the job, triggering background processing |
| `CancelWriteJob` | Cancel a queued job |

**InitWriteJob**

```bash theme={null}
grpcurl -plaintext -d '{
  "tableId": "demo.event_log",
  "normalUpsert": { }
}' localhost:19002 blockdb.v1.BatchWriteService/InitWriteJob
```

## Putting it together

A minimal "read a row" call end to end:

```bash theme={null}
# List services (reflection is enabled)
grpcurl -plaintext localhost:19002 list

# List methods of TableReadService
grpcurl -plaintext localhost:19002 list blockdb.v1.TableReadService

# Read a row
grpcurl -plaintext -d '{"tableId":"demo.event_log","rowId":{"stringValue":"evt_001"}}' \
  localhost:19002 blockdb.v1.TableReadService/GetRow
```

## Related topics

* [Logical types](/reference/value-types)
* [Error codes](/reference/errors)
* [Write architecture](/architecture/batch-write)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.