Skip to content

API Reference ​

Kassie exposes both gRPC and REST APIs for programmatic access. The gRPC services are automatically exposed as REST endpoints via grpc-gateway.

Base URLs ​

ProtocolDefault AddressDescription
gRPClocalhost:50051Binary protocol for high-performance clients
HTTP/RESThttp://localhost:8080JSON-based REST API (grpc-gateway)

Authentication ​

All API endpoints (except /api/v1/profiles and /api/v1/session/login) require authentication via JWT tokens.

Obtaining a Token ​

bash
# Login to get access token
curl -X POST http://localhost:8080/api/v1/session/login \
  -H "Content-Type: application/json" \
  -d '{"profile": "local"}'

Response:

json
{
  "access_token": "eyJhbGc...token",
  "refresh_token": "eyJhbGc...refresh",
  "expires_at": 1707500000,
  "profile": {
    "name": "local",
    "hosts": ["127.0.0.1"],
    "port": 9042,
    "keyspace": "system",
    "ssl_enabled": false
  }
}

Using the Token ​

Include the access token in the Authorization header:

bash
curl -H "Authorization: Bearer eyJhbGc...token" \
  http://localhost:8080/api/v1/schema/keyspaces

SessionService ​

Handles authentication, profile management, and session lifecycle.

Login ​

POST /api/v1/session/login

Authenticate with a configured profile and obtain access/refresh tokens.

Request:

json
{
  "profile": "local"
}

Response:

json
{
  "access_token": "string",
  "refresh_token": "string",
  "expires_at": 1707500000,
  "profile": {
    "name": "local",
    "hosts": ["127.0.0.1"],
    "port": 9042,
    "keyspace": "system",
    "ssl_enabled": false
  }
}

Status Codes:

  • 200: Success
  • 400: Invalid profile name
  • 401: Authentication failed
  • 500: Server error

Refresh Token ​

POST /api/v1/session/refresh

Refresh an expired access token using the refresh token.

Request:

json
{
  "refresh_token": "eyJhbGc...refresh"
}

Response:

json
{
  "access_token": "string",
  "expires_at": 1707500000
}

Status Codes:

  • 200: Success
  • 401: Invalid or expired refresh token
  • 500: Server error

Logout ​

POST /api/v1/session/logout

Invalidate the current session and tokens.

Request:

json
{}

Response:

json
{}

Requires: Authorization header

Status Codes:

  • 200: Success
  • 401: Unauthorized

Get Profiles ​

GET /api/v1/profiles

List all configured connection profiles.

Request: None

Response:

json
{
  "profiles": [
    {
      "name": "local",
      "hosts": ["127.0.0.1"],
      "port": 9042,
      "keyspace": "system",
      "ssl_enabled": false
    },
    {
      "name": "production",
      "hosts": ["prod-1.example.com", "prod-2.example.com"],
      "port": 9042,
      "keyspace": "app_data",
      "ssl_enabled": true
    }
  ]
}

Status Codes:

  • 200: Success
  • 500: Server error

Note: This endpoint does not require authentication.


SchemaService ​

Provides schema introspection for keyspaces, tables, and columns.

List Keyspaces ​

GET /api/v1/schema/keyspaces

Retrieve all keyspaces in the connected cluster.

Request: None

Response:

json
{
  "keyspaces": [
    {
      "name": "system",
      "replication_strategy": "org.apache.cassandra.locator.LocalStrategy",
      "replication": {}
    },
    {
      "name": "app_data",
      "replication_strategy": "org.apache.cassandra.locator.NetworkTopologyStrategy",
      "replication": {
        "dc1": "3",
        "dc2": "3"
      }
    }
  ]
}

Requires: Authorization header

Status Codes:

  • 200: Success
  • 401: Unauthorized
  • 500: Server error

List Tables ​

GET /api/v1/schema/keyspaces/{keyspace}/tables

Retrieve all tables in a specific keyspace.

