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
messagespayload 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/finalizewhen 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()withwithAgentId(...)
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:
- Find the exact runtime callsite that sends data to the LLM provider.
- Confirm whether that callsite uses a supported wrapper surface.
- If yes, pass the wrapped client/model into the real provider call.
- If not, implement explicit
guard -> provider -> ingest. - Route the exact full prompt or
messagespayload through the protected path. - Ensure one provider call produces one guard correlation and one completion correlation.
- Preserve provider
model, token usage, and latency in completion telemetry. - Use
workflowRunIdto group the overall run, not to replace per-event correlation. - Remove parallel raw provider calls in the same request path.
- 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
workflowRunIdis 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_msprocessing_time_ms- warm route / deployment timing
Recommended Next Pages
After this contract, use the guide that matches the real integration surface: