Metering API reference

Base URL: https://r3ckon.com

The metering API accepts usage metadata and nothing else. It exists so gateways, self-hosted stacks, and internal pipelines can feed the ledger without a managed provider connection.

Authentication

Every request carries a bearer key:

Authorization: Bearer aiok_ingest_…

Keys are account-scoped and role-scoped:

  • aiok_admin_…: full console access plus every API below. This is the key revealed once at signup.
  • aiok_ingest_…: may push usage (both ingest endpoints) but cannot read the console or manage the account.
  • aiok_sdk_…: may push to the SDK feed only.
  • aiok_read_…: read-only. May call the account read surfaces (the overview API, certificate PDFs, and every MCP tool) and can neither push usage nor manage anything. The only key that belongs in an MCP client config.

Only a SHA-256 hash of each key is stored. Rotation is currently by request (info@r3ckon.com) after billing-email verification.

POST /api/v1/ingest/authoritative

The billing-grade feed: daily (or coarser) aggregates that estimates, retirements, and invoices are computed from.

curl -X POST https://r3ckon.com/api/v1/ingest/authoritative \
  -H "Authorization: Bearer $R3CKON_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "apiConnection": "anthropic",
      "model": "claude-sonnet-5",
      "region": "us-east-1",
      "inputTokens": 18400000,
      "cachedInputTokens": 12100000,
      "outputTokens": 2300000,
      "reasoningTokens": 410000,
      "serviceTier": "standard",
      "timestamp": "2026-08-04T00:00:00Z"
    }]
  }'

Response: { "accepted": 1 }

Event fields

Field Type Required Notes
model string (1..200) yes The provider's model identifier, verbatim. Unknown models estimate under the registry's most conservative class.
inputTokens integer >= 0 yes The FULL input stream, including any cached portion.
outputTokens integer >= 0 yes Completion tokens.
region string (1..64) yes Cloud region code when known (e.g. us-east-1). Unrecognized values estimate under conservative unknown-facility parameters. A recognized code selects that cloud's published facility profile, which can lower the estimate relative to the unknown default, so report the region truthfully; region values are recorded with the event and are auditable.
timestamp ISO 8601 datetime with offset yes The period the aggregate covers (use the day start for daily buckets).
cachedInputTokens integer >= 0 no The cached SUBSET of inputTokens. Omitted means unreported, not zero.
reasoningTokens integer >= 0 no The reasoning SUBSET of outputTokens, where the provider reports it (never billed twice). Omitted means unreported; for reasoning-class models that triggers the methodology's conservative hidden-reasoning multiplier.
serviceTier standard | batch | priority no Batch and priority tiers carry their own energy multipliers.
apiConnection string (1..64) no Which source produced this usage; groups the console's per-API filters. Accounts support up to 50 distinct connections.
endUserPseudoId string (<=128) no An opaque pseudonymous id if you segment end users. Never send anything identifying.

The content boundary (HTTP 422)

Every payload is deep-scanned before parsing. If any key anywhere in the JSON matches a content-shaped name (prompt, completion, content, messages, message, text, input, output, system, body, response, query, answer, transcript), the request is rejected with 422 and nothing is stored:

{ "error": "Ingest rejected: payload contains content-shaped key \"prompt\". This platform is metadata-only and must never receive prompt or completion content." }

This is an architectural invariant, enforced by tests, not a policy.

Idempotency and re-submission

By default a POST ADDS the events you send. To restate a period instead, include a replace object naming the window you own:

{
  "replace": { "apiConnection": "anthropic-prod", "start": "2026-08-05", "end": "2026-08-08" },
  "events": [ … ]
}

Everything previously recorded for that apiConnection in that window (start inclusive, end exclusive) is removed, then your events are stored. This makes an overlapping or repeated cron safe: re-running the same window restates it rather than doubling it, and a missed day is fixed by running again with a wider window.

The replace is scoped to the one apiConnection you name, so it can never remove usage that arrived from a different source. Send replace on the first request of a multi-batch upload only; later batches would otherwise delete the rows the earlier ones just wrote.

The sync scripts do this for you.

Errors

Status Meaning
400 Malformed JSON, missing events array, or field validation failure (message says which).
401 Missing or unknown bearer key, or a role without ingest rights.
422 Content boundary violation (see above).

POST /api/v1/usage-events

The SDK feed: identical body and validation, accepted from sdk, ingest, or admin keys. It powers live views and is never billed from; billing and retirement always come from the authoritative feed.

Account APIs

The console is built on account-scoped APIs authenticated with your aiok_admin_… key. The stable, supported surface today:

  • GET /api/admin/overview: everything the dashboard shows, as JSON: monthly footprint with P05/P50/P90/P95 per category, retirements, credit position, invoices, certificates, connections, filters.
  • GET /api/admin/connections / POST (create) / DELETE /api/admin/connections/{id} / POST /api/admin/connections/{id}/sync with { "periodStart": "YYYY-MM-DD", "periodEnd": "YYYY-MM-DD" }.
  • GET /api/admin/certificates/{id}/pdf: the certificate PDF.
  • GET /api/admin/exports/{name}: reporting CSVs (water restoration summary, CDP Water and ESRS E3 inputs).
  • POST /api/admin/billing-portal: a Stripe customer portal session URL.

These return JSON errors in the same { "error": "…" } shape. Endpoints not listed here (reconcile, seeding) are operator-only and may change.

Machine-readable: this page as markdown, the whole product as llms-full.txt.

Next: what it costs

See pricing