Product Alternatives and Details
This example separates an unavailable exact product request from verified alternatives and shows how product-detail requests use chat, a Flow, or direct routed-tool execution.
Consent Before Broader Results
With a Flow configured for offer_first, an exact miss returns an offer without product cards:
{
"action": "OFFER_PRODUCT_ALTERNATIVES",
"content": "I could not find the exact requested variant. Would you like to see the same model in other colors?",
"session_id": "{{session_id}}",
"data": {
"text": "I could not find the exact requested variant. Would you like to see the same model in other colors?",
"requested_query": "red phone model 17",
"alternative_query": "phone model 17",
"match": {
"exact": false,
"fallback_used": false,
"requested_query": "red phone model 17",
"applied_query": "red phone model 17",
"relaxed_constraints": ["color:red"]
}
},
"trace_id": "trace_example_offer"
}
Send the customer's acceptance through /api/v1/runtime/ai/chat in the same authorized session without flow_id. ACP then executes the saved bounded query and returns RENDER_PRODUCT_ALTERNATIVES with catalog-backed products. The client must preserve the requested query, applied query, and relaxed constraints and must not label the products as exact matches.
Product-Detail Route Selection
| Input | Endpoint | Successful contract |
|---|---|---|
| Natural-language reference to a recent result | /api/v1/runtime/ai/chat without flow_id | RENDER_PRODUCT_DETAILS, or CLARIFY without lookup when unresolved |
| Known selection requiring an ACP action envelope | /api/v1/runtime/execute with a protected product-details Flow | Runtime execution containing RENDER_PRODUCT_DETAILS |
| Known widget selection requiring only the tool result | /api/v1/runtime/mcp/tools/execute | Raw routed-tool result; no runtime action |
For the direct widget route, the BFF uses a fixed allowlisted operation-to-tool mapping:
{
"session_id": "{{session_id}}",
"tool_name": "catalog_get_product_details",
"arguments": {
"catalogId": "1234567890"
},
"strategy": "first_healthy"
}
A successful response has the routed-tool envelope:
{
"mcp_server_id": "{{selected_mcp_server_id}}",
"tool_name": "catalog_get_product_details",
"result": {
"success": true,
"data": {
"catalogId": "1234567890",
"name": "Phone Model 17",
"price": "1299.99",
"currency": "USD",
"inStock": true
}
}
}
The BFF validates the expected tool name and returned identifier before mapping result.data to its product-detail view. The browser/mobile client supplies only the selected product identifier to the BFF; it never supplies tool_name, MCP server IDs, or flow_id. Do not run both deterministic routes for the same selection.