Getting started
Invariant is “Datadog meets statsd” for humans and their agents: name a metric, write numbers, and look at them over time. Postgres + Django; not a timeseries DB.
Hosts
- https://invariant123.com — app apex
- https://<org>.invariant123.com — your org host
Auth
HTML pages use your session. The API is bearer-token only: Authorization: Bearer inv_…. Agents and the CLI use INVARIANT_URL + INVARIANT_TOKEN.
How to get a token: – Sign in and visit /tokens/ to issue an inv_… token (shown once), or – Ask an org admin to create one (e.g., via manage.py create_api_token --username <user> --name <label>).
Write verbs
- metrics create — idempotent on name
- points record — write one value on an existing metric
- ingest — many numbers at one timestamp; creates metrics if missing
- events send — something happened, to a subject, with properties; bumps events/<name> and events/<name>/subjects for that UTC day
- timers send — something took n ms, with properties; rebuilds timers/<name>/count|mean|p50|p95|max for that UTC hour. Filter and group by any property on /timers/ or GET /api/timers/stats/
One write rule everywhere: a metric holds one value per timestamp, and the last write wins. Recording or ingesting at an existing timestamp replaces that value.
API examples
All API requests are scoped to your current org and require Authorization: Bearer inv_….
curl -sS -X POST "$INVARIANT_URL/api/metrics/" \
-H "Authorization: Bearer $INVARIANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"use.sh/total-users","unit":"count"}'
curl -sS -X POST "$INVARIANT_URL/api/metrics/use.sh/total-users/points/" \
-H "Authorization: Bearer $INVARIANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value":1847}'
curl -sS -X POST "$INVARIANT_URL/api/metrics/body/bf-pct/points/" \
-H "Authorization: Bearer $INVARIANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value":11.2,"at":"2026-08-26T16:22:00Z"}'
curl -sS -X POST "$INVARIANT_URL/api/ingest/" \
-H "Authorization: Bearer $INVARIANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"at":"2026-08-10T11:02:00-07:00","values":{"body/weight":171,"body/muscle-mass":133.6,"body/fat-lb":27.6,"body/trunk-fat":15.6,"body/bf-pct":16.2}}'
curl -sS -X POST "$INVARIANT_URL/api/events/" \
-H "Authorization: Bearer $INVARIANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"events":[{"name":"center/source-connected","subject":"user:42","properties":{"source":"gmail"},"at":"2026-09-03T18:00:00Z","key":"outbox-123"}]}'
curl -sS -X POST "$INVARIANT_URL/api/timers/" \
-H "Authorization: Bearer $INVARIANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"timers":[{"name":"center/page-load","ms":412,"properties":{"user":"omar","route":"dashboard"}}]}'
# aggregate, filter, group — same numbers as /timers/center/page-load/
curl -sS "$INVARIANT_URL/api/timers/stats/?name=center/page-load&since=7d&group=user" \
-H "Authorization: Bearer $INVARIANT_TOKEN"
Python (the app you are timing)
from invariant_cli.timers import Timers
timers = Timers.from_env() # INVARIANT_URL + INVARIANT_TOKEN; background flush
timers.send("center/page-load", 412, {"user": "omar", "route": "dashboard"})
with timers.time("center/page-load", {"route": "dashboard"}) as props:
render() # sent on exit, even on exception
props["status"] = 200
CLI
export INVARIANT_URL="https://<org>.invariant123.com"
export INVARIANT_TOKEN="inv_…"
# Omar-taste
invariant metrics create use.sh/total-users --unit count
invariant points record use.sh/total-users 1847
invariant ingest --at 2026-08-10T11:02:00-07:00 \
body/weight=171 body/muscle-mass=133.6 body/fat-lb=27.6 body/trunk-fat=15.6 body/bf-pct=16.2
# Stranger-legible
invariant metrics create product/active-users --unit count
invariant points record product/active-users 1847
invariant events send product/signup --subject user:42 -p plan=pro
invariant timers send product/page-load 412 -p user=omar -p route=dashboard
invariant timers stats product/page-load --since 7d --group user
invariant timers time product/build -- pnpm build
MCP (agents)
Set env for your model client. Tool names are metrics_list, metrics_create, points_record, ingest, events_send, events_list, timers_send, timers_stats, etc.
export INVARIANT_URL="https://<org>.invariant123.com"
export INVARIANT_TOKEN="inv_…"
# Example: Grok MCP config expands ${INVARIANT_TOKEN}. Then:
grok mcp doctor invariant