Skip to main content
Company endpoints use 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}
  • fieldKey is a lowercase namespace.name pair such as journey.occasion; the epode.* namespace is reserved for built-in fields.
  • label is the question topic Epode inserts into the customer permission prompt.
  • type is a product-authored lowercase snake_case category of 1–48 characters, such as preference, constraint, or customer_goal.
  • allowedValues carries 1–32 distinct lowercase snake_case values. Free-form text, arbitrary numbers, and sensitive categories are never accepted.
  • operations optionally 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
Identity references are optional; omit all of them for an ephemeral interaction. A new request is 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/consent
  • POST /_epode/v1/enrichment/answers
Those are fixed company-owned relay paths. They forward only the 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
The response includes a 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
Every signal ID must be an exact subset of that retrieval. Identical retries are idempotent; changing a retry returns 409.

Record the outcome

POST /api/v2/personalization/outcomes
Outcome is conversion, completion, engagement, dismissal, or abandonment. Identical retries are idempotent and conflicting retries return 409.