Accept agent bookings
Let customers' AI agents check your time slots, book, change and cancel; you confirm on the Order Desk.
LiveBooking is live. Online payment is live too; the first merchant, a personal trainer, turned on online payment on 2026-10-22.
This page is written for merchants and for the people who build the integration for them.
How it works
Your services, prices and time slots go live only after you confirm each item. A customer asks their own AI to book, and the AI sends it through SOP's booking interface; you confirm or decline on the Order Desk and get a LINE notification.
Steps
- Customer → Agent: Asks for a service
- Agent → SOP: Search businesses and open slots
- SOP → Agent: Services, prices and open slots
- Customer → Agent: Chooses a slot
- Agent → SOP: request_booking
- SOP → Agent: Slot held: awaiting_payment, amount, expires_at, payment_handlers
- Customer → Agent: Approves the exact amount in the wallet
- Agent → SOP: complete_booking with the payment credential
- SOP → SOP: Charge once; if the business declines or doesn't confirm, refund in full
- SOP → Seller: Notify the new booking
- Seller → SOP: Confirms a first visit; if not in time, it is released and refunded
- SOP → Agent: Booking status (confirmed or pending) and payment result
- Agent → Customer: Booking confirmed, or waiting for the business
What agents can do
| Operation | MCP tool | REST |
|---|---|---|
| Check availability | get_availability | GET availability |
| Create a booking | request_booking | POST bookings |
| Look up a booking | get_booking_status | GET bookings/{booking_id} |
| Change a booking | modify_booking | PATCH bookings/{booking_id} |
| Cancel | cancel_booking | DELETE bookings/{booking_id} |
| Pay | complete_booking | POST bookings/{booking_id}/complete |
Parameters and responses for each operation are in Connect to bookings; the examples below show what the merchant side encounters.
Check availability
An agent first checks your open slots. Every slot has a stable slot_id, used by the later calls that create or change a booking; the response also includes remaining places and the price.
Check open time slots
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"));
})();
Example response
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"
}
},
…
]
}Run against the live service (read-only): Shortened for display; … marks omitted parts. price.amount is in the smallest currency unit (TWD x100); times are Asia/Taipei. The slots depend on the day you call.
Create a booking
After the customer agrees to the service, time, quantity and contact details, the agent sends the booking. The result depends on your settings: a pay-at-venue booking is confirmed immediately (confirmed) or waits for you (pending); an online-payment booking starts as awaiting_payment, and you are notified only once payment completes.
Create a booking (pay at the venue)
curl
# This creates a real booking at a real business. Use a slot value from the availability response.
curl -sS -X POST "https://mcp.srct.tw/api/v1/businesses/<BUSINESS_ID>/bookings" \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-d '{"service_id":"pt","slot":"<SLOT>","quantity":1,"customer_name":"Example Customer","phone":"0900000000","email":"customer@example.com"}'
Python
# This creates a real booking at a real business. Use a slot value from the availability response.
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))
BUSINESS_ID = "<BUSINESS_ID>"
status, body = call("POST", f"/api/v1/businesses/{BUSINESS_ID}/bookings", {
"service_id": "pt",
"slot": "<SLOT>", # the "slot" value from the availability response
"quantity": 1,
"customer_name": "Example Customer",
"phone": "0900000000",
"email": "customer@example.com",
})
show(status, body)
# Keep booking_id and status_token: every later call for this booking needs both.
Node.js
// This creates a real booking at a real business. Use a slot value from the availability response.
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 BUSINESS_ID = "<BUSINESS_ID>";
(async () => {
show(await call("POST", `/api/v1/businesses/${BUSINESS_ID}/bookings`, {
service_id: "pt",
slot: "<SLOT>", // the "slot" value from the availability response
quantity: 1,
customer_name: "Example Customer",
phone: "0900000000",
email: "customer@example.com",
}));
// Keep booking_id and status_token: every later call for this booking needs both.
})();
Example response
HTTP 201
{
"ok": true,
"booking": {
"booking_id": "2QAC3NW5",
"status": "pending",
"status_text": "Waiting for the business to confirm",
"business_id": "coach-dunken",
"business_name": "Dunken教練",
"service_id": "pt",
"service_name": "PT一對一教練課",
"slot": "2026-10-25T08:00",
"start": "2026-10-25T08:00:00+08:00",
"label": "10/25(日)08:00",
"quantity": 1,
"quantity_unit": "位",
"payment": "Pay at the venue",
"details": [
"老師:Dunken"
],
"why_pending": "First booking with this business from these contact details, so the business confirms it.",
"confirm_by": "2026-10-22T01:00:00+08:00",
"hold": "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.",
"online_cancel_until": "2026-10-24T08:00:00+08:00"
},
"status_token": "<STATUS_TOKEN>",
"status_token_note": "Needed with booking_id to check, pay, change or cancel this booking later. It gives access to this booking only, and … does not show it again.",
"contact_note": "If this booking cannot be cancelled with cancel_booking, the customer can contact the business directly: …"
}Run against a local copy of the service: Run against a local copy of the connector that holds a copy of the live business's services, so the response has the same fields as production. status_token is replaced by <STATUS_TOKEN>. A first booking from new contact details waits for the business to confirm (status pending).
Change a booking
An agent can change a customer's slot or quantity, and the old place is released. A booking paid online can change its slot only; changing the quantity changes the amount, so the customer must cancel and book again. Changes and online cancellation are allowed up to the same cutoff.
Change a booking's time
curl
curl -sS -X PATCH "https://mcp.srct.tw/api/v1/businesses/<BUSINESS_ID>/bookings/<BOOKING_ID>" \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <STATUS_TOKEN>" \
-d '{"slot":"<NEW_SLOT>"}'
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))
BUSINESS_ID = "<BUSINESS_ID>"
BOOKING_ID = "<BOOKING_ID>" # from the create response
STATUS_TOKEN = "<STATUS_TOKEN>" # from the create response (shown once)
AUTH = {"Authorization": f"Bearer {STATUS_TOKEN}"}
NEW_SLOT = "<NEW_SLOT>" # another "slot" value from the availability response
show(*call("PATCH", f"/api/v1/businesses/{BUSINESS_ID}/bookings/{BOOKING_ID}", {"slot": NEW_SLOT}, AUTH))
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 BUSINESS_ID = "<BUSINESS_ID>";
const BOOKING_ID = "<BOOKING_ID>"; // from the create response
const STATUS_TOKEN = "<STATUS_TOKEN>"; // from the create response (shown once)
const AUTH = { Authorization: `Bearer ${STATUS_TOKEN}` };
const NEW_SLOT = "<NEW_SLOT>"; // another "slot" value from the availability response
(async () => {
show(await call("PATCH", `/api/v1/businesses/${BUSINESS_ID}/bookings/${BOOKING_ID}`, { slot: NEW_SLOT }, AUTH));
})();
Example response
HTTP 200
{
"ok": true,
"changed": {
"from": {
"slot": "2026-10-25T08:00",
"quantity": 1
},
"to": {
"slot": "2026-10-26T08:00",
"quantity": 1
}
},
"booking": {
"booking_id": "2QAC3NW5",
"status": "pending",
"status_text": "Waiting for the business to confirm",
"business_id": "coach-dunken",
"business_name": "Dunken教練",
"service_id": "pt",
"service_name": "PT一對一教練課",
"slot": "2026-10-26T08:00",
"start": "2026-10-26T08:00:00+08:00",
"label": "10/26(一)08:00",
"quantity": 1,
"quantity_unit": "位",
"payment": "Pay at the venue",
"details": [
"老師:Dunken"
],
"why_pending": "First booking with this business from these contact details, so the business confirms it.",
"confirm_by": "2026-10-22T01:00:00+08:00",
"hold": "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.",
"online_cancel_until": "2026-10-25T08:00:00+08:00"
}
}Run against a local copy of the service: Local copy of the connector. The old slot is freed and the new slot is held; the response shows from and to.
Cancel
When a customer cancels through their AI, the place is released and your Order Desk keeps a record. You set the online cancellation cutoff; after it, the customer has to contact you directly. When a customer cancels a booking paid online, it is refunded minus the payment processing fee and the platform fee.
Cancel a booking
curl
curl -sS -X DELETE "https://mcp.srct.tw/api/v1/businesses/<BUSINESS_ID>/bookings/<BOOKING_ID>" \
-H "User-Agent: my-agent/1.0" \
-H "Authorization: Bearer <STATUS_TOKEN>"
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))
BUSINESS_ID = "<BUSINESS_ID>"
BOOKING_ID = "<BOOKING_ID>" # from the create response
STATUS_TOKEN = "<STATUS_TOKEN>" # from the create response (shown once)
AUTH = {"Authorization": f"Bearer {STATUS_TOKEN}"}
show(*call("DELETE", f"/api/v1/businesses/{BUSINESS_ID}/bookings/{BOOKING_ID}", headers=AUTH))
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 BUSINESS_ID = "<BUSINESS_ID>";
const BOOKING_ID = "<BOOKING_ID>"; // from the create response
const STATUS_TOKEN = "<STATUS_TOKEN>"; // from the create response (shown once)
const AUTH = { Authorization: `Bearer ${STATUS_TOKEN}` };
(async () => {
show(await call("DELETE", `/api/v1/businesses/${BUSINESS_ID}/bookings/${BOOKING_ID}`, undefined, AUTH));
})();
Example response
HTTP 200
{
"ok": true,
"booking": {
"booking_id": "2QAC3NW5",
"status": "cancelled",
"status_text": "Cancelled",
"business_id": "coach-dunken",
"business_name": "Dunken教練",
"service_id": "pt",
"service_name": "PT一對一教練課",
"slot": "2026-10-26T08:00",
"start": "2026-10-26T08:00:00+08:00",
"label": "10/26(一)08:00",
"quantity": 1,
"quantity_unit": "位",
"payment": "Pay at the venue",
"details": [
"老師:Dunken"
],
"cancelled_by": "customer"
}
}Run against a local copy of the service: Local copy of the connector. A booking paid at the venue has no refund fields; for online payment see refund_result.
Structured errors
Every error returns a code and a message, so the agent knows what happened and whether to retry. For example, if the slot the customer chose was just taken, the response is capacity_full (409 over REST) with a few alternative slots, and the agent can book another one.
Handle a structured error: slot already taken
curl
# This creates a real booking at a real business. Use a slot value from the availability response.
curl -sS -X POST "https://mcp.srct.tw/api/v1/businesses/<BUSINESS_ID>/bookings" \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-d '{"service_id":"pt","slot":"<FULL_SLOT>","quantity":1,"customer_name":"Example Customer","phone":"0900000000","email":"customer@example.com"}'
Python
# This creates a real booking at a real business. Use a slot value from the availability response.
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))
BUSINESS_ID = "<BUSINESS_ID>"
status, body = call("POST", f"/api/v1/businesses/{BUSINESS_ID}/bookings", {
"service_id": "pt",
"slot": "<FULL_SLOT>", # a slot that is already full
"quantity": 1,
"customer_name": "Example Customer",
"phone": "0900000000",
"email": "customer@example.com",
})
show(status, body)
# status 409, body["error"]["code"] == "capacity_full": offer the customer one of body["next_slots"] and call again.
Node.js
// This creates a real booking at a real business. Use a slot value from the availability response.
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 BUSINESS_ID = "<BUSINESS_ID>";
(async () => {
const result = await call("POST", `/api/v1/businesses/${BUSINESS_ID}/bookings`, {
service_id: "pt",
slot: "<FULL_SLOT>", // a slot that is already full
quantity: 1,
customer_name: "Example Customer",
phone: "0900000000",
email: "customer@example.com",
});
show(result);
// status 409, body.error.code === "capacity_full": offer the customer one of body.next_slots and call again.
})();
Example response
HTTP 409
{
"error": {
"code": "capacity_full",
"message": "Only 0 left in this slot. Choose another slot."
},
"next_slots": [
"2026-10-22T08:00",
"2026-10-22T09:00",
"2026-10-22T10:00"
]
}Run against a local copy of the service: Local copy of the connector; before each run the slot was booked by another customer. next_slots lists open alternatives.
| Code | Meaning | REST status |
|---|---|---|
capacity_full | Not enough places left in this slot | 409 |
invalid_slot | The slot doesn't exist or can't be booked | 409 |
duplicate | The same contact details were sent again for the same service and slot | 409 |
too_late_to_cancel | Past the online cancellation cutoff | 409 |
payment_declined | The payment was declined; retry with another payment method | 402 |
payment_expired | The payment window has passed and the place was released | 410 |
rate_limited | Too many requests | 429 |
Paid but not booked: automatic refund
A booking paid online is refunded in full, immediately, in these cases: you decline or cancel, or you don't confirm within the time limit. When the customer cancels online before the cutoff, a card payment is refunded minus the payment processing fee and the platform fee. A refund that fails is retried every 5 minutes.
Steps
- Agent → SOP: complete_booking with the credential
- SOP → SOP: The charge succeeds
- Seller → SOP: The business declines, or doesn't confirm in time
- SOP → SOP: Refund in full, automatically
- Agent → SOP: Or: the customer cancels before the cutoff
- SOP → SOP: Refund minus the payment processing fee and the platform fee
- SOP → Agent: Reports the refund amount and the fees
Customer cancels a paid booking: refund minus fees
curl
curl -sS -X DELETE "https://mcp.srct.tw/api/v1/businesses/<BUSINESS_ID>/bookings/<BOOKING_ID>" \
-H "User-Agent: my-agent/1.0" \
-H "Authorization: Bearer <STATUS_TOKEN>"
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))
BUSINESS_ID = "<BUSINESS_ID>"
BOOKING_ID = "<BOOKING_ID>" # from the create response
STATUS_TOKEN = "<STATUS_TOKEN>" # from the create response (shown once)
# When the customer cancels a card-paid booking before the cutoff, it is refunded minus the
# payment processing fee and the platform fee. If the business declines it or does not confirm it, it is refunded in full.
show(*call("DELETE", f"/api/v1/businesses/{BUSINESS_ID}/bookings/{BOOKING_ID}", headers={"Authorization": f"Bearer {STATUS_TOKEN}"}))
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 BUSINESS_ID = "<BUSINESS_ID>";
const BOOKING_ID = "<BOOKING_ID>"; // from the create response
const STATUS_TOKEN = "<STATUS_TOKEN>"; // from the create response (shown once)
// When the customer cancels a card-paid booking before the cutoff, it is refunded minus the
// payment processing fee and the platform fee. If the business declines it or does not confirm it, it is refunded in full.
(async () => {
show(await call("DELETE", `/api/v1/businesses/${BUSINESS_ID}/bookings/${BOOKING_ID}`, undefined, { Authorization: `Bearer ${STATUS_TOKEN}` }));
})();
Example response
HTTP 200
{
"ok": true,
"booking": {
"booking_id": "WQEMHR2E",
"status": "cancelled",
"status_text": "Cancelled",
"business_id": "example-studio",
"business_name": "Example Studio (test data)",
"service_id": "small_group",
"service_name": "Small-group strength class",
"slot": "2026-10-25T10:00",
"start": "2026-10-25T10:00:00+08:00",
"label": "10/25(日)10:00",
"quantity": 1,
"quantity_unit": "seat",
"payment": "Refunded NT$518 of NT$600 (the Stripe processing fee and the platform fee were deducted because the customer cancelled)",
"details": [],
"payment_details": {
"mode": "online",
"status": "refunded",
"amount": {
"value": 60000,
"currency": "twd",
"text": "NT$600"
},
"collected_by": "Seller Tech U.S.A. LLC through Stripe",
"paid_at": "2026-10-22T11:51:00+08:00",
"card": {
"brand": "visa",
"last4": "4242"
},
"method": "stripe_spt",
"transaction_id": "pi_1",
"refund": "refunded",
"refund_amount": {
"value": 51800,
"currency": "twd",
"text": "NT$518"
},
"refund_fees": {
"stripe": "NT$39",
"platform": "NT$43",
"reason": "customer_cancel"
}
},
"cancelled_by": "customer"
}
}Response taken from the service's automated tests: Taken from the connector's test setup: the connector code runs unchanged, and Stripe is replaced by the test suite's fake Stripe server, so transaction_id (pi_1) and the processing fee (USD 1.20, shown as NT$39) are test values. Nothing was charged anywhere.
What you see on the Order Desk
- Bookings awaiting payment are listed separately and can't be confirmed; you are notified only after payment completes.
- After payment, the LINE notification states the amount paid, with no card number or customer personal data.
- The confirmation page shows the payment status, card brand and last four digits.
- Declining or canceling a paid booking refunds it immediately and shows the result.
- The exported CSV adds payment method, payment status, amount, payment time and refund time.
Merchant settings
- Payment mode: pay at the venue by default; online payment is set when your listing goes live. A service priced at 0 is never charged.
- Payment window: 5 to 1440 minutes, 15 minutes if not set.
- If a merchant has turned on online payment but its payment setup isn't complete, creating a booking returns
payment_unavailableand no booking is created. - You decide the online cancellation cutoff and the confirmation rules.
Online payments are processed by Seller Tech U.S.A. LLC through Stripe; fees and settlement timing are stated in the quotation.
Status today
The booking interface is live. A merchant can take payment at the venue or online; the first merchant, a personal trainer, turned on online payment on 2026-10-22.
Before you begin
- Your services: names, prices and durations.
- When you are open and how many people each slot takes.
- Your cancellation and rescheduling cutoff.
- Whether you take online payment, and if so, the details for payouts.
Retries are safe
Agents can retry a request after a network error without creating a second booking or a second charge: they send an Idempotency-Key, and SOP returns the original result for 48 hours. A card charge also carries its own idempotency key with Stripe.
Before you go live
We set up your listing with you and you check every item before it goes live. Online payment is turned on only after you ask for it.
Related pages
- Connect to bookings: Search businesses, check availability, create and manage bookings
- Pay for a customer: Complete a booking payment with a credential the customer approved
- Payment credentials: Shared Payment Tokens, and the three roles: seller, agent and platform
- Sell products to agents: Our plan for retail products