capless
Docs API reference

API reference

Base URL https://api.capless.sh. Every endpoint is under /v1 and takes authorization: Bearer cpl_… (except signup). The claim secret of an unclaimed project (Bearer csk_…) is accepted for the read-only endpoints listed under Claiming. The complete, machine-readable contract is the OpenAPI 3.1 spec: /openapi.yaml (also served at api.capless.sh/v1/openapi.yaml).

Account

MethodPath
POST/v1/signupCreate user, project (trial) and API key; with claimSecret, take over that unclaimed project. No auth. Details
GET/v1/projectThe project: plan, billing (including spendCap), claimed, role, allowance, usage, web
PATCH/v1/billingSet or clear the monthly spend cap: {"spendCap": 50} or {"spendCap": null} (owners and admins)

Claiming

A snippet from analytics.capless.sh creates an unclaimed project with its first event. Its claim secret (csk_…) is a bearer token with read-only access to that project until it is claimed:

MethodPath
GET/v1/project, /v1/usage, /v1/web, /v1/streams, /v1/streams/{stream}, /v1/replays, /v1/replays/{session}/{tab}
POST/v1/streams/{stream}/read, /v1/streams/{stream}/locate

Anything else returns 403 claim_required. Once the project is claimed, the secret returns 401 project_claimed.

MethodPath
POST/v1/claim{"secret": "csk_…"} with a dashboard session: claim the project for the signed-in account
POST/v1/signup{"email": "…", "claimSecret": "csk_…"}: create an account and claim the project in one call

Both return 409 already_claimed if another account owns the project. Quickstart

Streams

MethodPath
POST/v1/streamsCreate a stream (name, optional validator)
GET/v1/streamsList streams, ordered by name
GET/v1/streams/{stream}Metadata: head cursor, entryCount, lastCommittedAt, status
PATCH/v1/streams/{stream}Rename and/or change the validator
DELETE/v1/streams/{stream}Delete (asynchronous, 202)

{stream} is the ID (str_…) or the name. Streams & cursors

Entries

MethodPath
POST/v1/streams/{stream}/entriesInsert a batch atomically, idempotently
POST/v1/streams/{stream}/readRead in cursor order with CEL filter/map
POST/v1/streams/{stream}/changesOrdered feed of inserts and erasures
POST/v1/streams/{stream}/locateCursor for a commit time (index seek)

Reading & change feed · Time seeking

Erasures

MethodPath
POST/v1/streams/{stream}/erasuresErase entries matching a CEL predicate (202)
GET/v1/streams/{stream}/erasuresList erasures in stream order
GET/v1/streams/{stream}/erasures/{erasure}Status, progress, downstream propagation
GET/v1/streams/{stream}/erasures/{erasure}/entriesExact affected entry IDs (once logically_applied)

Erasures & GDPR

Functions

MethodPath
GET/v1/functionsDescribe the CEL environment
POST/v1/functions/evaluateEvaluate an expression against a sample entry

CEL functions

Web analytics

MethodPath
GET/v1/webThe project's ingest key, snippet, replaySampleRate, allowedOrigins, and hosts (hostnames that sent events)
PATCH/v1/webChange replaySampleRate and/or allowedOrigins (owners and admins)
GET/v1/replaysSession recordings from the last 7 days
GET/v1/replays/{session}/{tab}Frames of one recording

Web SDK

Materializations

MethodPath
POST/v1/materializationsCreate (name, type: "clickhouse")
GET/v1/materializationsList
GET/v1/materializations/{id}Get
DELETE/v1/materializations/{id}Delete with all its data (streams untouched)
POST/v1/materializations/{id}/queryRun one ClickHouse SQL statement
POST/v1/materializations/{id}/sourcesConnect a stream to a table
GET/v1/materializations/{id}/sourcesList sources with checkpoints
DELETE/v1/materializations/{id}/sources/{sourceId}Detach a source
GET/v1/materializations/{id}/statusInsert and deletion freshness

ClickHouse materializations

Conventions

  • Bodies are JSON, ≤ 8 MiB. Timestamps are UTC ISO 8601 with millisecond precision.
  • Lists: ?limit= (1–1000, default 100) and ?pageToken= → {"data": [...], "nextPageToken": "…" | null}.
  • Writes accept idempotencyKey (kept 7 days).

Errors

{ "error": { "code": "validation_failed", "message": "1 of 2 entries failed validation; nothing was committed.",
  "details": { "failedCount": 1, "failures": [{ "index": 1, "message": "validator returned false" }] }, "requestId": "1b4e…" } }
HTTPCodes
400invalid_json, invalid_request, invalid_name, invalid_claim_secret, invalid_spend_cap, invalid_expression, invalid_cursor, invalid_limit, invalid_timestamp, invalid_page_token, cursor_out_of_range, query_failed, unsupported_output_format, unsupported_type, project_required
401 / 403unauthenticated, project_claimed, forbidden, claim_required, built_in_stream, origin_not_allowed, query_forbidden
402trial_ended, no_active_plan, event_limit_reached, storage_limit_reached, spend_cap_reached, plan_required, plan_limit
404stream_not_found, erasure_not_found, materialization_not_found, source_not_found, site_not_found, route_not_found
409stream_name_taken, materialization_name_taken, idempotency_key_reused, erasure_not_resolved, email_taken, already_claimed
410stream_deleted
413request_too_large, batch_too_large, entry_too_large
422validation_failed, expression_evaluation_failed, result_too_large, limit_too_small, table_not_found, missing_lineage_column, unsupported_table_engine
429rate_limited
502 / 503engine_error, engine_unavailable, engine_unreachable

Branch on code; messages are for humans and may change.

This page as Markdown

← ClickHouse materializations · Limits & quotas →