search_products

Search Elixir's marketplace when the user wants a product and names no merchant, or asks for Elixir or Envoy.

POST https://api.envoy.money/tools/search_products

Search Elixir's marketplace when the user wants a product and names no merchant, or asks for Elixir or Envoy. Show each with its price. To buy one or several, call start_purchase once with items [{name, quantity, product_id}], total = the sum of their total.display (e.g. "₹998.00") and their merchant_url. 1mg products may add a handling fee; if Envoy answers with a different total, ask the user and resend with it. Moves no money.

TitleServiceRead onlyDestructiveIdempotentExternal sites
Search Elixir marketplacepurchasetruefalsetruefalse

Send a Central Auth bearer with agent scope. A key-bound connection also sends Envoy-Installation-Proof over this exact request. JSON bodies are limited to 64 KB; a larger body returns HTTP 413 with INVALID_INPUT.

Input

FieldRequiredTypeDescription and rules
queryYesstringWhat the user wants, in their words. minLength 1; maxLength 200
quantityNointegerHow many; default 1. minimum 1; maximum 99; default 1
address_idNostringSaved address ID from get_account to deliver to; default: the default saved address. minLength 1; maxLength 64

Unknown fields are rejected. The exact input schema is on the schemas page.

Synthetic request

{
  "query": "whey protein 1kg",
  "quantity": 2
}

Success data

Every reply uses the shared envelope: ok, the purchase, next, user_action and error. Follow next; outputs may gain fields, so ignore ones you do not recognize.

This tool returns its payload in data:

Field in dataRequiredTypeDescription and rules
productsYesarray of objectProducts from Elixir's own marketplace, price for one and total for the quantity. Empty when Elixir has nothing to offer right now. maxItems 5
products[].product_idYesstringThis product's offer id. Never show it to the user. pattern ^ofr_[a-f0-9]{32}$
products[].nameYesstringProduct name, as Elixir lists it. minLength 1; maxLength 200
products[].brandYesstring or nullBrand, when Elixir lists one. minLength 1; maxLength 500
products[].imageYesstring or nullProduct image. format uri; pattern ^https://
products[].priceYesobject—
products[].quantityYesintegerQuantity priced. minimum 1; maximum 999
products[].totalYesobject—
products[].valid_untilYesstringThe price holds until this time; search again after it. format date-time
products[].merchant_urlYesstringElixir's store. Pass it to start_purchase as merchant_url. format uri; pattern ^https://

Synthetic result

Example synthetic-products (synthetic data):

{
  "ok": true,
  "contract": "envoy@2026-10",
  "tool": "search_products",
  "request_id": "req_synthetic",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "server_time": "2026-09-29T12:00:00Z",
  "replayed": false,
  "purchase": null,
  "data": {
    "products": [
      {
        "product_id": "ofr_0123456789abcdef0123456789abcdef",
        "name": "Whey Protein Isolate 1kg",
        "brand": "Synthetic Nutrition",
        "image": "https://cdn.example.test/whey.jpg",
        "price": {
          "minor": 49900,
          "currency": "INR",
          "display": "₹499.00"
        },
        "quantity": 2,
        "total": {
          "minor": 99800,
          "currency": "INR",
          "display": "₹998.00"
        },
        "valid_until": "2026-09-29T12:30:00Z",
        "merchant_url": "https://elixir.cards"
      }
    ]
  },
  "next": {
    "action": "continue",
    "tool": null,
    "args": null,
    "instruction": "Show the user each product with its price and total. To buy what the user picks, one or several, call start_purchase once with items [{name, quantity, product_id}], total = the sum of their total.display (e.g. \"₹998.00\"), their merchant_url and the same address_id you searched with. Never buy one the user did not pick.",
    "after_seconds": null,
    "by": null
  },
  "user_action": null,
  "follow_ups": [],
  "error": null
}

Errors

A failure has ok: false, changes nothing, and carries error plus one next. Branch on error.code and next, not on the HTTP status. The contract declares every code below for every tool; which one a call returns depends on the request and the purchase state.

CodeHTTP statusMeaningWhat the agent does
INVALID_INPUT400Fix the supplied input.Fix every field listed in error.fields, then call the same tool once more.
UNAUTHENTICATED401Reconnect through Central Auth.Ask the user to reconnect Envoy, then retry this call.
NOT_ALLOWED403The operation is not allowed.This connection may not do that. Tell the user; do not retry.
SERVICE_NOT_ENABLED403The service is not enabled.This Envoy service is not enabled for the user. Tell the user; do not retry.
NOT_FOUND404Purchase absent or owned by another user.No purchase matches that reference. Check the purchase id from this task; do not start a new purchase to recover.
IDEMPOTENCY_CONFLICT409The same key has different input.Retry with the original inputs unchanged, or use a new idempotency_key only for a genuinely new purchase.
ACTIVE_PURCHASE_EXISTS409An active purchase already exists.Another purchase is in progress. Show the user the user_action message and stop this attempt.
STATE_CONFLICT409The operation conflicts with the current state.This conflicts with the current state. Tell the user; do not retry.
NO_RAIL422No rail can serve this purchase; nothing was created.Envoy cannot pay this purchase. Tell the user why using error.message; do not retry or split it.
RATE_LIMITED429Wait before retrying.Wait next.after_seconds, then retry the same call unchanged.
UNAVAILABLE503The service is unavailable; follow retryable and next.Wait next.after_seconds, then retry the same call unchanged; never pay elsewhere meanwhile.
CLIENT_UPDATE_REQUIRED426Update the client.This client is too old. Tell the user to update it.

Downloads

OpenAPI · MCP catalog · Synthetic examples · Schemas

On this page