Booking policies
The booking policy is what a client accepts before booking: deposit, card on file, free-cancellation window, late and no-show fees, notice. Tuppence words it in plain English and the booking keeps those words, so a later, stricter policy never applies to it.
| Default policy | |
|---|---|
| Deposit | None |
| Card on file | Never asked for |
| Cancelling | Free up to 24 hours before, then 50% of the price |
| Not turning up | 50% of the price |
| Moving a booking | Up to 24 hours before |
| Notice | At least 60 minutes; bookable up to 90 days ahead |
| New bookings | Confirmed at once |
| Slots | Held 10 minutes while booking; start every 15 minutes |
| Charging fees, marking no-shows | By you; nothing is automatic |
Change it
Section titled “Change it”Dashboard: Booking policy groups the fields into When they book, Cancelling and not turning up, Notice, approval and slots, Your own terms and Blocked clients. What a client reads previews the words for a £45.00$45.00€45.00 service as you type. Save policy makes a new version.
API: POST /v1/booking_policy takes any field; the rest carry over.
POST /v1/booking_policy/render returns the words for an amount or a service:
# A 20% deposit, two days' free cancellation, and a card from new clients.curl -sS https://api.tuppence.ai/v1/booking_policy \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"deposit": {"type": "percent", "value": 20, "refundable": true}, "card_required": "new_clients", "cancellation": {"free_until_hours": 48, "late_fee": {"type": "percent", "value": 50}}, "no_show_fee": {"type": "percent", "value": 100}}' | jq .version
# What a client reads, for a £45.00 visit.curl -sS https://api.tuppence.ai/v1/booking_policy/render \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"amount": 4500}' | jq -r .text# A 20% deposit, two days' free cancellation, and a card from new clients.curl -sS https://api.tuppence.ai/v1/booking_policy \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"deposit": {"type": "percent", "value": 20, "refundable": true}, "card_required": "new_clients", "cancellation": {"free_until_hours": 48, "late_fee": {"type": "percent", "value": 50}}, "no_show_fee": {"type": "percent", "value": 100}}' | jq .version
# What a client reads, for a $45.00 visit.curl -sS https://api.tuppence.ai/v1/booking_policy/render \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"amount": 4500}' | jq -r .text# A 20% deposit, two days' free cancellation, and a card from new clients.curl -sS https://api.tuppence.ai/v1/booking_policy \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"deposit": {"type": "percent", "value": 20, "refundable": true}, "card_required": "new_clients", "cancellation": {"free_until_hours": 48, "late_fee": {"type": "percent", "value": 50}}, "no_show_fee": {"type": "percent", "value": 100}}' | jq .version
# What a client reads, for a €45.00 visit.curl -sS https://api.tuppence.ai/v1/booking_policy/render \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"amount": 4500}' | jq -r .textimport { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);
// A 20% deposit, two days' free cancellation, and a card from new clients.const policy = await tuppence.bookingPolicy.update({ deposit: { type: "percent", value: 20, refundable: true }, card_required: "new_clients", cancellation: { free_until_hours: 48, late_fee: { type: "percent", value: 50 } }, no_show_fee: { type: "percent", value: 100 },});console.log("version", policy.version);
// What a client reads, for a £45.00 visit.const words = await tuppence.bookingPolicy.render({ amount: 4500 });console.log(words.text);import { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);
// A 20% deposit, two days' free cancellation, and a card from new clients.const policy = await tuppence.bookingPolicy.update({ deposit: { type: "percent", value: 20, refundable: true }, card_required: "new_clients", cancellation: { free_until_hours: 48, late_fee: { type: "percent", value: 50 } }, no_show_fee: { type: "percent", value: 100 },});console.log("version", policy.version);
// What a client reads, for a $45.00 visit.const words = await tuppence.bookingPolicy.render({ amount: 4500 });console.log(words.text);import { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);
// A 20% deposit, two days' free cancellation, and a card from new clients.const policy = await tuppence.bookingPolicy.update({ deposit: { type: "percent", value: 20, refundable: true }, card_required: "new_clients", cancellation: { free_until_hours: 48, late_fee: { type: "percent", value: 50 } }, no_show_fee: { type: "percent", value: 100 },});console.log("version", policy.version);
// What a client reads, for a €45.00 visit.const words = await tuppence.bookingPolicy.render({ amount: 4500 });console.log(words.text);import osfrom tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
# A 20% deposit, two days' free cancellation, and a card from new clients.policy = tuppence.booking_policy.update( deposit={"type": "percent", "value": 20, "refundable": True}, card_required="new_clients", cancellation={"free_until_hours": 48, "late_fee": {"type": "percent", "value": 50}}, no_show_fee={"type": "percent", "value": 100},)print("version", policy.version)
# What a client reads, for a £45.00 visit.words = tuppence.booking_policy.render(amount=4500)print(words.text)import osfrom tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
# A 20% deposit, two days' free cancellation, and a card from new clients.policy = tuppence.booking_policy.update( deposit={"type": "percent", "value": 20, "refundable": True}, card_required="new_clients", cancellation={"free_until_hours": 48, "late_fee": {"type": "percent", "value": 50}}, no_show_fee={"type": "percent", "value": 100},)print("version", policy.version)
# What a client reads, for a $45.00 visit.words = tuppence.booking_policy.render(amount=4500)print(words.text)import osfrom tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
# A 20% deposit, two days' free cancellation, and a card from new clients.policy = tuppence.booking_policy.update( deposit={"type": "percent", "value": 20, "refundable": True}, card_required="new_clients", cancellation={"free_until_hours": 48, "late_fee": {"type": "percent", "value": 50}}, no_show_fee={"type": "percent", "value": 100},)print("version", policy.version)
# What a client reads, for a €45.00 visit.words = tuppence.booking_policy.render(amount=4500)print(words.text)Output:
A deposit of 20% (£9.00) is taken when you book and comes off your bill. It is refunded if you cancel more than 2 days before.New clients are asked for a card to hold the booking. Nothing is charged unless you cancel late or don't turn up.Cancel free of charge up to 2 days before. After that, a late-cancellation fee of 50% (£22.50) applies.If you don't turn up, a fee of 100% (£45.00) applies.You can move your booking up to 24 hours before.If Bella's Barks cancels, your deposit is refunded in full and no fee is ever due.A deposit of 20% ($9.00) is taken when you book and comes off your bill. It is refunded if you cancel more than 2 days before.New clients are asked for a card to hold the booking. Nothing is charged unless you cancel late or don't turn up.Cancel free of charge up to 2 days before. After that, a late-cancellation fee of 50% ($22.50) applies.If you don't turn up, a fee of 100% ($45.00) applies.You can move your booking up to 24 hours before.If Bella's Barks cancels, your deposit is refunded in full and no fee is ever due.A deposit of 20% (€9.00) is taken when you book and comes off your bill. It is refunded if you cancel more than 2 days before.New clients are asked for a card to hold the booking. Nothing is charged unless you cancel late or don't turn up.Cancel free of charge up to 2 days before. After that, a late-cancellation fee of 50% (€22.50) applies.If you don't turn up, a fee of 100% (€45.00) applies.You can move your booking up to 24 hours before.If Bella's Barks cancels, your deposit is refunded in full and no fee is ever due.render also returns mandate_description (shown when saving a card: what it may be charged
for), deposit_amount, late_fee_amount, no_show_fee_amount and the snapshot a new booking
would keep. Send draft with the same fields to preview unsaved changes.
The fields
Section titled “The fields”| Field | Values |
|---|---|
deposit | {type, value, refundable}. type: none; fixed (minor units, ≤ the price); percent (1–100, rounded down to the penny); full. refundable (default true) refunds it if the client cancels in time; keep a non-refundable deposit small. |
card_required | never, new_clients (first booking with you), always, or over_amount (above card_required_over_amount). |
card_after_no_shows | 1–10: clients with that many no-shows must save a card to book online, overriding card_required. null (default) never asks. |
cancellation | free_until_hours (0–8,760; 0 charges every cancellation) and late_fee; send either alone. |
late_fee, no_show_fee | {type, value}: none, fixed (minor units) or percent (1–100, rounded up to the penny). |
reschedule_until_hours | Latest a client can move their booking (0–8,760). You always can. |
min_notice_minutes, max_advance_days | Minimum notice (0–43,200 min) and furthest ahead (1–730 days). |
approval_mode | instant, or request: you approve or decline each booking, which lapses after approval_window_hours (1–168). |
hold_minutes | Slot hold while a client books and pays (5–60). |
slot_interval_minutes, slot_mode | Starts every 5–120 minutes (multiple of 5). smart also offers existing bookings’ end times, avoiding gaps. |
auto_charge, auto_no_show, charge_balance_after | Charge fees automatically, mark no-shows automatically, charge the balance after the visit if the client opts in. All off by default; see Deposits and no-shows. |
terms_text | Your terms (≤ 2,000 characters), shown after Tuppence’s. null clears them. |
blocked_message | Shown to a blocked client booking online, ≤ 500 characters. null restores ours. |
A fee is never more than the price
Section titled “A fee is never more than the price”| Rule | Why / what happens |
|---|---|
| A percentage fee is ≤ 100; a fixed fee is never charged above the visit’s price | The CMA’s unfair-terms guidance (CMA37) limits a cancellation charge to the business’s actual loss, at most the slot’s price |
| Over the limit | 400 parameter_invalid with the reason (shown beside the field in the dashboard) |
| You cancel | Full refund, including any non-refundable deposit, and no fee, whatever the policy. Every client’s accepted words say so |
You moved a booking inside the client’s window without consented | No fee is owed |
A booking keeps what its client accepted
Section titled “A booking keeps what its client accepted”Each change makes a new version (GET /v1/booking_policy/versions, newest first). A booking’s
policy_snapshot keeps the version, fields, words and card mandate its client accepted. Fees, fee
receipts (which quote the sentence) and dispute evidence use the snapshot.
Different terms for one service
Section titled “Different terms for one service”A long colour or tattoo can need stricter terms than a cut. A service’s policy_override
(POST /v1/services/{id}) replaces any client-facing field: deposit,
card_required, card_required_over_amount, card_after_no_shows, cancellation,
no_show_fee, reschedule_until_hours, min_notice_minutes, max_advance_days,
approval_mode, terms_text.
{ "policy_override": { "deposit": { "type": "fixed", "value": 5000, "refundable": false } } }POST /v1/booking_policy/render with service previews the merge at the service’s price.
Overrides are API-only; the dashboard edits the account policy.
- Deposits and no-shows: collecting and charging.
- The Bookings API: taking a booking under the policy.