Developers
Agent Commerce API
Everything an AI agent needs to shop through OTTR for a customer, over REST, MCP or A2A. Try it first in the live playground.
Overview
Every integration follows the same four moves: search, prepare a purchase, ask the customer to approve, then complete it. Your agent never handles payment details and can never approve a purchase. The customer approves on OTTR, signed in, and OTTR pays the retailer with a single-use virtual card locked to that purchase.
Base URL: https://ottr.si/v1/agent. Machine-readable description: OpenAPI 3.1.
Getting access
While OTTR is in sandbox, agent keys are issued by the OTTR team. A key belongs to your agent integration and carries the scopes you are allowed to request: search, purchase_intents, approval_requests and orders. The key is shown once; store it as a secret.
Authentication
Send your key on every call: Authorization: Bearer ottr_ak_.... Search needs nothing else.
To act for a customer, you also need their permission. The customer authorises your agent from their OTTR Security page, choosing scopes, and gets a customer reference to give your agent. Send it as OTTR-Customer-Ref: ottr_cr_.... A reference only works with the key it was issued to, expires after 90 days, and stops working the moment the customer revokes it. OTTR stores only a keyed hash of keys and references.
REST endpoints
| Method | Path | Scope | What it does |
|---|---|---|---|
| POST | /search | none | Search products. Body: query, max_price (pounds, including delivery), limit (up to 10). |
| POST | /purchase-intents | purchase_intents | Prepare a purchase at the current price. Body: offer_id, quantity. Charges nothing. |
| GET | /purchase-intents/{id} | purchase_intents | The purchase's state, and its order once confirmed. |
| POST | /purchase-intents/{id}/approval-requests | approval_requests | Send the customer a secure approval link, on their own channel as well as in the response. |
| POST | /purchases | purchase_intents | Proceed with an approved purchase. 409 approval_required until the customer approves. |
| GET | /orders/{id} | orders | Order status, shipment and refunds, by order id or merchant reference. |
| POST | /orders/{id}/returns | orders | Start a return. Body: reason (optional). |
POST /v1/agent/search
{ "query": "coffee machine", "max_price": 500, "limit": 3 }
200 OK
{ "results": [ {
"offer_id": "cmv2izg65000o451sprqg5ekw",
"title": "De'Longhi Magnifica Evo Bean to Cup Coffee Machine",
"merchant": "Ottr Test Merchant", "merchant_environment": "sandbox",
"price": { "amount": 449, "currency": "GBP" },
"delivery": { "amount": 0, "currency": "GBP", "estimate": "2026-10-13" },
"total": { "amount": 449, "currency": "GBP" },
"in_stock": true, "purchasable": true, "url": null } ] }Prices, stock and delivery come from the retailer at the time of the call. OTTR re-checks the live price when the customer approves; if it has changed, the approval is void and nothing is charged.
Errors
Errors are JSON: { "code": "...", "message": "..." }. Messages are safe to show to a person.
| 401 | unauthorised | Missing or invalid agent key. |
| 401 | customer_required | No OTTR-Customer-Ref header. |
| 403 | customer_permission | The customer has not authorised your agent, or revoked it. |
| 403 | scope | The customer did not grant this scope. |
| 404 | not_found / unknown_offer | No such purchase, order or offer for this customer. |
| 409 | not_purchasable | OTTR cannot buy this offer; link the customer to the retailer instead. |
| 409 | already_purchasing | This purchase is already in progress. |
| 409 | approval_required | The customer has not approved yet. |
MCP server
Streamable HTTP at https://ottr.si/api/mcp, stateless, with the same headers as the REST API. Tools: ottr_search_products, ottr_compare_products, ottr_create_purchase_intent, ottr_request_approval, ottr_execute_purchase, ottr_get_order_status.
claude mcp add --transport http ottr https://ottr.si/api/mcp \ --header "Authorization: Bearer ottr_ak_..." \ --header "OTTR-Customer-Ref: ottr_cr_..."
A2A
Agent card at /.well-known/agent-card.json; JSON-RPC at https://ottr.si/api/a2a, speaking A2A 1.0 and 0.3 (send A2A-Version). Methods: SendMessage, GetTask and CancelTask, with their 0.3 names.
- search_products (no key): plain text, or data
{ "skill": "search_products", "query": "...", "max_price": 250 }. Returns a completed task with the offers as a data artifact. - compare_products (no key): data
{ "skill": "compare_products", "offer_ids": [...] }. - purchase (key and customer reference): data
{ "skill": "purchase", "offer_id": "..." }. Prepares the purchase, sends the customer the approval request, and returns tasktsk_...in auth-required. Poll GetTask: the task always reflects the real purchase. - order_status (key and customer reference): data
{ "skill": "order_status", "order_id": "..." }.
Clients that cannot set custom headers may pass the customer reference as message.metadata.customerRef.
Purchase states
| REST state | A2A task state | Meaning |
|---|---|---|
| OFFER_SELECTED | input-required | Prepared; approval not yet requested. |
| AWAITING_APPROVAL | auth-required | Waiting for the customer. |
| APPROVED … ORDER_CONFIRMED | working | Funding, virtual card, checkout. |
| OUTCOME_UNKNOWN | working | Confirming with the retailer before any retry. Never ordered twice. |
| COMPLETED | completed | Ordered; order reference available. |
| FAILED | failed | Not completed; any funding is refunded. |
| CANCELLED | canceled | Declined or cancelled; nothing charged. |
| REFUND_PENDING / REFUNDED | completed | Ordered, then refunded. |
Sandbox
OTTR is in development. Today every purchasable offer comes from Ottr Test Merchant, a sandbox retailer run by OTTR; funding, virtual cards and checkout run in sandbox and nothing real is charged or delivered. Offers from other retailers can appear in search with purchasable: false: show them, and link the customer to the retailer. The API, MCP tools and A2A skills will not change shape when live retailers and payments are switched on.