Claude Telemetry Reference — Request & Response Parameters, Element by Element

Scenario: LiteLLM proxy (hosted in Azure) receives OpenAI-format requests from clients and forwards them to Claude 3P (third-party) — Claude served via Amazon Bedrock. You want to investigate user problems — were requests successful, what errors/status codes came back, and which parameters you must capture in logs to answer those questions.

Companion page: Logging & extraction tutorial · Last updated: 2026-08-05


Table of Contents

  1. The Two Wire Formats (Why There Are Two Sets of Parameters)
  2. Leg A — Client → LiteLLM: Request Parameters (OpenAI format)
  3. Leg B — LiteLLM → Bedrock: Request Envelope + Claude Body
  4. Claude Messages API Request Body — Element by Element
  5. Claude Response Body — Element by Element
  6. Bedrock Response Metadata (Headers)
  7. Leg A Response — What the Client Sees (OpenAI format)
  8. Status Codes & Errors at Every Layer
  9. "Success Illusions" — 200s That Are Actually Problems
  10. Symptom → Signal: Diagnosing User Problems
  11. Capture Checklist — What to Log, by Priority
  12. Reference Log Record Schema (JSONL)

1. The Two Wire Formats

One user request crosses two different API formats. When you read logs you must know which leg you're looking at, because field names differ for the same concept:

Client ──(OpenAI format)──▶ LiteLLM (Azure) ──(Bedrock envelope + Anthropic Messages format)──▶ Claude on Bedrock
       ◀─(OpenAI format)──            ◀──────(Anthropic Messages response)──────
Concept OpenAI format (Leg A) Anthropic/Bedrock format (Leg B)
Prompt token count usage.prompt_tokens usage.input_tokens
Completion token count usage.completion_tokens usage.output_tokens
Why generation stopped choices[0].finish_reason stop_reason
Response text choices[0].message.content content[] blocks
Model model model (+ Bedrock modelId in the URL)
Request ID id (chatcmpl-...) id (msg_...) + x-amzn-RequestId header

LiteLLM translates between them. Capture both sides where possible — translation is where subtle bugs live (dropped parameters, remapped finish reasons).

Note on "Claude 3P": 3P means third-party — Claude consumed via a partner platform (here: Amazon Bedrock) instead of Anthropic's first-party (1P) API. On 3P, auth is the platform's (SigV4/IAM on Bedrock), model IDs carry a platform prefix (anthropic.claude-...), quotas are per AWS account+region, and many errors are platform-layer errors that never reached the Claude model. One extra gotcha: model snapshots are deprecated per-platform on their own schedules — a deployment pinned to an old snapshot eventually gets ValidationException/404 on every request, a classic "all users suddenly failing" root cause.


2. Leg A — Client → LiteLLM: Request (OpenAI format)

What your users' applications send. Endpoint: POST /v1/chat/completions on the proxy.

Parameter Type Required What it does Why it matters for investigation
model string LiteLLM alias (e.g. claude-sonnet) — mapped by config.yaml to a real Bedrock model ID The alias can be re-pointed silently. A "model got worse" complaint may be an alias change, not the model. Always compare with the response model.
messages array Conversation: [{role: system|user|assistant|tool, content}] The actual user input. Needed to reproduce any reported problem. Malformed role sequences cause 400s.
max_tokens / max_completion_tokens int Anthropic requires it Output cap Too low ⇒ truncated answers (finish_reason: length) — one of the most common "the model cut off" complaints.
temperature float 0–2 Sampling randomness OpenAI range is 0–2, Anthropic is 0–1 — LiteLLM rescales. Note: rejected entirely on the newest Claude models (4.7+).
top_p float Nucleus sampling On Claude 4+, sending both temperature and top_p errors — a source of mysterious 400s after model upgrades.
stream bool Server-sent events streaming Streaming failures look different in logs (connection drops mid-stream, partial usage). Log whether the request streamed.
stop string/array Custom stop sequences Explains outputs that end abruptly with finish_reason: stop.
tools / tool_choice array/obj Function-calling definitions Schema errors here are a top cause of 400s; also changes response shape (tool_calls).
n int Number of completions Anthropic doesn't support n>1; LiteLLM behavior varies — a silent-incompatibility candidate.
user string End-user identifier Capture it. This is how you slice logs per user when investigating "which users are affected."
metadata / extra_body object Free-form tags (LiteLLM passes into spend logs) Put run IDs, feature names, app versions here — your join key across systems.
Header: Authorization: Bearer sk-... header LiteLLM virtual key Identifies the team/app. Hashed in spend logs. 401s at this layer never reached Claude at all.
Header: x-litellm-tags, custom headers header Request tagging Alternative tagging path if body metadata is inconvenient.

