Haggl Negotiation Protocol
Buyer agents request merchant-authorized terms using relevant context. Haggl returns a quote, guidance on useful information and the actions currently available. Browser tools, remote MCP, REST and the human interface use the same negotiation session.
See current merchant pricing. A quote is not an acceptance, and an accepted quote is not a completed payment or enrollment.
1. Discovery
The embed adds missing storefront discovery tags even when the badge is hidden or the browser does not support WebMCP.
<meta name="haggl-vendor" content="{vendorId}" />
<meta name="haggl-negotiate" content="https://www.haggl.ai/api/negotiate?vendor={vendorId}" />
<meta name="haggl-mcp" content="https://www.haggl.ai/api/mcp" />
<meta name="haggl-mcp-manifest" content="https://www.haggl.ai/.well-known/mcp.json" />
<meta name="haggl-protocol" content="https://www.haggl.ai/protocol" />Compatible browser runtimes discover page-registered tools. Refresh their definitions after actions, navigation or human edits. Where WebMCP is unavailable, follow the advertised REST or remote MCP endpoint.
Remote MCP uses Streamable HTTP. Its tool list is stable because the endpoint serves multiple sessions. Each response supplies state.allowed_actions; the server enforces these rules.
| Tool | Purpose |
|---|---|
| haggl_start_negotiation | Create a session and obtain context guidance. |
| haggl_search_catalog | Read catalog products. |
| haggl_get_negotiation | Read current state, offer version and permitted actions. |
| haggl_submit_offer | Request terms with available context. |
| haggl_accept_offer | Accept the exact offer approved by the buyer. |
2. Session and state
GET /api/negotiate?vendor={vendorId} creates a session; use the returned status URL for read-only checks. The response includes merchant identity, public prices, guidance, available actions and session URLs.
Reuse session.id across transports. Pass it as session_id to remote MCP without opening another session. Read back after reloads or uncertain results.
state.offer_version identifies the offer round. Expiry, remaining rounds and permitted actions are enforced on the server even when an agent holds older tool definitions.
3. Contextual guidance
Guidance comes from the merchant category, public products or services and current session context. Profiles describe relevant circumstances, not onboarding personas the buyer must select.
Signals explain what information might help, why it matters and how it can be substantiated. Required quote inputs are separated from optional context. Supply everything already known in one request.
4. Evidence and consent
An opening request can omit evidence. Self-reported information stays self-reported; documents or excerpts are not automatically authenticated. For relevant signed email, data.proofs[].raw_mime_b64enables server-side DKIM checks of signed origin and integrity, not independent verification of every claim.
When a session includes proof_request, a signed claim from one of its accepted_issuers can be submitted as data.proofs[] = { type: "link_signed_claim", sd_jwt }: an SD-JWT whose key-binding JWT carries that aud and nonce and is under five minutes old. It proves only the facts it discloses, is weighed as evidence rather than required, and is stored as a hash.
Obtain permission for the specific personal information and destination before submitting it. Do not request inbox access or unrelated documents as a universal step. Raw email bytes are removed before storage; submitted excerpts and other context may remain. Redacted history does not become fresh verified proof.
5. Request a quote
POST requested products or services and any available context to session.negotiate_url. The data object is optional. The response can contain personalized terms, ordinary public terms, necessary missing inputs or no available offer.
POST {session.negotiate_url}
Content-Type: application/json
{
"data": {},
"ask": { "items": [{ "product_handle": "headphones", "quantity": 1 }] }
}Add useful context within remaining rounds; do not manufacture extra bargaining steps. Legacy target_segment_id inputs remain accepted for compatibility. New clients do not need them.
6. Review and accept
Review price, currency, covered items, quantities, conditions and validity with the buyer. Remaining rounds describe a session limit, not a promise of improved terms. Accept only the version they approve.
Read state.acceptance_effects and state.commits_purchase before approval. A merchant callback may have unknown purchase effects. A checkout link alone is not payment confirmation.
POST {session.accept_url}
Content-Type: application/json
{ "expected_offer_version": 1 }
// Use the actual state.offer_version reviewed by the buyer.
// GET session.status_url to recover an uncertain response.Stale versions are rejected. Retry the same reserved version to recover an existing handoff when the response is retryable. If the checkout provider definitively rejects fulfillment, the state becomes merchant_action_required: the approved terms stay reserved, acceptance is withdrawn, and state.merchant_assistance names who to contact. Do not retry; give the buyer that path. Follow returned checkout or enrollment URLs. On Stripe, discover its live tools and refresh after payment-form changes; do not hard-code provider tool names. Payment requires separate buyer authorization.
7. Utilities
Name the service or tariff. Usage, service area, time-of-use distribution and current contract terms may help; a bill is not a universal prerequisite.
POST {session.negotiate_url}
Content-Type: application/json
{
"data": { "usage": { "amount": 4200, "unit": "kWh",
"period_start": "2025-09-01", "period_end": "2026-08-31" } },
"ask": { "product": "electricity" }
}Keep rate units, currency, time windows, fixed fees, taxes and commitment terms distinct. Preserve sub-cent unit rates. Annual consumption alone cannot establish total cost when other rates or fees are unknown. Do not infer savings or completed enrollment from an indicative quote.
8. Recovery
| Response | Next step |
|---|---|
| 400 / 422 | Correct identified inputs without collecting unrelated evidence. |
| 404 | Check the returned session or merchant identifier. |
| 409 | Read canonical state; review changed terms before accepting. |
| 410 | Request fresh terms if the buyer still wants them. |
| 429 / 503 | Respect capacity or temporary availability; do not restart in a loop. |
A timeout is uncertain, not proof that nothing happened. Read the same session before repeating a mutation. An unavailable fulfillment configuration must not be presented as a completed order.
Merchant setup
Connect the site, configure authorized commercial limits and enable the supported fulfillment handoff. Public guidance does not require merchant-written customer personas. Existing private policies remain enforced while campaign-budget support is rolled out separately.
Test actual consumer clients and adapters. Registration, invocation, valid quotes, recommendation and completed payment are distinct outcomes.