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

# Track Claude Code & Codex Spend

> See what your team spends on Claude Code and Codex, per developer and per model, inside MarginFront

# Track Claude Code & Codex Spend

`@marginfront/code-cost-clarity` is a small command-line tool. One command wires your coding-agent usage telemetry (from Claude Code, Codex, or both) through a local collector and into MarginFront, priced per developer, per model, with accurate prompt-cache token splitting.

**This is a guardrail, not a hard limit. It tracks spend and can pause work on machines where it's installed. It can't stop charges at Anthropic or OpenAI, and it won't catch a machine that never installed it. For a hard ceiling, set a spend limit in your Anthropic Console or OpenAI account. You're responsible for your actual provider charges.**

**By default this is internal cost visibility, not billing.** Your company watches its own AI coding spend (per developer, per model). It doesn't charge developers. Out of the box it doesn't cut anyone off either: turning on spend caps is a separate, opt-in step (see [Spend Control](#spend-control-opt-in-caps) below).

**This tool never reads, needs, or transmits your Anthropic or OpenAI API key. The only credential it uses is your MarginFront key, and only to send usage to MarginFront.**

> **Beta (pilot software).** Code Cost Clarity is in active development. Pin a version, expect rough edges, and report issues.

> **Use `npx`, not `npm install`.** This is a CLI tool, not a library. Installing it into a project pulls in its dependencies and can surface unrelated audit warnings. Run it with `npx @marginfront/code-cost-clarity@latest init` (the `@latest` skips a stale npx cache). The `npm i ...` box on npmjs.com is npm's auto-generated default for every package and is not the intended usage here.

***

## What this does (one sentence)

Every time a developer runs Claude Code or Codex, this tool captures the token usage and sends it to MarginFront as a usage event, so you can see who used what, on which model, and how much it cost.

***

## The mental model (read this first)

Think of it like a cash-register receipt system:

1. **Claude Code (and Codex)** is the register. As it works, it broadcasts receipts: how many tokens, which model, which developer.
2. **The collector** (`otelcol-contrib`, open source) is the catcher. It runs in the background, catches the receipts, and writes them to a file.
3. **The forwarder** (the glue inside this tool) reads those receipts and sends each one to MarginFront.
4. **MarginFront** records the event, prices it, and shows it under the developer's email.

```
Claude Code (your task)
    |  broadcasts usage on a timer (you choose the interval) + once at session end
    v
Collector (otelcol-contrib, local)  --- converts cumulative->delta so nothing double-counts
    |  writes usage lines to a local file
    v
Forwarder (this tool, `run`)
    |  POST /v1/sdk/usage/record
    v
MarginFront (cost dashboard)
```

Codex feeds the **same** catcher, so the collector and forwarder don't change. Turning Codex on is one config block. See [Also capture Codex](#also-capture-codex) below.

**How often does it report?** It is timer-based, not per-message. Claude Code flushes usage on the export interval (`OTEL_METRIC_EXPORT_INTERVAL`) plus a final flush when the session ends. It ships at **5 minutes** by default (batched, so you get roughly one event per turn). Lower it (60 seconds or 5 seconds) for a more live drip, or raise it (10 minutes) to batch harder. The collector keeps repeated reports from double-counting no matter the interval.

***

## Quick start

**Get your MarginFront key first.** Log in at app.marginfront.com, then go to Build -> API keys -> Create key pair and copy the secret key (`mf_sk_...`). `init` will ask you to paste it.

```bash theme={null}
# One command. It asks you to paste your MarginFront secret key (mf_sk_...),
# wires telemetry into Claude Code and Codex, downloads the collector (~360 MB,
# one time), and starts a background meter that restarts itself at every login.
# Use the secret key (mf_sk_...); a publishable key (mf_pk_...) is rejected.
# Add --no-prompt on a server or in a script.
npx @marginfront/code-cost-clarity init
```

Then **just code**:

```bash theme={null}
claude          # or: codex   (or open the Claude/Codex desktop apps)
```

That's it. No `source`, no second terminal. Your spend shows up in MarginFront under your developer email, automatically, and the background meter restarts itself at every login.

> **Already running Claude or Codex? Quit and reopen them after `init`** (and start a fresh terminal session). The desktop apps and each terminal session read the telemetry config when they launch, so anything that was already open won't emit until it's reopened.

> **There is no `code-cost-clarity` command on its own.** Installing it does not add a new command to your computer, so typing `code-cost-clarity status` by itself says "command not found" and nothing is wrong. Every command runs through `npx`, like the ones on this page.

Check the meter anytime:

```bash theme={null}
npx @marginfront/code-cost-clarity status
```

**Prefer a live terminal view** instead of the background meter? After `init`, run `npx @marginfront/code-cost-clarity run` to stream your spend in the terminal (Ctrl-C stops it, and shuts its collector down too). You'll see a line per turn like:

```
[14:22:07] recorded developer@example.com · in=3210 out=287 · server=$0.0232 · cc=$0.0236 event=9f0c2a71-...
```

Also use Codex? See [Also capture Codex](#also-capture-codex). It's one config block and the same automatic capture.

***

## What gets captured, and what doesn't

This meters the coding agents: it reads the `claude_code.*` and `codex.*` telemetry that Claude Code and Codex emit to the local collector. `init` wires telemetry into **both** places these agents look, so it captures them in the terminal **and** the desktop apps:

* **Terminal / CLI** (`claude`, `codex`) and local IDE coding sessions read the config from `~/.claude/settings.json` and `~/.codex/config.toml`.
* **The Claude Code and Codex desktop apps** don't read that config block, and a Dock-launched app never sees your shell, so `init` also writes the same vars into the macOS launchd GUI-session environment, which Dock-launched apps inherit. Quit and reopen a desktop app after `init` so it reads them at launch.

It also labels each MCP tool call with its server and tool name, so a paid MCP tool (a web-search API, an enrichment service) can be priced apart from a free one. Only those two names ever leave your machine; the tool never sends the arguments a tool call was made with.

It does **not** capture:

* **The Claude consumer chat app** (the chat window). That's the general assistant, not Claude Code, so it never emits the coding-agent telemetry this reads.
* **Anything in a cloud sandbox or not connected to your machine**: a cloud session, or a remote / devcontainer / SSH setup where the agent can't reach the local collector at `127.0.0.1:4318`. If a surface doesn't run on your machine and reach the local collector, this can't see it.

***

## The commands

| Command                  | What it does                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init`                   | Creates `~/.marginfront-ccc/`, writes your settings and the collector config, downloads the collector binary, and **installs a background meter (a launchd job) that starts at login**. Wires telemetry into Claude Code and Codex, including the **desktop apps** (via the launchd GUI-session env). On a normal terminal it also **asks you to paste your MarginFront secret key (`mf_sk_...`) and saves it for you** (nothing to hand-edit). If your team shares one Claude/Codex login, it also asks which developer this machine's cost belongs to (see [Sharing one login across the team](#sharing-one-login-across-the-team)). Safe to re-run: it never re-asks once a key is saved, and never overwrites it. Add `--no-prompt` on a server or in a script. Add `--control` to also turn on spend caps (see [Spend Control](#spend-control-opt-in-caps)). |
| `status`                 | Shows whether the background meter is running, plus recent activity.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `start`                  | Starts (or reinstalls) the background meter. `init` does this for you; use it to bring the meter back after `stop`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `preview <capture.json>` | Prints the exact record it would send for one captured snapshot (Claude Code or Codex). Needs no MarginFront API key, so it's great for a dry run.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `run`                    | **Optional live terminal view.** Starts the collector in the foreground and streams your spend to the terminal until Ctrl-C (Claude Code, Codex, or both, no extra flag). The background meter from `init` already does this without a terminal; use `run` when you want to watch it live. Add `--fold-cache` only for an unpriced model (see below).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `stop`                   | Pauses the background meter (and any foreground `run`). `start` brings it back.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `uninstall`              | Stops everything (the background meter and the collector), reverts the telemetry config it wrote (Claude `settings.json`, the Codex `[otel]` block, the spend-cap hook and check script if you turned Control on, and the desktop GUI-session env), and deletes the collector binary and runtime files (reclaims the \~360 MB). Keeps your settings. Add `--purge` to also delete your settings and MarginFront API key.                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `help`, `version`        | The usual.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |

***

## Spend Control (opt-in caps)

By default this tool only **watches** spend. Turn on Spend Control and it can **stop** a Claude Code tool call when your team's admin-set cap is reached.

**What it does.** Your admin sets a spend cap in MarginFront (for example, "\$200 per developer per week"), in the dashboard's Money tab or through the [Spend Controls API](/api-reference/spend-controls). With Control on, this tool installs a tiny check that Claude Code runs before every tool call. If the cap is reached, the tool call is stopped with a message that names the cap and tells you to contact your admin for a raise. It runs on the same spend numbers you already meter; nothing new leaves your machine.

**Turn it on:**

```bash theme={null}
npx @marginfront/code-cost-clarity init --control
```

You'll see a consent prompt that spells out the change (Control converts this from a meter that watches you into one that can stop you). Say `y` to arm it. It arms on your **next** `claude` session (or a `/hooks` reload). After that, cap changes from your admin apply even to sessions already running.

**Turn it off:**

```bash theme={null}
npx @marginfront/code-cost-clarity uninstall
```

That removes the cap hook and the check script completely, along with the rest of the setup.

**Honest limits (read these):**

* **It fails open.** A missing, unreadable, or out-of-date cap file never blocks you. A broken cap should never brick your machine, so it doesn't.
* **It stops tool calls, not tokens.** The prompt that proposed a blocked call is already billed. What the cap kills is unbounded agentic work: the runaway sessions that produce surprise bills.
* **Caps in track mode never block.** They only watch and send alert emails. Only a cap in **enforce** mode that covers **all** AI tools stops anything on your machine; a cap scoped to a single provider is charted but doesn't block.
* **Per-developer caps need to know who the developer is.** A whole-team cap enforces on every machine. A cap aimed at one developer enforces only on machines where `CCC_DEVELOPER_EMAIL` names that developer (see [Sharing one login across the team](#sharing-one-login-across-the-team)). The tool never guesses whose machine this is, because guessing wrong would block the wrong person.
* **Claude Code only for now.** Codex, Gemini, and xAI tools aren't covered by the device cap yet.
* **"Your cap" vs. "the model refused."** If the model itself declines a request (its own safety layer), that isn't this cap. The block message says so, so you can tell the two apart.
* Requires `python3` on the machine (standard on macOS and Linux dev setups).

***

## Per-developer attribution is automatic

What makes "who spent what" work is the developer's email, and **Claude Code puts it in the telemetry on its own.** It's the developer's logged-in Claude account email. There is no manual email setup. Each developer just loads the telemetry settings and runs Claude Code normally.

**Works whether the developer signs in with an org-managed seat or an interactive login.** On org-managed Claude seats the email is stamped for free. If a developer's sign-in doesn't surface an email, the tool doesn't drop the usage. It attributes it to a clearly labeled placeholder customer (`claude-code-no-identity`) and prints how to fix it (sign in with an org-managed seat, or attach a customer mapping). You'll see the placeholder in `preview` or `run` output if it ever kicks in.

Codex attributes the same way, with one wrinkle: whether your email shows up depends on how you sign in to Codex (an org-managed seat or an OpenAI API-key sign-in stamps it; some interactive ChatGPT logins may not). That sign-in is between you and OpenAI; this tool never sees your OpenAI key. See [Also capture Codex](#also-capture-codex).

> Your MarginFront **API key** is separate from the developer's identity: it's the tool's own credential for posting to MarginFront. `run` needs it; `preview` does not.

***

## Sharing one login across the team

Some teams share **one** Claude or Codex login across the whole team. When that happens, every developer's telemetry carries the **same** email, so all the AI cost piles onto one person and per-developer attribution breaks. This tool fixes that without any change to how you sign in, because it runs locally on each laptop, so each laptop can carry its own developer identity.

**During `init` it asks one question:** which developer this machine's AI cost belongs to. The question is pre-filled with your global git email (`git config --global user.email`), which is usually the right person sitting at this laptop. You have three choices:

* **Press Enter** to accept the pre-filled email.
* **Type a different email** to attribute this machine to someone else.
* **Clear it and leave it blank** to auto-detect from whatever email the coding-agent login reports.

Whatever you choose is saved as `CCC_DEVELOPER_EMAIL` in `~/.marginfront-ccc/.env` (mode 600, right next to your key). To change it later, edit that one line and then `stop` and `start` (or re-run `init`).

**Which email wins, highest first:**

1. `CCC_DEVELOPER_EMAIL` (this machine's developer). When set, it wins for **every** record this machine sends: Claude and Codex, token turns and tool calls.
2. Otherwise the email the coding-agent login reports.
3. Otherwise the no-identity placeholder (`claude-code-no-identity` or `codex-no-identity`).

Leave it blank and nothing changes: attribution follows the coding-agent login's own email, then the placeholder.

> This is internal cost visibility only. `CCC_DEVELOPER_EMAIL` re-labels which developer the cost shows up under in your MarginFront. It does **not** change who pays or any customer's bill. The `--no-prompt` install (server or script) skips this question and leaves `CCC_DEVELOPER_EMAIL` unset.

***

## Cache pricing

Claude Code reports four kinds of tokens (fresh input, output, cache-read, and cache-creation), and Anthropic prices them differently. This tool **splits them into their correct typed fields** (`cacheReadTokens` / `cacheWriteTokens`) so MarginFront can price each at its own cache rate instead of lumping everything into one input number. **That token split is accurate by default.**

* **Default (recommended).** Cache tokens go in the `cacheReadTokens` and `cacheWriteTokens` fields; MarginFront prices each at the model's catalog rate.
* **`--fold-cache` (emergency round-up only):** for a model MarginFront can't price yet, this rolls the cache tokens into billed input at the fresh-input rate, pushing the number up toward reality so it's never silently low (a conservative ceiling). Use it only when a model has no cache price; otherwise the default is more accurate.

The dollar figure uses your MarginFront catalog's rate for each model. If a model isn't priced yet, its usage lands `NEEDS_COST_BACKFILL` (a **visible** gap, never a silent \$0), and one click in MarginFront prices it **both retroactively and going forward**. The raw cache numbers are also kept in `metadata`, and Claude Code's own cache-accurate cost is in `metadata.claudeCodeCostUsd` to reconcile against.

### Which cache-write rate you pay (one setup question)

Anthropic bills **cache writes** at two different rates, and which one you pay depends on how Claude Code signs in on the machine:

* **Claude subscription (Pro / Max / Team)**: 1-hour cache, billed at **2x input**
* **API key / Bedrock / Vertex**: 5-minute cache, billed at **1.25x input**

`init` figures this out for you. If the machine already sets `FORCE_PROMPT_CACHING_5M` or `ENABLE_PROMPT_CACHING_1H` (in the shell or in `~/.claude/settings.json`'s `env` block), the answer is detected and saved with no question. Otherwise `init` asks **one** question and saves the answer as `CCC_CACHE_WRITE_TTL` in `~/.marginfront-ccc/.env`. Every usage record then carries the answer in `metadata.cacheWriteTtl`, and MarginFront prices that record's cache writes at the matching rate.

**If it's unset** (you skipped the question, answered "not sure", or installed with `--no-prompt`): MarginFront prices cache writes at the **1-hour ceiling (2x input)**. It can overstate, but it can never under-count. Edit `CCC_CACHE_WRITE_TTL` (values `5m` or `1h`) anytime if the machine's sign-in changes.

> A long-context model id like `claude-opus-4-8[1m]` is normalized to `claude-opus-4.8` to match MarginFront's pricing table. The raw id is kept in `metadata.rawModel`.

***

## Also capture Codex

The same tool captures **Codex** too, and there's nothing extra to install: Codex reports to the **same** local collector. You get three setups for free: Claude Code only, Codex only, or both (the same developer's email shows up across all of it).

**Turn it on.** Add an `[otel]` block to your `~/.codex/config.toml`, pointing at the same collector:

```toml theme={null}
[otel]
exporter = { otlp-http = { endpoint = "http://127.0.0.1:4318/v1/logs", protocol = "binary" } }
```

The `exporter` is a **table**, with the endpoint nested inside it and ending in `/v1/logs`. (A flat `exporter = "otlp-http"` with a bare top-level endpoint silently exports nothing. Verified.)

Then run Codex normally. `run` already watches both sources from the one collector file, so there's no extra flag. `init` prints this same block at the end, so you have it handy.

### Why Codex's numbers are counted differently

Anthropic and OpenAI count tokens differently, and getting this wrong silently mis-charges you. For **Codex**, two of the token counts are **already inside** the others:

* The cached tokens are **already part of** the input count.
* The reasoning tokens are **already part of** the output count.

So this tool does **not** add reasoning on top of output, and does **not** bill input and cached as if they were separate. The accurate split it sends is:

| MarginFront field | From Codex                 | Why                                                                            |
| ----------------- | -------------------------- | ------------------------------------------------------------------------------ |
| `inputTokens`     | input count - cached count | the fresh, non-cached input only                                               |
| `cacheReadTokens` | cached count               | priced at the cheaper cache-read rate                                          |
| `outputTokens`    | output count               | reasoning is already inside this, billed at the output rate as OpenAI bills it |
| *(no cacheWrite)* | (none)                     | OpenAI has no cache-creation tokens                                            |

The raw reasoning, tool, and cached counts are still recorded in `metadata` for your own audit. They're just never billed twice. (If you ever hit a Codex model MarginFront can't price yet, `--fold-cache` works the same conservative way: it bills the whole input at the input rate and drops the cache-read field, so the number reads high, never low.)

### Whether your email shows up depends on how you sign in

Per-developer attribution rides on the developer's email from Codex's telemetry, the same idea as Claude Code. Whether that email surfaces depends on how you sign in to Codex: an org-managed seat or an OpenAI API-key sign-in stamps it; some interactive ChatGPT logins may not. (That sign-in is between you and OpenAI; this tool never sees your OpenAI key.) If it doesn't surface, the usage is **not** dropped. It lands under the `codex-no-identity` placeholder with a fix hint, exactly like the Claude Code path.

### Good to know

* **A `gpt-5-codex` pricing row must exist first.** A full-rate row (including its cache rate) has to be in MarginFront's pricing catalog. Until it is, a Codex event lands as `NEEDS_COST_BACKFILL` (a visible gap, never a silent `$0`). Adding that row is a one-time catalog step.
* **`codex-auto-review` is skipped.** It's an internal pseudo-model, not a real billable model, so the tool drops it rather than mis-price it.
* **`codex` and `codex exec` both export the usage logs this tool reads** (verified against a live session). Some Codex versions don't emit OTel *metrics*, and `codex mcp-server` telemetry has had bugs, but neither is the *logs* stream this tool uses, so neither affects capture here.
* **The `[otel]` block above is the verified shape** (exporter as a table, endpoint nested with `/v1/logs`). If a future Codex version changes it, keep the exporter pointed at the local collector on `/v1/logs`.

> **One thing to confirm:** check the exact Codex dollar figure against one real captured session, both that Codex reports per-turn (not running-total) token counts, and that your developer email shows up under your sign-in mode. The mapping above is the verified-safe default; the confirmation is a quick one-session check, not a reason to wait before installing.

***

## Confirm it landed (independent read-back)

Pull the most recent events for one developer straight from the API:

```bash theme={null}
KEY="<your MarginFront API key>"
curl -s -H "x-api-key: $KEY" \
  "https://api.marginfront.com/v1/events?customerExternalId=you@example.com&limit=5&sortBy=usageDate&sortOrder=desc"
```

***

## Maintain / shut off

* **Collector version is pinned for safety.** The install downloads one known-good collector release and verifies its fingerprint before it ever runs. Moving to a newer collector ships in a package update (so the integrity check always has a fingerprint to verify against). There is no useful per-run version override.
* **Stop temporarily:** `stop` pauses the background meter; `start` (or re-running `init`) brings it back. `status` shows whether it's running.
* **Remove it:** `uninstall` frees the \~360 MB, stops everything, and reverts the telemetry config it wrote (Claude `settings.json`, the Codex `[otel]` block, and the desktop GUI-session env). It keeps your key; `uninstall --purge` deletes your settings too.

***

## Troubleshooting

* **"No MARGINFRONT\_API\_KEY found":** paste your MarginFront key into `~/.marginfront-ccc/.env`, or `export MARGINFRONT_API_KEY=...` in the `run` shell. `preview` works without one.
* **HTTP 401 / 403:** wrong or expired key, or you pasted a **publishable** key (`mf_pk_*`) where a **secret** key (`mf_sk_*`) is required. The forwarder sends your usage, and publishable keys are rejected on writes. Pull a fresh secret key from your MarginFront dashboard under **Build → API keys**.
* **HTTP 422 / validation error:** body-shape mismatch. Run `preview <capture.json>` and compare the record.
* **Collector file stays empty:** make sure `init` finished and the background meter is running (`status`), and that you **opened a fresh Claude/Codex session after `init`** (an already-open app or shell won't emit until reopened). If it's still empty, re-run `init` to rewrite the collector config: it defaults to http/protobuf/4318, the transport that works with Claude Code, and gRPC/4317 silently exports nothing.
* **`usageCost` is null:** the normalized model id didn't match the pricing table. Tokens are still recorded; the cache-accurate cost is in `metadata.claudeCodeCostUsd`.
* **Numbers ballooning:** the collector's delta conversion isn't running. Re-run `init` to rewrite the collector config.
* **Seeing `claude-code-no-identity` (or `codex-no-identity`)?** Your sign-in didn't surface an email. Use an org-managed Claude seat, or attach a customer mapping. (Codex uses the `codex-no-identity` placeholder when a ChatGPT login doesn't surface an email.)
* **Codex spend not showing up?** Confirm the `[otel]` block is in your user-level `~/.codex/config.toml` (not a project config), the endpoint is `http://127.0.0.1:4318`, and `run` is going. Some Codex versions/subcommands have telemetry bugs, so check your version.
* **"Collector integrity check FAILED" or "Refusing to run an unverified collector":** the collector that downloaded didn't match the fingerprint the tool trusts, so it was stopped before it ran. The most common cause is a corporate network that inspects and rewrites downloads (a TLS-inspecting proxy), or a corrupted transfer. Re-run `init` on a network without that kind of proxy. Don't try to bypass the check: a download that keeps failing the fingerprint on a clean network is exactly the case the check exists to stop. The "unverified" wording instead means you pointed `CCC_OTELCOL_VERSION` at a release the tool has no fingerprint for. Unset `CCC_OTELCOL_VERSION` to use the built-in pinned version.

***

## Security

* This tool never reads, needs, or transmits your Anthropic or OpenAI API key. The only credential it touches is your MarginFront key, used only to POST usage to MarginFront.
* Your MarginFront API key never lives in the package. It's saved only in `~/.marginfront-ccc/.env` (mode 600) on your machine. The forwarder reads it from the environment only, never hardcoded, never logged.
* The published package contains only the built code and its README. Captured telemetry, the collector binary, and your `.env` are all kept off the machine that runs this tool and out of the package.
* The collector this tool downloads is checked against a built-in **SHA-256 fingerprint** (pinned from OpenTelemetry's signed checksums) before it is ever made runnable. If the downloaded file doesn't match, it is deleted and the install stops, so a download that was tampered with or rewritten in transit never runs on your machine.

***

## For developers (technical appendix)

**Input shape:** OTLP/JSON, `resourceMetrics[].scopeMetrics[].metrics[]`. Two metrics matter: `claude_code.token.usage` (one datapoint per `type` in input/output/cacheRead/cacheCreation) and `claude_code.cost.usage` (USD).

**Grouping key:** `(user.email, model, session.id)` → one MarginFront record per group.

**Token mapping:** `input`→`inputTokens`, `output`→`outputTokens`, `cacheRead`→`cacheReadTokens`, `cacheCreation`→`cacheWriteTokens` (Anthropic's `cache_creation_input_tokens`). With `--fold-cache`, cache tokens are added into `inputTokens` instead and the typed fields are omitted (no double count).

**Temporality:** Claude Code emits cumulative counters. The collector converts them to deltas; the forwarder trusts each line is already an increment.

**Ingest:** `POST https://api.marginfront.com/v1/sdk/usage/record`, headers `Content-Type: application/json` and `x-api-key: <key>` (not Bearer). Body envelope `{ records: [...] }`. The endpoint auto-creates the customer (by `customerExternalId`) and agent (by `agentCode`) on first POST, and resolves your org from the MarginFront API key (the body can't override it).

### Codex source (the second input shape)

**Input shape:** OTLP/JSON **logs**, `resourceLogs[].scopeLogs[].logRecords[]` (a different tree than Claude Code's `resourceMetrics`). The usage event is named `codex.sse_event`; it's detected by that name (on the `event.name` attribute or the record body) or, failing that, by the presence of any token-count attribute.

**Value encoding:** log attribute values arrive as `intValue` (a JSON string, per the protobuf int64 rule) or `doubleValue` (a JSON number) for token counts, and `stringValue` for identity fields. All three are handled.

**Token fields:** `input_token_count`, `output_token_count`, `cached_token_count`, `reasoning_token_count`, `tool_token_count`, plus `model`, `user.email`, and a session id (`conversation.id` / `session.id`). The accurate nested mapping is in the Codex section above. Cached is subtracted from input, reasoning is never added to output.

**Granularity:** one MarginFront record per `codex.sse_event` (one per turn). There's no cumulative-to-delta step on the logs pipeline (that's a metrics-only processor); each Codex log record is already one turn's usage.

**Identity:** `agentCode: "codex"`, `signalName: "codex-turn"`, `modelProvider: "openai"`, `environment: "development"`. Records carry `metadata.source: "codex"`.

**Routing:** both sources write one JSON document per line to the same collector file. The forwarder routes each line by which tree it has (`resourceMetrics` → Claude Code, `resourceLogs` → Codex), so one watcher handles either or both.
