Skip to main content
Documentation

Booking

booking.addons

List the paid extras sold alongside a booking, with their prices
Read-onlyRead budget

REST

Shipping
POST /api/v1/booking/addons

MCP tool

Live
booking.addons

Exposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Booking domain.

Operating contract

The upsell shelf: every extra the shop sells alongside its services — an interior clean, a headlight restoration, a second key coded — with what it costs and how many extra minutes in the bay it takes.

THIS CANNOT CURRENTLY BE BOOKED THROUGH THE REGISTRY. booking.book submits addons: [], so an extra named here is one a machine can QUOTE and cannot ADD. Say the price if asked; do not promise it is on the appointment, and let the shop add it. That is also why this capability is deliberately absent from the public booking server: an agent that offers an extra it cannot book is worse than one that never mentions extras.

AN ADD-ON IS NOT A SERVICE and does not appear in booking.services. A customer cannot book one on its own; it rides on top of a service.

EVERY EXTRA LENGTHENS THE VISIT. duration_minutes is time added to the appointment, not a separate slot.

price is the flat price; tiers carries the per-vehicle price when the extra is priced by size, and it wins where it has an entry. This is what the storefront publishes: no cost, no margin.

AN EXTRA THE SHOP TOOK OFFLINE IS NOT HERE, and neither is a free one or one priced by the hour — a free extra that lengthens the bay is free labour, and an hourly rate is not a price a booking page can sell. Same gate as the storefront's, so the two lists always agree.

Every sellable extra is returned, so nothing is truncated.

Who may call it

Permission
NoneThe extras shelf on the public storefront at /book/<slug>, which a visitor ticks before they confirm with no login involved. The prices are already published there.
Plan
Every planNo plan gate. Available on every Service VIN plan.
Retries
naturalNaturally idempotent — running it twice leaves the same world as running it once. A retrying integration needs no key.
Rate class
readCounted against the read budget — the widest of the four.

Input

This capability takes no arguments.

Output

FieldTypeDescription
itemsobject[]

items[].idstring

items[].namestring

items[].descriptionstring | null

items[].pricenumber

items[].tiersobject

items[].duration_minutesnumber

items[].discount_labelstring | null

currencystring

bookable_through_registryboolean

totalnumber

Examples

Built from this capability's own schema — required fields and the ones carrying a default, and nothing invented. Paste one and it validates.

curl
export SERVICEVIN_API_KEY=svk_live_…

curl -X POST https://www.servicevin.com/api/v1/booking/addons \
  -H "Authorization: Bearer $SERVICEVIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

TypeScript (fetch)
const res = await fetch("https://www.servicevin.com/api/v1/booking/addons", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({}),
});

// Success and failure are both envelopes. Switch on error.code, never
// on error.message — the codes are stable, the messages are for people.
const payload = await res.json();
if (!res.ok) throw new Error(payload.error.code);
const data = payload.data;

Python (requests)
import os, requests

res = requests.post(
    "https://www.servicevin.com/api/v1/booking/addons",
    headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
    json={},
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(payload["error"]["code"])
data = payload["data"]

MCP tools/call — https://www.servicevin.com/api/mcp
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "booking.addons",
    "arguments": {}
  }
}

Refusals

The four gates run in this order on every surface, and the order is not arbitrary — see Authentication.

StatusCodeWhen
404not_foundThe id is unknown, or the feature is not enabled for this account. Deliberately the same answer for both.
422validation_errorAn argument was wrong. The message names the field.
429rate_limitedToo many read calls. Back off and retry.
500internal_errorSomething failed on our side. Nothing was changed.