Authorization: Bearer $EPODE_API_KEY. Agent answer endpoints use only the short-lived
aqr1_... request handle returned for that interaction. The company key must never enter an agent response.
Define product questions
By default a product shares Epode’s built-in global question catalog. A product can instead define its own closed catalog; as soon as one enabled question exists, it replaces the global catalog for that product’s requests. Every request stores an immutable snapshot of the questions it may ask, so editing or deleting a definition never changes an in-flight or historical request.PUT /api/v2/enrichment/fields/{fieldKey}
fieldKeyis a lowercasenamespace.namepair such asjourney.occasion; theepode.*namespace is reserved for built-in fields.labelis the question topic Epode inserts into the customer permission prompt.typeis a product-authored lowercase snake_case category of 1–48 characters, such aspreference,constraint, orcustomer_goal.allowedValuescarries 1–32 distinct lowercase snake_case values. Free-form text, arbitrary numbers, and sensitive categories are never accepted.operationsoptionally binds the field to specific business operations. Omit it to allow every operation. A request that names the field for an operation it is not bound to fails closed, and a request for an operation with no eligible fields creates no question at all.
GET /api/v2/enrichment/fields lists the product’s definitions and whether the legacy global catalog is still
active. DELETE /api/v2/enrichment/fields/{fieldKey} removes a definition; existing requests keep their
snapshots.
Request enrichment
POST /api/v2/enrichment/requests
answer_ready immediately: the submit action and field catalog arrive in the first response and no permission
question is ever asked. declined means an explicit opt-out is on record for this customer and purpose — the
consent decision endpoint still accepts explicit declined decisions (and approved for backward
compatibility), and a recorded no is always honored. answered and no_relevant_context mark completed
requests. The response contains only the currently allowed next action.
targeted_advertising is the other supported purpose and always requires its own separate permission; a
product_personalization grant is never reused for it.
fieldKeys optionally selects the exact fields this request may collect: one to eight keys drawn from the
product’s effective catalog (its own definitions once it has any, otherwise the global catalog). The answer
schema then offers only them, and the backend rejects answers that name anything else. Retrying the same
interaction with a different selection conflicts.
statusCode, durationMs, sessionRef, and runtimeHint are optional same-call evidence. Framework adapters
set status and duration automatically. A company may add only a product-issued sessionRef and a bounded,
non-sensitive runtime label; agent/model arguments are not evidence.
Express and Fastify also populate requestObservation automatically from a fixed allowlist: the framework’s
resolved peer IP, HTTP method, user agent, accepted language, referrer origin, and User-Agent Client Hints.
Proxy forwarding is used only when the host application has configured that framework to trust the proxy. The
adapter never copies cookies, credentials, bodies, full referrer URLs, query strings, or arbitrary headers.
MAC addresses are link-layer identifiers and are not available to an internet-facing HTTP server.
surface is exactly http_json, html, or mcp. Node adapters set it automatically. JSON and HTML begin as
unclassified opportunities; an MCP business-tool result is confirmed immediately by the same enrichment request,
so companies do not install a second observability wrapper.
The Node HTTP adapter rewrites agent action URLs to:
POST /_epode/v1/enrichment/consentPOST /_epode/v1/enrichment/answers
aqr1_ handle and allowlisted body—never the
company key, product request, prompt, transcript, cookies, or arbitrary headers.
Both the initial response and the post-consent relay response preserve the backend’s explicit
stageInstruction, optional answerInstruction, and exact bodySchema for the currently allowed action. Every
action returned by the relay is rewritten again, so an HTTP agent is never instructed to write to the Epode
origin.
Submit an answer
POST /api/v2/enrichment/answers accepts:
status is answered, declined, or no_relevant_context. items contains at most eight entries and must be
empty for the latter two statuses. The action’s bodySchema.catalog is the only accepted key, type, and value
vocabulary; unknown combinations fail closed. Epode generates the stored summary from that catalog instead of
storing agent-written prose. Remembered items require the matching grant. Requested expiry is clamped to the
product retention policy.
For a product-authored category, the catalog entry exposes that category as questionType while its type
remains the canonical compatibility class the answer must submit. This keeps in-flight requests safe across a
rolling API deploy; retrieved context still reports the product-authored category.
The catalog includes bounded, non-sensitive preferences for several product categories. Current examples include:
These preferences describe how the product should serve the customer. They are not permission to collect travel
documents, holdings, balances, income, creditworthiness, diagnoses, symptoms, medications, disabilities, or other
sensitive or free-form facts. Those fields are absent from the catalog and fail closed. All four category-specific
groups above are also marked
targetedAdvertisingSafe: false.
For targeted_advertising, the backend also requires the selected catalog entry’s
targetedAdvertisingSafe field to be true. For example, the bounded interest.topic preference accepts
non-sensitive values such as outdoor_travel; B2B company size, purchase intent, unknown audience labels, and
sensitive or free-form attributes fail closed even after advertising permission is approved.
Retrieve permitted context
POST /api/v2/customer-context
retrievalId, identity level, context version, and active items with signalId,
provenance, allowed uses, remembered state, and expiry. It is read-only: unknown references do not create a
customer. Revoked, expired, unresolved, or wrong-purpose items are omitted.
For an ephemeral interaction, pass the original interactionId instead of an identity reference. The Node HTTP
adapter exposes this as Epode-Context-Interaction for the immediate retry and customer.contextFor(request)
validates and reads it. It is not durable identity and must not be reused for another operation or later visit.
Record the decision
POST /api/v2/personalization/decisions
409.
Record the outcome
POST /api/v2/personalization/outcomes
conversion, completion, engagement, dismissal, or abandonment. Identical retries are
idempotent and conflicting retries return 409.