Skip to main content
Every backend can implement the customer-enrichment contract with HTTPS JSON:
  1. POST /api/v2/enrichment/requests with the server-only EPODE_API_KEY, operation, purpose, and optional verified/product-owned identity references.
  2. Rewrite the returned consent and answer actions to fixed company-owned same-origin routes.
  3. At those routes, forward only the aqr1_ authorization and allowlisted request body to Epode. Never forward cookies, product authentication, arbitrary headers, or the company key.
  4. POST /api/v2/customer-context before personalization.
  5. Record the exact signal IDs with /api/v2/personalization/decisions, then link the business result with /api/v2/personalization/outcomes.
See the exact Customer context API and the runnable dependency-free Python company example. If Epode is unavailable, omit _epode and return the normal JSON or server-rendered HTML response. The initial company request is one typed same-call event:
surface defaults to http_json and is exactly http_json, html, or mcp. Status, duration, session, and runtime are optional. A session reference must come from a product-issued journey, and a runtime hint must be a bounded, non-sensitive label observed by the company’s server. Never use a prompt, transcript, model/tool argument, raw customer content, or regulated trait. Disable automatic redirect following on every request carrying the product key and treat any 3xx as a failed enrichment call; return the unchanged product response.

Legacy structured outcome feedback

Use the manual protocol only when a first-party SDK is unavailable. Your implementation needs standard HTTP, JSON, UUIDs, SHA-256, HMAC-SHA256, and base64url encoding.

Download the contract

The archive contains the protocol guide, JSON Schemas, and a deterministic signing vector. Keep the same server-only configuration used by first-party SDKs:
For customer intelligence, emit accountRef and userRef only from verified product authentication and anonymousRef only from a stable, product-owned first-party pre-login ID. Keep customerRef only for durable ask_once; when accountRef is also present it must be the exact same opaque account ID. Never pair customerRef with userRef alone, and never use a name, email address, prompt, caller value, or agent-supplied value. Emit sessionRef only from server/product state that already proves a journey.

Implement the response path

1

Validate configuration at startup

Require a v2 af_live_... product key. Read AGENT_FEEDBACK_MODE; never place the key in a browser.
2

Select eligible responses

Include only configured product routes. Exclude errors, redirects, assets, health/metrics, streams, binaries, and Epode’s own endpoints.
3

Create a stable Ask once subject

For ask_once with a stable customer reference, derive afsub1_ + base64url(HMAC-SHA256(SHA256("epode-consent-scope:" + consent_scope), "customer-ref:" + trimmed_customer_ref)), where consent_scope is the key’s scope segment as defined in the protocol README. This keeps subjects stable across product-key rotation. The bundled protocol/v1/README.md and conformance.json are canonical; verify your derivation against the conformance vector before shipping. Do not put a consent lookup on the product response path. A cold response can safely emit a subject-bound consent_required capability. Epode Companion inspects that capability and treats Epode’s remembered decision as authoritative before asking or reporting. You may refresh /api/v2/consent/state in a bounded background task and cache approved or declined decisions briefly; an outage must never delay or fail the product response.
4

Sign a capability locally

Follow the compact claim order and signing vector in protocol/v1/conformance.json. The capability must expire within two hours. Append the derived subject as the optional s claim only for durable Ask once, followed by r, the non-negative consent revision returned by Epode. Use 0 when the subject is unknown. A stale revision cannot overwrite a newer decision. Never include the raw customer reference or product content. The pseudonymous subject is intentionally linkable within that product’s consent scope. Without a cached decision, emit the cold consent_required stage rather than waiting on Epode.
5

Add the stage-appropriate handoff

Append _agentFeedback to JSON objects without wrapping them. Use the Agent-Feedback header for arrays, scalars, or immutable shapes. Add Cache-Control: private, no-store. never_ask and previously approved Ask once interactions use state: "feedback_ready". Unknown Ask once and every Ask always interaction use state: "consent_required" with only requiredAction.submitDecision; do not expose submit or a report schema before approval. A declined Ask once decision emits a non-requesting state: "feedback_disabled" envelope with only a manageConsent block so an explicit user request can later change the decision. A failed background Ask once refresh leaves the cold subject-bound handoff intact.
6

Queue telemetry

Send opportunity telemetry in bounded background batches. Identity and journey references belong only in this private telemetry event; they never enter the agent-facing envelope. Neither telemetry delivery nor Ask once state refresh may delay the response.

HTML

For safe, finite server-rendered HTML, serialize the same complete, schema-valid envelope inside <script id="agent-feedback" type="application/json">. Escape it for an HTML script-data context. If safe mutation is not possible, use the response header contract.

Verify

Run the signing vector, validate produced envelopes and decisions against envelope.schema.json and consent-decision.schema.json, and exercise never_ask, unknown/approved/declined Ask once, and Ask always. Simulate Epode downtime and verify that a duplicate report returns the first accepted result. See Protocol overview.

Customer-agent coverage

Your protocol implementation creates the HTTP/HTML feedback contract; it cannot make an independent customer agent act on it. For reliable Codex and Claude Code handling, customers install Epode Companion once for all Epode-instrumented products. This is a user-side plugin, not company-side protocol code, an account, or a product key. View the runnable language-neutral HTTP example