Skip to main content

HTTP API reference

This document covers every HTTP endpoint exposed by the tsink server, including request formats, query parameters, request/response headers, and error codes.

Base URL

All paths are relative to the server listen address (e.g. http://127.0.0.1:9201).

Authentication

Scopes

ScopePathsNotes
Probe/healthz, /readyAlways unauthenticated.
Internal/internal/v1/*mTLS peer-to-peer traffic; validated separately.
PublicAll other non-admin pathsBearer token or RBAC.
Admin/api/v1/admin/*Requires admin token or elevated RBAC role; must be enabled with --admin-api-enabled.

Bearer token

Pass credentials in the Authorization header:
Authorization: Bearer <token>
For public endpoints a static token can be configured via --auth-token. For admin endpoints a separate token can be set with --admin-auth-token; if only a public token is configured it is accepted for admin too.

RBAC / OIDC

When RBAC is enabled the bearer token is validated as a service-account token or an OIDC JWT (RS256/HS256). The server attaches the resolved principal and role to the request internally; see the Security model for role definitions.

Authentication error headers

All 401/403 responses include:
HeaderValuesMeaning
WWW-AuthenticateBearerSignals that a bearer token is expected (401 only).
X-Tsink-Auth-Error-Codeauth_token_missingNo token provided for a protected endpoint.
auth_token_invalidToken provided but did not match.
admin_auth_token_missingNo token provided for an admin endpoint.
admin_auth_token_invalidAdmin token provided but did not match.
auth_scope_deniedPublic token used on an admin-only endpoint (403).

Multi-tenancy

Pass the tenant identifier on every request using the request header:
x-tsink-tenant: <tenant-id>
If omitted, the request is attributed to the default tenant. Tenant IDs must be non-empty strings that do not start with __. See Multi-tenancy for quota and isolation details.

Common response envelope

JSON responses from query and admin endpoints use one of two shapes: Success
{
  "status": "success",
  "data": <result>
}
PromQL error
{
  "status": "error",
  "errorType": "<type>",
  "error": "<message>"
}
errorType is one of bad_data (request validation) or execution (query evaluation).

Cluster response headers

When clustering is active, query responses include additional metadata headers:
HeaderDescription
X-Tsink-Read-ConsistencyEffective read consistency level used (one, quorum, all).
X-Tsink-Read-Partial-PolicyActive partial-response policy (fail, warn).
X-Tsink-Read-Responsetrue if the response is partial (some shards unavailable).
X-Tsink-Read-Partial-WarningsNumber of partial-response warning messages.
The JSON body of cluster query responses also contains a partialResponse object:
{
  "status": "success",
  "data": ...,
  "partialResponse": {
    "enabled": true,
    "policy": "warn",
    "consistency": "quorum",
    "warningCount": 1
  },
  "warnings": ["shard 3: node node-2 unreachable"]
}
Write responses set per-request consistency headers when clustering is active:
HeaderDescription
X-Tsink-Write-ConsistencyEffective write consistency mode used.
X-Tsink-Write-Required-AcksNumber of replica acknowledgements required.
X-Tsink-Write-Acknowledged-ReplicasMinimum replica acknowledgements actually received.
To override the write consistency on a per-request basis, set:
x-tsink-write-consistency: one|quorum|all
To override the partial-response behaviour on reads, set:
x-tsink-read-partial-response: true|false

Health & readiness probes

GET /healthz

Liveness probe. Always returns 200 ok as plain text.

GET /ready

Readiness probe. Returns 200 ready as plain text when the server is ready to serve traffic. Both probes are unauthenticated and are suitable for Kubernetes livenessProbe / readinessProbe configuration.

Self-instrumentation

GET /metrics

Returns self-instrumentation counters and gauges in Prometheus text exposition format. Authentication: public scope (bearer token if configured). Response: 200 text/plain — Prometheus text exposition. Key exported metric families include tsink_memory_*, tsink_series_total, tsink_uptime_seconds, tsink_wal_*, tsink_compaction_*, tsink_cluster_*, tsink_ingest_*, tsink_query_*, and tsink_exemplar_*.

PromQL query

GET|POST /api/v1/query

Instant PromQL evaluation. Query parameters / POST form fields:
ParameterRequiredDescription
queryYesPromQL expression string.
timeNoEvaluation timestamp. Unix milliseconds (default precision) or RFC 3339. Defaults to current server time.
Response: 200 application/json
{
  "status": "success",
  "data": {
    "resultType": "vector",
    "result": [
      {
        "metric": {"__name__": "cpu_usage", "host": "web-1"},
        "value": [1700000000.000, "42"]
      }
    ]
  }
}

GET|POST /api/v1/query_range

Range PromQL evaluation. Query parameters / POST form fields:
ParameterRequiredDescription
queryYesPromQL expression string.
startYesRange start. Unix milliseconds or RFC 3339.
endYesRange end. Must be ≥ start.
stepYesStep duration in milliseconds or Go-style duration string (e.g. 15s, 1m).
Response: 200 application/json
{
  "status": "success",
  "data": {
    "resultType": "matrix",
    "result": [
      {
        "metric": {"__name__": "cpu_usage", "host": "web-1"},
        "values": [[1700000000.000, "42"], [1700000015.000, "43"]]
      }
    ]
  }
}
Error codes: 400 invalid parameters, 413 per-tenant range-points quota exceeded.

Metadata queries

GET /api/v1/series

Returns all time series matching one or more label selectors. Query parameters:
ParameterRequiredDescription
match[]YesRepeated. PromQL vector selector, e.g. up{job="prometheus"}.
Response: 200 application/json
{
  "status": "success",
  "data": [
    {"__name__": "cpu_usage", "host": "web-1"},
    {"__name__": "cpu_usage", "host": "web-2"}
  ]
}

GET /api/v1/labels

Returns a sorted list of all label names present in the store. Response: 200 application/json
{"status": "success", "data": ["__name__", "host", "region"]}

GET /api/v1/label/{name}/values

Returns the set of values for a single label name. Path parameters:
ParameterDescription
{name}URL-encoded label name, e.g. host.
Response: 200 application/json
{"status": "success", "data": ["web-1", "web-2", "db-1"]}

GET /api/v1/metadata

Returns metric metadata (type, help, unit). Query parameters:
ParameterRequiredDefaultDescription
metricNoFilter by metric name.
limitNo1000Maximum number of results (max 10000).
Response: 200 application/json
{
  "status": "success",
  "data": {
    "http_requests_total": [
      {"type": "counter", "help": "Total HTTP requests", "unit": ""}
    ]
  }
}

GET|POST /api/v1/query_exemplars

Returns exemplars for the series matched by a PromQL expression. Query parameters / POST form fields:
ParameterRequiredDescription
queryYesPromQL expression (vector or matrix selector).
startNoStart timestamp.
endNoEnd timestamp.
Response: 200 application/json
{
  "status": "success",
  "data": [
    {
      "seriesLabels": {"__name__": "http_request_duration_seconds"},
      "exemplars": [
        {
          "labels": {"traceID": "abc123"},
          "value": "0.042",
          "timestamp": 1700000000.000
        }
      ]
    }
  ]
}

Status

GET /api/v1/status/tsdb

Returns a comprehensive JSON status snapshot covering memory usage, WAL state, compaction levels, cluster topology, admission guardrails, ingestion protocol status, exemplar store metrics, rules and rollup state, edge-sync state, and tenant policy. Authentication: public scope, read permission. Response: 200 application/json — Large nested object; contents vary by configuration.

Ingestion

POST /api/v1/write

Prometheus Remote Write. Accepts snappy-compressed protobuf (WriteRequest). Request headers:
HeaderValue
Content-Typeapplication/x-protobuf
Content-Encodingsnappy
X-Prometheus-Remote-Write-Version0.1.0 (optional)
Response: 200 on success (empty body). Optional response headers set when relevant features are active:
HeaderDescription
X-Tsink-Metadata-AppliedNumber of metadata updates applied.
X-Tsink-Histograms-AcceptedNumber of native histogram samples accepted.
X-Tsink-Exemplars-AcceptedNumber of exemplars accepted.
X-Tsink-Exemplars-DroppedNumber of exemplars dropped (over per-series limit).
Feature flags (environment variables):
VariableDefaultDescription
TSINK_REMOTE_WRITE_METADATA_ENABLEDtrueAccept metric metadata updates.
TSINK_REMOTE_WRITE_EXEMPLARS_ENABLEDtrueAccept exemplars.
TSINK_REMOTE_WRITE_HISTOGRAMS_ENABLEDtrueAccept native Prometheus histograms.
TSINK_REMOTE_WRITE_MAX_METADATA_UPDATES512Max metadata updates per request.
TSINK_REMOTE_WRITE_MAX_HISTOGRAM_BUCKET_ENTRIES16384Max total histogram bucket entries per request.
Error codes: 400 invalid protobuf, 413 write quota exceeded, 422 disabled feature, 503 admission unavailable.

POST /api/v1/read

Prometheus Remote Read. Accepts snappy-compressed protobuf (ReadRequest); responds with snappy-compressed protobuf (ReadResponse). Request headers:
HeaderValue
Content-Typeapplication/x-protobuf
Content-Encodingsnappy
Response headers:
HeaderValue
Content-Typeapplication/x-protobuf
Content-Encodingsnappy
X-Prometheus-Remote-Read-Version0.1.0
Only Samples response type is supported. Chunked streaming is not yet available. Error codes: 400 invalid request, 413 queries-per-request quota exceeded, 503 admission unavailable.

POST /api/v1/import/prometheus

Prometheus text exposition bulk import. Request headers:
HeaderValue
Content-Typetext/plain
Request body: Prometheus text exposition format, one sample per line:
http_requests_total{method="GET"} 1027 1700000000000
Response: 200 on success (empty body).

POST /v1/metrics

OTLP HTTP metrics ingest. Accepts protobuf-encoded ExportMetricsServiceRequest. Request headers:
HeaderValue
Content-Typeapplication/x-protobuf or application/protobuf
Supported metric kinds: gauges, monotonic sums, histograms, summaries, and exponential histograms. Exemplars within OTLP payloads are forwarded to the exemplar store. Feature flag: TSINK_OTLP_METRICS_ENABLED (default true). Response: 200 application/json — OTLP ExportMetricsServiceResponse JSON. Error codes: 415 unsupported content type, 422 OTLP ingest disabled.

POST /write and POST /api/v2/write

InfluxDB line protocol (v1 and v2 API paths). Accepts plain-text line protocol. Request body: InfluxDB line protocol, e.g.:
cpu_usage,host=web-1 value=42.0 1700000000000000000
Response: 204 on success (no body), matching the InfluxDB convention.

Admin API

Admin endpoints require --admin-api-enabled on the server. All admin requests require the admin bearer token (or a public token if no dedicated admin token is configured). Mutating admin operations are recorded to the cluster audit log with the actor identity derived from the Authorization header. The actor ID can be overridden by passing x-tsink-actor-id.

Data management

POST /api/v1/admin/snapshot

Take a local data snapshot. Request (query param or JSON body):
FieldRequiredDescription
pathYesFilesystem path to write the snapshot to.
Response: 200 application/json
{"status": "success", "data": {"path": "/var/snapshots/snap-1"}}

POST /api/v1/admin/restore

Restore a local data snapshot. Request (query params or JSON body):
FieldRequiredAliasesDescription
snapshot_pathYessnapshotPathPath to an existing snapshot directory.
data_pathYesdataPathDestination data directory to restore into.
Response: 200 application/json
{
  "status": "success",
  "data": {"snapshotPath": "...", "dataPath": "..."}
}

POST /api/v1/admin/delete_series

Tombstone-delete series matching label selectors within an optional time range. Request (query params or JSON body):
FieldRequiredDescription
match[]YesRepeated PromQL vector selectors.
startNoDelete range start (Unix ms or RFC 3339). Defaults to i64::MIN.
endNoDelete range end. Must be > start. Defaults to i64::MAX.
The JSON body equivalent uses selectors (array of strings), start, and end. Response: 200 application/json
{
  "status": "success",
  "data": {
    "tenantId": "default",
    "matchersProcessed": 1,
    "matchedSeries": 3,
    "tombstonesApplied": 3,
    "start": -9223372036854775808,
    "end": 9223372036854775807
  }
}

Rules & rollups

POST /api/v1/admin/rules/apply

Apply a recording/alerting rules configuration. Request body (JSON):
{
  "groups": [
    {
      "name": "recording",
      "tenantId": "default",
      "interval": "1m",
      "rules": [
        {
          "kind": "recording",
          "record": "job:http_requests:rate5m",
          "expr": "rate(http_requests_total[5m])"
        }
      ]
    }
  ]
}
Response: 200 application/json — rules snapshot.

POST /api/v1/admin/rules/run

Trigger an immediate rules evaluation cycle. Response: 200 application/json — rules snapshot. 409 if already running.

GET /api/v1/admin/rules/status

Return current rules scheduler state and last evaluation results. Response: 200 application/json — rules snapshot including metrics.configuredGroups, per-group rule results, and last-run timestamps.

POST /api/v1/admin/rollups/apply

Apply rollup downsampling policies. Request body (JSON):
{
  "policies": [
    {
      "metric": "cpu_usage",
      "resolution": "5m",
      "retention": "90d",
      "aggregations": ["avg", "max"]
    }
  ]
}
Response: 200 application/json — rollup policies snapshot.

POST /api/v1/admin/rollups/run

Trigger an immediate rollup materialization run. Response: 200 application/json — rollup snapshot.

GET /api/v1/admin/rollups/status

Return current rollup scheduler state. Response: 200 application/json — rollup snapshot from storage observability.

RBAC

GET /api/v1/admin/rbac/state

Return a full RBAC configuration snapshot (roles, service accounts, OIDC providers). Response: 200 application/json
{"status": "success", "data": { ... }}

GET /api/v1/admin/rbac/audit

Query RBAC authorization audit log. Query parameters:
ParameterDefaultDescription
limit100Maximum number of entries to return.
Response: 200 application/json{entries: [...]}.

POST /api/v1/admin/rbac/reload

Hot-reload the RBAC configuration from disk. Response: 200 application/json — updated RBAC state snapshot.

POST /api/v1/admin/rbac/service_accounts/create

Create a new service account and return its bearer token. Request body (JSON): ServiceAccountSpec
{
  "id": "my-writer",
  "role": "writer",
  "description": "CI pipeline ingest account"
}
Response: 200 application/json
{
  "status": "success",
  "data": {
    "serviceAccount": { "id": "my-writer", "role": "writer", ... },
    "token": "<bearer-token>"
  }
}

POST /api/v1/admin/rbac/service_accounts/update

Update an existing service account (role, description). Request body (JSON): ServiceAccountSpec with the target account id. Response: 200 application/json — updated service account.

POST /api/v1/admin/rbac/service_accounts/rotate

Rotate the bearer token of a service account. Request (query param or JSON body):
FieldRequiredDescription
idYesService account identifier.
Response: 200 application/json — service account and new token (same shape as create).

POST /api/v1/admin/rbac/service_accounts/disable

Disable a service account (token will be rejected). Request (query param or JSON body): id Response: 200 application/json.

POST /api/v1/admin/rbac/service_accounts/enable

Re-enable a previously disabled service account. Request (query param or JSON body): id Response: 200 application/json.

Secrets & TLS rotation

GET /api/v1/admin/secrets/state

Return current security and secret state (TLS certificate expiry, token rotation status, mTLS materials). Response: 200 application/json
{
  "status": "success",
  "data": { ... }
}

POST /api/v1/admin/secrets/rotate

Rotate a TLS certificate, bearer token, or mTLS material. Request body (JSON):
FieldRequiredDescription
targetYesSecret target identifier (e.g. tls, admin_token, mtls_ca).
modeYesRotation mode (replace or overlap).
new_valueNoNew credential value (if not auto-generated).
overlap_secondsNoGrace period during which the old credential remains valid.
Response: 200 application/json
{
  "status": "success",
  "data": {
    "target": "admin_token",
    "mode": "overlap",
    "issuedCredential": "<new-token>",
    "state": { ... }
  }
}

Usage accounting

GET /api/v1/admin/usage/report

Return aggregated usage report for one or all tenants. Query parameters:
ParameterDefaultDescription
tenantTenant ID to filter. Omit for all tenants.
startUnix milliseconds start (optional).
endUnix milliseconds end (optional).
bucketTime bucket granularity (none, hour, day).
reconcilefalseIf true, reconciles counters against storage before reporting.
Response: 200 application/json{report, reconciliation, reconciledStorageSnapshots}.

GET /api/v1/admin/usage/export

Stream raw per-request usage records as newline-delimited JSON. Query parameters: Same tenant, start, end as the report endpoint. Response: 200 application/x-ndjson — one JSON record per line.

POST /api/v1/admin/usage/reconcile

Force immediate usage ledger reconciliation against live storage. Response: 200 application/json{journal, storageSnapshots}.

Support bundle

GET /api/v1/admin/support_bundle

Download a bounded JSON diagnostic snapshot for a tenant. Includes status, usage, RBAC state, RBAC audit, security state, cluster audit, handoff/repair/rebalance status, rules, and rollup state. Query parameters:
ParameterDefaultDescription
tenantdefaultTenant to scope the bundle to.
Response: 200 application/json — downloaded as tsink-support-bundle-<tenant>-<timestamp>.json.

Cluster management

All cluster endpoints require an active cluster runtime (--cluster-enabled). Parameters can be supplied as query params or in a JSON request body using either snake_case or camelCase field names.

POST /api/v1/admin/cluster/join

Add a node to the cluster ring. Parameters: node_id (or nodeId), endpoint Response: 200 application/json — membership operation result.

POST /api/v1/admin/cluster/leave

Remove a node from the cluster ring. Parameters: node_id Response: 200 application/json.

POST /api/v1/admin/cluster/recommission

Re-add a previously removed node. Parameters: node_id, endpoint (optional) Response: 200 application/json.

Shard handoff

Shard handoff is the mechanism for migrating a shard between nodes.
MethodPathDescription
POST/api/v1/admin/cluster/handoff/beginStart a handoff. Parameters: shard, from_node_id, to_node_id, activation_ring_version (optional).
POST/api/v1/admin/cluster/handoff/progressReport handoff progress. Parameters: shard, phase, copied_rows, pending_rows, last_error.
POST/api/v1/admin/cluster/handoff/completeMark a handoff as complete. Parameters: shard, from_node_id, to_node_id.
GET/api/v1/admin/cluster/handoff/statusReturn handoff scheduler state.

Digest-based repair

MethodPathDescription
POST/api/v1/admin/cluster/repair/runTrigger an immediate repair cycle.
POST/api/v1/admin/cluster/repair/pausePause the repair scheduler.
POST/api/v1/admin/cluster/repair/resumeResume the repair scheduler.
POST/api/v1/admin/cluster/repair/cancelCancel an in-progress repair.
GET/api/v1/admin/cluster/repair/statusReturn repair scheduler state.

Rebalance

MethodPathDescription
POST/api/v1/admin/cluster/rebalance/runTrigger an immediate rebalance.
POST/api/v1/admin/cluster/rebalance/pausePause rebalance.
POST/api/v1/admin/cluster/rebalance/resumeResume rebalance.
GET/api/v1/admin/cluster/rebalance/statusReturn rebalance state.

Cluster audit log

MethodPathDescription
GET/api/v1/admin/cluster/auditQuery audit entries. Query params: limit (default 100), operation, target_kind, target_id.
GET/api/v1/admin/cluster/audit/exportDownload audit log as newline-delimited JSON. Query params: limit, format (jsonl/ndjson).

Cluster snapshots

MethodPathDescription
POST/api/v1/admin/cluster/snapshotCoordinate a cluster-wide data snapshot. Parameter: path.
POST/api/v1/admin/cluster/restoreCoordinate a cluster-wide restore. Parameters: snapshot_path, data_path.
POST/api/v1/admin/cluster/control/snapshotSnapshot the Raft control-plane log. Parameter: path.
POST/api/v1/admin/cluster/control/restoreRestore the Raft control-plane log from snapshot. Parameters: snapshot_path, data_path.

Managed control plane

These endpoints manage deployment records in the embedded control-plane store. They are only available when the managed control-plane feature is configured.
MethodPathDescription
GET/api/v1/admin/control-plane/stateReturn full control-plane state snapshot.
GET/api/v1/admin/control-plane/auditQuery control-plane audit log.
POST/api/v1/admin/control-plane/deployments/provisionProvision or update a managed deployment record.
POST/api/v1/admin/control-plane/deployments/backup-policyApply a backup policy to a managed deployment.
POST/api/v1/admin/control-plane/deployments/backup-runRecord a completed backup run.
POST/api/v1/admin/control-plane/deployments/maintenanceApply a maintenance window to a deployment.
POST/api/v1/admin/control-plane/deployments/upgradeApply an upgrade intent to a deployment.
POST/api/v1/admin/control-plane/tenants/applyCreate or update a managed tenant record.
POST/api/v1/admin/control-plane/tenants/lifecycleApply a lifecycle transition to a managed tenant.
All control-plane mutation requests require a JSON body. Responses follow {"status":"success","data":{"deployment"|"tenant": ...}}.

Error codes summary

HTTP statusMeaning
200Success.
204Success with no body (InfluxDB write).
400Invalid request parameters or body.
401Missing or invalid authentication token.
403Token has insufficient scope.
404Path not found.
409Conflict (e.g. scheduler already running, duplicate provisioning).
413Request exceeds a configured quota (rows, queries, range points, histogram buckets).
415Unsupported Content-Type.
422Feature disabled on this node.
500Internal server error.
503Required subsystem not configured or unavailable.
On write and read admission errors the response also sets:
HeaderDescription
X-Tsink-Write-Error-CodeMachine-readable write rejection code.
X-Tsink-Read-Error-CodeMachine-readable read rejection code.