🔌

LiteLLM + Meridian

Send LiteLLM Generic API callback logs to Meridian gateway ingest, attribute calls by virtual key in the gateway key registry, and verify usage in the ledger.

Early accessShipped: 2026-09-22Time: 1-2 hoursDifficulty: Intermediate

Available to customers now. We confirm the first import with you in a sandbox before production.

Best for: Teams running LiteLLM as an AI gateway

LiteLLM + Meridian gateway ingest

Egress: api.onaro.io:443 (HTTPS)

What it does

LiteLLM's proxy can send a log line for every model call to any HTTP endpoint through its Generic API callback. Point that callback at Meridian and each call becomes a usage record: model, provider (when LiteLLM includes it), input and output tokens, LiteLLM's own cost for the call, and the virtual key that made it. Meridian maps each log line to the Open Agent Spend Attribution (OASA) 0.2.0 usage record, ties it to an agent, and rolls it into the spend ledger every hour.

You run LiteLLM. Onaro does not host or proxy your traffic, and Meridian stores no LiteLLM credentials. The only thing that crosses your boundary is the log line LiteLLM already builds, sent from your proxy to api.onaro.io over HTTPS.

There is no Connect card for LiteLLM in the Meridian app. Setup is a key from Onaro, a few lines of LiteLLM configuration, and a mapping from virtual keys to agents.

Prerequisites

  • A LiteLLM proxy with the Generic API callback available. The configuration below follows LiteLLM's Generic API documentation and its StandardLoggingPayload specification (verified as of 2026-10-07). Meridian's mapping is pinned to the payload shape of LiteLLM 1.74.0.
  • Virtual keys issued by LiteLLM to the applications or agents that call it. Meridian attributes spend by the hash of the virtual key, so traffic made with the master key cannot be told apart.
  • A Meridian ingest key. An organization admin creates it in Meridian: open Settings → Usage ingest keys, select Create key, name it after this proxy (for example "LiteLLM production"), and copy the key from the dialog. The key has the meridian:ingest scope only. The keys created on the Discovery event keys page are a different kind and are rejected by this endpoint with 401. The ingest key is shown once when it is created; store it in your secret manager. See Manage ingest keys.
  • Outbound HTTPS from the LiteLLM host to https://api.onaro.io, through your forward proxy if you use one.
  • Access to the Gateway key registry in Meridian at /meridian/settings/gateway-keys. The page is not linked from the Settings menu yet; open it by path in the Meridian app.

Setup

1. Confirm the endpoint

POST https://api.onaro.io/v1/meridian/ingest/gateways/litellm Authorization: Bearer MERIDIAN_INGEST_KEY Content-Type: application/json

The body is either one LiteLLM log object or a JSON array of them, up to 1,000 per request. The gateway name in the path must be litellm; any other name returns 404.

2. Add the callback to config.yaml

Add a named Generic API callback and only send successful calls. Failed calls have no usage to bill and would otherwise land as zero-token records.

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY litellm_settings: callbacks: ["meridian"] callback_settings: meridian: callback_type: generic_api endpoint: https://api.onaro.io/v1/meridian/ingest/gateways/litellm headers: Authorization: Bearer os.environ/MERIDIAN_INGEST_KEY event_types: - llm_api_success log_format: json_array

If your LiteLLM version does not resolve os.environ/ inside headers, template the value in at deploy time instead of committing the key.

PlaceholderValue
MERIDIAN_INGEST_KEYThe meridian:ingest key created in Settings → Usage ingest keys
OPENAI_API_KEYYour own provider key; Meridian never sees it

3. Keep batches under 1,000

LiteLLM queues log lines and sends them as one JSON array when the queue reaches its batch size or the flush interval passes. The defaults (512 lines or 5 seconds) fit Meridian's limit. If you change them, keep the batch size at or below 1,000:

export DEFAULT_BATCH_SIZE=512 export DEFAULT_FLUSH_INTERVAL_SECONDS=5

These variables apply to every batching logger on the proxy, not only this callback.

4. Map virtual keys to agents

Each log line carries metadata.user_api_key_hash, the hash of the virtual key that made the call. Map those hashes to agents in the Gateway key registry:

  1. Open /meridian/settings/gateway-keys in the Meridian app.
  2. Choose Gateway litellm, paste the key hash into Gateway key ref, and enter the Agent id. Optionally add Cost center, Pool id and Endpoint id, then Save mapping.
  3. For many keys, upload a CSV. The required columns are gateway,gateway_key_ref; optional columns are agent_id,cost_center,pool_id,endpoint_id,active.
gateway,gateway_key_ref,agent_id,cost_center,pool_id,endpoint_id litellm,88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b,support-triage,CC-4100,, litellm,2f1c9a77e0b54a1d8e3f6c2b9d0a4e7f1c3b5d7e9f0a2c4e6b8d0f1a3c5e7b9d,invoice-matcher,CC-2200,,

If no agent has the id you enter yet, Meridian creates one on the first matching call. Rename it and give it a department on the Agents page.

You can map keys before or after traffic arrives. Hashes Meridian has seen without a mapping are listed under Unmapped keys on the same page, with first seen, last seen and a count; map them there and later calls are attributed.

5. Forward proxy

