# Cost traces

> Tag requests with a task id, list your tasks, and read back what each step cost, with an estimate of what one baseline model would have cost instead.

Tag the requests that make up one piece of work with a task id, then ask for the task's trace: which model ran each step, how many tokens it used, what it cost, and, if you name a baseline model, an estimate of what the whole task would have cost on that model alone.

## Tag your requests

Add `task_id`, and optionally `step_index`, to each [chat completion](https://app.alphaneural.io/docs/chat-completions) that belongs to the task:

```bash
curl https://backend.alfnrl.io/v1/chat/completions \
  -H "Authorization: Bearer $ALPHANEURAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openrouter/openai/gpt-4o-mini",
    "messages": [{"role": "user", "content": "List the line items on this invoice: ..."}],
    "task_id": "invoice-run-914",
    "step_index": 0
  }'
```

| Field | Type | |
|---|---|---|
| `task_id` | string | Any string you choose. Requests with the same `task_id` form one task. |
| `step_index` | integer | The request's position in the task. Steps are ordered by it, then by time. Optional, but send it: a request tagged with `task_id` alone is recorded as `step_index: -1` and sorts before step `0`. |

Both travel to the gateway as request `metadata`, which is how the usage is linked back to the task. Sending either one replaces any `metadata` of your own on that request.

## Read the trace

`GET https://backend.alfnrl.io/v1/tasks/{task_id}/trace`

| Query parameter | |
|---|---|
| `baseline` | Optional. A model id to compare against, for example `openrouter/anthropic/claude-opus-5`. |

```bash
curl "https://backend.alfnrl.io/v1/tasks/invoice-run-914/trace?baseline=openrouter/anthropic/claude-opus-5" \
  -H "Authorization: Bearer $ALPHANEURAL_API_KEY"
```

```json
{
  "task_id": "invoice-run-914",
  "steps": [
    {
      "step_index": 0,
      "model": "openrouter/openai/gpt-4o-mini",
      "provider": "openrouter",
      "prompt_tokens": 1840,
      "completion_tokens": 260,
      "upstream_cost": "0.000432",
      "charged": "0.0004752",
      "cost_source": "provider_reported",
      "billed": false,
      "at": "2026-09-10T14:20:41.204Z"
    },
    {
      "step_index": 1,
      "model": "openrouter/anthropic/claude-haiku-4.5",
      "provider": "openrouter",
      "prompt_tokens": 2100,
      "completion_tokens": 180,
      "upstream_cost": "0.003",
      "charged": "0.0033",
      "cost_source": "provider_reported",
      "billed": false,
      "at": "2026-09-10T14:20:49.877Z"
    }
  ],
  "totals": {
    "steps": 2,
    "unpriced_steps": 0,
    "prompt_tokens": 3940,
    "completion_tokens": 440,
    "upstream_cost": "0.003432",
    "charged": "0.0037752"
  },
  "counterfactual": {
    "baseline_model": "openrouter/anthropic/claude-opus-5",
    "estimated_cost": "0.0307",
    "estimated_saving": "0.027268",
    "basis": "estimate: this task's token counts priced at the baseline model's rates. Token counts are not portable between models, so treat it as indicative, not a quote."
  }
}
```

Money fields are decimal numbers encoded as strings, in US dollars.

### Steps

| Field | |
|---|---|
| `model`, `provider` | What ran the step. |
| `prompt_tokens`, `completion_tokens` | Tokens used, as reported for the request. |
| `upstream_cost` | What the provider charged for the step, before our margin. |
| `charged` | The step at the price we quote, margin included. |
| `cost_source` | Where the cost came from: `provider_reported` (the provider's own figure), `litellm_computed` (worked out by the gateway from its price table), or `unpriced` (no cost could be determined). |
| `billed` | Whether this record is the one your balance was debited from. Balances are currently debited by a separate meter, so this reads `false` for every step; the usage is still taken from your balance, and your wallet's balance history is the record of it. |
| `at` | When the usage was recorded. |

`totals` sums the steps. `unpriced_steps` counts the steps with `cost_source: "unpriced"`: those are steps whose cost was not captured, not free steps, so a total with unpriced steps understates the task.

### The baseline estimate

`counterfactual` prices the task's total prompt and completion tokens at the baseline model's list price and compares the result with the task's `upstream_cost`. Both figures are before our margin.

It is left out when the baseline is unknown or has no price, and when it would not have cost more than what you actually spent. It is an estimate, and says so in `basis`: the same text is a different number of tokens on different models.

## List your tasks

`GET https://backend.alfnrl.io/v1/tasks`

Every `task_id` with usage recorded under your account, most recently active first, each with the same totals its trace reports.

| Query parameter | |
|---|---|
| `limit` | How many to return. Default 50, at most 200. A negative or non-numeric value gets `400` `limit must be a non-negative integer`. |
| `cursor` | The `next_cursor` from the previous page, unchanged. Any other value gets `400`. |

```bash
curl "https://backend.alfnrl.io/v1/tasks?limit=20" \
  -H "Authorization: Bearer $ALPHANEURAL_API_KEY"
```

```json
{
  "tasks": [
    {
      "task_id": "invoice-run-914",
      "totals": {"steps": 2, "unpriced_steps": 0, "prompt_tokens": 3940, "completion_tokens": 440, "upstream_cost": "0.003432", "charged": "0.0037752"},
      "started_at": "2026-09-10T14:20:41.204Z",
      "last_active_at": "2026-09-10T14:20:49.877Z"
    }
  ],
  "has_more": false
}
```

`next_cursor` is present only when `has_more` is `true`. The list is paged by cursor rather than offset because a task that is still running moves to the top between pages. An account with no tagged usage gets `200` with an empty `tasks` array.

## Empty tasks

Usage is recorded from the gateway after each request completes, so a step can take a moment to appear. A task with nothing recorded yet, including a `task_id` you have never used, returns `200` with an empty `steps` array, zero totals and a `note`:

```json
{
  "task_id": "invoice-run-914",
  "steps": [],
  "totals": {"steps": 0, "unpriced_steps": 0, "prompt_tokens": 0, "completion_tokens": 0, "upstream_cost": "0", "charged": "0"},
  "note": "no usage recorded for this task"
}
```

## Scope

A trace, and the task list, cover only your account's usage. Task ids are strings you choose and often end up in logs, so another account using the same `task_id` never sees your steps, and you never see theirs. Any of your keys can read your traces.

If cost tracing is switched off on the server, both endpoints answer `404` `cost tracing is not enabled`.
