Skip to main content

9. Integration Contract

Read this page first if you are implementing AgentID in an SDK, backend route, AI assistant, agent workflow, or custom provider integration.

This page is the canonical runtime contract. The SDK and API guides provide surface-specific details, but this page defines the rules that must stay true across all implementations.

The Golden Rule

One real provider call is one protected unit of work.

That means:

  • build the exact prompt or messages payload that will be sent to the provider
  • protect that payload once
  • run one guard decision for that provider call
  • send the protected payload to the provider
  • write one correlated completion ingest after the provider returns

If the application loops over old messages and emits one guard event per prior turn, the audit trail becomes misleading and looks duplicated.

Canonical Lifecycle

The intended runtime lifecycle is:

assemble prompt/history
-> guard
-> if denied: stop
-> if allowed: call provider with protected input/history
-> protect output if needed
-> ingest completion telemetry
-> finalize SDK transport timing when supported

In compact form:

guard -> model execution -> ingest

Important consequence:

  • guard() alone is not a full LLM lifecycle
  • a final spend-bearing completion row requires post-model ingest
  • masked logging after a raw provider call is not protection

Event Correlation Contract

Use these identifiers for distinct purposes.

workflowRunId / workflow_run_id

Use this to group a whole agent run or workflow timeline.

Examples:

  • one inbox triage run
  • one customer support workflow
  • one agent orchestration sequence with tool calls and one or more LLM steps

Do reuse workflowRunId across:

  • tool events
  • delivery events
  • inbox events
  • workflow lifecycle events
  • LLM steps that belong to the same run

Do not use workflowRunId as the idempotency key for one prompt/completion lifecycle.

clientEventId / client_event_id

Use this for one guarded operation event, especially one provider call.

For one provider invocation, the same correlated lifecycle should carry through:

  • /guard
  • provider execution
  • /ingest
  • /ingest/finalize when supported

Do not reuse one clientEventId for the whole workflow. A workflow can contain multiple provider calls and multiple non-LLM operations.

event_id on ingest

For completion ingest, event_id should resolve to the same canonical correlation as the guarded provider call. In the official SDKs, this is canonicalized to the guarded client_event_id.

guard_event_id

This is the guard-side event identifier returned by AgentID for guard telemetry. Log it for diagnostics and support, but do not replace workflow grouping or provider-call correlation with it.

What the UI Should Show

For a classic chatbot or one-off LLM request, one top-level Activity row is the intended UX. The prompt preflight / guard step and the completion step are correlated in the detail view instead of appearing as two separate list rows.

For an agent run, two views are normal and intentional:

  • a standalone prompt/guard Activity row for forensic prompt inspection
  • a grouped workflow timeline row for the larger operational sequence

This is not a duplication bug by itself.

The design intent is:

  • standalone prompt row = exact prompt/security inspection surface
  • workflow timeline = surrounding operational context such as tool calls, delivery actions, inbox steps, and LLM steps

If workflowRunId is present, the UI can show both at the same time. If workflowRunId is absent, official wrappers treat the call as a simple LLM request and collapse the related preflight/completion telemetry into one Activity row.

Correct vs Incorrect Patterns

Correct: wrapped or explicit protected provider call

raw prompt/history -> AgentID protection/guard -> protected provider call
-> protected output -> AgentID ingest

Incorrect: raw provider call plus masked logging afterwards

raw prompt/history -> provider
masked copy -> AgentID log

This can make the dashboard look masked, but the provider already saw the raw content.

Correct: one provider call, one guard decision

messages[] -> protect once -> one guard() -> one provider call -> one ingest

Incorrect: one conversation, many guard events for one provider call

message[0] -> guard()
message[1] -> guard()
message[2] -> guard()
then one real provider call

This is a common source of duplicate or triplicate guard rows.

Supported Automatic Wrapper Surfaces

Current official automatic wrapper coverage:

  • Node.js / TypeScript SDK: wrapOpenAI(...).chat.completions.create(...)
  • Python SDK: wrap_openai(...).chat.completions.create(...)
  • Vercel AI SDK wrapper: generateText() / streamText() with withAgentId(...)

If the application uses an unsupported surface, use explicit integration:

  • responses.create
  • Assistants API
  • custom provider helpers
  • raw fetch-based provider calls
  • custom app-local abstractions that never call agent.log() or /ingest

First-Read Implementation Checklist

If you are an AI assistant or engineer implementing AgentID, verify these in order:

  1. Find the exact runtime callsite that sends data to the LLM provider.
  2. Confirm whether that callsite uses a supported wrapper surface.
  3. If yes, pass the wrapped client/model into the real provider call.
  4. If not, implement explicit guard -> provider -> ingest.
  5. Route the exact full prompt or messages payload through the protected path.
  6. Ensure one provider call produces one guard correlation and one completion correlation.
  7. Preserve provider model, token usage, and latency in completion telemetry.
  8. Use workflowRunId to group the overall run, not to replace per-event correlation.
  9. Remove parallel raw provider calls in the same request path.
  10. Verify the UI shows the expected standalone prompt row and/or grouped workflow timeline.

Troubleshooting Map

I see a guard row and a workflow row

Usually normal.

Check:

  • the prompt/guard row is the forensic Activity surface
  • the workflow row is the grouped operational timeline
  • both can exist for the same run when workflowRunId is used

I see duplicate or triplicate guard rows

Usually an integration issue.

Common causes:

  • the app called guard() once per historical message instead of once per provider call
  • the app retried guard with a new client_event_id
  • multiple code paths guarded the same provider invocation independently

I see Activity but no cost or ROI

Usually completion telemetry is incomplete.

Check:

  • event_type: complete
  • real provider model
  • token usage present
  • latency present when available
  • priced model id exists in AgentID pricing

The first guard request is much slower than later ones

Usually warmup or cold-path behavior, not a correlation bug.

Check:

  • runtime warm state
  • sdk_guard_ms
  • processing_time_ms
  • warm route / deployment timing

After this contract, use the guide that matches the real integration surface: