---
title: "Ingest telemetry batch from on-prem instance"
method: POST
path: "/api/v1/telemetry/batch"
tags: ["On-Prem Telemetry"]
---

# Ingest telemetry batch from on-prem instance

`POST /api/v1/telemetry/batch`

Receive and store a batch of telemetry events from an on-prem Compresr instance.
    
    **Required Headers:**
    - `X-API-Key`: Compresr API key (authentication)
    - `X-Compresr-Signature`: HMAC signature (integrity)
    
    **Optional Headers:**
    - `X-Instance-ID`: On-prem instance identifier
    
    **First Request (auto-registration):**
    Include `signing_secret` (`opm_xxx`) in the body - it will be registered
    automatically. The on-prem image generates this credential itself; `cmp_`
    API keys are rejected.

    **Signature Format:**
    ```
    t=timestamp,v1=HMAC-SHA256(timestamp + "." + JSON(payload), signing_secret)
    ```
    
    **Event Types:**
    - `compression`: Counted for billing
    - `error`: Logged only
    - `startup`/`shutdown`: Operational
    
    **Idempotency:**
    Same batch_id within 24h returns success without reprocessing. A batch
    with failed events stays replayable: retry it and already-billed events
    are returned as status "duplicate" (never billed twice).

## Headers

- `x-instance-id` string, nullable — On-prem instance ID
- `x-compresr-signature` string, nullable — HMAC signature
- `X-API-Key` string, required

## Request body

- TelemetryBatchRequest
  - `batch_id` string, required — Unique batch ID (UUID)
  - `instance_id` string, required — On-prem instance ID (UUID)
  - `events` TelemetryEvent[], required — List of telemetry events (max 100 per batch)
    - `event_id` string, required — Unique event ID (UUID)
    - `timestamp` string, required — Event timestamp (ISO8601)
    - `event_type` string, required — Event type (compression, error, startup, shutdown)
    - `model_name` string, required — Compression model used
    - `original_tokens` integer — Original token count
    - `compressed_tokens` integer — Compressed token count
    - `tokens_saved` integer — Tokens saved (original - compressed)
    - `duration_ms` integer — Processing duration in milliseconds (max 1h)
    - `metadata` object — Additional metadata (max 10KB, 50 keys, 3 levels deep)
    - `event_hmac` string, nullable — Per-event HMAC-SHA256(api_key, canonical_event_bytes) computed by on-prem at insert time. Optional — older on-prem builds don't send it. The platform rejects events whose tag doesn't verify.
  - `identity` IdentityManifest — On-prem identity manifest, sent in each upload for rebirth detection. The on-prem instance persists this manifest in ``/data/identity.json`` HMAC-bound to the api_key on first authenticated request. Subsequent uploads carry the same values. If we see a *new* ``instance_id`` or an earlier ``created_at`` for the same api_key, it signals that the operator deleted the local manifest — typically an attempt to reset a per-instance grace period or quota.
    - `instance_id` string, required — UUID matching /data/identity.json
    - `api_key_hash` string, required — SHA-256 hex of the api_key the on-prem instance was first bound to
    - `created_at` string, required — ISO-8601 timestamp manifest was created
  - `signing_secret` string, nullable — Signing secret for HMAC verification (include on first request only)

## Response `200`

Successful Response

- TelemetryBatchResponse
  - `success` boolean, required — Whether batch was accepted
  - `batch_id` string, required — Echo of batch ID
  - `events_received` integer, required — Number of events received
  - `events_stored` integer, required — Number of events stored
  - `message` string, nullable — Additional message
  - `events_rejected` integer — Events outside the reporting window; will never be billed
  - `rejected_reasons` object — Rejection reason -> count
  - `signing_secret_registered` boolean, nullable — Whether the signing_secret in this request is now stored. Cache 'registered' only on an explicit true - null also means an older server that predates this field, so treating null as success is the very bug this field exists to fix. Anything else means keep sending the secret on subsequent batches, because the server has nothing to verify future uploads against and they will 401.
  - `signing_secret_error` string, nullable — Machine-readable reason registration did not happen. 'hash_owned_by_other_account': this deployment is using a signing secret belonging to a different account - usually a cloned data volume - and only an operator can clear it, so do not retry on it. 'not_attempted_duplicate_batch': idempotent replay, the next batch will register. 'registration_failed': transient, retry.
  - `events_billed` integer — Compression events charged through the billing pipeline
  - `events_unbilled` integer — Compression events recorded but not charged
  - `events_duplicate` integer — Compression events skipped because an earlier delivery of this batch already billed them (replay protection)
  - `events_failed` integer — Compression events that were not recorded; the batch is left replayable so a retry reprocesses exactly these events
  - `event_results` TelemetryEventResult[], nullable — Per-event billing outcomes for compression events
    - `event_id` string, required — Echo of the on-prem event ID
    - `status` 'billed' | 'recorded_unbilled' | 'failed' | 'duplicate', required — Billing outcome: billed | recorded_unbilled | failed | duplicate
    - `reason` string, nullable — Reason code when status is not 'billed'

## Other responses

- `400` — Invalid request body
- `401` — Invalid API key or signature
- `422` — Validation Error
- `500` — Internal server error

---

[API](https://skmtc.dev/compresr/apis/compresr-platform-api.md) · [All operations](https://skmtc.dev/compresr/apis/compresr-platform-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/compresr/compresr-platform-api/revisions/a86533a8067d/schema)