If the proxy must leave through a corporate proxy, set HTTPS_PROXY on the LiteLLM process, allowlist api.onaro.io on the proxy and any TLS inspection device, and redact the Authorization header from proxy and application logs.

What Meridian receives

Each log object is mapped field by field. Anything not listed is ignored, including prompts and responses: Meridian does not store message content from this path.

Meridian fieldLiteLLM field
Record idid (turned into a stable UUID, so replays are dropped as duplicates)
Occurred at (required)endTime (epoch seconds, or milliseconds when above 10¹²)
Providercustom_llm_provider, when present
Modelmodel
Input tokensprompt_tokens
Output tokenscompletion_tokens
Costresponse_cost (USD)
Operationcall_type, lowercased; chat when absent
Gateway key refmetadata.user_api_key_hash

Records are written as usage records with source_system gateway:litellm and currency USD. Every number must arrive as a number; a numeric field sent as a string rejects that record.

Cost. When LiteLLM reports response_cost, that is the cost Meridian uses. When it does not, Meridian applies your organization's rate card (Settings → Rate cards), then its model price catalog, and records $0 only if neither has the model.

Timing. Records are stored as they arrive. The rollup into the spend ledger runs every hour at 20 minutes past, in UTC, and looks back 7 days. Records older than 7 days when they arrive are stored but not rolled up.

Verification

  1. Send one call through the proxy with a mapped virtual key.
  2. Read the response body, not the status code. Meridian returns HTTP 200 for any well-formed batch, even when every record in it is rejected. LiteLLM logs the response; check it, or replay one log line with curl:
curl -s https://api.onaro.io/v1/meridian/ingest/gateways/litellm \ -H "Authorization: Bearer $MERIDIAN_INGEST_KEY" \ -H "Content-Type: application/json" \ -d @one-log-line.json

A good response has accepted of 1 and an empty rejected list:

{"accepted": 1, "capacity_accepted": 0, "duplicates": 0, "rejected": [], "agents_created": 0, "unmapped_keys": []}
  1. Check the health endpoint with the same key. GET https://api.onaro.io/v1/meridian/ingest/health returns last_ingest_at, running totals, the duplicate rate and a key expiry warning.
  2. Check the registry. If the hash appears under Unmapped keys, map it.
  3. Check the ledger after the next rollup (20 minutes past the hour, UTC): the call appears under the agent on Agents & allocation, or under Unattributed if the key was not mapped.

What gets attributed and what doesn't

  • Attributed: calls made with a virtual key whose hash is mapped in the Gateway key registry. They roll up under that agent.
  • Unattributed: calls whose key hash has no mapping, and calls made with the master key. They roll up as unattributed and appear in the attribution inbox (Agents & allocation → Unattributed), where you can assign them by hand. Manual assignments survive later rollups.
  • Declared fields are not read from LiteLLM today. Meridian's mapping looks for metadata.agent_id, metadata.cost_center, metadata.pool_id, metadata.endpoint_id and metadata.hosting. LiteLLM's standard logging payload reports metadata you attach to a request under metadata.requester_metadata, not at those positions, so values set on a request or a model entry do not reach Meridian. Use the registry.
  • Cost center values from the registry are stored with the raw event but are not written to the ledger, so they do not drive department reporting.

Troubleshooting

SymptomCause and fix
401 "API key required"The key is not a meridian:ingest key (for example a discovery event key). Create an ingest key in Settings → Usage ingest keys.
401 "Invalid API key", "revoked" or "expired"Create a new key in Settings → Usage ingest keys, update the callback header, then revoke the old key.
403 "Scope 'meridian:ingest' required"The key has other scopes only. meridian:* does not imply meridian:ingest; the scope must be listed exactly.
403 "IP address not allowed"The key has an IP allowlist that excludes the LiteLLM host's egress address.
429 with Retry-AfterLimits count HTTP requests per key, per minute and per day; the lowest key tier allows 20 a minute and 100 a day. Keep the JSON array format and default batching so one request carries many calls.
200 with {"error": "batch_too_large"}More than 1,000 log lines in one request. Lower DEFAULT_BATCH_SIZE.
200 with entries in rejected and field: "operation"Meridian accepts chat, completion, embeddings, tool_call, inference, storage, api_call and other. LiteLLM reports some calls under other names, such as acompletion or aembedding, and those records are rejected. Contact Onaro support with the rejected value.
200 with field: "occurred_at"The log line had no endTime. Check that you are sending LiteLLM's standard logging payload, not a custom shape.
Calls show as unattributedThe key hash is not mapped, or the call used the master key. Map the hash under Unmapped keys.
duplicates is above zeroThe same log line was sent twice. Duplicates are dropped by record id and time, so retries are safe.

Limits

  • Early access. The field mapping is pinned to LiteLLM 1.74.0 and was built from LiteLLM's documented payload. Check your first responses for rejections before relying on the totals.
  • Ingest keys are managed by organization admins. Up to 10 active ingest keys per organization. See Manage ingest keys.
  • The registry pages are reachable by path only until they are linked from Settings.
  • HTTP 200 does not mean accepted. Always read accepted and rejected.
  • Attribution is by virtual key only. Declared metadata is not read from LiteLLM payloads, and the registry's cost center does not reach the ledger.
  • Late data. Records that arrive more than 7 days after the call are stored but not rolled up.
  • Message content is never stored from this path; only the fields in the table above.