Skip to main content
Use this guide when you need to show which account and end user made a request, produce per-user usage reports, and investigate improper actions. The MKA1 API gives you public usage endpoints, live request correlation headers, and user-linked observability fields. For endpoint details, see the API Reference and open the Usage group.

What is available today

The live API edge and observability stack expose the following pieces:
  • Every API response includes X-Request-ID — including rejections (401/429) issued at the gateway edge.
  • The gateway ships a global Kong correlation-id plugin (part of the platform’s deployment configuration, applied on every install) with header_name: X-Request-ID, echo_downstream: true, and generator: uuid#counter. A client-supplied X-Request-ID is preserved; requests without one get a generated ID.
  • The audit trail records the same ID as correlation_id on every audited request — for requests rejected at the edge, the audit shipper resolves it from the echoed response header, so the ID a client quotes from an error response still finds the audit record.
  • For requests that reach the gateway, log lines carry the ID as httpRequestId (alongside traceId/spanId) and request traces carry it as the http.request_id span attribute — so one ID joins the response header, the audit trail, logs, and traces.
  • Live SigNoz logs include userId, externalUserId, and userContext.
  • Existing SigNoz assets include the MKLLM Gateway, Guardrail Dashboard, and Sandbox Commands dashboards, plus the Sandbox Commands 24h, Sandbox Command Errors 24h, and MKLLM Errors log views.
Recent examples from live systems:
  • GET /api/v1/llm/responses returned X-Request-ID: 0736e48b-3d39-48d8-806c-952c907*****#12
  • GET /api/v1/agents returned X-Request-ID: 0736e48b-3d39-48d8-806c-952c90*****#13
  • SigNoz 24-hour aggregates showed externalUserId values such as docs-pt-br-user with 14 events and docs-test-user with 7 events
  • SigNoz 24-hour aggregates showed userId values such as dRdj8VeoyuE7P9txS7ZVQ8ixI2***** with 63 events and uy95pqlp4ABgWCn5oLikRGV1TT***** with 23 events

What this looks like in our dashboard

list view of SigNoz individual log view

Send a stable end-user ID on every delegated request

For multi-user server-side integrations, send both Authorization and X-On-Behalf-Of. This is what ties usage, resources, and logs back to the right end user.
Capture the response headers from that request and store X-Request-ID with your own application log entry. That gives you a stable join key between the API edge and downstream observability.

Query per-user usage reports

The public usage endpoints support external_user_ids (delegated end-user IDs from X-On-Behalf-Of) and user_ids (platform user IDs) filters, plus group_by dimensions. Use them for account-level reporting, then use SigNoz logs to investigate a specific end user or request.
TypeScript SDK
Use externalUserIds for your end-user IDs from X-On-Behalf-Of. The usage endpoints cover completions, responses, conversations, embeddings, rerank, extract, classify, vector stores, and files. GET /api/v1/llm/usage/costs sums LLM spend over a time range. For spend across every service, including Compute and Sandbox, use the platform cost report; see Which report to use. Grouped by model, Responses and Completions usage both report the model that served each request. For Responses, that is the response’s resolved_model: the model that auto and any failover resolved to. The models filter matches the same served names, so filtering on auto matches nothing on responses that recorded a served model. Responses recorded before the served model was stored keep the name the request sent. The LLM cost report groups by the same served model. To see which model served one Responses request, read its resolved_model field (see Auto routing).

Read a long window page by page

Every usage endpoint except costs returns its window as time buckets, one page at a time:
  • bucket_width is 1m, 1h, or 1d, the default. Buckets follow UTC boundaries, so a 1d bucket runs from midnight to midnight UTC. When start_time or end_time falls inside a bucket, the first bucket starts at start_time and the last ends at end_time, so no bucket counts usage from outside your window. Read each bucket’s own start_time rather than assuming aligned times.
  • limit is the number of buckets per page. It defaults to 60 for 1m, 24 for 1h, and 7 for 1d. It accepts 1 to 1440, and a larger value returns 400. A 1h page holds at most 168 buckets and a 1d page at most 31, whatever limit you pass.
  • When the window holds more buckets than one page, the response has has_more: true and a next_page cursor. Pass the cursor back as page, with every other parameter unchanged, to get the next page. Repeat until has_more is false; the last page has next_page: null.
  • The cursor is opaque. Send it back only with the same parameters as the page that returned it. A malformed cursor returns 400, and so does one from a query with a different start_time or bucket_width, or one that does not resume inside the window. A change to any other parameter, such as end_time or a filter, is not detected.
  • end_time is optional. Without it, each page’s window ends when that page is computed, so the pages of one query can end at different times. Pass an explicit end_time when you page.
For example, 30 days of 1d buckets take five pages at the default limit of 7, or one page with limit=31. No usage endpoint drops buckets silently: a window larger than one page always comes back with has_more: true. Each page also says when it was computed and whether its figures are final. Usage from the last 24 hours is provisional and can still grow; see When usage figures are final.

Register actions as a fixed audit vocabulary

Per-user and per-unit misuse detection depends on your services emitting consistent action events. The shared audit contract defines the required fields:
  • request_id
  • actor_id
  • unit_id
  • route_name
  • action
  • resource_type
  • resource_id
  • outcome
  • policy_action
If a policy or guardrail fires, record that in the same event:

Detect improper actions

Group your logs and dashboards on actor_id, unit_id, action, outcome, and policy_action. This lets you answer two different questions:
  • Which end user or unit generated the activity?
  • Which actions were denied, throttled, or blocked by policy?
The audit contract defines these outcomes:
  • success
  • denied
  • throttled
  • validation_error
  • policy_violation
The corresponding policy_action values are:
  • warn
  • block
  • escalate
A live example already visible in SigNoz on March 30, 2026 shows runtime limiting of tool activity in the gateway:
  • message: Limiting parallel tool calls to respect max_tool_calls
  • requestedToolCalls: 2
  • allowedToolCalls: 1
  • maxToolCalls: 5
  • responseId: resp_bcc0e8bba9524e8d9c14289c0c6*****
That pattern is useful for improper-action detection because it shows the attempted action count and the allowed action count in the same event.

Practical investigation flow

When you investigate a questionable request, use this order:
  1. Start with the account-level usage report in the API Reference usage endpoints.
  2. Narrow to the end user by X-On-Behalf-Of and externalUserId.
  3. Use X-Request-ID to correlate the exact edge request.
  4. Inspect SigNoz logs for userId, externalUserId, userContext, route_name, action, and outcome.
  5. Check the MKLLM Gateway, Guardrail Dashboard, and Sandbox Commands dashboards for surrounding policy or tool activity.
If the investigation involves Agents resources, you can also list Agents with deleted=true to return soft-deleted agents for audit. This gives you a consistent path from usage reporting to a specific request and then to the exact action event that was allowed, warned, blocked, or escalated.