> ## Documentation Index
> Fetch the complete documentation index at: https://jdcodec.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit connector-side latency measurements

> Submit connector-measured timings (Node `hrtime.bigint()`,
sub-millisecond resolution) for a snapshot the connector has
already received a `/v1/snapshot` response for. The codec service
validates the bearer token, sanity-checks values, and writes them
to its `telemetry` store. Connector NEVER writes to the
store directly — trust boundary preserved.

Telemetry is **observability-only and never billable**. Billing
derives from server-authoritative facts in `usage_events`, which
are never affected by this endpoint.

The request is fire-and-forget from the connector's perspective.
Failure to deliver telemetry should not be retried and never
affects the connector's response to its agent. If the connector
is offline or the service is degraded, telemetry is dropped
(best-effort, no buffering).

See `SPEC.md` for the underlying `telemetry` table
shape and the canonical display rule (`NULL` = not recorded;
`0` = `<1 ms`; `≥1` = numeric ms).




## OpenAPI

````yaml /openapi.yaml post /v1/telemetry
openapi: 3.1.0
info:
  title: JD Codec Cloud API
  version: 1.0.0
  summary: Per-snapshot compression API for browser-agent perception streams.
  description: |
    JD Codec is a stateful, lossy perception codec for browser-based AI agents.
    The local connector sends already-redacted DOM/ARIA snapshots to this API
    and receives compressed output — a full keyframe on the first or forced-
    refresh step, and smaller deltas on subsequent steps — that the connector
    splices into its agent's tool response in place of the original snapshot.

    Three wire types cross the connector ↔ cloud boundary:

    - `SnapshotRequest` — one snapshot per request.
    - `SnapshotResponse` — the codec-emitted compressed output (or a
      pass-through signal indicating the connector should reuse the snapshot
      it sent).
    - `ErrorResponse` — uniform shape for every non-2xx response.

    A `UsageEvent` is also emitted server-side per successful snapshot. It is
    persisted to JDC's metering store and is not returned in the response
    body. Customers who want to consume their own usage data should query
    the dashboard at https://jdcodec.com (forthcoming customer dashboard).

    ### Privacy posture

    The connector runs an on-device Privacy Shield over every snapshot before
    it is sent. The cloud refuses any request that does not carry the
    `client_redacted: true` audit signal. Redacted values never reach the
    cloud; only category-level counts (e.g. `{ email: 2, CC_GENERIC: 1 }`)
    flow through to operators for observability.

    ### Sessions and tasks

    A **session** is the technical container for codec state. A **task** is
    a user-meaningful unit of work that fully fits inside one session. Steps
    are 0-indexed snapshots within a session and must increase monotonically.

    Sessions expire after 30 minutes of inactivity (idle TTL) and 4 hours
    absolute, whichever fires first. Per-key overrides are available — talk
    to support if your workload needs a different envelope.
  contact:
    name: JD Codec
    email: hello@jdcodec.com
    url: https://jdcodec.com
  license:
    name: Proprietary
    url: https://jdcodec.com/terms
servers:
  - url: https://api.jdcodec.com
    description: Production
  - url: https://api-staging.jdcodec.com
    description: Staging
security:
  - bearerAuth: []
tags:
  - name: Snapshot
    description: |
      Submit one snapshot, receive the codec-emitted compressed output (or a
      pass-through flag).
  - name: Telemetry
    description: |
      Submit connector-side latency measurements. Observability-only —
      never billable. The codec service is the sole writer to the
      backing telemetry store; the connector does not hold database
      credentials.
  - name: Health
    description: Liveness probes.
