start_purchase

Start a purchase when the user asks to pay with Envoy or has chosen Envoy as the default for this checkout.

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

Start a purchase when the user asks to pay with Envoy or has chosen Envoy as the default for this checkout. Never ask for card details. Retry with the same key; a new key is a new purchase. Send the full cart exactly as the user asked, the delivery address, each item's image and the merchant_logo: the user sees them on the approval screen. Only items bought; no gifts. Never split a cart or lower a total. Envoy decides who checks out: read checkout_by in each reply. agent: you get a one-time card. envoy: Envoy checks out; never open the payment step. Envoy never shares a browser live-view link.

TitleServiceRead onlyDestructiveIdempotentExternal sites
Start a purchasepurchasefalsetruetruetrue

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
idempotency_keyYesstringOne key per intent; retry with the same key. A new key is a new purchase. minLength 16; maxLength 64; pattern ^(?:[A-Za-oq-z0-9_-]|p[A-Za-tv-z0-9_-]|pu[A-Za-qs-z0-9_-]|pur[A-Za-z0-9-])[A-Za-z0-9_-]*$
merchant_urlYesstringMerchant checkout or product URL; the merchant name is taken from its domain. Starts with https://; a bare domain or http:// is upgraded to https://. pattern ^(?:[Hh][Tt][Tt][Pp][Ss]?://)?[^\s/?#:@]+\.[^\s/?#:@]+(?:[/?#]\S*)?$
merchant_logoNostringMerchant logo or brand image URL, as shown on the page. Starts with https://; a bare domain or http:// is upgraded to https://. pattern ^(?:[Hh][Tt][Tt][Pp][Ss]?://)?[^\s/?#:@]+\.[^\s/?#:@]+(?:[/?#]\S*)?$
totalYesstringCheckout total in INR, with delivery, tax and fees. Rupees, up to 2 decimals; number also accepted, "₹" and thousands commas are stripped. maxLength 20; pattern ^[0-9]+(?:\.[0-9]{1,2})?$
itemsYesarray of objectWhat to buy, one entry per product. Only items being bought: no free gifts or ₹0 lines. minItems 1; maxItems 20
items[].nameYesstringItem name, including size, colour or variant. minLength 1; maxLength 200
items[].quantityNointegerQuantity; default 1. Numeric string also accepted. minimum 1; maximum 99; default 1
items[].urlNostringItem page URL. Starts with https://; a bare domain or http:// is upgraded to https://. pattern ^(?:[Hh][Tt][Tt][Pp][Ss]?://)?[^\s/?#:@]+\.[^\s/?#:@]+(?:[/?#]\S*)?$
items[].imageNostringDirect URL of the product image shown on the cart or product page. Used only to show the user what they are approving. Starts with https://; a bare domain or http:// is upgraded to https://. pattern ^(?:[Hh][Tt][Tt][Pp][Ss]?://)?[^\s/?#:@]+\.[^\s/?#:@]+(?:[/?#]\S*)?$
items[].product_idNostringA search_products product's product_id, on every item or none. pattern ^ofr_[a-f0-9]{32}$
items[].priceNostringLine price in rupees, quantity included.
user_requestYesstringThe user's own words for this purchase, including any notes. minLength 1; maxLength 300
address_idNostringSaved address ID from get_account with include addresses. Preferred over address. Do not also send address. minLength 1; maxLength 64
addressNostringDelivery address on one line, with recipient name, phone and 6-digit PIN code. Prefer address_id from get_account. Do not also send address_id. minLength 1; maxLength 500
max_totalNostringHighest total the user accepts; defaults to total. The card's hard limit: set it above total if fees may still be added. Rupees, up to 2 decimals; number also accepted, "₹" and thousands commas are stripped. maxLength 20; pattern ^[0-9]+(?:\.[0-9]{1,2})?$
product_idNostringFor a product from search_products, pass its product_id, merchant_url and total exactly; Envoy buys it from Elixir and the user approves in the app. pattern ^ofr_[a-f0-9]{32}$
railNo"gift_card" | "operated_checkout"gift_card: buy a gift card of a merchant list_gift_card_merchants lists, for this total. operated_checkout: Envoy places the order itself or refuses; no card is released. Omit for a one-time card. Not with retry_of or product_id.
order_titleNostringDisplay only, as shown, like price_lines and facts; Envoy drops what it cannot show. Order title.
price_linesNoarray of objectBill lines adding up to total.
price_lines[].codeNostringitem_total|delivery|taxes_fees|packaging|discount
price_lines[].amountNostringRupees, positive.
factsNoarray of objectOrder facts.
facts[].codeNostringdeliver_to|arrives|sold_by|restaurant|check_in|check_out|cancellation|booked_for
facts[].valueNostring—
retry_ofNostringRetry a card purchase you reported failed on Envoy's browser checkout: its purchase ID or original idempotency key. Same merchant site, items and total. Envoy finishes it with that card if it can still use it, otherwise as a new purchase on the user's own card. Envoy may also accept a purchase that failed because its card could not be issued. minLength 16; maxLength 64; pattern ^(?:pur_[a-f0-9]{32}|(?:[A-Za-oq-z0-9_-]|p[A-Za-tv-z0-9_-]|pu[A-Za-qs-z0-9_-]|pur[A-Za-z0-9-])[A-Za-z0-9_-]*)$

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

