Customer enrichment (recommended)
When the same application serves an agent experience graph, callmountExperienceGraph(app, options)
immediately after express() and before any global app.use(...) middleware. Its fail-closed startup check
allows earlier route-specific layers only when they cannot shadow the graph namespace.
customer.context.get, customer.personalization.decide, and customer.outcomes.track as shown
in the quickstart.
The same enrichment request records the final HTTP status, product-handler duration, optional product-issued
journey, and optional bounded runtime label. Never populate sessionRef or runtimeHint from query parameters,
agent input, or other unverified values.
The selected response waits at most timeoutMs (250 ms by default) for the enrichment request. A timeout or
failure returns the original JSON unchanged. For routes without a stable company identity, use
customer.contextFor(request); it validates the agent-returned Epode-Context-Interaction header so context is
available only on the immediate retry of the same interaction.
authenticate runs only on included business routes. The adapter bypasses it only for the exact POST relay
paths, which authenticate with their short-lived aqr1_ capability. Do not mount rejecting global product auth
above the Epode middleware.
Server-rendered HTML
Bounded successful HTML strings on included routes receive a non-executable<meta name="epode-customer-context"> marker containing base64url JSON. This preserves the page’s CSP and visible
content. Streams, buffers, documents over maxHtmlBytes (512 KiB by default), and pages that already own the
marker remain unchanged. Mutated pages become private, no-store; stale length, digest, and ETag headers are
removed or regenerated.
Run the anonymous-to-known retail example ·
Run the anonymous advertising example
Legacy structured outcome feedback
Install
Configure once
Run product authentication and authorized tenant selection first, then register Epode before the included route handlers. Identity accessors only read state your product already verified or owns.Cached APIs
The defaultcacheMode: "safe" does not silently turn a response with public, max-age, s-maxage,
immutable, or stale-while-revalidate into a private response. It skips those responses and logs one
configuration warning.
If ordinary callers share a CDN response but feedback-aware agent callers can opt in, use request mode:
Agent-Feedback-Request: 1. Only that request receives feedback metadata and
becomes private, no-store; the public response keeps its existing cache policy. The SDK also adds
Vary: Agent-Feedback-Request, so a CDN cannot replay the ordinary cached body to an opted-in caller. Use
cacheMode: "private" only when disabling shared caching on every included response is intentional.
Eligible ordinary 2xx GET and HEAD responses also carry a same-path-and-query Link marker, allowing
an installed Epode Companion to make one authenticated opt-in refetch without changing the public body.
* matches one path segment (/docs/* matches /docs/guide). Use ** for any depth
(/docs/** also matches /docs/platform/authentication).
Async jobs
Instrument the result, not every polling response:authorizedCrawl.id must be the server-loaded job the
authenticated caller is allowed to access—not a raw header, query value, or unverified body field.
Server-rendered HTML
Use the same middleware and include the HTML route:<script id="agent-feedback" type="application/json"> contract.
It does not change the visible page.
Verify
Serverless and edge-hosted Node
Keep the middleware object outside the request handler when the platform reuses isolates. If the platform provideswaitUntil, attach feedback.flush() to that lifecycle hook after the response is committed:
Customer-agent coverage
Your middleware creates the HTTP feedback contract; it cannot make an independent customer agent act on that contract. For reliable Codex and Claude Code handling, customers install Epode Companion once for all Epode-instrumented products. This is a user-side plugin, not another company SDK or product key. Generic agents without the Companion can still use the API normally, but feedback remains best effort.What is not instrumented
Errors, redirects, health and metrics routes, assets, streams, binary responses, and routes outsideinclude remain untouched. JSON arrays and scalar values use response headers instead of being wrapped.
View the runnable Express example
View the cached search API example