Ingestion API
A single, high-throughput endpoint for sending telemetry — POST /api/v1/ingest. Authenticated by a workspace-scoped API key (see Authentication), not a session token, since ingestion clients are external services and SDKs rather than signed-in browser users. Already instrumenting with OpenTelemetry? See OpenTelemetry for that ingestion path instead.
Single event
curl -X POST https://<your-api-host>/api/v1/ingest \-H "X-API-Key: <your-api-key>" \-H "Content-Type: application/json" \-d '{"signalKey": "order_completed","value": 149.99,"timestamp": "2026-09-15T18:04:00.000Z","tags": { "currency": "USD", "customerTier": "gold" }}'
Batching
The same endpoint also accepts a JSON array of up to 500 payloads in one request. Batches are all-or-nothing: if any payload fails validation, the entire request is rejected and nothing is queued.
[{ "signalKey": "order_completed", "value": 149.99 },{ "signalKey": "queue_wait_time", "value": 3400, "tags": { "region": "us-east" } }]
Payload fields
signalKey(string, required) — must match an active signal in the workspace the API key is scoped to.value(number, required) — the measurement itself. Its meaning depends on the signal's data type (a raw count, a currency amount, a duration in the signal's configured unit, etc.).timestamp(ISO 8601 string, optional) — defaults to the ingestion server's current time if omitted.tags(object, optional) — string, number, or boolean values keyed by tag name. Validated against the signal's tag schema, if one is configured — see Signal Schemas & Tags.
Response
A 201 Created response returns the number of events accepted:
{ "accepted": 1 }
Error responses:
400 Bad Request— a payload targeted an unknown or inactive signal, or failed tag schema validation.401 Unauthorized— missing, invalid, or expired API key.403 Forbidden— the API key is missing thesignals:emitscope, or isn't scoped to a workspace.
Processing model
Each event is validated synchronously against its resolved workspace's signal and tag schema before being queued for asynchronous storage as partitioned Parquet files, which dashboards, Live Telemetry, and Reports query directly via embedded DuckDB — no separate ETL step.
For application code, the @datius/node SDK wraps this endpoint with in-memory buffering, automatic batching, and retry/backoff, so you rarely need to call it directly from a hot path.
Try it
Explore this endpoint's full request/response schema, or generate a real test key against one of your workspaces, on the API Reference page.