Skip to main content
Every other page in Zespan measures what an LLM call cost and how it behaved. Value measures whether it mattered. Your own backend reports a business outcome — a ticket that stayed deflected, a refund that was avoided, an SLA that was met — attributed to the trace that produced it, and this page joins that outcome to the trace’s real cost so you can see cost-per-success and value-per-dollar broken down by the agent and model that actually drove the result, not just cost-per-call.
Outcomes are usually known well after the trace that produced them has finished — a ticket stays closed for a few hours before you’d call it “deflected,” a refund is only “avoided” once the customer’s session ends. Reporting is out-of-band by design: your backend calls it whenever the result becomes known, from wherever that logic already lives (a webhook handler, a nightly reconciliation job), not from inside the same request that ran the agent.

Reporting an outcome

1

Instrument your traces as usual

Nothing about outcome attribution changes how you trace — keep using the SDK wrappers or manual spans you already have. You just need the traceId of the trace an outcome should attach to.
2

Report the outcome when it's known

From wherever your backend learns the result (a support-desk webhook, a billing reconciliation job, a scheduled check), call zespan.getClient().outcome() in TypeScript, or POST to the ingest endpoint directly from any other language.
3

See it attributed

The outcome appears on this page attributed to the trace’s agent and model, joined to that trace’s real cost — no matter which of the two arrived first.
kind is a free-form label — ticket_deflected, refund_avoided, sla_met, or whatever your team tracks — not a fixed enum. Whatever strings you report show up as filterable dimensions on this page.
See Outcomes for the full zespan.outcome() reference — parameter docs, how traceId resolution works, and error behavior.

What the dashboard shows

Summary stats

Breakdown table

Switch between By agent and By model to change which dimension the table (and the chart above it) groups by. Each row shows: Click any column header to sort by it. The chart above the table plots the top outcomes by value for the current dimension.
An outcome’s cost comes from a project-wide join against the trace’s LLM events — it isn’t limited by the selected time range. Only which outcomes are counted is windowed by the range selector; a trace’s cost is fixed at ingestion and doesn’t need windowing the way a correctable outcome does.

Corrections

Re-reporting the same kind against the same trace — for example, a ticket that reopens after you’d already reported ticket_deflected: true — doesn’t edit the earlier row. It’s a new outcome with the same identity, and the most recently reported one wins when this page reads the data. You never need to know an outcome’s prior value to correct it; just report the new one.

Before you’ve reported anything

Until at least one outcome has been reported for the project, this page shows a setup state with copy-paste snippets (TypeScript and the raw HTTP call) instead of the summary and table above — there’s nothing to attribute until outcomes start arriving.

Next steps

  • Outcomes — the full SDK reference for zespan.outcome(), including how traceId resolution and error handling work
  • Costs — the cost side of this join: per-model spend, trends, and forecasting, without the business-outcome attribution this page adds
  • Agent tracingwithAgent, for producing traces with the agentName that this page’s By agent breakdown groups by