Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Intellij
.idea/site/

# MkDocs build output
site/
tools/__pycache__/
__pycache__/
*.pyc
5 changes: 5 additions & 0 deletions CHANGE_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
- Prohibited publishing a USP policy on a UCP binding such as `dev.ucp.shopping`, and forbade a platform reading the absence of `config.authorization` on a UCP binding as "no authentication required", since the namespace rule that keeps USP declarations under `dev.usp-protocol.*` is exactly what makes that absence uninformative rather than permissive
- Recorded why the rule is needed: Section 7.2 already says Section 10.1.6 applies to UCP-Native checkout and booking-extension operations, while a policy scoped to "the USP endpoint that binding declares" left those operations unauthorised by anything in the profile — so a fail-closed client had to either refuse a conformant business or send a proof it could not justify
- Mirrored both rules in `site-docs/deployment-modes/ucp-native.md`
## 13/09/26 at 08:15:00 by [Maor Yehuda](mailto:maorye@wix.com)

- Made `Action.expires_at` OPTIONAL in `schemas/booking.json`, because it was the only REQUIRED field on `Action` that a conformant business can be unable to state truthfully: an action that has no deadline has no value to put there, and the schema forced one to be invented. `Booking.expires_at` already settled this the other way — it is optional precisely so a business that holds no slot capacity is not made to advertise an expiry it does not enforce — and an action is in the same position
- Stated in Section 8.5.4 and in `site-docs/deployment-modes/standalone.md` that an absent `Action.expires_at` means the business sets no deadline and a platform **MUST NOT** infer one, and that a business which *will* expire the action **MUST** publish it, because the failure mode of a hidden deadline is that the platform cannot act on it and the buyer first learns of it when the booking is already canceled
- Reworded the two places in Section 5 that enumerated an action's fields as `type`, `status`, `continue_url`, `expires_at`, since they read as a list of what is always present and are the sentences a reader checks before the schema

---

