Skip to content

REST API

The REST API provides simple HTTP access to EvidentSource functionality. It’s ideal for web applications, scripting, and environments where gRPC is not available.

http://localhost:8080 # Development
https://api.example.com # Production

Currently supports:

  • No auth (development mode)
  • API keys (via X-API-Key header)
  • Bearer tokens (planned)
Terminal window
# With API key
curl -H "X-API-Key: your-api-key" https://api.example.com/databases

The API accepts and returns JSON by default:

Content-Type: application/json
Accept: application/json

For State Views, additional content types may be returned based on the view definition.

GET /databases

Response:

{
"databases": [
{
"name": "my-events",
"created_at": "2024-01-15T10:30:00Z",
"latest_revision": 12345,
"event_count": 12345
}
]
}
POST /databases

Request:

{
"name": "my-events"
}

Response:

{
"database": {
"name": "my-events",
"created_at": "2024-01-15T10:30:00Z",
"latest_revision": 0,
"event_count": 0
}
}

Status Codes:

  • 201 Created: Database created successfully
  • 409 Conflict: Database already exists
  • 400 Bad Request: Invalid database name
GET /databases/{database_name}

Response:

{
"name": "my-events",
"created_at": "2024-01-15T10:30:00Z",
"latest_revision": 12345,
"event_count": 12345,
"state_views": ["order-summary", "customer-profile"],
"state_changes": ["process-order", "update-inventory"]
}
DELETE /databases/{database_name}

Response:

204 No Content

Status Codes:

  • 204 No Content: Database deleted
  • 404 Not Found: Database doesn’t exist
POST /databases/{database_name}/events

Request:

{
"events": [
{
"specversion": "1.0",
"id": "550e8400-e29b-41d4-a716-446655440000",
"source": "order-service",
"type": "OrderCreated",
"subject": "order-123",
"time": "2024-01-15T10:30:00Z",
"datacontenttype": "application/json",
"data": {
"orderId": "order-123",
"customerId": "customer-456",
"total": 99.99
}
}
],
"constraints": [
{
"type": "min_revision",
"revision": 12344
}
]
}

Response:

{
"revision": 12345,
"events_count": 1
}

Status Codes:

  • 200 OK: Events stored successfully
  • 409 Conflict: Constraint violation
  • 400 Bad Request: Invalid event format
  • 413 Payload Too Large: Event or transaction too large
POST /databases/{database_name}/query

Request:

{
"selectors": [
{
"stream": "order-service",
"event_type": "OrderCreated",
"subject": "order-123"
}
],
"revision_range": {
"start": 1,
"end": 1000
},
"effective_time_range": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-01-31T23:59:59Z"
},
"direction": "forward",
"limit": 100
}

Response:

{
"events": [
{
"specversion": "1.0",
"id": "550e8400-e29b-41d4-a716-446655440000",
"source": "order-service",
"type": "OrderCreated",
"subject": "order-123",
"time": "2024-01-15T10:30:00Z",
"datacontenttype": "application/json",
"data": {
"orderId": "order-123",
"customerId": "customer-456",
"total": 99.99
},
"revision": 12345,
"recordedtime": "2024-01-15T10:30:01Z"
}
],
"has_more": false
}
GET /databases/{database_name}/events/{revision}

Response:

{
"specversion": "1.0",
"id": "550e8400-e29b-41d4-a716-446655440000",
"source": "order-service",
"type": "OrderCreated",
"subject": "order-123",
"time": "2024-01-15T10:30:00Z",
"data": {...},
"revision": 12345,
"recordedtime": "2024-01-15T10:30:01Z"
}
GET /databases/{database_name}/state-views

Response:

{
"state_views": [
{
"name": "order-summary",
"description": "Summary of orders by status",
"content_type": "application/json",
"priority": "high",
"parameterized": false
}
]
}
POST /api/v1/db/{database_name}/state-views
Content-Type: multipart/form-data

State Views are published via multipart/form-data with two parts:

  • metadata: JSON containing the state view configuration
  • wasm: The compiled WebAssembly component binary

Example with curl:

Terminal window
curl -X POST http://localhost:3000/api/v1/db/my-events/state-views \
-F 'metadata={
"state_view_name": "order-summary",
"description": "Summary of orders by status",
"content_type": "application/json",
"event_selector": {
"type": "or",
"selectors": [
{"type": "equals", "attr": {"event_type": "OrderCreated"}},
{"type": "equals", "attr": {"event_type": "OrderCompleted"}}
]
},
"query_temporality": "revision"
};type=application/json' \
-F 'wasm=@order_summary.wasm;type=application/wasm'

Metadata Fields:

FieldRequiredDescription
state_view_nameYesUnique name for the state view
descriptionNoHuman-readable description
content_typeYesMIME type of rendered state
content_schemaNoURI to content schema
event_selectorYesSelector for events to process
query_temporalityYesrevision or effective_timestamp
source_code_uriNoURI to source code

Response: 201 Created with redirect to admin page for the new version.

