Recaudo Docs

MCP & agents

Stores for shopping agents

How an AI agent shopping for a person (Claude, ChatGPT, Gemini) finds the products of a Recaudo store and builds their cart.

Turning it on

The business turns on “Open my store to AI agents” in Online store → Settings. While it is off, the store MCP and catalog return 404. The product page markup is always published.

Store MCP (Claude and other MCP clients)

Every store has its own public MCP server. It needs no account or token because it is for the shopper's agent; /mcp is a different server, for the business's team.

https://recaudo.app/store/{slug}/mcp

Tools

  • search_products Searches the catalog by text or category. Returns in-stock products with tax-included price, link and image.
  • get_product Product detail: description, images and presentations with their price.
  • create_cart_link Builds a link that opens the store cart with those products. The agent gives it to the person, who reviews and pays.
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "search_products",
        "arguments": {
            "query": "café",
            "limit": 5
        }
    }
}

Note

The agent does not charge or create the order. The order is created when the person pays on the store, at the store's prices at that moment.

Catalog in ChatGPT format

The full catalog as JSON Lines, with the fields of the ChatGPT product feed (Agentic Commerce Protocol). To show up in ChatGPT Shopping, OpenAI has to onboard the store; this file is what you hand them. Only active products with a photo and price are included.

https://recaudo.app/store/{slug}/agent-feed.jsonl

Markup on every product page

Every product page carries a schema.org Product block in JSON-LD: name, images, tax-included price, currency, availability and seller. Browsing agents (Claude, Gemini, Perplexity) read it, and so does Google.

Tip

All three paths use the same product record, so the price matches everywhere.

Checkout from the chat (Agentic Commerce Protocol)

So ChatGPT or another platform can buy without sending the shopper to the store. The platform opens a session with catalog ids (SKU or product id), Recaudo sets prices, taxes and shipping from the store, and on completion creates the order and charges the single-use payment token.

  • POST /checkout_sessions Opens the session (201).
  • POST /checkout_sessions/{id} Changes items, buyer or address.
  • GET /checkout_sessions/{id} Reads the session.
  • POST /checkout_sessions/{id}/complete Creates the order and charges.
  • POST /checkout_sessions/{id}/cancel Cancels; a completed session answers 405.
https://recaudo.app/acp/{slug}/checkout_sessions
{
    "items": [
        {
            "id": "SKU-123",
            "quantity": 2
        }
    ],
    "buyer": {
        "first_name": "Ana",
        "last_name": "López",
        "email": "ana@example.com"
    },
    "fulfillment_address": {
        "name": "Ana López",
        "line_one": "Av. Reforma 1",
        "city": "CDMX",
        "state": "CDMX",
        "country": "MX",
        "postal_code": "06600"
    }
}

Authenticate with Authorization: Bearer and the key the business generates in Online store → Settings (shown once). Every POST carries Idempotency-Key: the same body returns the same response; a different body with the same key answers 422 idempotency_conflict and a request still running, 409 idempotency_in_flight.

Amounts are integers in cents and currency is lowercase. Both items [{id, quantity}] and line_items [{item: {id, quantity}}] are accepted. The session is ready_for_payment once there is a buyer email, an address (if the store asks for one) and everything is available; otherwise not_ready_for_payment with per-field messages. Payment is a Stripe Shared Payment Token (spt_…) charged with the business's Stripe account; if declined it answers 402 payment_declined, the order is canceled, stock returns and the session takes another token.

Note

If the business has no Stripe or the bank asks for authentication (3D Secure), the session goes to requires_escalation with continue_url: the ready cart in the store or the order's payment page. The shopper finishes there.

If the business saves the notification URL and secret the platform gives it, Recaudo sends order_created on completion and order_updated on every order change, with the Merchant-Signature header (hex HMAC-SHA256 of the body) and Timestamp. Statuses: created, confirmed, shipped, fulfilled, canceled. With checkout on and terms and privacy policy set, the catalog flags is_eligible_checkout.

AP2 mandates (v0.2)

For agents using AP2 (FIDO Alliance): the person signs which cart they authorize and the store verifies it before charging. It is optional; without a mandate the checkout works the same.

  1. When the session is ready_for_payment, the response carries ap2.checkout_jwt: the cart (items, quantities, currency and total) signed by the store with ES256, and ap2.jwks_uri with its public key.
  2. The agent builds the checkout mandate (SD-JWT mandate.checkout.1) with that checkout_jwt, its checkout_hash (base64url SHA-256) and the person's key in the jwk header and cnf.jwk, plus a KB-JWT with sd_hash and aud equal to the complete URL.
  3. It sends it in ap2.checkout_mandate when completing. Recaudo checks signatures, disclosures, hash, dates and that the cart is unchanged. If anything is off it answers 400 invalid_mandate and does not charge.
  4. On charge, the response carries ap2.checkout_receipt: a JWT signed by the store with status Success, reference (the checkout_hash) and order_id. If the charge is declined, the receipt says Error.
https://recaudo.app/store/{slug}/jwks.json
{
    "payment_data": {
        "token": "spt_…",
        "provider": "stripe"
    },
    "ap2": {
        "checkout_mandate": "<issuer-jwt>~<disclosure>~…~<kb-jwt>"
    }
}

Note

Every mandate is recorded, valid or not, with the thumbprint of the key that signed it. The payment mandate is not verified by the store: it is for the network or the gateway.
Was this page helpful? Thanks for letting us know.
Esc