Path Parameters:

  • keyspace (string, required): Keyspace name

Request: None

Response:

json
{
  "tables": [
    {
      "name": "users",
      "keyspace": "app_data",
      "estimated_rows": 1000000
    },
    {
      "name": "orders",
      "keyspace": "app_data",
      "estimated_rows": 5000000
    }
  ]
}

Requires: Authorization header

Status Codes:

  • 200: Success
  • 401: Unauthorized
  • 404: Keyspace not found
  • 500: Server error

Get Table Schema ​

GET /api/v1/schema/keyspaces/{keyspace}/tables/{table}

Retrieve detailed schema information for a specific table.

Path Parameters:

  • keyspace (string, required): Keyspace name
  • table (string, required): Table name

Request: None

Response:

json
{
  "schema": {
    "keyspace": "app_data",
    "table": "users",
    "columns": [
      {
        "name": "id",
        "type": "uuid",
        "is_partition_key": true,
        "is_clustering_key": false,
        "position": 0
      },
      {
        "name": "email",
        "type": "text",
        "is_partition_key": false,
        "is_clustering_key": false,
        "position": 1
      },
      {
        "name": "created_at",
        "type": "timestamp",
        "is_partition_key": false,
        "is_clustering_key": true,
        "position": 0
      }
    ],
    "partition_keys": ["id"],
    "clustering_keys": ["created_at"]
  }
}

Requires: Authorization header

Status Codes:

  • 200: Success
  • 401: Unauthorized
  • 404: Table not found
  • 500: Server error

DataService ​

Provides data access and pagination for table rows.

Query Rows ​

POST /api/v1/data/query

Query rows from a table with pagination.

Request:

json
{
  "keyspace": "app_data",
  "table": "users",
  "page_size": 100
}

Response:

json
{
  "rows": [
    {
      "cells": {
        "id": {
          "string_val": "550e8400-e29b-41d4-a716-446655440000",
          "is_null": false
        },
        "email": {
          "string_val": "user@example.com",
          "is_null": false
        },
        "created_at": {
          "string_val": "2024-01-15T10:30:00Z",
          "is_null": false
        }
      }
    }
  ],
  "cursor_id": "cursor_abc123",
  "has_more": true,
  "total_fetched": 100
}

Requires: Authorization header

Status Codes:

  • 200: Success
  • 400: Invalid request (missing keyspace/table)
  • 401: Unauthorized
  • 404: Table not found
  • 500: Server error

Get Next Page ​

POST /api/v1/data/next

Fetch the next page of results using a cursor.

Request:

json
{
  "cursor_id": "cursor_abc123"
}

Response:

json
{
  "rows": [
    {
      "cells": {
        "id": {
          "string_val": "650e8400-e29b-41d4-a716-446655440000",
          "is_null": false
        },
        "email": {
          "string_val": "user2@example.com",
          "is_null": false
        }
      }
    }
  ],
  "cursor_id": "cursor_def456",
  "has_more": true
}

Requires: Authorization header

Status Codes:

  • 200: Success
  • 400: Invalid cursor ID
  • 401: Unauthorized
  • 404: Cursor not found or expired
  • 500: Server error

Note: Cursors expire after 30 minutes of inactivity.


Filter Rows ​

POST /api/v1/data/filter

Query rows with a CQL WHERE clause filter.

Request:

json
{
  "keyspace": "app_data",
  "table": "users",
  "where_clause": "email = 'user@example.com'",
  "page_size": 50
}

Response:

json
{
  "rows": [
    {
      "cells": {
        "id": {
          "string_val": "550e8400-e29b-41d4-a716-446655440000",
          "is_null": false
        },
        "email": {
          "string_val": "user@example.com",
          "is_null": false
        }
      }
    }
  ],
  "cursor_id": "cursor_filter_xyz789",
  "has_more": false
}

Requires: Authorization header

Status Codes:

  • 200: Success
  • 400: Invalid WHERE clause syntax
  • 401: Unauthorized
  • 404: Table not found
  • 500: Server error

Note: WHERE clause must be valid CQL syntax without the WHERE keyword.

Execute Query (CQL) ​

Run an ad-hoc read-only SELECT statement.

POST /api/v1/data/cql

json
{
  "cql": "SELECT id, name FROM app_data.users LIMIT 100",
  "page_size": 100
}

Response:

json
{
  "rows": [{ "cells": { "id": { "intVal": 1, "isNull": false } } }],
  "cursor_id": "cursor_abc123",
  "has_more": true,
  "total_fetched": 100
}

Requires: Authorization header

Constraints:

  • Only SELECT statements are allowed
  • Multiple statements are rejected
  • page_size is capped at 1000
  • Results page through the standard cursor_id (see Get Next Page)

Status Codes:

  • 200: Success
  • 400: Invalid query (non-SELECT, multiple statements, too long)
  • 401: Unauthorized
  • 500: Query execution error

Export Rows (Streaming) ​

Stream a table (optionally filtered) as CSV or newline-delimited JSON chunks.

POST /api/v1/data/export

json
{
  "keyspace": "app_data",
  "table": "users",
  "where_clause": "region = 'eu'",
  "format": "EXPORT_FORMAT_CSV",
  "fetch_size": 1000
}

Response: chunked stream; each line is an ExportChunk:

json
{ "data": "<base64>", "rows_exported": 1000, "done": false }

Concatenate data (after base64-decoding) across chunks in order until the chunk with done: true. The server streams ~512KB chunks and pages through Cassandra internally, so exports work for arbitrarily large tables.

Requires: Authorization header

Status Codes:

  • 200: Success (streaming)
  • 400: Invalid keyspace/table/WHERE clause
  • 401: Unauthorized

Get Table Stats ​

Row count and partition size statistics for a table.

POST /api/v1/schema/stats

json
{
  "keyspace": "app_data",
  "table": "users"
}

Response:

json
{
  "stats": {
    "row_count": 125000,
    "mean_partition_size_bytes": 2048,
    "max_partition_size_bytes": 18432,
    "estimate_available": true
  }
}

Prefers system.size_estimates; falls back to exact COUNT(*) when estimates are not materialized (check estimate_available).

Query History ​

List recently executed ad-hoc queries for the authenticated profile.

GET /api/v1/history/queries?limit=50

json
{
  "entries": [{ "cql": "SELECT id FROM app_data.users", "executed_at": 1756170000 }]
}

Saved Queries ​

List, create, and delete named queries. Saved queries are per-profile and persisted server-side.

GET /api/v1/history/saved → { "queries": [{ "name": "eu-users", "cql": "...", "created_at": 1756170000 }] }

POST /api/v1/history/saved (body: { "name": "eu-users", "cql": "SELECT ..." })

DELETE /api/v1/history/saved/{name}

Metrics ​

Active session and pagination-cursor counts.

GET /api/v1/metrics

json
{
  "metrics": { "active_sessions": 3, "active_cursors": 7, "uptime_seconds": 0 }
}

Requires: Authorization header


AIService ​

Natural language to CQL translation using locally installed agent CLIs. Generated CQL is SELECT-validated and dry-run before being returned; it is never auto-executed.

NL to CQL ​

Translate a plain-English question into validated CQL.

POST /api/v1/ai/nl-to-cql

json
{
  "question": "critical events for device 123e yesterday",
  "keyspace": "app_data",
  "table": "events"
}

Response:

json
{
  "cql": "SELECT * FROM app_data.events WHERE device_id = ... AND day = '2026-09-06' AND severity = 'critical'",
  "validated": true,
  "explanation": "...",
  "provider": "opencode"
}

Requires: Authorization header, an authenticated agent CLI on the server host (opencode, claude, codex, or gemini), and a reachable Cassandra connection for the dry-run.

