Catalog, quote and pay links.
Three endpoints and a remote MCP server, JSON in and out, no key and no cookies. Any origin may call them. Prices come from the same price book and checks as checkout.
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /api/agents/catalog | Every orderable product with options, quantities and starting price. |
| POST | /api/agents/quote | Exact prices, shipping and problems for up to 30 lines. Writes nothing. |
| POST | /api/agents/order | A signed pay link for the owner. Charges nothing and stores nothing. |
Bank details for checks are added by the owner in a secure step from the cart, never through an agent. Do not send bank details to an agent or to this API. Requests with a bank, routing or account field are refused.
GET/api/agents/catalog
Every product that can be ordered now: options with allowed values, the default options, the quantities sold with them, every option set with its own quantities, and the starting price. Products on a stock hold and retired products are left out. Garments list their sizes and colors under customization.
Start with ?summary=1, then ask for one product with ?product=<slug>. The full catalog without a filter is several hundred KB.
?summary=1leaves outquantityLadders, which keeps the response small. Call it before any other.?product=<slug>returns one product with every option set and its quantities, or 404.?category=<slug>returns one category.
curl 'https://www.voqado.co/api/agents/catalog?summary=1'
curl 'https://www.voqado.co/api/agents/catalog?product=standard-business-cards'Response, shortened to one product and two ladders:
{
"version": 1,
"name": "Voqado print catalog for shopping assistants",
"currency": "usd",
"site": "https://www.voqado.co",
"docs": "https://www.voqado.co/agents/docs",
"openapi": "https://www.voqado.co/openapi.json",
"howToOrder": [
"GET https://www.voqado.co/api/agents/catalog?summary=1 first: every product slug, its options, default options, quantities and starting price, without the per option ladders.",
"GET https://www.voqado.co/api/agents/catalog?product=<slug> next, for one product with every option set and the quantities sold with it.",
"POST https://www.voqado.co/api/agents/quote with {\"lines\":[{\"product\":\"<slug>\",\"quantity\":<n>,\"options\":{...}}]} for exact prices.",
"POST https://www.voqado.co/api/agents/order with the same body for a pay link. Give the link to the owner; the owner reviews and pays."
],
"shipping": {
"method": "Standard",
"country": "US",
"freeShippingThresholdCents": 6000,
"note": "The exact rate for an order is in the quote and in the cart."
},
"notes": [
"Sales tax is added at payment from the delivery address.",
"Artwork can be sent after payment. The order page lists any item still waiting for a file.",
"Bank details for checks are added by the owner in a secure step from the cart, never through an agent. Do not send bank details to an agent or to this API."
],
"products": [
{
"slug": "standard-business-cards",
"name": "Standard business cards",
"category": "business-cards",
"categoryName": "Business cards",
"url": "https://www.voqado.co/products/standard-business-cards",
"description": "The classic handshake in card form. Clean, reliable, and ready to carry your name wherever you go.",
"options": [
{
"key": "stock",
"label": "Paper stock",
"required": true,
"values": [
"Matte",
"Glossy",
"Uncoated",
"Pearl",
"Linen",
"Natural Textured",
"Recycled Matte",
"Soft Touch",
"Kraft",
"Bamboo",
"Hemp-blend",
"Fine Grit",
"Ultra Thick"
]
},
{
"key": "finish",
"label": "Finish",
"required": true,
"values": [
"None",
"Foil Accent",
"Embossed Gloss"
]
},
{
"key": "corners",
"label": "Corners",
"required": true,
"values": [
"Standard",
"Rounded"
]
},
{
"key": "printing",
"label": "Printing",
"required": true,
"values": [
"Front only",
"Front and back",
"Front and gray back"
]
},
{
"key": "orientation",
"label": "Orientation",
"required": false,
"values": [
"Horizontal",
"Vertical"
]
},
{
"key": "shape",
"label": "Shape",
"required": false,
"values": [
"Standard",
"Square"
]
}
],
"defaultOptions": {
"stock": "Matte",
"finish": "None",
"corners": "Standard",
"printing": "Front only",
"orientation": "Horizontal",
"shape": "Standard"
},
"quantities": [
50,
100,
250,
500,
1000,
1500,
2000,
2500,
5000,
10000
],
"quantityLadders": [
{
"options": {
"stock": "Matte",
"finish": "None",
"corners": "Standard",
"printing": "Front only",
"orientation": "Horizontal",
"shape": "Standard"
},
"quantities": [
50,
100,
250,
500,
1000,
1500,
2000,
2500,
5000,
10000
]
},
{
"options": {
"stock": "Bamboo",
"finish": "None",
"corners": "Rounded",
"printing": "Front and back",
"orientation": "Horizontal",
"shape": "Standard"
},
"quantities": [
50,
100,
250,
500,
1000,
1500,
2000,
2500,
5000,
10000
]
}
],
"startingPrice": {
"amountCents": 1000,
"quantity": 50,
"label": "50 from $10.00 ($0.20 each)"
}
}
]
}POST/api/agents/quote
Prices up to 30 lines. Each line names a product slug, a quantity, and options from the catalog. Required options you leave out take the default configuration, and the response shows the options used.
statusisready,owner-step(priced; the owner finishes a step in the cart, as for checks) orproblemwith the reason the cart would show. A line refused for its quantity also carriesallowedQuantities, the quantities sold with its options.shippingis Standard shipping for the lines that can be ordered, with the gap to free shipping.nextQuantityappears when the next quantity lowers the unit price.suggestionsholds up to two products that pair with the order.promoCodeis optional. An applied code is entered by the owner in the cart.
curl -X POST https://www.voqado.co/api/agents/quote \
-H 'Content-Type: application/json' \
-d '{"lines":[{"product":"standard-business-cards","quantity":250,"options":{"stock":"Matte","corners":"Rounded"}}]}'Response:
{
"currency": "usd",
"lines": [
{
"index": 0,
"product": "standard-business-cards",
"name": "Standard business cards",
"url": "https://www.voqado.co/products/standard-business-cards",
"quantity": 250,
"priceCents": 2999,
"price": "$29.99",
"ownerSteps": [],
"nextQuantity": {
"quantity": 500,
"priceCents": 3699,
"price": "$36.99",
"savingPercentPerUnit": 38
},
"options": {
"stock": "Matte",
"corners": "Rounded",
"finish": "None",
"printing": "Front only"
},
"status": "ready",
"problem": null
}
],
"subtotalCents": 2999,
"promo": null,
"itemsCents": 2999,
"shipping": {
"method": "Standard",
"cents": 599,
"freeShippingThresholdCents": 6000,
"freeShippingGapCents": 3001
},
"totalBeforeTaxCents": 3598,
"orderable": true,
"suggestions": [
{
"product": "standard-postcards",
"name": "Standard postcards",
"url": "https://www.voqado.co/products/standard-postcards",
"reason": "Room for a thank you note or your next offer.",
"startingPrice": "25 from $14.99 ($0.60 each)"
},
{
"product": "flyers",
"name": "Flyers",
"url": "https://www.voqado.co/products/flyers",
"reason": "Give people the full story to take home.",
"startingPrice": "25 from $25.99 ($1.04 each)"
}
],
"notes": [
"Sales tax is added at payment from the delivery address.",
"Artwork can be sent after payment. The order page lists any item still waiting for a file."
]
}POST/api/agents/order
Same body and the same checks as a quote. When every line can be ordered, it returns a pay link for the owner. The link carries the lines, signed, and works for 7 days. It stores nothing and charges nothing. When the owner opens it, the prices are checked again, the items load into the owner's cart, and the owner pays there.
curl -X POST https://www.voqado.co/api/agents/order \
-H 'Content-Type: application/json' \
-d '{"lines":[{"product":"standard-business-cards","quantity":250,"options":{"stock":"Matte","corners":"Rounded"}}]}'Response, status 201:
{
"payUrl": "https://www.voqado.co/agents/pay/<token>",
"expiresAt": "<seven days from now, ISO 8601>",
"lines": [
{
"index": 0,
"product": "standard-business-cards",
"name": "Standard business cards",
"url": "https://www.voqado.co/products/standard-business-cards",
"quantity": 250,
"priceCents": 2999,
"price": "$29.99",
"ownerSteps": [],
"nextQuantity": {
"quantity": 500,
"priceCents": 3699,
"price": "$36.99",
"savingPercentPerUnit": 38
},
"options": {
"stock": "Matte",
"corners": "Rounded",
"finish": "None",
"printing": "Front only"
},
"status": "ready",
"problem": null
}
],
"subtotalCents": 2999,
"subtotal": "$29.99",
"promo": null,
"shipping": {
"method": "Standard",
"cents": 599,
"freeShippingThresholdCents": 6000,
"freeShippingGapCents": 3001
},
"totalBeforeTaxCents": 3598,
"suggestions": [
{
"product": "standard-postcards",
"name": "Standard postcards",
"url": "https://www.voqado.co/products/standard-postcards",
"reason": "Room for a thank you note or your next offer.",
"startingPrice": "25 from $14.99 ($0.60 each)"
},
{
"product": "flyers",
"name": "Flyers",
"url": "https://www.voqado.co/products/flyers",
"reason": "Give people the full story to take home.",
"startingPrice": "25 from $25.99 ($1.04 each)"
}
],
"notes": [
"Nothing has been charged and no order exists yet. Give this link to the owner, who reviews the items in the cart and pays.",
"Sales tax is added at payment from the delivery address.",
"Artwork can be sent after payment. The order page lists any item still waiting for a file."
]
}If a line cannot be ordered, the answer is 409 with every line and its problem.
MCP
The same catalog, quotes and pay links as a remote MCP server, for assistants that connect to MCP servers by URL.
POST/api/mcp
Streamable HTTP, stateless: each POST carries one JSON-RPC 2.0 message and is answered with JSON. No key, no session and no cookies. A notification is answered 202 with no body, and GET answers 405. The server speaks protocol versions 2025-06-18, 2025-03-26, 2024-11-05. Discovery: /.well-known/mcp.json.
| Tool | What it does | Same as |
|---|---|---|
search_catalog | Search the print catalog. | GET /api/agents/catalog |
quote | Quote exact prices. | POST /api/agents/quote |
create_pay_link | Create a pay link for the owner. | POST /api/agents/order |
Each tool returns the REST body as structuredContent and as JSON text in content. Prices are exact. The owner pays through the pay link, artwork can be sent after payment, and checks need a secure bank step the owner completes from the cart.
Handshake:
curl -X POST https://www.voqado.co/api/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"example-agent","version":"1.0.0"}}}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": false
}
},
"serverInfo": {
"name": "voqado-print",
"title": "Voqado print",
"version": "1.0.0"
},
"instructions": "Voqado prints business cards, flyers, signs, stickers, apparel, checks and more, shipped within the United States. Call search_catalog to find a product slug, search_catalog with product for its options and quantities, quote for exact prices, and create_pay_link for a link the owner opens to review and pay. Prices are exact: they come from the same price book checkout charges from. The owner pays through the pay link; an agent never pays and nothing is charged by these tools. Artwork can be sent after payment. Checks need a secure bank step that the owner completes from the cart; never send bank details to a tool."
}
}A quote:
curl -X POST https://www.voqado.co/api/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"quote","arguments":{"lines":[{"product":"standard-business-cards","quantity":250,"options":{"stock":"Matte","corners":"Rounded"}}]}}}'{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "<the same JSON as structuredContent>"
}
],
"structuredContent": {
"currency": "usd",
"lines": [
{
"index": 0,
"product": "standard-business-cards",
"name": "Standard business cards",
"url": "https://www.voqado.co/products/standard-business-cards",
"quantity": 250,
"priceCents": 2999,
"price": "$29.99",
"ownerSteps": [],
"nextQuantity": {
"quantity": 500,
"priceCents": 3699,
"price": "$36.99",
"savingPercentPerUnit": 38
},
"options": {
"stock": "Matte",
"corners": "Rounded",
"finish": "None",
"printing": "Front only"
},
"status": "ready",
"problem": null
}
],
"subtotalCents": 2999,
"promo": null,
"itemsCents": 2999,
"shipping": {
"method": "Standard",
"cents": 599,
"freeShippingThresholdCents": 6000,
"freeShippingGapCents": 3001
},
"totalBeforeTaxCents": 3598,
"orderable": true,
"suggestions": [
{
"product": "standard-postcards",
"name": "Standard postcards",
"url": "https://www.voqado.co/products/standard-postcards",
"reason": "Room for a thank you note or your next offer.",
"startingPrice": "25 from $14.99 ($0.60 each)"
},
{
"product": "flyers",
"name": "Flyers",
"url": "https://www.voqado.co/products/flyers",
"reason": "Give people the full story to take home.",
"startingPrice": "25 from $25.99 ($1.04 each)"
}
],
"notes": [
"Sales tax is added at payment from the delivery address.",
"Artwork can be sent after payment. The order page lists any item still waiting for a file."
]
},
"isError": false
}
}Arguments that do not match the schema answer a JSON-RPC error with code -32602. Too many calls answer code -32029 with retryAfterSeconds.
Limits
- Body up to 64 KB, sent as
Content-Type: application/json. - Up to 30 lines a request.
- Per address: 30 quotes and 10 pay links a minute. Over that, the answer is 429 with a
Retry-Afterheader in seconds. - MCP tool calls spend from the same limits:
search_catalogandquotecount as quotes,create_pay_linkas a pay link. - Prices are in US cents. Sales tax is added at payment from the delivery address. Shipping is within the United States.
Errors
Every error is JSON: {"error": "...", "status": 400}.
| Status | Meaning |
|---|---|
| 400 | The body does not match the schema, or it holds a bank field. |
| 404 | No orderable product with that slug (catalog filter). |
| 409 | A pay link was asked for, and at least one line cannot be ordered as asked. |
| 413 | Body over 64 KB, or more than 30 lines. |
| 415 | Content type is not JSON. |
| 429 | Too many requests. Wait the seconds in Retry-After. |
| 503 | Pay links are switched off just now. Quotes still work. |