For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.
Render agent artifacts
このページはまだあなたの言語に翻訳されていません。 現在取り組んでいますので、後でもう一度確認してください。
An artifact is a small, versioned JSON envelope your agent attaches to a tool call or AI response to describe a visual it showed a user. Agent Analytics validates the envelope and redraws that visual as a card in the session view, so a reviewer sees what the user saw instead of the JSON behind it. Amplitude draws the card with built-in renderers that follow the viewer's theme, so you never ship UI code and Amplitude never executes yours.
Emit artifacts when the visual was the answer and the raw payload doesn't tell you whether the answer was any good, such as a chart your agent generated or a set of choices it offered. If you only need the underlying numbers for analysis, you don't need artifacts: tool content already lands as event properties you can query in charts and cohorts.
Agent Analytics renders valid $agent_artifact envelopes automatically for every organization.
How it works
- Your agent produces a visual for its user, as it does today.
- Alongside it, your code attaches an artifact envelope to the tool call's
inputoroutput, or to an AI response attachment for a direct answer. - Agent Analytics recognizes the envelope, validates it, and renders a card in both the conversation view and the trace panel.
- The original tool payload or response text stays visible next to the card. Amplitude never replaces or hides it.
If a payload doesn't validate, Agent Analytics shows a short reason next to the raw output instead of a card. It never guesses at your data or renders a partial result.
Instrument artifacts with your coding agent
Use your coding agent to add artifact serializers where your application produces visuals. Open the repository in your coding agent, then copy and run this prompt. If your application doesn't send sessions to Agent Analytics yet, set up instrumentation first.
Add artifact instrumentation to this application's existing Amplitude
Agent Analytics integration.
Read the artifact contract:
https://amplitude.com/docs/amplitude-ai/agent-analytics/render-artifacts
Also read the instrumentation guide for the SDK or ingest path this
repository uses. If you can't access the documentation, ask me for it
before implementing the contract. Identify the existing content-capture
and redaction settings for the installed SDK or exporter. HTTP, OTLP,
and imported data do not inherit AI SDK settings.
1. Find the code that produces charts, summaries, tables, choices, or
static HTML shown to users. Trace each visual to its tool call and
existing instrumentation. Don't infer a displayed visual from arbitrary
JSON. Find the actual data used to render each visual; a chart ID, URL,
or configuration without values isn't a data snapshot. Preserve the
displayed filters, time range, timezone, and transformations. If that
data or an editable emission point is unavailable, report the gap.
Prefer capturing that data where the application produces the visual,
before truncation, then attaching it through existing instrumentation.
Avoid fetching it again just for analytics. If a reference needs a new
read, preserve resource permissions, bound the total work, and disclose
that the snapshot may differ from what the user originally saw.
2. Write reusable serializer functions in the project's language that
return a root-level $agent_artifact envelope with v: 1, the appropriate
type, and its required fields. Prefer chart, summary, table, or options.
If those types would lose meaningful content, implement a static html
fallback when feasible. Examples include choices with descriptions,
tables with semantic highlights, and reports containing several visuals.
Generate self-contained HTML/CSS/inline SVG from the actual source data,
escaping dynamic text. Follow the documented HTML restrictions and
per-type size limits. The result captures what was shown; controls stay
inert. For script-dependent visuals, generate a faithful static snapshot
from their data rather than simply removing scripts. Before skipping a
visual, evaluate both structured types and static html. If neither fits,
explain what content or meaning would be lost or what data is missing.
3. Derive labels, values, units, ordering, and scope from the application
code. Ask me about ambiguous mappings, such as cents versus dollars or
missing values. Don't invent values, silently truncate data, or change
the meaning of the visual to make an envelope validate.
4. Attach the envelope to the corresponding tool output, or tool input
for an input-side artifact such as a choice prompt. For a direct final-answer
visual, use an explicit artifact attachment on the existing AI response.
Follow the existing SDK, HTTP, or OTLP path and its serialization rules. Reuse session,
trace, and tool or message identifiers. Preserve application behavior and existing
payload fields, and avoid recording the same tool call or response twice.
Account for every visual the tool or final answer produces. For multiple visuals, evaluate one
static html report that preserves all meaningful content within the
documented limits. If that isn't feasible and no structured type fits,
explain the limitation and ask me how to proceed. Don't silently select
one visual or invent a multi-artifact envelope. Keep snapshot-only
failures from failing the underlying tool or response. Preserve request
cancellation and existing background persistence; don't add a storage
wait solely to capture data already available at the producer.
5. Preserve content-capture, exclusion, and redaction settings. Don't enable
capture to bypass metadata-only mode. The producer owns the content it
sends, including raw HTML, attributes, CSS, and inline SVG. Select only
source data approved for analytics capture. Use the existing SDK redaction
path without private helpers or an additional redaction pass. For HTTP,
OTLP, and imports, apply the source pipeline's policy before export.
Ordinary string redaction is best-effort; it doesn't interpret rendered
HTML or guarantee detection of values split across markup or encoded in
styles. Don't build a rendered-HTML PII detector, or omit HTML solely
because redaction is enabled. Viewer sanitization protects rendering,
not sensitive data at ingestion. Use synthetic fixtures for tests.
6. Add focused tests for representative inputs, optional or missing
fields, and invalid or oversized data. Validate the artifact envelope
and the complete serialized content field, including sibling fields,
against artifact and transport limits. Report an oversized snapshot
without truncating it or dropping the underlying event. Verify capture
exclusions, configured redaction, and the actual outbound payload.
Redaction can change markup or serialized JSON; check that the resulting
envelope remains parseable and represents the approved content faithfully.
Show the changed files, mapping assumptions, test results, and a
representative emitted envelope. Test slow or failed snapshot work and
cancellation. Record fixed omission reasons, such as missing data,
unsupported representation, size limit, or timeout, using existing span
identifiers. Don't log source content or exception bodies. Distinguish
expected capture exclusions from failures and ordinary nonvisual output.
Explain how to verify the artifact in
an Agent Analytics test session. Distinguish local serializer/contract
checks from live ingestion and visual verification; report which checks
you actually performed. For HTML fallbacks, verify that meaningful
content remains visible under the documented rendering restrictions.
7. Finish with an instrumented / skipped / needs a decision checklist.
For each visual path, name its source and emission point, coverage,
representation, and any limitation or unanswered mapping question. For
skipped visuals, explain why neither a structured type nor static html
works. Don't claim full coverage based on one successful example.
Review the generated changes and coverage checklist, especially skipped visuals, units, and field mappings, before using them. After running the tests, send a test session and confirm the artifact matches the visual your agent showed. If Agent Analytics reports Artifact not rendered, use the validation reason to correct the serializer.
To implement the serializer yourself, use the envelope, artifact types, and tool-call examples.
The envelope
Every artifact is a JSON object under a single reserved key. The key is deliberately vendor-neutral and nothing in the payload grammar is Amplitude-specific, so the same builder can emit to another platform that adopts this shape. You change the key, not your code.
{
"$agent_artifact": {
"v": 1,
"spec": "https://amplitude.com/docs/amplitude-ai/agent-analytics/render-artifacts",
"type": "chart",
"title": "Monthly payouts",
"caption": "August was the highest month.",
"scope": { "label": "Aug 2026" },
"locale": "en-IN",
"payload": {}
}
}
| Field | Required | Notes |
|---|---|---|
v | yes | Contract version. Always 1. |
spec | no | URL of the contract this payload follows. The key is vendor-neutral, so spec is how a payload says whose shape it is. Amplitude ignores it when rendering. |
type | yes | One of chart, summary, options, table, html. |
title | by type | Required for chart, table, and html; optional for summary and options. |
caption | no | One line of narrative under the card. |
scope | no | { label, dateRange? }, the period or population the data covers. Amplitude renders it beside the title, so a month's numbers never read as all-time. |
locale | no | BCP-47 tag. Drives number formatting. |
generatedAt, id | no | Provenance. Amplitude stores these but doesn't render them. |
payload | yes | Per-type body. Refer to Artifact types. |
Two conventions apply everywhere:
unitis a display string, not a concept. Send"INR","%","ms","req/s", or anything else. Amplitude appends it to values as-is and never interprets it. Number formatting comes fromlocale.- Colors are semantic, never literal. Send
primary,success, ordangerand Amplitude maps them to theme colors that work in light and dark mode, and that stay distinguishable for color-blind readers. Hex values, fonts, and sizes aren't part of the contract.
Attach the envelope to a tool call
Artifacts travel on the tool call that produced them. Pass the envelope as the tool's output, or as input when the artifact is the input, as with a question you asked the user.
const artifact = {
$agent_artifact: {
v: 1,
type: "chart",
title: "Monthly payouts",
payload: {
shape: "categorical",
kind: "bar",
unit: "INR",
items: [
{ label: "Aug 2026", value: 37279.7, color: "danger" },
{ label: "Jul 2026", value: 23925, color: "primary" },
],
},
},
};
session.trackToolCall("render_payouts_chart", latencyMs, true, {
input: JSON.stringify({ range: "last-3-months" }),
output: JSON.stringify(artifact),
});
Tool content comes from the input and output options. Amplitude ignores other spellings without an error, so your artifact never arrives. That includes toolInput and toolOutput, which are the names of the resulting event properties. If you rely on patch() for zero-code instrumentation, note that auto-extracted tool calls carry no input or output, so artifacts need an explicit trackToolCall. Refer to Track tool calls in the SDK reference.
Attach a direct-answer artifact to an AI response
When your application displays a visual directly in an assistant answer, attach its envelope to that existing AI response. Use the same v: 1 envelope and artifact types as tool artifacts. Each attachment has type: "artifact" and a root-level $agent_artifact field.
The Python SDK accepts the attachment as a dictionary and handles serialization:
agent = ai.agent(
"Your agent",
user_id=user_id,
session_id=session_id,
trace_id=trace_id,
)
agent.track_ai_message(
content=response_text,
model=model,
provider=provider,
latency_ms=latency_ms,
message_id=message_id,
attachments=[{
"type": "artifact",
"$agent_artifact": {
"v": 1,
"type": "table",
"title": "Sessions by region",
"payload": {
"columns": ["Region", "Sessions"],
"rows": [["Alpha", 12], ["Beta", 18]],
},
},
}],
)
For HTTP ingestion, serialize the attachment list as the [Agent] Attachments event property on the existing [Agent] AI Response event. Keep the response text and session, trace, and message identifiers. Don't emit a second response or a synthetic tool call for the same answer. Add attachments only after the application accepts the final answer; intermediate drafts aren't final-answer snapshots.
Agent Analytics renders artifact attachments in list order in both the conversation and trace views, including responses without text. It recognizes only explicit type: "artifact" attachments in this channel. It doesn't infer visuals from response text or fetch attachment URLs.
The attachment list supports up to 32 entries and 256 KB of serialized data, including other attachment types. Each artifact also follows its per-type limits. Your SDK or ingest path can impose a smaller limit; check its serialized output and reject an oversized snapshot instead of truncating it. For several visuals that form one report, prefer one static html artifact that preserves the complete report.
SDK attachments follow the SDK's content-capture and redaction settings. metadata_only mode omits attachment bodies, so artifact cards don't render. For direct HTTP, apply your source pipeline's privacy policy before exporting the attachment list.
Emit from any ingest path
The envelope is the same on every path. What changes is only where you put it: artifacts ride the tool call's existing content fields, so any path that carries tool content carries artifacts.
AI SDK
Pass the envelope as the output (or input) option, as in the examples above. Amplitude serializes it into the [Agent] Tool Output property.
HTTP API or a standard Amplitude SDK
Set the [Agent] Tool Output property directly on an [Agent] Tool Call event. Refer to Set up Agent Analytics for the full event contract and the ID fields every event needs.
curl -X POST https://api2.amplitude.com/2/httpapi \
-H "Content-Type: application/json" \
-d '{
"api_key": "YOUR_API_KEY",
"events": [{
"event_type": "[Agent] Tool Call",
"user_id": "user-123",
"event_properties": {
"[Agent] Session ID": "ticket-42",
"[Agent] Turn ID": 2,
"[Agent] Trace ID": "trace-abc",
"[Agent] Agent ID": "support-bot",
"[Agent] Tool Name": "render_refund_summary",
"[Agent] Tool Success": true,
"[Agent] Tool Input": "{\"order_id\":\"A-1\"}",
"[Agent] Tool Output": "{\"$agent_artifact\":{\"v\":1,\"type\":\"summary\",\"title\":\"Refund total\",\"payload\":{\"headline\":{\"value\":42.5,\"label\":\"Refunded\",\"unit\":\"USD\"}}}}"
}
}]
}'
Both properties take a JSON string, so serialize the envelope before assigning it.
OTLP
Put the envelope in whichever result attribute your instrumentation already uses on the tool span. Amplitude reads the first one present, in this order:
| Attribute | Convention |
|---|---|
gen_ai.output.messages | OpenTelemetry GenAI |
gen_ai.tool.call.result | OpenTelemetry GenAI |
output.value | OpenInference |
Tool input follows the same pattern, from gen_ai.tool.call.arguments or input.value. The tool name comes from gen_ai.tool.name or tool.name. Refer to Set up Agent Analytics for the OTLP endpoint and headers.
Artifact types
chart
Describe a chart by its data shape, which Amplitude validates, plus an optional kind that hints at the picture you drew.
shape | Payload | Use for |
|---|---|---|
categorical | items: [{ label, value, color? }] | Bars, columns, pies, donuts |
series | series: [{ label, color?, points: [{ x, y }] }] | Lines and areas over an ordered x |
steps | steps: [{ label, value }] | Funnels, waterfalls |
points | points: [{ x, y, size? }], where x and y are numbers | Scatter, bubble |
matrix | xLabels, yLabels, values: number[][] | Heatmaps, grids |
kind is a free string: bar, column, pie, donut, line, area, or anything else you call your chart. Amplitude draws a kind that has its own renderer that way. Anything else renders as the shape's default presentation with a visible note saying so, so a reviewer always knows they're looking at a substitute. Shapes that have no dedicated renderer yet (steps, points, matrix) render as a labeled table and carry the same note.
Optional chart fields: unit, xLabel, yLabel, and highlightedIndex (for categorical, to emphasize one item).
{
"$agent_artifact": {
"v": 1,
"type": "chart",
"title": "Monthly cash flow",
"locale": "en-IN",
"payload": {
"shape": "series",
"kind": "line",
"unit": "INR",
"xLabel": "Month",
"yLabel": "Amount",
"series": [
{
"label": "Cash-in",
"color": "success",
"points": [
{ "x": "Apr 2026", "y": 222389 },
{ "x": "May 2026", "y": 42810 }
]
},
{
"label": "Cash-out",
"color": "danger",
"points": [
{ "x": "Apr 2026", "y": 61453 },
{ "x": "May 2026", "y": 32580 }
]
}
]
}
}
}
summary
One number card at whatever density you need: a bare headline, a headline with a comparison, or a full overview with supporting metrics and facts. Send a headline; every other field is optional.
{
"$agent_artifact": {
"v": 1,
"type": "summary",
"title": "Net cash flow",
"scope": { "label": "Sep 2026" },
"payload": {
"headline": {
"value": 25008,
"label": "Net cash flow",
"unit": "SAR",
"tone": "positive"
},
"delta": { "value": 80192, "direction": "up", "comparedTo": "vs August" },
"metrics": [{ "label": "Entries", "value": 41, "tone": "neutral" }],
"facts": [
{ "label": "All-time balance", "value": 918220, "tone": "positive" }
],
"suggestions": ["Show entries for this month"]
}
}
}
tone is positive, negative, or neutral. It carries direction, not judgment. suggestions are the follow-ups your agent offered; they render as labels, because the session view is a record of what happened, not a place to continue the conversation.
options
A choice you presented to the user. Because the user has already answered by the time anyone reviews the session, the choices render as labels.
{
"$agent_artifact": {
"v": 1,
"type": "options",
"payload": {
"question": "Which quarter should I show?",
"options": [
{ "id": "2026-q1", "label": "Jan – Mar 2026" },
{ "id": "2026-q2", "label": "Apr – Jun 2026" }
],
"reason": "too_many_results"
}
}
}
Attach this one to the tool's input, since the question is what your tool received. reason is your own short code for why the agent asked; it's stored for analysis and not rendered.
table
{
"$agent_artifact": {
"v": 1,
"type": "table",
"title": "Spend by region",
"payload": {
"columns": ["Region", "Sessions", "Spend"],
"rows": [
["EU", 1200, 4517.5],
["US", 880, 91000]
]
}
}
}
Cells are strings or numbers; numbers format by locale and align right. Every row needs exactly one cell per column. Amplitude rejects a row of any other length instead of padding it, because padding would misstate your data.
html
For a visual the other types can't express, send self-contained markup.
{
"$agent_artifact": {
"v": 1,
"type": "html",
"title": "Custom SLA gauge",
"payload": {
"html": "<html><head><style>…</style></head><body>…</body></html>"
}
}
}
Amplitude enforces all of these:
- Static markup only: Amplitude rejects script tags, and the rendering frame can't execute scripts at all. Interactivity belongs in the declarative types.
- Fully self-contained: inline your CSS and data. Amplitude blocks images (including
data:images) and external requests for fonts, stylesheets, and APIs. Use HTML, CSS, or inline SVG for visuals. - No navigation: Amplitude removes link destinations, so links display as content without navigating when clicked.
- Bring your own styling: the frame isolates your document, so Amplitude's theme doesn't reach inside it.
- 100,000 characters maximum.
Amplitude shows an html artifact collapsed, with a button to render it, then draws it in a sealed frame that can't reach the surrounding page.
Prefer a declarative type wherever one fits: those cards follow the viewer's theme, work with screen readers, and appear in exports. html trades all of that for freedom of layout.
Limits
The Agent Analytics viewer rejects oversized artifact payloads instead of rendering partial data. Your SDK or exporter can impose smaller limits or truncate content before ingestion. Check the complete serialized content field, including the envelope, escaping, and sibling fields. If a snapshot exceeds a limit, report the failure and preserve the underlying event without the oversized snapshot. Don't truncate the snapshot to make it fit.
| Limit | Value |
|---|---|
Chart items (categorical) | 12 |
| Chart series | 4, up to 100 points each |
| Steps | 12 |
| Table | 8 columns × 50 rows |
| Summary metrics / facts / suggestions | 12 / 12 / 8 |
| Options | 12 |
html | 100,000 characters |
| Whole payload | 256 KB |
Privacy
You control what artifact content you send to Amplitude, including raw HTML, attributes, CSS, and inline SVG. Select source data approved for analytics capture and preserve your application's resource permissions and exclusions. Viewer validation and sandboxing protect rendering; they don't remove sensitive data before ingestion.
AI SDKs
Artifact envelopes use the same content-capture and redaction settings as other tool content and message attachments. Use the existing tracking methods and configured redaction pipeline. Don't call private SDK helpers or add a second redaction pass. Modes such as metadata_only and customer_enriched omit content bodies, so artifacts don't render.
SDK redaction applies configured patterns and callbacks to strings. It's best-effort and doesn't interpret browser rendering. For example, an email split across HTML elements or encoded in CSS may remain in the payload. Sending an HTML artifact doesn't establish that its contents are free of sensitive data. Redaction settings don't add an HTML-specific omission policy.
Configured redactors can also change markup or serialized JSON. Use synthetic fixtures to check the actual outbound envelope for parseability and fidelity. Select or mask excluded source values before constructing HTML, while preserving the normal SDK redaction configuration. No artifact-specific HTML preparation API is required. Check your installed SDK's tracking methods and transport limits.
HTTP, OTLP, and imports
HTTP, OTLP, Batch, S3, and warehouse imports don't inherit AI SDK privacy settings. Apply your source pipeline's capture, exclusion, and redaction policy before data leaves the source. Follow the non-SDK privacy guide and the procedure for your integration.
| Path | Policy enforcement point |
|---|---|
| HTTP forwarder, standard Amplitude SDK, Batch, or S3 | Before sending or uploading event content |
| Native OTLP or stored-span replay | Before the exporter sends span content |
| Warehouse import | In the source view before Amplitude reads it |
| Gateway or local model tracked through an AI SDK | The SDK tracking methods and their configured content policy |
Raw HTML has the same producer-owned content boundary on every path. An external pipeline doesn't need an artifact-specific SDK helper. Existing source redactors still apply, with the same limits of string matching. A successful event-schema or artifact validation check doesn't prove that the payload contains no sensitive data. Verify your policy against the final exported content, including other attributes and error text.
Troubleshooting
| Symptom | Cause |
|---|---|
| Raw JSON, no card | The $agent_artifact envelope key is missing or misspelled. Confirm the envelope is in the tool call's input or output, or an explicit AI response artifact attachment. |
| Nothing at all, no card and no raw content | Tool content isn't reaching Amplitude. Confirm you're passing input/output (not toolInput/toolOutput) and that your content mode captures tool content. |
| "Artifact not rendered" with a reason | Amplitude recognized the envelope, but the payload failed validation. The reason names the field. Fix it and re-emit. |
| A note about a substituted presentation | Your kind or shape has no dedicated renderer yet, so Amplitude drew the closest honest equivalent. The data is intact. |
An html card that looks broken | The document loads something external or relies on scripts. Generate a faithful static snapshot from its data. |
これは役に立ちましたか?