Accept Example ​

Save an accepted question/CQL pair as a few-shot example (max 50 per profile).

POST /api/v1/ai/examples (body: { "question": "...", "cql": "SELECT ...", "keyspace": "app_data" })

List Examples ​

GET /api/v1/ai/examples?keyspace=app_data → { "examples": [{ "id": "nl-...", "question": "...", "cql": "...", "keyspace": "app_data" }] }

Delete Example ​

DELETE /api/v1/ai/examples/{id}

Detect Providers ​

List agent CLIs found on the server host and the configured provider.

GET /api/v1/ai/providers → { "available": ["opencode", "claude"], "configured": "auto" }


Common Data Types ​

CellValue ​

Represents a single cell value in a row. Uses oneof for type-safe value encoding.

protobuf
message CellValue {
  oneof value {
    string string_val = 1;
    int64 int_val = 2;
    double double_val = 3;
    bool bool_val = 4;
    bytes bytes_val = 5;
  }
  bool is_null = 6;
  string cql_type = 7;
}

Cassandra Type Mappings:

  • text, varchar, ascii → string_val
  • uuid, timeuuid → string_val (canonical string form)
  • inet → string_val
  • timestamp → string_val (RFC3339Nano)
  • int, bigint, smallint, tinyint, counter → int_val
  • float, double → double_val
  • decimal → string_val
  • boolean → bool_val
  • blob → bytes_val (raw bytes; render as hex client-side)
  • map, list, set, tuple, UDTs → string_val (JSON-encoded)

The cql_type field carries the exact CQL type signature (e.g. map<varchar, int>, list<bigint>, blob) so clients can render type-aware inspectors.

  • null → is_null = true

Column ​

Describes a table column.

json
{
  "name": "user_id",
  "type": "uuid",
  "is_partition_key": true,
  "is_clustering_key": false,
  "position": 0
}

Fields:

  • name: Column name
  • type: CQL data type (e.g., text, int, uuid)
  • is_partition_key: True if part of partition key
  • is_clustering_key: True if part of clustering key
  • position: Position in key (0-based)

Error Handling ​

All errors follow a consistent structure:

json
{
  "error": {
    "code": "INVALID_QUERY",
    "message": "Invalid WHERE clause syntax",
    "details": {
      "query": "invalid syntax here",
      "line": "1",
      "column": "10"
    }
  }
}

Common Error Codes:

  • UNAUTHENTICATED: Missing or invalid authentication token
  • PERMISSION_DENIED: Insufficient permissions for operation
  • NOT_FOUND: Resource (keyspace/table/cursor) not found
  • INVALID_ARGUMENT: Invalid request parameters
  • INVALID_QUERY: Invalid CQL syntax in filter
  • INTERNAL: Server internal error
  • UNAVAILABLE: Database connection failed

Rate Limits ​

No rate limits are currently enforced. Future versions may implement per-session limits.


gRPC API ​

For gRPC clients, import the proto definitions from:

github.com/kashifKhn/kassie/api/proto/*.proto

Example (Go):

go
import (
    kassiev1 "github.com/kashifKhn/kassie/api/gen/go"
    "google.golang.org/grpc"
)

conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
    log.Fatal(err)
}
defer conn.Close()

client := kassiev1.NewSessionServiceClient(conn)
resp, err := client.Login(ctx, &kassiev1.LoginRequest{
    Profile: "local",
})

Client SDKs ​

Official client SDKs:

  • Go: Built-in at internal/client/client.go
  • TypeScript: Generated from proto (coming with Web UI)

Community SDKs:

  • Python, Rust, Java - Coming soon

OpenAPI Specification ​

An OpenAPI/Swagger specification can be generated from proto files:

bash
protoc --openapiv2_out=docs/ \
  --openapiv2_opt=logtostderr=true \
  api/proto/*.proto

(OpenAPI doc generation planned for future release)


Next Steps ​

Released under the MIT License.