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
| Protocol | Default Address | Description |
|---|---|---|
| gRPC | localhost:50051 | Binary protocol for high-performance clients |
| HTTP/REST | http://localhost:8080 | JSON-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
# Login to get access token
curl -X POST http://localhost:8080/api/v1/session/login \
-H "Content-Type: application/json" \
-d '{"profile": "local"}'Response:
{
"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:
curl -H "Authorization: Bearer eyJhbGc...token" \
http://localhost:8080/api/v1/schema/keyspacesSessionService
Handles authentication, profile management, and session lifecycle.
Login
POST /api/v1/session/login
Authenticate with a configured profile and obtain access/refresh tokens.
Request:
{
"profile": "local"
}Response:
{
"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: Success400: Invalid profile name401: Authentication failed500: Server error
Refresh Token
POST /api/v1/session/refresh
Refresh an expired access token using the refresh token.
Request:
{
"refresh_token": "eyJhbGc...refresh"
}Response:
{
"access_token": "string",
"expires_at": 1707500000
}Status Codes:
200: Success401: Invalid or expired refresh token500: Server error
Logout
POST /api/v1/session/logout
Invalidate the current session and tokens.
Request:
{}Response:
{}Requires: Authorization header
Status Codes:
200: Success401: Unauthorized
Get Profiles
GET /api/v1/profiles
List all configured connection profiles.
Request: None
Response:
{
"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: Success500: 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:
{
"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: Success401: Unauthorized500: 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:
{
"tables": [
{
"name": "users",
"keyspace": "app_data",
"estimated_rows": 1000000
},
{
"name": "orders",
"keyspace": "app_data",
"estimated_rows": 5000000
}
]
}Requires: Authorization header
Status Codes:
200: Success401: Unauthorized404: Keyspace not found500: 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 nametable(string, required): Table name
Request: None
Response:
{
"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: Success401: Unauthorized404: Table not found500: 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:
{
"keyspace": "app_data",
"table": "users",
"page_size": 100
}Response:
{
"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: Success400: Invalid request (missing keyspace/table)401: Unauthorized404: Table not found500: Server error
Get Next Page
POST /api/v1/data/next
Fetch the next page of results using a cursor.
Request:
{
"cursor_id": "cursor_abc123"
}Response:
{
"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: Success400: Invalid cursor ID401: Unauthorized404: Cursor not found or expired500: 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:
{
"keyspace": "app_data",
"table": "users",
"where_clause": "email = 'user@example.com'",
"page_size": 50
}Response:
{
"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: Success400: Invalid WHERE clause syntax401: Unauthorized404: Table not found500: 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
{
"cql": "SELECT id, name FROM app_data.users LIMIT 100",
"page_size": 100
}Response:
{
"rows": [{ "cells": { "id": { "intVal": 1, "isNull": false } } }],
"cursor_id": "cursor_abc123",
"has_more": true,
"total_fetched": 100
}Requires: Authorization header
Constraints:
- Only
SELECTstatements are allowed - Multiple statements are rejected
page_sizeis capped at 1000- Results page through the standard
cursor_id(see Get Next Page)
Status Codes:
200: Success400: Invalid query (non-SELECT, multiple statements, too long)401: Unauthorized500: Query execution error
Export Rows (Streaming)
Stream a table (optionally filtered) as CSV or newline-delimited JSON chunks.
POST /api/v1/data/export
{
"keyspace": "app_data",
"table": "users",
"where_clause": "region = 'eu'",
"format": "EXPORT_FORMAT_CSV",
"fetch_size": 1000
}Response: chunked stream; each line is an ExportChunk:
{ "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 clause401: Unauthorized
Get Table Stats
Row count and partition size statistics for a table.
POST /api/v1/schema/stats
{
"keyspace": "app_data",
"table": "users"
}Response:
{
"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
{
"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
{
"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
{
"question": "critical events for device 123e yesterday",
"keyspace": "app_data",
"table": "events"
}Response:
{
"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.
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_valuuid,timeuuid→string_val(canonical string form)inet→string_valtimestamp→string_val(RFC3339Nano)int,bigint,smallint,tinyint,counter→int_valfloat,double→double_valdecimal→string_valboolean→bool_valblob→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.
{
"name": "user_id",
"type": "uuid",
"is_partition_key": true,
"is_clustering_key": false,
"position": 0
}Fields:
name: Column nametype: CQL data type (e.g.,text,int,uuid)is_partition_key: True if part of partition keyis_clustering_key: True if part of clustering keyposition: Position in key (0-based)
Error Handling
All errors follow a consistent structure:
{
"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 tokenPERMISSION_DENIED: Insufficient permissions for operationNOT_FOUND: Resource (keyspace/table/cursor) not foundINVALID_ARGUMENT: Invalid request parametersINVALID_QUERY: Invalid CQL syntax in filterINTERNAL: Server internal errorUNAVAILABLE: 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/*.protoExample (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:
protoc --openapiv2_out=docs/ \
--openapiv2_opt=logtostderr=true \
api/proto/*.proto(OpenAPI doc generation planned for future release)
Next Steps
- CLI Commands - Command-line interface
- Configuration Schema - Config reference
- Architecture - System design