連接預約服務
代理用MCP或REST API搜尋商家、查空檔,並建立與管理預約。
已上線預約與查詢功能已上線。預約時的線上付款見替客人付款;第一家商家(一位私人教練)已於2026-10-22啟用線上付款。
這頁是給串接的工程師看的。商家不需要讀這頁;想了解商家端怎麼運作,請看接受代理預約。
運作方式
SOP的預約連接器提供兩種介面:MCP伺服器與REST API,兩者共用同一套預約核心,行為一致。代理的典型流程是:搜尋商家、讀商家資料與價格、查空檔、建立預約;需要線上付款時再付款,之後可以查詢、改期或取消。
步驟
- 客人 → 代理:請代理找服務
- 代理 → SOP:搜尋商家與空檔
- SOP → 代理:回傳服務、價格與空檔
- 客人 → 代理:選定時段
- 代理 → SOP:request_booking
- SOP → 代理:先佔名額:awaiting_payment、金額、期限、payment_handlers
- 客人 → 代理:在錢包裡核准確切金額
- 代理 → SOP:帶付款憑證呼叫complete_booking
- SOP → SOP:只扣一次款;商家婉拒或沒確認就全額退款
- SOP → 商家:通知新預約
- 商家 → SOP:新客人的預約由商家確認;逾時未確認就釋出並退款
- SOP → 代理:回傳預約狀態(已成立或待確認)與付款結果
- 代理 → 客人:告知已成立,或等待商家確認
MCP伺服器
端點是https://mcp.srct.tw/mcp,使用Streamable HTTP,無狀態,回應為JSON。連線後先呼叫initialize,再用tools/list取得工具清單。
開啟MCP連線:initialize
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-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":"my-agent","version":"1.0"}}}'
Python
import json
import urllib.error
import urllib.request
BASE = "https://mcp.srct.tw"
def call(method, path, body=None, headers=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
)
try:
with urllib.request.urlopen(req) as res:
return res.status, json.loads(res.read())
except urllib.error.HTTPError as err: # 4xx and 5xx responses still carry a JSON body
return err.code, json.loads(err.read())
def show(status, body):
print("HTTP", status)
print(json.dumps(body, ensure_ascii=False, indent=2))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "my-agent", "version": "1.0"}},
}, MCP))
Node.js
const BASE = "https://mcp.srct.tw";
async function call(method, path, body, headers = {}) {
const res = await fetch(BASE + path, {
method,
headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
body: body === undefined ? undefined : JSON.stringify(body),
});
return { status: res.status, body: await res.json() };
}
function show({ status, body }) {
console.log("HTTP", status);
console.log(JSON.stringify(body, null, 2));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "my-agent", version: "1.0" } },
}, MCP));
})();
回應範例
HTTP 200
{
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": true
}
},
"serverInfo": {
"name": "…-booking",
"title": "…",
"version": "1.0.0",
"websiteUrl": "https://mcp.srct.tw/docs"
},
"instructions": "… lists local service businesses (gyms, personal trainers, studios) in Taiwan that accept bookings through AI agents. Times are Asia/Taipei. Bookings are paid at the venue, unless the business takes online payment: then request_booking returns status awaiting_payment with amount, currency, expires_at and payment_handlers, and complete_booking takes a Stripe shared payment token (payments are collected by Seller Tech U.S.A. LLC through Stripe; unconfirmed paid bookings are refunded in full). A booking is either confirmed immediately or waits for the business to confirm; the status field says which. A pending booking is held only for the business's pending_hold time (10 minutes unless the business sets another); if not confirmed by then, it is released."
},
"jsonrpc": "2.0",
"id": 1
}已對正式服務實際執行(唯讀)
列出工具:tools/list
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
Python
import json
import urllib.error
import urllib.request
BASE = "https://mcp.srct.tw"
def call(method, path, body=None, headers=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
)
try:
with urllib.request.urlopen(req) as res:
return res.status, json.loads(res.read())
except urllib.error.HTTPError as err: # 4xx and 5xx responses still carry a JSON body
return err.code, json.loads(err.read())
def show(status, body):
print("HTTP", status)
print(json.dumps(body, ensure_ascii=False, indent=2))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}, MCP))
Node.js
const BASE = "https://mcp.srct.tw";
async function call(method, path, body, headers = {}) {
const res = await fetch(BASE + path, {
method,
headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
body: body === undefined ? undefined : JSON.stringify(body),
});
return { status: res.status, body: await res.json() };
}
function show({ status, body }) {
console.log("HTTP", status);
console.log(JSON.stringify(body, null, 2));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 2, method: "tools/list" }, MCP));
})();
回應範例
HTTP 200
{
"result": {
"tools": [
{
"name": "search_businesses",
"title": "Search businesses",
"description": "Search …'s listed businesses by keyword (business name, city or district, or type of class) and/or category. Returns up to 10 businesses with a few matching services. Read-only.",
"inputSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"keyword": {
"description": "Words to match, e.g. 'personal training' or 'Taipei'",
"type": "string",
"maxLength": 40
},
"category": {
"description": "gym, coaching (personal trainers), sauna, or recovery",
"type": "string",
"enum": [
"gym",
"coaching",
"sauna",
"recovery"
]
},
"limit": {
"description": "Maximum results, default 5",
"type": "integer",
"minimum": 1,
"maximum": 10
}
}
},
"annotations": {
"title": "Search businesses",
"readOnlyHint": true,
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": false
},
"execution": {
"taskSupport": "forbidden"
}
},
…
]
},
"jsonrpc": "2.0",
"id": 2
}已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。服務回傳9個工具:search_businesses、get_business、get_availability、request_booking、complete_booking、modify_booking、get_booking_status、cancel_booking、get_booking_link。這裡只顯示第一個。
目前共有9個工具:
| 工具 | 用途 | 唯讀 |
|---|---|---|
search_businesses | 依關鍵字或類別搜尋商家,最多回傳10筆。 | 是 |
get_business | 取得商家資料、預約與取消規則,以及可預約的服務、價格與時長。 | 是 |
get_availability | 查某項服務未來1至14天的空檔與剩餘名額。 | 是 |
request_booking | 為客人建立一筆預約。 | 否 |
complete_booking | 帶客人核准的付款憑證,完成需要線上付款的預約。 | 否 |
modify_booking | 改預約的時段或人數。 | 否 |
get_booking_status | 查預約狀態,含付款與退款資訊。 | 是 |
cancel_booking | 取消預約;已付款的會退款,客人自己取消時扣除金流手續費與平台手續費。 | 否 |
get_booking_link | 商家用自己的預約系統時,取得該系統的預約連結。 | 否(會記錄一次點擊) |
寫入類工具可帶idempotency_key,讓重試安全,見替客人付款。
搜尋商家與讀取資料
search_businesses可依關鍵字(商家名稱、城市或行政區、課程類型)與類別搜尋。get_business回傳公開資料與可預約的服務。請直接使用這兩個工具回傳的business_id與service_id,不要自己組。
搜尋商家:search_businesses
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_businesses","arguments":{"keyword":"Taipei","limit":3}}}'
Python
import json
import urllib.error
import urllib.request
BASE = "https://mcp.srct.tw"
def call(method, path, body=None, headers=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
)
try:
with urllib.request.urlopen(req) as res:
return res.status, json.loads(res.read())
except urllib.error.HTTPError as err: # 4xx and 5xx responses still carry a JSON body
return err.code, json.loads(err.read())
def show(status, body):
print("HTTP", status)
print(json.dumps(body, ensure_ascii=False, indent=2))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "search_businesses", "arguments": {"keyword": "Taipei", "limit": 3}}}, MCP))
Node.js
const BASE = "https://mcp.srct.tw";
async function call(method, path, body, headers = {}) {
const res = await fetch(BASE + path, {
method,
headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
body: body === undefined ? undefined : JSON.stringify(body),
});
return { status: res.status, body: await res.json() };
}
function show({ status, body }) {
console.log("HTTP", status);
console.log(JSON.stringify(body, null, 2));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "search_businesses", arguments: { keyword: "Taipei", limit: 3 } } }, MCP));
})();
回應範例
HTTP 200
{
"result": {
"content": [
{
"type": "text",
"text": …
}
],
"structuredContent": {
"ok": true,
"count": 1,
"results": [
{
"business_id": "coach-dunken",
"name": "Dunken教練",
"city": "台北市信義區(Taipei Xinyi)",
"categories": [
"coaching"
],
"booking_mode": "request_here",
"services": [
{
"service_id": "first_assessment",
"name": "初次體能檢查(含InBody一次)",
"price": "免費"
},
{
"service_id": "pt",
"name": "PT一對一教練課",
"price": "NT$18,000/10堂"
},
{
"service_id": "trx",
"name": "TRX懸吊訓練",
"price": "NT$18,000/10堂"
}
]
}
]
},
"isError": false
},
"jsonrpc": "2.0",
"id": 3
}已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。result.content[0].text(同一份JSON的字串版)已省略。
取得商家與服務:get_business
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_business","arguments":{"business_id":"coach-dunken"}}}'
Python
import json
import urllib.error
import urllib.request
BASE = "https://mcp.srct.tw"
def call(method, path, body=None, headers=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
)
try:
with urllib.request.urlopen(req) as res:
return res.status, json.loads(res.read())
except urllib.error.HTTPError as err: # 4xx and 5xx responses still carry a JSON body
return err.code, json.loads(err.read())
def show(status, body):
print("HTTP", status)
print(json.dumps(body, ensure_ascii=False, indent=2))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "get_business", "arguments": {"business_id": "coach-dunken"}}}, MCP))
Node.js
const BASE = "https://mcp.srct.tw";
async function call(method, path, body, headers = {}) {
const res = await fetch(BASE + path, {
method,
headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
body: body === undefined ? undefined : JSON.stringify(body),
});
return { status: res.status, body: await res.json() };
}
function show({ status, body }) {
console.log("HTTP", status);
console.log(JSON.stringify(body, null, 2));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 4, method: "tools/call", params: { name: "get_business", arguments: { business_id: "coach-dunken" } } }, MCP));
})();
回應範例
HTTP 200
{
"result": {
"content": [
{
"type": "text",
"text": …
}
],
"structuredContent": {
"ok": true,
"business": {
"business_id": "coach-dunken",
"name": "Dunken教練",
"categories": [
"coaching"
],
"booking_mode": "request_here",
"city": "台北市信義區(Taipei Xinyi)",
"profile": {
"city": "台北市信義區(Taipei Xinyi)",
"address": "…",
"hours": "每天08:00至20:00",
"booking_policy": "取消與改期需在課程開始24小時前(經AI助理線上取消也是開始前24小時截止)。遲到15分鐘將直接結束當次課程,且無法退費。",
"payment": "現金或轉帳。課程一次購買10堂NT$18,000(每堂折合NT$1,800)。",
"notes": "請自行準備合適的訓練服裝,至少提早10分鐘抵達教室。",
"contact": "…"
},
"faq": [],
"quantity": {
"label": "人數",
"unit": "位",
"max_per_booking": 1
},
"online_cancellation": {
"closes_minutes_before_start": 1440,
"text": "Online cancellation closes 24 hours before the start time; after that, contact the business directly."
},
"pending_hold": {
"minutes": 10,
"text": "Bookings that need the business's confirmation are held for 10 minutes. If the business does not confirm by then, the hold is released automatically and the customer can choose another slot."
},
"payment": {
"mode": "venue",
"text": "Pay at the venue."
}
},
"services": [
{
"service_id": "first_assessment",
"name": "初次體能檢查(含InBody一次)",
"category": "一對一",
"summary": "第一次上課:基本動作與體能檢查,並免費做一次InBody身體組成量測(非醫療評估)。",
"price": {
"amount": 0,
"currency": "TWD",
"unit": "每次",
"text": "免費"
},
"duration_min": 60,
"schedule": [
{
"weekday": "sun",
"time": "08:00",
"teacher": "Dunken"
},
{
"weekday": "sun",
"time": "09:00",
"teacher": "Dunken"
},
…
],
"capacity_per_slot": 1,
"teachers": [
"Dunken"
],
"credit_cost": 0
},
…
],
"payment": "Paid services are paid at the venue; services priced at 0 are free. This connector does not take payments."
},
"isError": false
},
"jsonrpc": "2.0",
"id": 4
}已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。result.content[0].text(同一份JSON的字串版)與商家的地址、聯絡方式已省略。
查空檔
get_availability需要business_id與service_id,可加days(1至14,預設14)與teacher。回傳的每個時段都有固定不變的slot_id(與slot的值相同)、剩餘名額與價格;時區是Asia/Taipei。
查詢可預約時段
curl
curl -sS "https://mcp.srct.tw/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2" \
-H "User-Agent: my-agent/1.0"
Python
import json
import urllib.error
import urllib.request
BASE = "https://mcp.srct.tw"
def call(method, path, body=None, headers=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
)
try:
with urllib.request.urlopen(req) as res:
return res.status, json.loads(res.read())
except urllib.error.HTTPError as err: # 4xx and 5xx responses still carry a JSON body
return err.code, json.loads(err.read())
def show(status, body):
print("HTTP", status)
print(json.dumps(body, ensure_ascii=False, indent=2))
show(*call("GET", "/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2"))
Node.js
const BASE = "https://mcp.srct.tw";
async function call(method, path, body, headers = {}) {
const res = await fetch(BASE + path, {
method,
headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
body: body === undefined ? undefined : JSON.stringify(body),
});
return { status: res.status, body: await res.json() };
}
function show({ status, body }) {
console.log("HTTP", status);
console.log(JSON.stringify(body, null, 2));
}
(async () => {
show(await call("GET", "/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2"));
})();
回應範例
HTTP 200
{
"ok": true,
"service_id": "pt",
"bookable_here": true,
"payment": "venue",
"timezone": "Asia/Taipei",
"earliest_bookable": "120 minutes from now",
"online_cancellation": "Online cancellation closes 24 hours before the start time; after that, contact the business directly.",
"quantity_unit": "位",
"slots": [
{
"slot_id": "2026-10-22T08:00",
"slot": "2026-10-22T08:00",
"start": "2026-10-22T08:00:00+08:00",
"label": "10/22(四)08:00",
"remaining": 1,
"price": {
"amount": 1800000,
"currency": "twd",
"per": "person or place",
"text": "NT$18,000/10堂"
},
"resource": {
"teacher": "Dunken"
}
},
{
"slot_id": "2026-10-22T09:00",
"slot": "2026-10-22T09:00",
"start": "2026-10-22T09:00:00+08:00",
"label": "10/22(四)09:00",
"remaining": 1,
"price": {
"amount": 1800000,
"currency": "twd",
"per": "person or place",
"text": "NT$18,000/10堂"
},
"resource": {
"teacher": "Dunken"
}
},
…
]
}已對正式服務實際執行(唯讀):為顯示而截短;…表示省略的部分。price.amount的單位是幣別最小單位(新臺幣乘以100);時間為Asia/Taipei。時段會隨呼叫當天而不同。
REST API
同一套核心也能用REST呼叫,路徑都在/api/v1/businesses/{business_id}/底下。預約相關端點用Authorization: Bearer <status_token>驗證;status_token只會在建立預約時回傳一次。
| 方法與路徑 | 用途 |
|---|---|
GET availability?service_id=…&days=… | 查空檔 |
POST bookings | 建立預約(內容同request_booking) |
GET bookings/{booking_id} | 查預約,含金額、期限與付款方式 |
POST bookings/{booking_id}/complete | 帶付款憑證完成預約 |
PATCH bookings/{booking_id} | 改時段或人數 |
DELETE bookings/{booking_id} | 取消預約(已付款的會退款,客人自己取消時扣除金流手續費與平台手續費) |
錯誤一律回{"error": {"code", "message"}}與對應的HTTP狀態,常見代碼見接受代理預約。
探索文件
https://mcp.srct.tw/openapi.json目前只描述機器付款的付款連結,並把可付款的操作標上x-payment-info。目前只有測試網的機器付款會用到,見機器付款。
讀取探索文件:/openapi.json
curl
curl -sS "https://mcp.srct.tw/openapi.json" \
-H "User-Agent: my-agent/1.0"
Python
import json
import urllib.error
import urllib.request
BASE = "https://mcp.srct.tw"
def call(method, path, body=None, headers=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
)
try:
with urllib.request.urlopen(req) as res:
return res.status, json.loads(res.read())
except urllib.error.HTTPError as err: # 4xx and 5xx responses still carry a JSON body
return err.code, json.loads(err.read())
def show(status, body):
print("HTTP", status)
print(json.dumps(body, ensure_ascii=False, indent=2))
show(*call("GET", "/openapi.json"))
Node.js
const BASE = "https://mcp.srct.tw";
async function call(method, path, body, headers = {}) {
const res = await fetch(BASE + path, {
method,
headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
body: body === undefined ? undefined : JSON.stringify(body),
});
return { status: res.status, body: await res.json() };
}
function show({ status, body }) {
console.log("HTTP", status);
console.log(JSON.stringify(body, null, 2));
}
(async () => {
show(await call("GET", "/openapi.json"));
})();
回應範例
HTTP 200
{
"openapi": "3.1.0",
"info": {
"title": "… booking payments",
"version": "1.0.0",
"description": "Pay for … bookings with x402 or MPP. Create the booking first with the MCP tool request_booking or POST /api/v1/businesses/{business_id}/bookings; use the payment_link it returns."
},
"servers": [
{
"url": "https://mcp.srct.tw"
}
],
"x-service-info": {
"categories": [
"booking",
"fitness",
"services"
],
"docs": {
"homepage": "https://mcp.srct.tw/",
"apiReference": "https://mcp.srct.tw/docs"
}
},
"paths": {
"/pay/{business_id}/{booking_id}": {
"post": {
"summary": "Pay for a booking (x402 or MPP)",
"parameters": [
{
"name": "business_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "booking_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "t",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "pay token from the booking's payment_link"
}
],
"x-payment-info": {
"offers": [
{
"intent": "charge",
"method": "evm",
"amount": null,
"currency": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"description": "USDC on Base Sepolia (test network); also accepted as x402 exact"
},
{
"intent": "charge",
"method": "tempo",
"amount": null,
"currency": "0x20c0000000000000000000000000000000000000",
"description": "pathUSD on Tempo test network (Moderato)"
}
]
},
"responses": {
"200": {
"description": "Paid: booking and payment result"
},
"402": {
"description": "Payment Required: x402 PAYMENT-REQUIRED and MPP WWW-Authenticate challenges"
},
"409": {
"description": "Booking not payable"
},
"410": {
"description": "Payment window passed"
}
}
},
"get": {
"summary": "Pay for a booking (x402 or MPP)",
"parameters": [
{
"name": "business_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "booking_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "t",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "pay token from the booking's payment_link"
}
],
"x-payment-info": {
"offers": [
{
"intent": "charge",
"method": "evm",
"amount": null,
"currency": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"description": "USDC on Base Sepolia (test network); also accepted as x402 exact"
},
{
"intent": "charge",
"method": "tempo",
"amount": null,
"currency": "0x20c0000000000000000000000000000000000000",
"description": "pathUSD on Tempo test network (Moderato)"
}
]
},
"responses": {
"200": {
"description": "Paid: booking and payment result"
},
"402": {
"description": "Payment Required: x402 PAYMENT-REQUIRED and MPP WWW-Authenticate challenges"
},
"409": {
"description": "Booking not payable"
},
"410": {
"description": "Payment window passed"
}
}
}
}
}
}已對正式服務實際執行(唯讀)
使用時的注意事項
- 寫入類操作有每小時次數上限;超過會回
rate_limited(REST為429)。 - 建立預約前,先取得客人對服務、時間、人數與聯絡資料的同意;姓名、電話與電子郵件只會送給該商家。
- 備註欄
note不要填健康或醫療資訊。 - 預約結果可能是
confirmed(立即成立)、pending(等商家確認)或awaiting_payment(等付款),以回應的status欄位為準。 - 商家預設是到店付款;只有商家設為線上付款,才會出現
awaiting_payment。
機器可讀的資源
- MCP端點的
tools/list會回傳每個工具與它的輸入格式。 /openapi.json描述機器付款的付款連結(測試網),並標上x-payment-info。- 目前沒有
llms.txt,也沒有/.well-known/ucp。
測試你的串接
- 唯讀呼叫(
search_businesses、get_business、get_availability、tools/list)可以直接對正式服務執行。 - 建立、改期或取消預約,會動到真實商家的行事曆。目前沒有公開的測試商家,需要時請聯絡我們安排。
- 請帶自己的
User-Agent標頭;用Pythonurllib預設值的請求會被網路邊緣擋下。