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

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

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
Org host pattern is <org>.invariant123.com. Unknown slugs 404 by design.