# Open Agent Spend Attribution (OASA) Specification

Version 0.1 · Initial public draft · 2026-09-14 · [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) · Pinned; latest at https://www.onaro.io/spec

## Abstract

OASA defines a canonical record format that attributes AI-agent resource consumption and spend to agent identity, task, and cost object, and joins runtime telemetry, normalized billing, and payment settlement into a single auditable ledger row. It is designed to interoperate with OpenTelemetry GenAI semantic conventions (telemetry), the FOCUS specification (billing), and x402 payment-rail records (settlement). It is not a payment protocol and not a billing format; it is the join between them.

Partial records are valid. Every field outside the envelope is optional so adapters can emit what they know and reconcile later. Within a major version, the schema is additive-only.

## Design principles

- Hub-and-spoke: canonical middle, adapters at the edges.
- Settlement-agnostic: invoice, card, wire, and on-chain rails reconcile into the same spine.
- Additive to FOCUS and OpenTelemetry — never competing with them.
- Envelope required; all other groups optional so partial records remain valid.
- Versioned; additive-only within a major version.

## Record schema v0.1

### Group 1 — Envelope (required)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `record_id` | string (UUIDv7) | Yes | Globally unique record identifier. |
| `record_type` | enum: usage \| charge \| settlement \| allocation \| outcome | Yes | One row = one event class. |
| `schema_version` | string | Yes | Schema version string; "0.1" for this draft. |
| `occurred_at` | timestamp (RFC 3339, UTC) | Yes | When the event happened. |
| `recorded_at` | timestamp (RFC 3339, UTC) | Yes | When the record was ingested. |
| `source_system` | string | Yes | e.g. otel-collector, openai-billing, x402-facilitator. |
| `source_record_id` | string | Yes | Key in the source system. |
| `currency` | string | Yes | ISO 4217, or CAIP-19 asset ID for on-chain assets. |

### Group 2 — Agent identity

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `agent_id` | string | No | Stable internal agent ID. |
| `agent_name` | string | No | Human-readable agent name. |
| `agent_version` | string | No | Agent build or prompt version. |
| `agent_owner` | string | No | Team or principal accountable. |
| `parent_agent_id` | string | No | Parent in a sub-agent chain. |
| `agent_framework` | string | No | e.g. langgraph, crewai, custom. |
| `identity_scheme` | enum: internal \| erc8004 \| other | No | How external identity is named. |
| `external_identity_ref` | string | No | e.g. ERC-8004 registration reference. |

### Group 3 — Task & session context

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `session_id` | string | No | Runtime session identifier. |
| `trace_id` | string | No | W3C Trace Context / OpenTelemetry trace id. |
| `span_id` | string | No | Span id within the trace. |
| `task_id` | string | No | Org-defined task identifier. |
| `task_type` | string | No | Free taxonomy; org-defined. |
| `workflow_id` | string | No | Workflow or pipeline id. |
| `trigger` | enum: human \| scheduled \| agent \| event | No | What started the run. |
| `initiating_principal` | string | No | Human or agent that authorized the run. |

### Group 4 — Consumption

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `provider` | string | No | Model or API provider. |
| `service` | string | No | Service product name. |
| `model` | string | No | Model identifier. |
| `operation` | enum: chat \| completion \| embeddings \| tool_call \| inference \| storage \| api_call \| other | No | Operation class. |
| `input_tokens` | integer | No | Input token count. |
| `output_tokens` | integer | No | Output token count. |
| `cached_tokens` | integer | No | Cached / reused tokens when reported. |
| `reasoning_tokens` | integer | No | Reasoning tokens when reported. |
| `unit_type` | enum: tokens \| requests \| seconds \| gb \| custom | No | Unit of consumption. |
| `units_consumed` | decimal | No | Quantity in unit_type. |
| `tool_name` | string | No | Tool invoked, if any. |
| `request_count` | integer | No | Request count for the event. |

