Tasks, outcomes, contracts and scorecards
Metrx reads an agent the way you would read anyone doing a job: what it was asked to do, what came of it, what it cost, and how that compares with what you expected. Four objects carry that, in this order:
task → outcome → contract → scorecard
Task
A task is one unit of the agent’s work: a ticket handled, a document extracted, a reply drafted. It is identified by a task_id, a UUID, stamped on every LLM call made for it.
- TypeScript SDK:
task(client, fn)runsfnin a task context. Thetask_idis a v4 UUID unless you pass your own with{ taskId }. A UUID is kept as is. Mapping any other string to the same UUIDv5 as the Python SDK needs@metrxbot/sdk0.7.0; on 0.6.0 a non-UUIDtaskIdcorrelates nothing, so pass a UUID. - Python SDK:
m.task(...)opens the same context. Calls made inside the block carry itstask_id. - Gateway: send the
X-Task-Idheader yourself. - OpenTelemetry: a span attribute
metrxbot.task_idnames the task; without one, the trace id is the task.
A task with no outcome is still valid: its calls are correlated, which is enough for cost per task.
Outcome
An outcome is the result of a task, posted for its task_id:
POST https://app.metrxbot.com/api/v1/tasks/{task_id}/outcomeThe SDKs post it for you when the task block ends after setOutcome(...) (TypeScript) or set_outcome(...) (Python). The body is success (required), plus optional value_microcents, category, metadata and notes. If the block throws, the SDK posts a success: false outcome unless you turn that off.
Rules the endpoint enforces:
- The
task_idmust be a UUID; anything else is refused with400. - The
task_idmust appear on at least one of your organization’s LLM calls. That is how Metrx knows the task is yours, and it is where the outcome gets its agent. An outcome that arrives before its calls is held for up to 24 hours and returned as202withstatus: "pending_correlation"; it is written when the calls land. This is why the TypeScript snippet flushes the exporter before posting the outcome. - Posting again for the same
task_idupdates the outcome rather than adding a second one.
An SDK-posted outcome lands in the human_verified confidence tier, which means “confirmed by a person in Metrx or asserted by your own SDK”. Metrx cannot tell whether a person reviewed an SDK-asserted outcome, and the scorecard labels the tier “Confirmed” for that reason.
Contract
A job contract states what the agent is for and what counts as doing it well. It is set per agent with:
POST https://app.metrxbot.com/api/v1/agents/{agent_key}/contractor from the agent’s Contract tab in the dashboard (org admins and owners). The POST needs an admin session or an API key with the admin scope; an ordinary read/write key gets 403 insufficient_scope. GET on the same path returns the current version and every earlier one.
The six job fields are required on every version:
| Field | What it holds |
|---|---|
objective | What the agent is for, in plain text |
unit_of_work | What one task is, e.g. “one support ticket” |
owner | The person accountable (user id or email) |
acceptance_criteria | 1 to 20 items, each { metric, op, threshold, window } |
kpi_target | { metric, target, window } |
budget | { limit_microdollars, period }, period daily, weekly or monthly (1,000,000 micro-dollars = $1) |
op is one of >=, >, <=, <, ==. A window is a rolling window such as 24h, 7d or 4w. An optional human_baseline (cost_per_unit_microdollars, optional minutes_per_unit, and a required basis) records what a person’s unit of the same work costs.
Versions are append-only. Each POST publishes a new version numbered one above the highest ever written, and supersedes the current one; nothing is edited in place. Engine terms (the policy that says what Metrx may change for this agent) carry over from the current version unless the body supplies them.
The first version must say where its engine terms come from, with policy_defaults:
observe_only: terms that grant no authority. Every action class isshadow, so Metrx collects evidence and changes nothing. This mode also needstask_verb(one ofextraction,generation,retrieval,analysis,classification,review,calculation,orchestration) anddomain(one offinance,legal,compliance,healthcare,marketing,sales,support,engineering,data,general).inherit_workload_policy: copies the organization’s own active policy for the workload, as a snapshot. It is refused when no such policy exists.
With neither, every engine term must be in the body.
A contract is declared, not enforced. The scorecard reports against it; the gateway does not read contracts, and nothing is blocked because a check fails. Budgets that block requests are a separate setting (Settings → Budgets).
Scorecard
The scorecard reads the agent’s measured figures against its contract: one check for the KPI target, one per acceptance criterion, and one for the budget. Each check is pass, fail or insufficient.
A contract metric is free text, but only these names are computed. Any other name is shown as not measured, never guessed at:
| Metric | Measures | Needs 20 outcomes |
|---|---|---|
outcomes | Outcomes reported in the window | No |
human_verified_outcomes | Outcomes in the Confirmed tier | No |
error_flagged_outcomes | Outcomes flagged as errors | No |
human_verified_rate_pct | Confirmed outcomes as a percent of all outcomes | Yes |
error_flagged_rate_pct | Error-flagged outcomes as a percent of all outcomes | Yes |
cost_usd | Model spend in the window | No |
cost_per_outcome_usd | Model spend divided by outcomes in the same window | Yes |
No rate until 20 outcomes. A rate or per-outcome figure over fewer than 20 outcomes in its window is insufficient. That minimum is Metrx’s rule, not a contract term.
A check is also insufficient, not a zero, when:
- no calls and no outcomes reached Metrx in the window (an idle agent and an uninstrumented one look the same);
- a spend term has no metered calls (an absent call is not $0), or an outcome term has no outcomes;
- the window is longer than 400 days, or longer than your plan’s history.
The budget check reads spend over a rolling window: daily is 24h, weekly is 7d, monthly is 30d. The human baseline, when set, is shown beside the measured spend per outcome and is not graded, because an outcome is not guaranteed to be the same unit of work as a person’s.
The scorecard as a whole is fail if any check fails, insufficient if none fails but at least one is insufficient, and pass otherwise.
Each unit above, as plain arithmetic with a worked example: How Metrx counts.