ALTARED Telemetry API

Version v1 · versioned, additive changes only

Console

Operator access

This service has no public sign-up and no public sign-in link. Account creation is disabled at the identity layer, so a new account cannot be registered from the browser or by calling the auth API directly. Nothing on the public pages links to the console.

There are exactly three ways to reach the data:

  1. Private console — the single owner account signs in at https://telemetry.altared.io/auth by typing that address directly. Sessions are role-gated: an account with no role granted sees no data.
  2. Lovable editor / backend tools — full read and write access to the telemetry database from the project editor, for maintenance and inspection. This path bypasses the console entirely and needs no browser session.
  3. Programmatic read API — the upstream hub and any code-based client read collected telemetry with a scoped credential carrying read permissions (telemetry:read / read-all). See Reading collected telemetry.

Reporting applications never need console access: they hold a write-only credential and only call the ingestion endpoints.

Unauthorized requests are rejected, not silently accepted:

# No credential (telemetry, heartbeat, verify, events, fleet)
curl -i -X POST https://telemetry.altared.io/api/public/v1/telemetry -d '{"event":"test"}'
→ 401  { "ok": false, "error": { "code": "invalid_credential", ... } }

# Unknown, revoked or expired credential
curl -i -X POST https://telemetry.altared.io/api/public/v1/telemetry -H "Authorization: Bearer alt_unknown" -d '{"event":"test"}'
→ 401  { "ok": false, "error": { "code": "invalid_credential", ... } }

# Write-only credential calling the read API
curl -i "https://telemetry.altared.io/api/public/v1/events?limit=1" -H "Authorization: Bearer $WRITE_ONLY_KEY"
→ 403  { "ok": false, "error": { "code": "permission_denied", ... } }

# Creating an account is refused by the identity layer
→ 422  { "error_code": "signup_disabled", "msg": "Signups not allowed for this instance" }

# Only the liveness check is open (it validates nothing and returns no data)
curl -i https://telemetry.altared.io/api/public/v1/status
→ 200

Quick Start

Register an application in the console, copy its API key once, then send your first event. Base URL for this deployment:

https://telemetry.altared.io/api/public/v1
curl -X POST https://telemetry.altared.io/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 }
  }'

Authentication

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:

  • telemetry:write — send telemetry
  • heartbeat:write — send heartbeats
  • telemetry:read / telemetry:search — read own telemetry
  • logs:read — read own log entries

There is no global master key. Revoking one application's credential never affects another.

Applications

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.altared.io/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_…"
}

Telemetry

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.altared.io/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" } }
      ] }'

Verify a credential

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.altared.io/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_…"
}

Reading collected telemetry (upstream hub)

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.altared.io/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.

Heartbeats

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.altared.io/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" } }'

Status

GET /api/public/v1/status is an unauthenticated liveness probe for the ingestion API. It never exposes application data.

{
  "ok": true,
  "service": "altared-telemetry",
  "version": "v1",
  "status": "healthy",
  "authenticated": false,
  "note": "This probe does not authenticate. Use GET /api/public/v1/verify to prove a credential works.",
  "request_id": "req_..."
}

Errors

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.

Rate Limits

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.

Retries & Reliability

Telemetry observes your application; it must never control it. Client requirements:

  • Short timeout — 3 seconds or less.
  • Non-blocking — never await telemetry on a user-facing code path.
  • Retry with exponential backoff and jitter, capped at a few attempts.
  • Optional local buffering while this service is unreachable, with a bounded queue.
  • Swallow all telemetry errors: a telemetry outage must be invisible to your users.
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
}

Idempotency

Send an Idempotency-Key header (or an idempotency_key field per event) with every event. A retry carrying a key this service has already stored for the same application is never stored twice. The response is still 202 Accepted; the duplicate is counted rather than inserted, so a retry is always safe.

{ "ok": true, "accepted": 2, "duplicates": 1, "ids": ["..."], "request_id": "req_..." }