Documentation / Attribution

Attribution

Attribution is the decision of which agent and which department a dollar of AI spend belongs to. A cost center, in Meridian, is that department: the team finance charges. A cutover is the first date Meridian starts splitting a vendor's bill more finely. Dates before the cutover stay as they were. Suspense is the unallocated line on a chargeback. The dollar is still in the month. It is not dropped. On chargeback and on GL mapping this line is called unallocated (suspense).

Do not upload a bill for spend a connector already pulls. The upload and the connector are two copies of the same dollars. Reconciliation does not merge them. Use one source of record. Detail is on the invoice and upload page.

Each source has its own page for what arrives, what stays unattributed, and the limits. The tagging convention is the set of keys to put on a bill when the provider copies tags. The three-project example walks one month through every path that can actually separate three agents.

How Meridian works

Meridian does this in three steps, then books the month.

Ingest. Spend arrives from a scheduled provider pull, a file you upload, a gateway, or an OASA event. OASA is the open record format for agent spend. The grain depends on the source. Some rows are one request. Some are one day. Some are one invoice line.

Attribute. Meridian attaches an agent, a department, or both, using the order in How Meridian decides. A row with neither stays unattributed and shows up in the inbox described below.

Reconcile. When the same organization has both a provider bill and finer event rows for the same vendor, model, and day, Meridian scales those event rows so they add up to the bill. The bill becomes the tie-out, so the dollars are not counted twice. If there are events and no matching bill, the measured amounts stay. If there is a bill and no matching events, the bill stays on its own grain. The match is vendor, model, and day. When the names differ, Meridian does not force the rows together. A row that carries a pool id from the Gateway key registry is not scaled. It stays at the measured amount.

Book. Chargeback drafts the month. Every dollar lands on a line. Dollars with no department land on unallocated (suspense). Locking the month freezes that draft. Later corrections are adjustment lines on a later month. They do not rewrite the locked month.

What a source can identify

Providers expose five kinds of identity. One sentence on each says whether it reaches that provider's bill.

Account partitions. A workspace, a project, or an API key is a bucket the provider already uses for billing, so it is on the bill without a tag you attach. Anthropic cost is daily by workspace. OpenAI cost is daily by project and by API key. Azure and Google bills aggregate by resource or project. One workspace, project, or key shared by many agents cannot be split further from that bill alone. (Anthropic usage and cost, OpenAI costs)

Resource tags and labels. You attach a key and a value to a resource or, on Amazon Bedrock and Google, to a call, and the provider copies them onto cost rows after a delay. They reach the bill only after that copy happens. On AWS they also have to be activated as cost allocation tags. (Bedrock cost management, activating tags, Google request labels)

Per-request metadata. An id you send on the request, such as Anthropic metadata.user_id, OpenAI safety_identifier or request metadata, or Bedrock request metadata, can be stored for logs or safety. It is not a billing group, so it does not reach the provider's bill. (Anthropic Messages, OpenAI safety identifiers, Bedrock request metadata)

Users and seats. A seat names a person who is licensed, and that license is what the bill charges. It does not say which agent used the seat. Microsoft 365 Copilot is this kind of charge. Copilot Studio credit reports can name an agent. A licensed seat cannot be divided across agents. (Copilot Studio licensing, credits reports)

Gateway keys. A LiteLLM virtual key, a Kong consumer, or a Portkey key can carry per-request cost in the gateway's own ledger. That cost reaches the provider's bill only when the call the gateway sends upstream also sets a partition, tag, or label the provider records. Otherwise the provider invoice still shows the upstream credential. (LiteLLM spend tracking)

How Meridian decides

Each spend row is attributed in one of these ways:

  • Declared by the source. The event named the agent. That is a mapped gateway key, or an OASA event that includes an agent id.
  • Mapped by you. On Source identities you assign a workspace, project, API key, or OASA cost center to an agent and a department.
  • Inherited from a tag or a code. A cost-allocation tag on a FOCUS upload matches a department, a seat file names a department that already exists, or an OASA cost center matches one department's code or name.
  • Assigned by a rule. An allocation rule on Agents & allocation matches something still unattributed, such as the vendor or the model.
  • Assigned by a person. You select rows in the unattributed inbox and assign them.
  • Unattributed. No agent and no department yet. The row stays on the ledger.