3. Leg B — LiteLLM → Bedrock: Request Envelope

Bedrock wraps the Claude request in AWS plumbing. Two Bedrock APIs exist; LiteLLM uses the InvokeModel path for bedrock/anthropic.* models (the Converse API is an alternative with its own field names — check your LiteLLM config).

Bedrock envelope (outside the Claude body)

Element Where What it is Investigation value
modelId URL path e.g. anthropic.claude-sonnet-5 or a versioned/inference-profile ARN (us.anthropic....) The actual model identity. Wrong/retired/not-enabled model IDs ⇒ ValidationException/AccessDeniedException.
AWS region endpoint hostname e.g. bedrock-runtime.us-east-1.amazonaws.com Quotas, latency, and model availability are per-region. Capacity issues often affect one region only.
SigV4 signature headers AWS IAM auth (not an Anthropic API key) 403 here = IAM/credentials problem in the proxy, not a user problem.
anthropic_version body Must be "bedrock-2023-05-31" for InvokeModel Missing/wrong ⇒ immediate ValidationException.
guardrailIdentifier / guardrailVersion headers (optional) AWS Bedrock Guardrails If configured, Guardrails can block/mask content — a separate refusal source from Claude itself.
contentType / accept headers application/json Rarely an issue; matters for streaming variant (invoke-with-response-stream).

4. Claude Request Body — Element by Element

The Anthropic Messages API body (inside the Bedrock envelope). This is the definitive list of what can be sent to Claude and what each element means.

