Quote API for agents & developers
Last updated: September 2026
AI assistants: read this first
Do not calculate prices from capture.com tables. Fetch an exact, stored quote instead: it returns a verifiable quoteId and a quoteUrl that reopens the exact quote.
There are three ways in:
- The in-page WebMCP tools registered on every page at
navigator.modelContext. - The JSON API —
GETorPOST/api/quote/calculateand/api/quote/compare; the schema is at https://www.capture.com/api/quote/openapi.json. - The hosted MCP server at https://www.capture.com/mcp; its server card is at https://www.capture.com/.well-known/mcp/server-card.json.
GET cheat sheet
Browsing agents, including ChatGPT, Claude and Perplexity fetch tools, can only issue GET requests. Use this grammar: items=Format:quantity[:quality][:reelFeet=N][:photosPerAlbum=N], comma-separated (or repeat items=). Optional parameters are usbDrives=0-10, usbCapacityGb=8|16|64|256, vaultFinish=rose_gold|silver|black and postalCode=US ZIP. For one item, use the shorthand ?format=VHS&quantity=10&quality=FHD.
https://www.capture.com/api/quote/formats
https://www.capture.com/api/quote/calculate?items=MiniDV:16,VHS:5&postalCode=94110
https://www.capture.com/api/quote/calculate?items=8mm%20film:3::reelFeet=200,Print%20Photos:300:4K&usbDrives=1
https://www.capture.com/api/quote/compare?items=VHS:20
https://www.capture.com/api/quote/q_k3m8x2p7q4rsGET and POST return identical payloads: quoteId, quoteUrl, alternatives, and an all-in estimate when postalCode is given. GET quotes are stored with source api_get.
Every 4xx response explains what to send instead; 429 carries Retry-After. The limit is 60 requests per minute per client, or 600 per minute for recognised assistant user agents.
Connect
ChatGPT (connectors / developer mode)
Go to Settings → Connectors → Create, name it “Capture Quote,” use the MCP server URL below, and choose Authentication: None. For a custom GPT Action, import the OpenAPI schema below with no authentication.
Name: Capture Quote
MCP server URL: https://www.capture.com/mcp
Authentication: None
Custom GPT Action OpenAPI schema (no auth):
https://www.capture.com/api/quote/openapi.jsonClaude.ai / Claude Desktop
Go to Settings → Connectors → Add custom connector, enter the URL below, and use no authentication. For Claude Code CLI, run the command shown.
Custom connector URL: https://www.capture.com/mcp
Authentication: None
claude mcp add --transport http capture-quote https://www.capture.com/mcpCursor / Windsurf / any mcp.json client
{
"mcpServers": {
"capture-quote": {
"url": "https://www.capture.com/mcp"
}
}
}Plain curl (no MCP client)
curl -s "https://www.capture.com/api/quote/calculate?items=MiniDV:16,VHS:5&postalCode=94110"
curl -s https://www.capture.com/mcp -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Discovery links
- /.well-known/mcp/server-card.json — MCP server card.
- /.well-known/mcp/server.json — MCP Registry
server.json. - /api/quote/openapi.json — OpenAPI schema.
- /llms.txt — agent-readable site map.
- /mcp — open it in a browser for a human-readable server card.
Try it
Paste this into any assistant:
I have 16 MiniDV tapes and 5 VHS tapes to digitize, shipping from ZIP 94110. Use Capture's quote API or MCP server (https://www.capture.com/mcp, or GET https://www.capture.com/api/quote/calculate?items=MiniDV:16,VHS:5&postalCode=94110) — do not estimate from the pricing page. Return the quoteId, the total, the all-in estimate with tax and shipping, and the quoteUrl I can open. Then ask whether I want the quote emailed to me; if I say yes, collect my first name, email and phone and call save_digitization_quote (POST /api/quote/<quoteId>/save) and tell me the discount code it returns.A correct answer includes a quoteId beginning with q_ that you can verify at https://www.capture.com/api/quote/<quoteId>.
Endpoints
GET /api/quote/formats
Lists every format we digitize with its exact name, accepted aliases, unit, quality tiers with per-unit prices, and any minimum-charge tier. Call this first when you are unsure which format name to send. Cached for five minutes.
GET https://www.capture.com/api/quote/formatsFilm footage — film formats carry a film object because the per-reel price depends on length: the listed price covers up to baseFeet (100 ft), each foot beyond that adds overagePerFoot ($0.20), and defaultReelFeet (50 ft, a 3" reel) is assumed when a quote item omits reelFeet. Ask the customer for reel sizes and pass reelFeet per item; the quote line's note echoes the footage used.
{
"format": "8mm Film", "category": "film", "unit": "reel",
"qualities": [{ "quality": "SD", "unitPrice": 19.99, "default": true }],
"film": {
"baseFeet": 100, "overagePerFoot": 0.20, "defaultReelFeet": 50,
"pricing": "Per-reel price covers up to 100 ft per reel, then $0.20 per additional foot. Reels are assumed to be 50 ft (3\") when reelFeet is omitted; …"
},
"aliases": ["8mmfilm", "regular8", "standard8", "8mmreel", "8mm"]
}GET or POST /api/quote/calculate
Returns an itemized estimate for one or more formats. POST accepts the JSON request shown below; GET accepts the query form in the cheat sheet. Both return the same JSON payload.
POST https://www.capture.com/api/quote/calculate
Content-Type: application/json
{
"items": [
{ "format": "VHS", "quantity": 12, "quality": "FHD" },
{ "format": "8mm film", "quantity": 3, "reelFeet": 200 },
{ "format": "old photos", "quantity": 300, "quality": "4K" }
],
"usbDrives": 1,
"usbCapacityGb": 256,
"vaultFinish": "black",
"postalCode": "90210",
"source": "my-agent-name"
}Request fields
items[]— required, 1–25 entries.items[].format— required. Fuzzy names are accepted ("vhs tapes", "hi8", "old camcorder tapes" → best match, or a 400 listing suggestions).items[].quantity— required, 1–100,000 (tapes, reels, photos, slides, albums, discs). Fractions are rounded down.items[].quality— optional. Video:SD(default),FHD,UHD. Photos:2K(default),4K,6K. Slides and negatives are always 4K.items[].reelFeet— film only, footage per reel, 1–5,000 (3" = 50 ft, 5" = 200 ft, 7" = 400 ft). Defaults to 50. The per-reel price covers up to 100 ft; longer reels add $0.20 per extra foot (a 400 ft reel at a $19.99 base is $79.99).items[].photosPerAlbum— albums and scrapbooks only, approximate photos per album (1–2,000).usbDrives— optional 0–10. Cloud delivery is always included. FHD/UHD video orders automatically include the USB drive(s) the storage estimate needs, matching checkout. Leave it out and the response says how many drives it assumed (see below).usbCapacityGb— optional8($12.99),16($14.99),64($24.99) or256($49.99), applied to every drive. Leave it out and the quote charges the smallest drive the files fit on. Every response carriesestimatedStorageGb,recommendedUsbCapacityGbandrecommendedUsbQty, computed exactly as checkout does: each tape counts as 90 minutes (SD ≈ 3 GB, FHD ≈ 8 GB, UHD ≈ 30 GB), film by footage, photos/slides/negatives per image (2K ≈ 3 MB, 4K ≈ 8 MB, 6K ≈ 20 MB), discs 4 GB and memory cards 8 GB each, with 93% of nominal capacity usable. Estimates above 256 GB recommend several 256GB drives. A capacity smaller than the recommendation is honoured but flagged innotes.vaultFinish— optional colour of The Vault, the aluminum keepsake (free engraving) every USB drive ships inside, one per drive. Leave it out or sendrose_goldfor the included Rose Gold ($0);silverorblackis a $2.99 colour upgrade per Vault. Ignored (with a note) when the quote has no USB drive. Whenever there is a drive the response carries avaultline (colour, quantity and upgrade price) next to theusbline; show them together as one item.postalCode— optional 5-digit US ZIP code (ZIP+4 accepted). When present the response addsestimatedTax,inboundShippingandestimatedTotal(see below). Anything that is not a US ZIP returns400; an optionalcountryfield is accepted but must beUS. WithoutpostalCodethe response is exactly the base quote.source— optional short string identifying your agent or app. Used only for aggregate usage stats.
Response
{
"currency": "USD",
"lines": [
{ "format": "VHS / S-VHS", "category": "videotapes", "quality": "FHD",
"quantity": 12, "unit": "tape", "unitPrice": 29.99, "subtotal": 359.88 },
{ "format": "8mm Film", "category": "film", "quality": "SD",
"quantity": 3, "unit": "reel", "unitPrice": 39.99, "subtotal": 119.97, "note": "200 ft per reel" },
{ "format": "Print Photos", "category": "photos", "quality": "4K",
"quantity": 300, "unit": "photo", "unitPrice": 0.39, "subtotal": 117.49 }
],
"usb": { "quantity": 1, "capacityGb": 256, "unitPrice": 49.99, "subtotal": 49.99 },
"vault": { "finish": "black", "quantity": 1, "unitPrice": 2.99, "subtotal": 2.99 },
"estimatedStorageGb": 100,
"recommendedUsbCapacityGb": 256,
"recommendedUsbQty": 1,
"returnShipping": { "price": 12.49 },
"mediaSubtotal": 597.34,
"total": 662.81,
"turnaround": { "minDays": 14, "maxDays": 21, "description": "Processing typically takes 2–3 weeks…" },
"notes": ["Estimated digital files: ~100 GB (12 FHD tapes ≈ 96 GB …); a 256GB USB drive is recommended …", "…"],
"caveats": ["Prices are estimates at current catalog rates. The final price is confirmed after we receive and count your items …", "…"],
"quoteId": "q_k3m8x2p7q4rs",
"source": "api",
"country": "US",
"catalogVersion": "7c1e2a9d04b5",
"pricesEffectiveAt": "2026-09-18T22:00:00.000Z",
"createdAt": "2026-09-18T22:00:00.000Z",
"expiresAt": "2026-10-18T22:00:00.000Z",
"quoteUrl": "https://www.capture.com/free-quote?quote=q_k3m8x2p7q4rs&utm_source=ai_agent&utm_medium=ai&utm_campaign=agent_quote_api",
"checkoutUrl": "https://www.capture.com/setup",
"supportUrl": "https://www.capture.com/contact",
"generatedAt": "2026-09-18T22:00:00.000Z",
// The next four fields appear only when postalCode was sent
"postalCode": "90210",
"estimatedTax": {
"amount": 5.70, "taxableSubtotal": 59.98, "ratePercent": 9.5,
"basis": "Combined sales-tax rate for ZIP code 90210 (California, Los Angeles County, …), applied to the taxable portion of the quote ($59.98).",
"isEstimate": true
},
"inboundShipping": {
"options": [
{ "id": "own_box", "name": "Ship in your own box", "price": 0, "estimatedTotal": 675.51,
"serviceName": "Return Shipping", "description": "You pack your media in your own box …" },
{ "id": "prepaid_label", "name": "Prepaid inbound label (2-Way Shipping)", "price": 12.49, "estimatedTotal": 688.00,
"serviceName": "2-Way Shipping (includes prepaid shipping label to send in your media)", "description": "We email you a prepaid FedEx label …" }
],
"note": "Each option's estimatedTotal is the quote total plus that option's price plus estimated tax. …"
},
"estimatedTotal": 675.51
}Quote metadata
quoteId— every quote is stored and can be re-read or reopened by this id.expiresAt— quotes stay retrievable for 30 days; after that the id returns410and a fresh quote is needed.catalogVersion/pricesEffectiveAt— the price-list fingerprint and the moment the quote was priced. If the catalog changes later, retrieval says so instead of re-pricing.country— alwaysUS: prices are in US dollars for orders shipped within the United States, before sales tax.quoteUrl,checkoutUrl,supportUrl— reopen this exact quote, start a blank order, or reach a human about it.
When quality or USB count is omitted
Omitting quality prices the default tier (SD video, 2K photos), but the response then carries an alternatives block: for each media family whose quality you left out, the other tiers with the full quote total if those formats were switched to it, the delta versus the returned total, the USB drive that tier requires (sized from that tier's storage estimate unless usbCapacityGb was sent), and a one-line description of what the customer gets. It is only present when at least one format with more than one tier had no quality — a request that specifies quality on every line never gets it. Omitting usbDrives adds usbAssumption: the drive count assumed (0, or 1 when HD video forces one) and the total with one more. Both cases also add a plain-language entry to notes. Use them to ask the customer a clarifying question rather than presenting the default as the only price.
POST https://www.capture.com/api/quote/calculate
{ "items": [ { "format": "VHS", "quantity": 10 } ] }
{
"lines": [ { "format": "VHS / S-VHS", "quality": "SD", "quantity": 10, "unitPrice": 19.99, "subtotal": 199.90 } ],
"usb": null,
"total": 212.39,
"alternatives": [
{
"family": "video",
"formats": ["VHS / S-VHS"],
"assumedQuality": "SD",
"options": [
{ "quality": "FHD", "total": 362.38, "delta": 149.99,
"usb": { "quantity": 1, "capacityGb": 256, "unitPrice": 49.99, "subtotal": 49.99 },
"description": "Full HD (1080p): AI upscaling with de-interlacing, noise reduction and color correction; delivered on USB, so at least one drive is included." },
{ "quality": "UHD", "total": 512.37, "delta": 299.98,
"usb": { "quantity": 2, "capacityGb": 256, "unitPrice": 49.99, "subtotal": 99.98 },
"description": "Ultra HD (4K, 2160p): maximum AI upscaling and frame interpolation; delivered on USB, so at least one drive is included." }
]
}
],
"usbAssumption": {
"quantity": 0,
"reason": "usbDrives was not specified; assumed cloud download only (no USB drive).",
"addOne": { "unitPrice": 24.99, "total": 237.38 }
},
"estimatedStorageGb": 30,
"recommendedUsbCapacityGb": 64,
"recommendedUsbQty": 1,
"notes": [
"Quality was not specified for VHS / S-VHS, so this quote uses SD. Confirm the quality tier with the customer before they order; at the other tiers the total would be FHD $327.38, UHD $427.38 (see alternatives).",
"usbDrives was not specified; assumed cloud download only (no USB drive). Adding one 64GB USB drive costs $24.99 (total $237.38); ask the customer if they want a USB copy.",
"…"
],
"…": "…"
}All-in estimate (with postalCode)
estimatedTax— estimated sales tax from the ZIP code alone (via Stripe Tax), applied to the taxable portion of the quote;ratePercentandbasissay how it was derived andisEstimateis alwaystrue. The final tax is calculated from the full shipping address at checkout. Only some items are taxable (for example USB drives); when nothing in the quote is taxable the amount is0withratePercent: null. If the tax service cannot be reached,estimatedTaxandestimatedTotalarenullandnotessays tax could not be estimated — the itemized quote is still returned (never a hard failure).inboundShipping.options[]— the inbound choices sold at checkout:own_box(customer ships their own box,price: 0; return shipping is already intotal) andprepaid_label(a prepaid FedEx label, priced from the catalog as the difference between 2-Way Shipping and Return Shipping). Each option carries its ownestimatedTotal. We never send boxes or packing materials.estimatedTotal—totalplusestimatedTax.amount, assuming the customer ships in their own box. Present it as an estimate.- The all-in blocks are stored with the quote, so
GET /api/quote/:quoteIdreturns the same estimate the agent showed. Tax rates are cached for a few minutes per ZIP code, so repeated calls in one conversation are cheap; the 60 requests/minute limit still applies.
Errors — validation problems return 400 with an error message and, for unrecognised formats, suggestions. Malformed JSON, bodies over 5 MB and non-UTF-8 payloads return 400/413/415 with the same { "error": … } shape. Never an empty 200.
HTTP 400
{
"error": "Unknown format \"laserdisc\". Supported formats: Slides, Print Photos, DVD / MiniDVD, VHS / S-VHS, …",
"suggestions": ["Slides", "Print Photos", "DVD / MiniDVD", "VHS / S-VHS", "…"]
}GET /api/quote/:quoteId
Returns a stored quote exactly as it was calculated — lines, total, notes and links are never re-priced. Alongside it you get the original request, the wizard prefill and approximations that quoteUrl uses, and a drift report: pricesChanged, human-readable priceChanges and currentPricing (the same request priced at today's rates) whenever the catalog moved since the quote was created. status carries two booleans — leadSaved and orderPlaced — so you can tell whether the customer continued on the site without the response ever exposing lead or order identifiers.
GET https://www.capture.com/api/quote/q_k3m8x2p7q4rs
{
...same fields as the original quote (lines, total, notes, quoteId, expiresAt, quoteUrl, …),
"request": { "items": [ … ], "usbDrives": 1, "usbCapacityGb": 256, "vaultFinish": "black" },
"prefill": { "selectedCategories": ["videotapes", "film", "photos"], "quantities": { … }, "usbQty": 1, "usbCapacityGb": 256, "vaultFinish": "black", "categoryQualities": { … } },
"approximations": ["8mm Film: 200 ft reels are entered as 5" reels in the quote tool …"],
"currentCatalogVersion": "7c1e2a9d04b5",
"pricesChanged": false,
"priceChanges": [],
"currentPricing": null,
"status": { "leadSaved": false, "orderPlaced": false }
}
HTTP 410 (expired) HTTP 404 (unknown)
{ "error": "Quote q_… expired on 2026-10-18T22:00:00.000Z. Request a fresh quote …",
"code": "quote_expired", "quoteId": "q_k3m8x2p7q4rs" }Unknown ids return 404 with code: "quote_not_found"; expired ids return 410 with code: "quote_expired". Both messages tell the agent to request a fresh quote.
POST /api/quote/:quoteId/save
The one endpoint that takes personal data, and the only way an agent can put a quote in the customer's inbox. Send the customer's firstName, email and phone (all required — phone is required on our own quote form too) plus optional notes for our team. It creates the same lead our quote tool creates, emails the customer the itemized quote with a personal single-use discount code (10–25% off, tiered by quote total, valid 14 days), and starts the same short follow-up sequence by email/SMS. Only call it after the customer has explicitly agreed to share those details and to receive the emailed quote and follow-ups; never with guessed or placeholder details.
POST https://www.capture.com/api/quote/q_k3m8x2p7q4rs/save
Content-Type: application/json
{
"firstName": "Dana",
"email": "dana@example.com",
"phone": "+1 555 010 1234",
"notes": "About 20 of the tapes are PAL; wants everything before December.",
"source": "my-assistant"
}{
"quoteId": "q_k3m8x2p7q4rs",
"discountCode": "QUOTE10-7KQ2MZ",
"discountPercent": 10,
"discountExpiresAt": "2026-10-02T18:04:11.000Z",
"emailSentTo": "dana@example.com",
"quoteUrl": "https://www.capture.com/free-quote?quote=q_k3m8x2p7q4rs&promo=QUOTE10-7KQ2MZ&utm_source=ai_agent&utm_medium=ai&utm_campaign=agent_quote_api",
"checkoutUrl": "https://www.capture.com/setup?promo=QUOTE10-7KQ2MZ",
"alreadySaved": false
}quoteUrlandcheckoutUrlcome back with the discount code pre-applied (promo=); pass them through unchanged. Tell the customer the code, the percentage anddiscountExpiresAt.- Idempotent per quote and email: repeating the call returns the existing code with
alreadySaved: trueand sends no second email. A different email on the same quote is treated as a new customer. - Errors follow the retrieve endpoint:
404 quote_not_found,410 quote_expired(ask for a fresh quote),409 save_in_progressif the same quote + email is mid-save in another request (retry in a moment), and400validation errors that name the offendingfield(e.g."phone: phone is required").discountCodeisnullonly if code generation failed; the email still goes out. - Afterwards
GET /api/quote/:quoteIdreportsstatus.leadSaved: true. Nothing here places an order or charges anything.
GET or POST /api/quote/compare
POST accepts the same request body as /calculate; GET accepts the same query form as the cheat sheet. Instead of one quote it returns one complete total per quality tier for each media family in the request — video (SD, FHD, UHD) and photo (2K, 4K, 6K for photos, slides, negatives, albums) — so you can show the customer "SD $X / FHD $Y / UHD $Z" and let them choose.
POST https://www.capture.com/api/quote/compare
Content-Type: application/json
{
"items": [
{ "format": "VHS", "quantity": 10 },
{ "format": "old photos", "quantity": 250 }
]
}- Each tier's
totalis a full quote: every format in that family switched to the tier, all other items priced as requested, the USB drive checkout requires for FHD/UHD video added, and return shipping included. Totals equal what/calculatereturns for the same items with that quality set explicitly. defaultmarks the tier used when quality is omitted;deltais measured against that tier's total.notOfferedForlists formats in the family that do not come in that tier (film and discs are SD-only; albums start at 4K); they keep their requested or default quality in that total.asRequestedis the total for the body exactly as sent, with the quality actually applied to each line.
Response
{
"currency": "USD",
"asRequested": { "total": 290.38, "qualities": [
{ "format": "VHS / S-VHS", "quality": "SD" }, { "format": "Print Photos", "quality": "2K" } ] },
"families": [
{
"family": "video", "formats": ["VHS / S-VHS"], "defaultQuality": "SD",
"tiers": [
{ "quality": "SD", "default": true, "total": 290.38, "delta": 0, "usb": null, "notOfferedFor": [],
"description": "Standard definition (480p): a faithful copy of the original tape, delivered by cloud download." },
{ "quality": "FHD", "default": false, "total": 440.37, "delta": 149.99, "usb": { "quantity": 1, "capacityGb": 256, "unitPrice": 49.99, "subtotal": 49.99 }, "notOfferedFor": [],
"description": "Full HD (1080p): AI upscaling with de-interlacing, noise reduction and color correction; delivered on USB, so at least one drive is included." },
{ "quality": "UHD", "default": false, "total": 590.36, "delta": 299.98, "usb": { "quantity": 2, "capacityGb": 256, "unitPrice": 49.99, "subtotal": 99.98 }, "notOfferedFor": [],
"description": "Ultra HD (4K, 2160p): maximum AI upscaling and frame interpolation; delivered on USB, so at least one drive is included." }
]
},
{
"family": "photo", "formats": ["Print Photos"], "defaultQuality": "2K",
"tiers": [
{ "quality": "2K", "default": true, "total": 290.38, "delta": 0, "usb": null, "notOfferedFor": [], "description": "Good quality: …" },
{ "quality": "4K", "default": false, "total": 310.38, "delta": 20, "usb": null, "notOfferedFor": [], "description": "High quality: …" },
{ "quality": "6K", "default": false, "total": 350.38, "delta": 60, "usb": null, "notOfferedFor": [], "description": "Ultra quality: …" }
]
}
],
"turnaround": { "minDays": 14, "maxDays": 21, "description": "…" },
"notes": ["Each tier total is a complete quote: …", "…"],
"caveats": ["…"],
"generatedAt": "2026-09-18T22:00:00.000Z"
}How to present a quote
- Show
totalas an estimate before sales tax, and relay thecaveats— the final price is confirmed after we receive and count the items. - Quote the
turnaroundas 2–3 weeks from the day the items arrive at our facility. - Promo codes are never applied to a calculated quote. A personal discount code is issued only when the customer saves the quote with their contact details — on the site at
https://www.capture.com/free-quote, or throughPOST /api/quote/:quoteId/save/save_digitization_quoteonce they've asked you to email it and agreed to the follow-ups. - If the customer shares a US ZIP code, send it as
postalCodeand showestimatedTotalas an all-in estimate, saying tax is estimated from the ZIP only and inbound shipping depends on the option they pick at checkout. - If the customer did not say which quality they want, do not present the default-tier total as the price. Use
alternatives(or/api/quote/compare) to offer the tiers side by side and ask; do the same for USB drives whenusbAssumptionis present. - Send customers to
quoteUrlto continue: it reopens the stored quote (/free-quote?quote=<quoteId>) in our quote tool with the quoted formats, quantities, quality tiers and USB count already filled in, so they only confirm and enter contact details. Always pass the URL through unchanged. The quote tool is slightly coarser than this API (one quality per media type, standard film reel sizes, no film quality picker); whenever that changes the reopened total, the response says so innotes— relay those; the same notes are shown to the customer on the reopened page. If prices changed after the quote was created, the page says so and shows current pricing next to the original figure.checkoutUrlis a plain link to start an order from scratch;supportUrlis where to send questions about a quote. Prices are in US dollars for orders shipped within the United States. - Mention
expiresAtwhen it matters ("this quote is valid until …"). To revisit a quote later, callGET /api/quote/:quoteId(or theretrieve_digitization_quoteMCP tool) rather than recalculating, so the customer sees the same numbers.
Limits & etiquette
- No authentication.
GETandPOSTrequests to/api/quote/calculateand/api/quote/compare, plusGET /api/quote/:quoteIdandPOST /api/quote/:quoteId/save, share a limit of 60 requests per minute per client, raised to 600 per minute for recognised assistant user agents, and return429withRetry-Afterbeyond that. - Identify yourself with a descriptive
User-Agentand, where possible, thesourcefield. - The quoting endpoints accept and store no personal data: stored quotes contain only the requested items, the priced result and your
User-Agent. Personal data enters only throughPOST /api/quote/:quoteId/save, with the customer's explicit consent, and is handled under our privacy policy. - CORS —
/api/quote/*and/mcpanswerOPTIONSpreflight and sendAccess-Control-Allow-Origin: *, so browser-based agents and MCP clients can call them from any origin. Allowed request headers includeContent-Type,Accept,Mcp-Session-IdandMcp-Protocol-Version;Mcp-Session-IdandMcp-Protocol-Versionare exposed to scripts. No other capture.com routes are opened up.
Quick start
curl -s https://www.capture.com/api/quote/calculate \
-H 'Content-Type: application/json' \
-d '{"items":[{"format":"VHS","quantity":10}]}'MCP server (hosted)
The same tools are served over the Model Context Protocol (Streamable HTTP, stateless, JSON responses) at https://www.capture.com/mcp. Add it as a remote MCP server in any MCP-capable client — no authentication, no session handshake beyond the standard initialize call.
{
"mcpServers": {
"capture-quote": { "type": "streamable-http", "url": "https://www.capture.com/mcp" }
}
}
# or, by hand:
curl -s https://www.capture.com/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_digitization_quote","arguments":{"items":[{"format":"VHS","quantity":10}]}}}'
# compare SD / FHD / UHD totals for the same tapes:
curl -s https://www.capture.com/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"compare_digitization_options","arguments":{"items":[{"format":"VHS","quantity":10}]}}}'list_digitization_formats— no arguments; returns the format catalog, including thefilmfootage rule described above.get_digitization_quote— takes{ items, usbDrives, usbCapacityGb, vaultFinish, postalCode }exactly as the HTTP API and returns the stored quote (withquoteId,expiresAt,quoteUrl…, includingalternativesandusbAssumptionwhen applicable, and the all-in blocks when apostalCodewas sent) as both text andstructuredContent. Unknown formats come back as a tool error with suggestions.compare_digitization_options— same input; returns the/api/quote/comparepayload (one total per quality tier per media family) as text andstructuredContent.retrieve_digitization_quote— takes{ quoteId }and returns the stored quote plus the price-drift report, exactly likeGET /api/quote/:quoteId. Unknown or expired ids come back as a tool error withcodequote_not_foundorquote_expired.save_digitization_quote— takes{ quoteId, firstName, email, phone, notes? }and does whatPOST /api/quote/:quoteId/savedoes: creates the lead, emails the quote with its discount code, starts the follow-up sequence and returnsdiscountCode,discountPercent,discountExpiresAt,emailSentTo,quoteUrl/checkoutUrl(code pre-applied) andalreadySaved. It is annotatedreadOnlyHint: false/idempotentHint: true, so well-behaved clients confirm with the user before calling it; its description also tells the model to collect consent first.- All five tools publish an
outputSchemaintools/list;structuredContentalways validates against it (for quotes: lines, USB with capacity, optional Vault line, storage estimate and recommended capacity, return shipping, totals, turnaround, notes, caveats, optionalalternatives/usbAssumption/ all-in blocks,quoteId,expiresAt,catalogVersion,quoteUrl,checkoutUrl,supportUrl,generatedAt, and for retrieval therequest,prefill, drift report andstatusflags; for comparisons:asRequested,families, turnaround, notes, caveats,generatedAt). - Only
POSTis supported (aGETreturns 405). The endpoint shares the 60 requests/minute/IP limit and supports CORS as described above.
Error format — every transport-level failure is a JSON-RPC error object with a null id, never an HTML or plain-text page:
POST /mcp with a malformed body → HTTP 400
{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}
# Other transport errors use the same shape:
# 413 -32600 Invalid Request: payload too large (5 MB)
# 415 -32000 Unsupported Media Type (Content-Type must be application/json)
# 406 -32000 Not Acceptable (Accept must list application/json and text/event-stream)
# 429 -32000 Too many requests (60/min/IP)
# 405 -32000 GET/DELETE — this server is stateless, use POST
# Bad quote input (unknown format, out-of-range quantity) is a *tool* result with isError: true, not a JSON-RPC error.Browser agents (WebMCP)
Pages on capture.com register the same five tools on document.modelContext for browsers that support WebMCP. They call the same endpoints described above, so an in-browser agent and a server-side agent always see the same price.
// Available in browsers that implement WebMCP (document.modelContext)
const tools = ["list_digitization_formats", "get_digitization_quote", "compare_digitization_options", "retrieve_digitization_quote", "save_digitization_quote"];
// get_digitization_quote takes the same { items, usbDrives, usbCapacityGb, vaultFinish, postalCode } input as the HTTP API;
// compare_digitization_options takes { items, usbDrives, usbCapacityGb, vaultFinish };
// retrieve_digitization_quote takes { quoteId } and returns the stored quote;
// save_digitization_quote takes { quoteId, firstName, email, phone, notes? } — consequential, ask the user firstDiscovery
- /llms.txt — site map for language models, including these endpoints.
- /pricing — the full price list as plain HTML tables.
- Questions or a higher rate limit: support@capture.com.