paths:
  /v1/telemetry:
    post:
      tags:
        - Telemetry
      summary: Submit connector-side latency measurements
      description: |
        Submit connector-measured timings (Node `hrtime.bigint()`,
        sub-millisecond resolution) for a snapshot the connector has
        already received a `/v1/snapshot` response for. The codec service
        validates the bearer token, sanity-checks values, and writes them
        to its `telemetry` store. Connector NEVER writes to the
        store directly — trust boundary preserved.

        Telemetry is **observability-only and never billable**. Billing
        derives from server-authoritative facts in `usage_events`, which
        are never affected by this endpoint.

        The request is fire-and-forget from the connector's perspective.
        Failure to deliver telemetry should not be retried and never
        affects the connector's response to its agent. If the connector
        is offline or the service is degraded, telemetry is dropped
        (best-effort, no buffering).

        See `SPEC.md` for the underlying `telemetry` table
        shape and the canonical display rule (`NULL` = not recorded;
        `0` = `<1 ms`; `≥1` = numeric ms).
      operationId: postV1Telemetry
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TelemetryRequest'
            examples:
              normal:
                summary: Connector posts telemetry after a normal snapshot
                value:
                  session_id: 9c1b2f6e-0b8e-4a77-9cfd-3e3f7b5e8d21
                  step: 3
                  client_round_trip_ms: 47.3
                  redaction_ms: 0.4
                  cloud_ms: 34.8
                  upstream_ms: 12.1
                  connector_version: jdcodec@0.5.5
              partial:
                summary: Connector measured only the round-trip wall-clock
                value:
                  session_id: 9c1b2f6e-0b8e-4a77-9cfd-3e3f7b5e8d21
                  step: 4
                  client_round_trip_ms: 51.2
                  connector_version: jdcodec@0.5.5
      responses:
        '204':
          description: |
            Telemetry accepted and queued for write. No body. Note that
            the codec service may still drop the row server-side under
            sampling rules; success here means "received and validated,"
            not "definitely persisted."
        '400':
          description: |
            `telemetry_value_invalid` — a timing value was negative,
            non-numeric, or above the 600 000 ms (10 minute) ceiling.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: |
            Standard auth failure — `auth_missing`, `auth_invalid`, or
            `auth_revoked`. Same envelope as `/v1/snapshot`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: |
            `telemetry_session_unknown` — the `session_id` is not known
            or is not owned by this bearer token. Connector should not
            retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '410':
          description: |
            `telemetry_too_late` — the session has expired (TTL) and
            telemetry has no useful join target. Connector drops the
            data without retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    TelemetryRequest:
      type: object
      required:
        - session_id
        - step
      description: |
        Connector-supplied latency measurements for a single snapshot.
        All timing fields are optional — the connector sends what it
        measured and omits what it didn't. The display rule applies
        uniformly across consumers: `NULL` (omitted) = "not recorded";
        `0` = "<1 ms" (rare on the connector side since Node
        `hrtime.bigint()` has nanosecond resolution, but the rule is
        uniform across all timing fields for consistency); `≥1` =
        numeric ms.

        Three components are designed to be additive:
        `client_round_trip_ms ≈ redaction_ms + cloud_ms + upstream_ms`.
        The residual surfaces in queries as a data-quality signal
        (untracked overhead) — not enforced as a CHECK constraint.

        See `SPEC.md` (`telemetry` section) for the storage
        schema and full rationale.
      properties:
        session_id:
          type: string
          format: uuid
          description: |
            Same `session_id` the connector used in the corresponding
            `POST /v1/snapshot` call. Must be owned by the bearer token.
            Joins to `usage_events` for billing/identity context.
        step:
          type: integer
          minimum: 0
          description: |
            0-indexed step within the session. The pair
            `(session_id, step)` is the join key to `usage_events` and
            uniquely identifies a snapshot. Retried POSTs for the same
            pair are safe — the underlying table has UNIQUE
            constraint with first-write-wins semantics.
        client_round_trip_ms:
          type: number
          minimum: 0
          maximum: 600000
          description: |
            Connector wall-clock from receiving the agent's tool call
            to returning the result. The customer-experienced
            wall-clock; the headline "JDC time" number for
            benchmarking and unit-economics.
        redaction_ms:
          type: number
          minimum: 0
          maximum: 600000
          description: |
            Connector-measured Privacy Shield CPU work.
        cloud_ms:
          type: number
          minimum: 0
          maximum: 600000
          description: |
            Connector-measured `POST /v1/snapshot` round-trip
            wall-clock — includes network RTT to/from the codec
            service, server-side processing including codec compute,
            and response transmit.
        upstream_ms:
          type: number
          minimum: 0
          maximum: 600000
          description: |
            Time spent calling the upstream MCP server
            (Playwright MCP in M1; additional framework adapters in
            future milestones). The connector populates this from
            the M1 implementation's measurement.
        connector_version:
          type: string
          maxLength: 64
          description: |
            Version string of the connector emitting telemetry (e.g.
            `"jdcodec@0.5.5"`). Useful for tracking per-release
            performance regressions.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
    ErrorBody:
      type: object
      required:
        - code
        - message
        - request_id
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          description: |
            Human-readable summary. Stable category-level wording; never
            includes algorithm names, internal state, or any portion of
            the submitted snapshot.
        request_id:
          type: string
          format: uuid
          description: |
            Echoes `X-Request-Id` for support correlation.
    ErrorCode:
      type: string
      enum:
        - version_unsupported
        - malformed_request
        - privacy_shield_missing
        - privacy_shield_structural
        - privacy_shield_violation
        - step_out_of_order
        - auth_missing
        - auth_invalid
        - auth_revoked
        - quota_exceeded
        - session_expired
        - payload_too_large
        - rate_limited
        - server_error
        - codec_overloaded
        - telemetry_value_invalid
        - telemetry_session_unknown
        - telemetry_too_late
      description: |
        Stable machine-readable error tokens. Clients may branch on these
        values. New codes are additive within v1 — existing codes never
        change meaning.

        Reserved (defined for forward-compatibility, not emitted in v1):

        - `privacy_shield_violation` — placeholder for a future server-side
          secondary check. Defined now so enabling it later requires no
          connector update.
        - `rate_limited` — per-key rate limiting is not enforced in v1.
          Connectors must still handle `429` gracefully so the limit can
          be enabled server-side without client work.

        Telemetry-specific (emitted only by the `/v1/telemetry`
        endpoint):

        - `telemetry_value_invalid` (400) — a timing value was negative,
          non-numeric, or above the 600 000 ms (10 minute) ceiling.
          Connector should log + drop, not retry.
        - `telemetry_session_unknown` (404) — the `session_id` is unknown
          or not owned by this bearer. Connector should log + drop.
        - `telemetry_too_late` (410) — session has expired (TTL) and the
          telemetry row would have no useful join target. Connector
          drops the data.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: <api_key_id>.<api_key_secret>
      description: |
        API key in two parts separated by a dot, sent as a single bearer:

        - `api_key_id` — public, stable, `jdck_`-prefixed lookup handle.
          Safe to log and reference in support conversations.
        - `api_key_secret` — opaque high-entropy string. Shown to the
          customer once at issuance and never again. Server stores only
          its hash.

        Example:

            Authorization: Bearer jdck_9f3a2b7c8d1e4f05.BASE32URL_SECRET

````