ALTARED Telemetry API
Version v1 · versioned, additive changes only
Register an application in the console, copy its API key once, then send your first event. Base URL for this deployment:
https://telemetry.example/api/public/v1
curl -X POST https://telemetry.example/api/public/v1/telemetry \
-H "Authorization: Bearer $TELEMETRY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"event": "payment.completed",
"severity": "info",
"message": "Invoice 4021 paid",
"tags": ["billing"],
"data": { "invoice_id": "4021", "amount_cents": 12900 }
}'Every request carries the application's own API key, either as Authorization: Bearer <key> or x-api-key: <key>. Keys are stored only as SHA-256 hashes, are shown exactly once, and carry least-privilege scopes:
There is no global master key. Revoking one application's credential never affects another.
Each application is fully independent: its own identity, credentials, environment and data. This service never connects to an application database, and an application never connects to the telemetry database.
GET https://telemetry.example/api/public/v1/application/status
Authorization: Bearer <key>
200 OK
{
"application": { "id": "…", "name": "Billing Service", "environment": "production", "status": "healthy" },
"last_telemetry_at": "2026-09-16T09:41:12.004Z",
"last_heartbeat_at": "2026-09-16T09:44:02.551Z",
"request_id": "req_…"
}POST /api/public/v1/telemetry — requires telemetry:write.
{
"event": "database.error", // required, 1–120 chars
"severity": "error", // debug | info | warning | error | critical (default info)
"message": "connection pool exhausted", // optional, ≤1000 chars
"occurred_at": "2026-09-16T09:40:00Z", // optional client timestamp
"tags": ["db", "pool"], // optional, ≤20 tags
"idempotency_key": "…", // optional; header Idempotency-Key also accepted
"data": { "pool": "primary", "waiting": 24 } // optional JSON object
}The server owns all security-sensitive metadata — application ID, environment, received timestamp, request ID, credential ID and processing status are derived from the authenticated credential, never from the request body.
202 Accepted
x-request-id: req_9f21…
x-telemetry-api-version: v1
x-ratelimit-limit: 600
x-ratelimit-remaining: 594
{ "ok": true, "accepted": 1, "duplicates": 0, "ids": ["…"], "request_id": "req_9f21…" }Batching: send up to 100 events in one request by posting { "events": [ … ] } with the same event shape in each element. The response reports how many were accepted and how many were duplicates.
curl -X POST https://telemetry.example/api/public/v1/telemetry \
-H "Authorization: Bearer $TELEMETRY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "events": [
{ "event": "service.started", "severity": "info" },
{ "event": "user.login", "severity": "info", "data": { "method": "google" } }
] }'GET /api/public/v1/verify is the only endpoint that proves a key works. It authenticates, reports the application identity, scopes and limits, and writes nothing to telemetry — so an application can self-check at startup without polluting data. GET /status requires no key and is never proof of a working credential.
curl https://telemetry.example/api/public/v1/verify \
-H "Authorization: Bearer $TELEMETRY_API_KEY"
200 OK
{
"verified": true,
"application": { "id": "…", "name": "Billing Service", "environment": "production" },
"scopes": ["telemetry:write", "heartbeat:write"],
"limits": { "max_payload_bytes": 65536, "max_batch_events": 100, "requests_per_minute": 600 },
"request_id": "req_…"
}GET /api/public/v1/events returns collected events in stable order with keyset pagination, so a consumer can resume exactly where it stopped and never skip or repeat a row. A credential with telemetry:read reads only its own application; telemetry:read:all (issued to the upstream analytics hub) reads every reporting application.
curl "https://telemetry.example/api/public/v1/events?limit=200&cursor=$CURSOR" \
-H "Authorization: Bearer $TELEMETRY_READ_KEY"
200 OK
{
"ok": true,
"count": 200,
"has_more": true,
"next_cursor": "MjAyNi0…", // persist this, send it back next poll
"scope": "all_applications",
"events": [
{
"id": "…",
"application": { "id": "…", "name": "Billing Service", "slug": "billing-service" },
"environment": "production",
"event": "database.error",
"severity": "error",
"received_at": "2026-09-18T11:04:02.118Z",
"occurred_at": "2026-09-18T11:04:01.902Z",
"request_id": "req_…",
"idempotency_key": null,
"tags": [],
"data": { }
}
],
"request_id": "req_…"
}Optional filters: since (ISO timestamp), environment, severity, application_id (ignored unless the credential holds telemetry:read:all). limit is 1–500, default 100. Poll on an interval, persist next_cursor durably, and keep requesting while has_more is true.
GET /api/public/v1/fleet requires telemetry:read:all and returns every reporting application with its live status, last heartbeat, last event and seconds_since_last_signal — enough to render fleet health without reading events.
POST /api/public/v1/heartbeat — requires heartbeat:write. Send on a fixed interval (30–60s is typical); a missing heartbeat is what marks an application offline.
curl -X POST https://telemetry.example/api/public/v1/heartbeat \
-H "Authorization: Bearer $TELEMETRY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "ok", "version": "1.4.2", "metadata": { "host": "worker-3" } }'GET /api/public/v1/status is an unauthenticated liveness probe for the ingestion API. It never exposes application data.
{ "status": "ok", "service": "altared-telemetry", "version": "v1", "time": "2026-09-16T09:45:00.000Z" }Errors are structured, human-readable and always carry a request ID. Stack traces are never returned.
401 invalid_credential The API key is missing, unknown or not active.
403 permission_denied The credential lacks the required scope.
403 application_disabled The application is disabled in ALTARED Telemetry.
429 rate_limit_exceeded Too many requests for this credential.
413 payload_too_large Payload exceeds 65536 bytes.
400 invalid_payload Payload failed schema validation (stored as a rejected event).
405 method_not_allowed Wrong HTTP method for this endpoint.
503 service_unavailable Telemetry database write failed — retry with backoff.
{ "error": { "code": "invalid_payload", "message": "…", "request_id": "req_…" } }Malformed telemetry is never silently discarded: it is stored as a rejected event and logged.
600 requests per minute per credential by default, plus a 65,536-byte payload limit. Exceeding either returns a structured error — treat both as retryable with backoff, never as a fatal application error.
Telemetry observes your application; it must never control it. Client requirements:
async function sendWithBackoff(payload, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
const res = await postTelemetry(payload); // 3s timeout inside
if (res.ok || res.status < 500) return; // 4xx is not retryable
} catch { /* ignore */ }
await new Promise((r) => setTimeout(r, 2 ** i * 250 + Math.random() * 100));
}
// give up silently — business functionality is unaffected
}Send an Idempotency-Key header (or idempotency_key field) with every event. Retries with the same key are detected as duplicates and return 200 with { "duplicate": true } instead of storing the event twice.