Parameter Type Required Meaning Importance for problem investigation
anthropic_version string ✅ (Bedrock) API version pin Wrong value ⇒ 400 before the model runs.
model string ✅ (first-party; on Bedrock it's the modelId) Which Claude serves the request Highest-value field. Pin exact versions in production; log the one from the response.
max_tokens int Hard cap on generated tokens (on thinking models this includes thinking tokens) Truncation root cause #1. Compare usage.output_tokens vs max_tokens: if equal, the answer was cut.
messages array Alternating user/assistant turns; content is a string or an array of content blocks Malformed histories (first message not user, unpaired tool_result) ⇒ 400 invalid_request_error.
↳ content block text obj {type:"text", text:"..."} The prompt text itself — capture for reproduction (with PII policy in mind).
↳ content block image obj base64 or URL image source Oversized images ⇒ 400/413. Image-bearing requests have very different token counts.
↳ content block document obj PDF input Page/size limits (e.g. 32 MB, page caps) ⇒ 400/413.
↳ content block tool_use / tool_result obj Prior tool calls and their results in history Every tool_use.id must have a matching tool_result.tool_use_id — mismatch ⇒ 400.
system string/array System prompt (not part of messages) Prompt-version changes here explain behavior shifts. Log a hash of it if content is sensitive.
temperature float 0–1 Randomness Removed on newest models (400 if sent). Relevant to "output is inconsistent" reports.
top_p, top_k float/int Alternative sampling controls Same removal caveat; temperature+top_p together errors on Claude 4+.
stop_sequences array Custom strings that halt generation Explains stop_reason: stop_sequence + which string via response stop_sequence.
stream bool SSE streaming Streamed errors can arrive mid-stream as an error event after a 200 — log stream aborts separately.
tools array Tool definitions: {name, description, input_schema} Invalid JSON Schema ⇒ 400. Tool-heavy traffic has different latency/token profiles.
tool_choice object auto / any / {type:"tool", name} / none Forced tool choice changes response shape; on Bedrock, forced tool_choice on newest Sonnet requires thinking disabled.
thinking object Extended/adaptive reasoning config ({type:"enabled", budget_tokens} on 3.7/4.x-era; {type:"adaptive"} on 4.6+) Thinking consumes max_tokens budget → truncation; wrong shape for the model generation ⇒ 400.
metadata.user_id string Opaque end-user ID passed to Anthropic Abuse investigation & per-user tracing on the provider side.
stop_reason-affecting betas / headers (anthropic-beta) header Feature flags (first-party API; limited on Bedrock) A beta header on an unsupported surface ⇒ 400.

5. Claude Response Body — Element by Element

The Messages API response — this is your primary telemetry payload.

Field Type Meaning Importance — what it tells you
id string Message ID, msg_... Provider-side trace ID. Include when escalating to AWS/Anthropic support.
type string "message" on success, "error" on failure body First branch when parsing logs.
role string Always "assistant" Sanity check only.
model string The model that actually served the request ⭐ Compare against requested alias — catches silent re-pointing, fallbacks, and A/B routing. Evals are meaningless without it.
content array Content blocks produced See sub-rows. Empty content + a refusal stop reason = blocked request.
{type:"text", text} obj The answer text Capture (subject to privacy policy) — required to judge quality later.
{type:"tool_use", id, name, input} obj Model wants a tool called Malformed input JSON or wrong tool name = model-side quality issue worth tracking as a rate.
{type:"thinking", thinking, signature} obj Reasoning block (thinking-enabled models) Thinking length explains latency & token spend; must be passed back on multi-turn.
stop_reason string Why generation stopped ⭐ The single most diagnostic field. See table below.
stop_sequence string/null Which custom stop sequence fired Only set when stop_reason: "stop_sequence".
usage.input_tokens int Prompt tokens billed Cost & context-pressure tracking; sudden jumps = prompt regression.
usage.output_tokens int Generated tokens If == max_tokens ⇒ truncation. Distribution shifts signal behavior change.
usage.cache_creation_input_tokens int Tokens written to prompt cache Cache economics; caching support varies by platform.
usage.cache_read_input_tokens int Tokens served from cache Cache hit-rate; a drop to 0 = silent cache invalidator (changed prefix).

stop_reason values — learn these cold

Value Meaning Is it a problem?
end_turn Model finished naturally ✅ Normal
max_tokens Hit the output cap ⚠️ Truncation — user saw an incomplete answer. Track its rate; alert on spikes.
stop_sequence A custom stop string fired Usually fine; verify it wasn't accidental
tool_use Model is requesting a tool call Normal in agentic flows; a problem if your client doesn't handle it
pause_turn Long server-tool turn paused (must be resumed) ⚠️ If not resumed, users get silently incomplete answers
refusal Safety refusal (Claude 4.5+ surfaces this explicitly) ⚠️ HTTP 200 but no useful content — invisible unless you log it
model_context_window_exceeded Input exceeded the context window (4.5+) ⚠️ Conversation too long — needs compaction/trimming

Streaming responses (if stream: true)

Events arrive as SSE: message_startcontent_block_startcontent_block_delta* → content_block_stopmessage_delta (carries final stop_reason + usage) → message_stop. For telemetry: the final usage and stop_reason are in message_delta, and an error event or dropped connection can occur mid-stream after the 200 status — log stream completion separately from HTTP status.


6. Bedrock Response Metadata

Bedrock adds HTTP headers around the Claude body — free telemetry, capture them:

Header Meaning Value for investigation
HTTP status code 200 / 4xx / 5xx First-line success indicator (but see §9).
x-amzn-RequestId AWS request ID ⭐ The ID AWS support needs. Join key for CloudTrail / Bedrock invocation logs.
X-Amzn-Bedrock-Input-Token-Count Input tokens Cross-check against body usage — mismatches indicate translation bugs.
X-Amzn-Bedrock-Output-Token-Count Output tokens Same.
X-Amzn-Bedrock-Invocation-Latency Model latency in ms, as measured by Bedrock Splits latency: total client latency − this = network + proxy overhead. Essential for "it's slow" complaints.

Bonus: if you enable Bedrock Model Invocation Logging (an AWS account setting), Bedrock itself writes full request/response JSON to CloudWatch Logs or S3 — an independent record you can reconcile against LiteLLM's logs.


7. Leg A Response — OpenAI Format

What LiteLLM returns to the client after translating Claude's response:

Field Meaning Notes
id chatcmpl-... — LiteLLM-generated Different from the Anthropic msg_... ID — log the mapping.
object chat.completion / chat.completion.chunk
created Unix timestamp
model Model name as LiteLLM reports it May be the alias or the resolved model — verify which your version emits.
choices[0].message.content Answer text
choices[0].message.tool_calls Translated tool_use blocks OpenAI shape: {id, type:"function", function:{name, arguments}} (arguments is a JSON string).
choices[0].finish_reason Translated stop reason Mapping: end_turn→stop, max_tokens→length, tool_use→tool_calls, stop_sequence→stop. ⚠️ Nuanced reasons (refusal, pause_turn, model_context_window_exceeded) don't survive translation cleanly — capture the raw stop_reason on the Bedrock leg or you lose them.
usage.prompt_tokens / completion_tokens / total_tokens Token counts Renamed from input_tokens/output_tokens.
Header x-litellm-* (e.g. x-litellm-call-id, x-litellm-model-id, x-litellm-response-cost) LiteLLM trace headers x-litellm-call-id is the join key into LiteLLM spend logs — have clients log it.

8. Status Codes & Errors at Every Layer

A failed user request can die at three different layers. The status code alone doesn't tell you where — the error body does.

8.1 Errors generated by LiteLLM itself (request never reached Bedrock)

Status Cause Body signature
401 Invalid/expired virtual key OpenAI-style {"error": {...}} mentioning auth
400 Budget exceeded, blocked model, invalid request LiteLLM message (budget/team text is a giveaway)
429 LiteLLM-configured TPM/RPM limit for the key/team Rate-limit message referencing key/team limits
500 Proxy bug, DB down, config error Stack trace in container logs (Azure Log Analytics)
408/timeout LiteLLM request_timeout fired Timeout message; check proxy timeout config vs long generations

8.2 Errors from AWS Bedrock (request reached AWS, maybe not the model)

AWS exception HTTP Meaning Typical root cause
ValidationException 400 Malformed body / bad modelId / wrong anthropic_version / model access not granted in this region Retired model ID; missing model-access grant; translation bug
AccessDeniedException 403 IAM policy or model access denied Proxy's AWS credentials/role misconfigured
ResourceNotFoundException 404 Model ID doesn't exist in this region Typo'd/retired model, wrong region
ThrottlingException 429 Bedrock account-level TPM/RPM quota exceeded Traffic spike; quota too low; no provisioned throughput
ModelTimeoutException 408 Model took too long Very long generations; retry usually works
ModelNotReadyException 429 On-demand model warming up Transient; retry
ServiceQuotaExceededException 400 Hard service quota hit Request a quota increase
ModelErrorException 424 The model itself errored Provider-side incident
InternalServerException 500 AWS-side failure Retry with backoff; check AWS health
ServiceUnavailableException 503 Bedrock capacity Retry with backoff; consider cross-region failover

8.3 Errors from the Anthropic API itself (for comparison / first-party leg)

Anthropic's error body is always:

{
  "type": "error",
  "error": { "type": "rate_limit_error", "message": "..." },
  "request_id": "req_..."
}
Status error.type Meaning
400 invalid_request_error Bad params (e.g., budget_tokens ≥ max_tokens, bad roles, removed params on newer models)
401 authentication_error Bad API key
403 permission_error Key lacks access to model/feature
404 not_found_error Retired/typo'd model ID — the "everyone broke overnight" failure mode
413 request_too_large Payload too big
429 rate_limit_error RPM/TPM/TPD exceeded — check retry-after header
500 api_error Provider-side error
529 overloaded_error Capacity — back off and retry

8.4 How LiteLLM maps errors back to clients

LiteLLM catches provider exceptions and re-raises them as OpenAI-style errors with the same status code where possible (ThrottlingException → 429 RateLimitError, etc.). The original provider message is usually embedded in the error text — capture the full error body, not just the code, or you can't tell a LiteLLM 429 (key budget) from a Bedrock 429 (AWS quota). Also log LiteLLM's retry/fallback events: a request that succeeded on attempt 3 to a fallback model looks like plain success unless retries are logged.


9. "Success Illusions"

The cases that make "were all requests successful?" a harder question than counting 200s:

Looks like Actually is How to detect in logs
HTTP 200 Truncated answer stop_reason == "max_tokens" / finish_reason == "length"
HTTP 200 Refusal (no useful content) stop_reason == "refusal", or refusal-style text with near-zero output tokens
HTTP 200 Guardrails intervention (Bedrock Guardrails masked/blocked) Guardrail trace fields in Bedrock response; output replaced by canned text
HTTP 200 Empty/whitespace content content empty or output_tokens ≤ 2
HTTP 200 (stream) Stream died mid-answer No message_stop event; client-side abort; missing final usage
HTTP 200 Wrong model served Response model ≠ expected pin
HTTP 200 Succeeded only after retries/fallback LiteLLM retry logs; latency outlier with fallback model in response
HTTP 200 Tool call the client never executed stop_reason == "tool_use" with no follow-up request in logs

Definition worth adopting: successful request = HTTP 2xx and terminal stop_reason in an accepted set (end_turn, stop_sequence, expected tool_use) and non-empty content and stream completed. Compute this as a derived boolean in your log pipeline and trend it.


10. Symptom → Signal: Diagnosing User Problems

User complaint First fields to check Likely causes
"It just fails" HTTP status + error body layer (§8) Auth (401), quota (429), retired model (400/404)
"Answers are cut off" stop_reason / output_tokens vs max_tokens max_tokens too low; thinking eating the budget
"It's slow" X-Amzn-Bedrock-Invocation-Latency vs total latency; retry count; TTFT Proxy overhead, retries, long thinking, cold caches, region
"It refuses to answer" stop_reason == refusal; Guardrails trace Safety classifiers, Guardrails config, prompt phrasing
"Quality got worse" Response model, system-prompt hash, config-change timeline Alias re-point, model snapshot change, prompt edit
"Random errors sometimes" 429/503/529 rate over time by region Quota exhaustion at peak; provider incidents
"Works for me, not for them" user / key ID slices; per-key budgets & limits Per-key budget exhausted or key-level rate limit
"Tool calls don't work" tools schema in request; tool_use.input parse errors; unpaired tool_result Schema errors (400), client not handling tool_calls

11. Capture Checklist

What to actually capture, by priority. P0 = you cannot investigate incidents without it.

P0 — always capture, every request

P1 — strongly recommended

P2 — payloads (subject to privacy review)


12. Reference Log Record Schema

A single JSONL record per request that answers ~95% of investigation questions:

{
  "ts_start": "2026-08-05T09:14:03.120Z",
  "ts_end": "2026-08-05T09:14:07.891Z",
  "latency_ms": 4771,
  "ttft_ms": 640,

  "litellm_call_id": "d3b9...",
  "anthropic_msg_id": "msg_01AB...",
  "amzn_request_id": "f1e2...",

  "requested_model": "claude-sonnet",
  "served_model": "anthropic.claude-sonnet-5",
  "region": "us-east-1",

  "http_status": 200,
  "error_layer": null,
  "error_type": null,
  "error_message": null,

  "stop_reason": "max_tokens",
  "finish_reason": "length",
  "derived_success": false,
  "truncated": true,

  "input_tokens": 1834,
  "output_tokens": 1024,
  "max_tokens": 1024,
  "cache_read_input_tokens": 1500,
  "cost_usd": 0.0209,

  "stream": true,
  "stream_completed": true,
  "retries": 0,
  "fallback_used": false,

  "api_key_hash": "a1b2...",
  "end_user": "user-4711",
  "tags": { "app": "support-bot", "prompt_version": "v12", "env": "prod" },

  "messages_ref": "blob://payloads/2026-08-05/d3b9....json"
}

Note the pattern in the last line: metrics inline, payloads referenced (stored separately in blob storage with tighter access control). That keeps your queryable log lean and your PII surface small.


See the companion page for how to extract these logs from Azure (KQL, Python SDK, exports) and how to turn them into evaluation datasets: Claude + LiteLLM on Azure — Logging & Evaluation Tutorial.