For the complete documentation index, see llms.txt. This page is also available as Markdown.

Overview

Base URL, auth, content type, errors, rate limits.

Base URL

Environment
URL

Local dev

http://localhost:8787

Production

https://api.millimetric.ai (your Worker deployment)

All endpoints are versioned under /v1/*.

Authentication

Authorization: Bearer {kind}_{env}_{prefix}_{secret}

The Worker resolves the prefix to a project + scope (see API keys). If the key is pk_*, the request must also include an Origin: header from the project's allowlist.

Content type

All ingest and admin endpoints accept application/json. Read endpoints accept query string parameters and return JSON.

Content-Type: application/json

Errors

All errors are returned as JSON with a stable error code field:

{ "error": "invalid_payload", "details": {...} }

Status

error

Meaning

400

invalid_payload

Body failed Zod validation. details is the flattened error.

400

invalid_params

Query string failed validation.

400

invalid_group_by

Unknown column passed to /v1/stats?group_by=.

401

missing_bearer_token

No Authorization header.

401

malformed_api_key

Key doesn't match `(pk

401

invalid_api_key

No matching key in the database.

401

key_kind_mismatch

Stored key has a different kind than the prefix claims.

401

invalid_session

Admin endpoint: user JWT failed Supabase validation.

403

origin_not_allowed

pk_* key from an origin not in the project's allowlist.

403

insufficient_scope

Key scope doesn't cover this endpoint.

403

forget_requires_secret_key

/v1/forget called with a pk_* key.

429

rate_limited

Token bucket exhausted. Retry-After header included.

500

internal_error

Unhandled server error. Worker logs have the trace.

Rate limits

Per project, per route, via in-memory token bucket:

Endpoint
Refill rate
Burst capacity

POST /v1/track

50/sec

200

POST /v1/batch

5/sec

20

Exceed → 429 rate_limited with Retry-After: <seconds>.

(Buckets are per-Worker-instance today. For production-scale fairness, swap to a Durable Object — see apps/api/src/auth/rateLimit.ts.)

CORS

Permissive on /v1/* and /admin/*. The actual authorization happens via Authorization + the project's origin allowlist for pk_* keys, not via a CORS allowlist.

Idempotency

Pass an event_id on /v1/track or per-event in /v1/batch:

If you send the same event_id twice the second call still inserts a row (we don't deduplicate at the database). Use this when you want a stable identifier for joining with your own systems — true server-side deduplication is on the roadmap.

Versioning

/v1/* is stable. Breaking changes will appear under /v2/*. Additive changes (new fields on existing endpoints, new endpoints) happen in place.

Last updated

Was this helpful?