Expand Down
7 changes: 3 additions & 4 deletions schemas/booking.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,11 @@
"Action": {
"type": "object",
"title": "Action",
"description": "A pending task the buyer must complete before a booking can be confirmed. Each action has a type, status, continue URL, and expiry.",
"description": "A pending task the buyer must complete before a booking can be confirmed. Each action has a type, status and continue URL, and an expiry when the action has a deadline.",
"required": [
"type",
"status",
"continue_url",
"expires_at"
"continue_url"
],
"properties": {
"type": {
Expand All @@ -468,7 +467,7 @@
"expires_at": {
"type": "string",
"format": "date-time",
"description": "RFC 3339 deadline for this action. Can be equal to or earlier than the booking expiry."
"description": "RFC 3339 deadline for this action. OPTIONAL. A business whose action does not expire MAY omit this field; a business that will expire the action MUST include it, since a platform cannot otherwise know the action has a deadline before it passes. Omitting it asserts only that this business sets no deadline - it does not relieve the business of Section 8.5.4's expiry obligations if it later does. Can be equal to or earlier than the booking expiry. Mirrors Booking.expires_at, which is likewise optional and for the same reason: a deadline that does not exist cannot be stated truthfully, and a fabricated one is acted on by agents."
},
"message": {
"$ref": "#/$defs/Message",
Expand Down
2 changes: 2 additions & 0 deletions site-docs/deployment-modes/standalone.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ The `POST /bookings/{booking_id}/confirm-payment` endpoint is called by the plat

!!! warning "Payment expiry"

A payment action **MAY** omit `expires_at`, and an absent value means this business sets no deadline for the action — a platform **MUST NOT** infer one. A business that *will* expire the action **MUST** publish `expires_at`, because a deadline a platform cannot see is one it cannot act on, and the buyer learns of it only when the booking is already canceled.

When a payment action's `expires_at` passes without `confirm-payment`, the business **SHOULD** set the action status to `expired`. If no other pending actions remain, the booking **MUST** transition to `canceled`. This payment-action clock is independent of whether the booking advertises `expires_at`. A late `confirm-payment` **MUST** return the canceled booking with code `payment_expired`. The payment action's `expires_at` **SHOULD** be no later than the booking's `expires_at` when the booking advertises that field, and no later than the slot hold's `expires_at` when a hold exists.

### Embedded / Generic Payment Flow
Expand Down
12 changes: 9 additions & 3 deletions specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -2715,7 +2715,7 @@ and the `confirm-payment` operation for payment confirmation.
| Status | Description |
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `pending` | Booking has been created and is awaiting confirmation. For `auto` confirmation mode, this state is transient - the booking moves to `confirmed` immediately (or to `requires_action` if payment is needed). For `manual` mode, the booking remains in `pending` until the business explicitly confirms it. |
| `requires_action` | One or more actions in the `actions` array have `status: pending`. Inspect `actions[]` for required tasks (e.g., payment, waiver signing). Each action has a `type`, `status`, `continue_url`, and `expires_at`. The booking **MUST** have this status if and only if `actions[]` contains at least one pending action. When the last pending action completes, the business **MUST** transition the booking out of `requires_action`. |
| `requires_action` | One or more actions in the `actions` array have `status: pending`. Inspect `actions[]` for required tasks (e.g., payment, waiver signing). Each action has a `type`, `status` and `continue_url`, plus an `expires_at` when the action has a deadline. The booking **MUST** have this status if and only if `actions[]` contains at least one pending action. When the last pending action completes, the business **MUST** transition the booking out of `requires_action`. |
| `confirmed` | The booking is confirmed and the service will proceed at the scheduled time. This is reached after auto-confirmation, manual business approval, or successful payment completion (via `confirm-payment` or webhook). |
| `in_progress` | The service is currently being delivered. Transitioned by the business when the appointment/session begins. |
| `completed` | The service has been delivered. Terminal state. |
Expand Down Expand Up @@ -2815,7 +2815,7 @@ at a specific time.
| `status` | string | **Yes** | Current booking status. See [Section 5.1](#51-booking-status-lifecycle). |
| `confirmation_mode` | string | **Yes** | `auto` or `manual`. Reflects the service's confirmation policy at booking time. |
| `payment` | BookingPayment | Conditional | Payment state. **MUST** be present when the service's `requires_payment` is `true` and `payment_timing` is `at_booking` or `deposit_required`. **MUST** be omitted when `requires_payment` is `false`. **MAY** be present with `status: not_required` when `payment_timing` is `at_service`. See [Section 8.5.1](#851-booking-payment-schema) (Standalone Mode). |
| `actions` | Array\[Action\] | Conditional | Ordered array of pending tasks the buyer must complete. **MUST** be present and non-empty when `status` is `requires_action`; **MUST** be absent or empty otherwise. The booking has `status: requires_action` if and only if this array contains at least one action with `status: pending`. Each action has `type`, `status`, `continue_url`, `expires_at`, and an optional `message`. The business places actions in recommended completion order; non-payment actions **SHOULD** precede payment actions. See [Section 8.5](#85-payment-integration). |
| `actions` | Array\[Action\] | Conditional | Ordered array of pending tasks the buyer must complete. **MUST** be present and non-empty when `status` is `requires_action`; **MUST** be absent or empty otherwise. The booking has `status: requires_action` if and only if this array contains at least one action with `status: pending`. Each action has `type`, `status` and `continue_url`, plus an optional `message` and an `expires_at` when the action has a deadline. The business places actions in recommended completion order; non-payment actions **SHOULD** precede payment actions. See [Section 8.5](#85-payment-integration). |
| `notes` | string | No | Buyer-provided special requests or notes. |
| `booking_url` | string | No | Stable URL where the buyer can view and manage this booking. Provided by the business. Used in confirmation emails, calendar events, and buyer portals. |
| `messages` | Array\[Message\] | No | Soft messages from the business providing context about the booking state (e.g., "Manual confirmation required — expect a response within 24 hours", "Free cancellation closes in 2 hours"). Informational only; do not block booking creation. Protocol errors are returned as HTTP error codes, not messages. See [Section 9.4](#94-error-code-mapping) for the distinction. |
Expand Down Expand Up @@ -6175,7 +6175,13 @@ to that `continue_url` **regardless** of the declared `checkout_systems` value,
then resume with `confirm-payment` after success. This aligns with UCP's
`requires_escalation` + `continue_url` pattern using USP's action model.

**Payment action expiry:** When a payment action's `expires_at` passes without
**Payment action expiry:** A payment action **MAY** omit `expires_at`, and an
absent value means this business sets no deadline for the action - a platform
**MUST NOT** infer one. A business that *will* expire the action **MUST**
publish `expires_at`, because a deadline a platform cannot see is one it cannot
act on, and the buyer learns of it only when the booking is already canceled.

When a payment action's `expires_at` passes without
`confirm-payment`, the business **SHOULD** set the action's `status` to
`expired`. If no other pending actions remain, the booking **MUST** transition
to `canceled`. This payment-action clock is independent of whether the booking
Expand Down
Loading