GET /databases/{database_name}/state-views/{view_name}

Query Parameters:

  • revision: Fetch state at specific revision
  • Additional parameters passed to the State View

Response: Content-Type based on State View definition.

Example JSON response:

{
"total_orders": 150,
"orders_by_status": {
"pending": 45,
"completed": 100,
"cancelled": 5
},
"total_revenue": 15000.00
}
DELETE /databases/{database_name}/state-views/{view_name}

Response:

204 No Content
GET /databases/{database_name}/state-changes

Response:

{
"state_changes": [
{
"name": "process-order",
"description": "Processes order creation commands",
"state_view_name": "customer-profile"
}
]
}
POST /api/v1/db/{database_name}/state-changes
Content-Type: multipart/form-data

State Changes are published via multipart/form-data with two parts:

  • metadata: JSON containing the state change configuration
  • wasm: The compiled WebAssembly component binary

Example with curl:

Terminal window
curl -X POST http://localhost:3000/api/v1/db/my-events/state-changes \
-F 'metadata={
"state_change_name": "process-order",
"description": "Processes order creation commands"
};type=application/json' \
-F 'wasm=@process_order.wasm;type=application/wasm'

Metadata Fields:

FieldRequiredDescription
state_change_nameYesUnique name for the state change
descriptionNoHuman-readable description
source_code_uriNoURI to source code

Response: 201 Created with redirect to admin page for the new version.

POST /databases/{database_name}/state-changes/{change_name}/process

Request:

{
"command": {
"type": "CreateOrder",
"customer_id": "CUST-123",
"items": [
{
"product_id": "PROD-456",
"quantity": 2,
"price": 29.99
}
]
},
"parameters": {
"customer_id": "CUST-123"
}
}

Success Response:

{
"success": true,
"events": [
{
"specversion": "1.0",
"id": "generated-id",
"source": "process-order",
"type": "OrderCreated",
"subject": "order-789",
"data": {...}
}
],
"revision": 12346
}

Error Response:

{
"success": false,
"error": {
"code": "INSUFFICIENT_CREDIT",
"message": "Customer credit limit exceeded",
"details": {
"available_credit": 100.00,
"order_total": 159.99
}
}
}

The REST API includes an interactive query builder:

GET /databases/{database_name}/query-builder

Returns an HTML page with HTMX-powered query interface.

All errors follow a consistent format:

{
"error": {
"code": "INVALID_ARGUMENT",
"message": "Database name must be lowercase alphanumeric",
"details": {
"field": "name",
"value": "My-Database"
}
}
}
CodeHTTP StatusDescription
NOT_FOUND404Resource doesn’t exist
ALREADY_EXISTS409Resource already exists
INVALID_ARGUMENT400Invalid request format
FAILED_PRECONDITION409Constraint violation
RESOURCE_EXHAUSTED429Rate limit exceeded
INTERNAL500Server error

Large result sets are paginated:

GET /databases/{database_name}/events?limit=50&after=12345

Response:

{
"events": [...],
"has_more": true,
"next_cursor": "12395"
}

Production deployments may include rate limiting:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642098130
Retry-After: 60
{
"error": {
"code": "RESOURCE_EXHAUSTED",
"message": "Rate limit exceeded. Please retry after 60 seconds."
}
}

CORS headers are included for browser-based access:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-API-Key

Configure allowed origins in production:

cors:
allowed_origins:
- https://app.example.com
- https://admin.example.com
GET /health

Response:

200 OK
Content-Type: text/plain
OK

Detailed health check:

GET /health/detailed

Response:

{
"status": "healthy",
"version": "0.1.0",
"storage": {
"adapter": "dynamodb",
"status": "connected"
},
"uptime_seconds": 3600
}
Terminal window
# Create database
curl -X POST http://localhost:8080/databases \
-H "Content-Type: application/json" \
-d '{"name": "test-db"}'
# Submit event
curl -X POST http://localhost:8080/databases/test-db/events \
-H "Content-Type: application/json" \
-d '{
"events": [{
"specversion": "1.0",
"id": "'$(uuidgen)'",
"source": "test-app",
"type": "TestEvent",
"data": {"message": "Hello, EvidentSource!"}
}]
}'
Terminal window
curl -X POST http://localhost:8080/databases/test-db/query \
-H "Content-Type: application/json" \
-d '{
"selectors": [{
"source": "test-app",
"event_type": "TestEvent"
}],
"limit": 10
}'
Terminal window
# Simple state view
curl http://localhost:8080/databases/test-db/state-views/event-counter
# Parameterized state view
curl "http://localhost:8080/databases/test-db/state-views/customer-profile?customer_id=CUST-123"

While you can use the REST API directly, client SDKs provide a better experience. Official SDKs are available through:

  • JVM (Java/Kotlin): Available via Maven Central
  • JavaScript/TypeScript: Coming soon via npm
  • Python: Coming soon via PyPI
  • Go: Coming soon via Go modules

See Client Libraries for detailed information.