Synthetic request

{
  "idempotency_key": "synthetic-intent-0001",
  "merchant_url": "https://shop.example.test",
  "merchant_logo": "https://shop.example.test/logo.png",
  "total": "2499.00",
  "items": [
    {
      "name": "Headphones",
      "image": "https://shop.example.test/headphones.jpg"
    }
  ],
  "user_request": "Buy the headphones"
}

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 declares no data payload: a success returns the purchase in purchase and what to do next in next.

Synthetic result

Example synthetic-waiting-for-approval (synthetic data):

{
  "ok": true,
  "contract": "envoy@2026-10",
  "tool": "start_purchase",
  "request_id": "req_synthetic",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "server_time": "2026-09-29T12:00:00Z",
  "replayed": false,
  "purchase": {
    "purchase_id": "pur_5b1e9c7a3f2d5a8e9c0b1d2e3f4a5b6c",
    "service": "purchase",
    "idempotency_key": "synthetic-intent-0001",
    "revision": 1,
    "terminal": false,
    "state": "waiting_for_user",
    "user_task": "approve",
    "reason_code": null,
    "status_text": "Approve this purchase in the Envoy app.",
    "rail": "otu_card",
    "rail_reason": "RAIL_OTU_PREFERRED",
    "checkout_by": "agent",
    "merchant": {
      "name": "Example Store",
      "url": "https://shop.example.test"
    },
    "amount": {
      "minor": 249900,
      "currency": "INR",
      "display": "₹2,499.00"
    },
    "description": "Headphones",
    "details": {
      "complete": true,
      "missing": []
    },
    "question": null,
    "follow_up": null,
    "approval": {
      "state": "pending",
      "method": "passkey",
      "decide_by": "2026-09-29T12:15:00Z",
      "approval_url": null
    },
    "payment_ready": {
      "available": false,
      "kind": "card",
      "expires_at": null
    },
    "money": {
      "status": "not_started",
      "paid": null,
      "merchant_order_ref": null
    },
    "receipt": null,
    "chain": {
      "supersedes": null,
      "superseded_by": null
    },
    "agent": {
      "agent_id": "origin:https://host.example.test",
      "display_name": "Synthetic Host",
      "verification": "origin"
    },
    "created_at": "2026-09-29T12:00:00Z",
    "updated_at": "2026-09-29T12:00:00Z"
  },
  "data": null,
  "next": {
    "action": "call_tool",
    "tool": "get_purchase_status",
    "args": {
      "purchase": "pur_5b1e9c7a3f2d5a8e9c0b1d2e3f4a5b6c",
      "after_revision": 1,
      "wait_seconds": 45
    },
    "instruction": "Tell the user the user_action message once; they cannot see tool replies. There is no link, id or code to give them. Call get_purchase_status with next.args exactly to check progress. Follow the returned next action until the state changes or next.by passes.",
    "after_seconds": 0,
    "by": "2026-09-29T12:15:00Z"
  },
  "user_action": {
    "task": "approve",
    "url": null,
    "by": "2026-09-29T12:15:00Z",
    "message": "Approve this purchase in the Envoy app. If your assistant hasn't continued after you approve, reply 'done'."
  },
  "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