### Group 5 — Cost

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `list_cost` | decimal | No | List price; mirrors FOCUS ListCost semantics. |
| `billed_cost` | decimal | No | Billed amount; mirrors FOCUS BilledCost. |
| `effective_cost` | decimal | No | Effective cost after discounts; mirrors FOCUS EffectiveCost. |
| `cost_source` | enum: measured \| rated \| invoiced | No | How cost was derived. |
| `pricing_ref` | string | No | Price-sheet row ID. |
| `invoice_id` | string | No | Vendor invoice identifier. |
| `focus_charge_ref` | string | No | Pointer into a FOCUS dataset row. |

### Group 6 — Allocation

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `cost_center` | string | No | Cost center or department. |
| `project_id` | string | No | Project identifier. |
| `customer_id` | string | No | Customer or account identifier. |
| `product_line` | string | No | Product line. |
| `environment` | enum: prod \| staging \| dev \| other | No | Runtime environment. |
| `tags` | map<string,string> | No | Free-form tags. |
| `allocation_method` | enum: direct \| rule \| split | No | How cost was allocated. |
| `allocation_rule_id` | string | No | Rule that produced the allocation. |
| `split_fraction` | decimal (0–1) | No | Share of a split allocation. |

### Group 7 — Settlement join

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `settlement_rail` | enum: invoice \| card \| ach \| wire \| x402 \| other_onchain | No | How money moved. |
| `settlement_network` | string | No | CAIP-2 chain ID where applicable. |
| `settlement_asset` | string | No | CAIP-19 asset identifier. |
| `settlement_amount` | decimal | No | Settled amount. |
| `settlement_ref` | string | No | Tx hash, statement line, or remittance ID. |
| `settled_at` | timestamp | No | Settlement timestamp. |
| `reconciliation_status` | enum: unmatched \| matched \| partial \| disputed | No | Join status against billing/telemetry. |

### Group 8 — Control & governance

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `budget_id` | string | No | Budget that constrained or funded the spend. |
| `policy_id` | string | No | Policy identifier. |
| `authorization_ref` | string | No | Mandate / verifiable-intent credential ID. |
| `approval_type` | enum: policy_auto \| pre_authorized \| human_approved | No | How spend was authorized. |

### Group 9 — Outcome (optional, forward-looking)

Outcome capture is intentionally minimal in v0.1; it exists so cost-per-outcome queries have a landing zone.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `outcome_event_id` | string | No | Linked outcome event. |
| `outcome_type` | string | No | Outcome class (org-defined). |
| `outcome_value` | decimal | No | Numeric outcome value. |
| `outcome_unit` | string | No | Unit for outcome_value. |
| `business_metric_ref` | string | No | Pointer to a business metric definition. |

## Mapping tables

### OASA ↔ OpenTelemetry GenAI

| OASA | OTel |
| --- | --- |
| `provider` | `gen_ai.provider.name` |
| `input_tokens` | `gen_ai.usage.input_tokens` |
| `output_tokens` | `gen_ai.usage.output_tokens` |
| `model` | `gen_ai.request.model` |
| `operation` | `gen_ai.operation.name` |
| `agent_id` | `gen_ai.agent.id` |
| `agent_name` | `gen_ai.agent.name` |
| `trace_id / span_id` | `W3C Trace Context / OTel trace_id, span_id` |

### OASA ↔ FOCUS

| OASA | FOCUS |
| --- | --- |
| `list_cost` | `ListCost` |
| `billed_cost` | `BilledCost` |
| `effective_cost` | `EffectiveCost` |
| `provider` | `ProviderName` |
| `service` | `ServiceName` |
| `tags` | `Tags` |
| `charge rows (record_type=charge)` | `ChargeCategory / charge rows` |

### OASA ↔ x402

| OASA | x402 |
| --- | --- |
| `settlement_network` | `PaymentRequired.network (PAYMENT-REQUIRED)` |
| `settlement_asset` | `PaymentRequired asset / token` |
| `settlement_amount` | `PaymentRequired amount / price` |
| `settlement_ref` | `on-chain tx / SettlementResponse reference` |
| `(payee context)` | `PaymentRequired.payTo` |

Canonical: https://www.onaro.io/spec