When more than one could apply, this is what Meridian keeps:

  1. A row declared by the source stays declared. A rule, a mapping, and a person's assignment do not replace it.
  2. A row inherited from a tag, a seat file, or a matching department code stays inherited. A person's assignment does not replace it.
  3. A person's assignment replaces a mapped row, and it replaces a row that is still unattributed.
  4. A mapping replaces a row that is still unattributed. A rule does not replace a mapped row.
  5. A rule fills only rows that are still unattributed.
  6. Anything left over stays unattributed and goes to the inbox.

Saving a new mapping does not walk back rows that were already mapped, inherited, or declared. It fills unlocked rows that are still unattributed. The amount does not change.

The unattributed inbox

The inbox is the Unattributed tab on Agents & allocation. Spend lands there when Meridian has the dollars and does not have a department for them. The Reason column shows a sentence for why. The reasons you will see:

  • "This Anthropic workspace is not mapped to an agent or department."
  • "This OpenAI project is not mapped to an agent or department."
  • "This OpenAI API key is not mapped to an agent or department."
  • "Organization-level spend, including Scale Tier. Project grouping does not split this." That row is labeled No project.
  • "This cost center is not mapped to an agent or department."
  • "This usage has more than one cost center, or the cost center matches more than one department."
  • "This usage has no cost center." Meridian adds this on an OASA rollup only when the row is otherwise unattributed. Declared traffic that simply had no cost center does not fill the inbox with this sentence.
  • "Invoked by model ID, no inference profile."
  • "Agent or other tags present, but no cost center tag."
  • "Cost center value not mapped."
  • "No cost allocation tags."

A provider pull that never asked for a workspace, project, or tag has no one of those sentences. The row is still unattributed. The Reason column is empty until a finer path, a rule, or a person supplies a department.

To fix a row: map the identity on Source identities, assign the selected rows to an agent, or add a rule and choose Apply rules now. Mapping and Apply rules now update unlocked dates. They do not rewrite a locked month. At month end, unattributed dollars still post. They post to unallocated (suspense), so the chargeback balances.

Source identities

Settings → Source identities lists identities Meridian has seen, with spend for the last 90 days. The groups are Anthropic workspaces, OpenAI projects, OpenAI API keys, and OASA cost centers. You assign each one to an agent and a department. Still unmapped hides the ones you have already assigned. Clearing a mapping stops it from applying later. It does not edit spend rows already stamped.

No project is a real OpenAI bucket, not a missing id. It is organization-level spend. Scale Tier bundle cost sits here, and so does any other cost OpenAI did not assign to a project. The screen says: "Mapping it assigns all organization-level OpenAI spend, including Scale Tier bundles, to one department." That mapping does not divide the bundle across the projects whose usage it covered.

Anthropic's default workspace has no workspace id. Meridian shows it as Default workspace. Mapping it assigns that whole workspace.

Gateway keys are a different screen, Settings → Gateway key registry. FOCUS tag keys are not edited on Source identities. See tagging.

Spend splitting

Anthropic and OpenAI can be split more finely than a daily total. The connector stays Available. The split itself is turned on by request during Early access. Write to support@onaro.io. A platform admin turns it on with you and confirms the first month. The Source identities screen does not include a switch. It shows whether splitting is on, and the cutover date.

The cutover is the first UTC date written at the finer grain. Dates before it stay on the grain they already have: Anthropic as one row per day and model, OpenAI as one row per day and line item. A re-sync of those dates keeps that grain. A date is never stored both ways. For an organization that already has rows, the cutover is the next day, or the day after a locked month if that month would otherwise contain tomorrow. Past days of the open month stay coarse.

If the organization has no rows yet for that vendor, the cutover is the start of the first pull, about 30 days back, so a new connection is split from the first day it writes. Days inside a locked month in that window are skipped rather than written.

OpenAI can add API-key grouping later, also on request. That has its own cutover, on or after the project cutover. Dates between the two stay by project only.

Turning splitting off after a cutover is recorded is refused. Writing the old grain beside the new one would double the bill.

While splitting is off, a later sync can refresh the amount on a date you have already locked. The locked chargeback file is not rewritten. Once splitting is on, sync skips dates inside a locked month.

Details and the Scale Tier limit are on the Anthropic and OpenAI pages.

Locked months

On Chargeback, Lock & download freezes the month. The locked file is what you post to the general ledger. Meridian regenerates that file from the snapshot only.

A correction after the lock — a mapping you save later, a late row, a FOCUS row marked as a correction for an earlier period — arrives as an adjustment line on a later draft. The chargeback screen labels it with the original month. The locked month is not rewritten, and its total does not move.

Unallocated (suspense) is part of the locked snapshot too. Moving those dollars to a department later shows up as an adjustment that takes them off suspense and puts them on the department, in the later draft.