UCP Services Protocol - Appointment Extension for Agentic Checkout
Extending UCP Checkout to support scheduling, validation, and booking creation for services.
Contents
- Introduction
- Problem statement
- Services are not products: structural differences
- Architectural motivation and adoption drivers
- Proposed solution: the Appointment extension
- End-to-end flow
- Reference implementation (based on the Square API)
- Technical considerations
- Further investments
- Conclusion
Executive summary
Universal Commerce Protocol (UCP) standardizes how agentic platform discover businesses, negotiate capabilities, assemble a checkout, and complete payment. That model works well for product commerce, where a cart is a set of SKUs plus fulfillment and discounts. Appointment-based services require additional state that is not currently represented in the UCP checkout flow. These could include a specific event start time, location, and (often) a staff or resource assignment, all of which need to be validated against real-time availability and business policies. This whitepaper proposes an extension to the UCP Checkout object for services-based transactions such as appointments. The extension models scheduling as slots bound to line items, with selectable options and a selected option identifier. This mirrors the proven pattern used by the Fulfillment extension (groups, options, selection). A reference implementation based on the Square API demonstrates how this protocol can work. The integration is a bounded adapter layer: availability search maps to option generation; checkout completion maps to idempotent booking creation; and existing product-commerce flows remain unchanged when the capability is not negotiated.
1. Introduction
UCP is an open, capability-negotiated protocol intended to make agentic commerce interoperable. In the UCP model, an agent surface discovers a business profile, negotiates supported capabilities, then uses a standardized Checkout interface to build a transaction that can be completed programmatically. For services platforms, the comparable experience is agentic booking: the user expresses intent, the agent finds eligible providers, proposes available times, and completes booking and payment without forcing the user into a platform-specific UI unless required.
2. Problem statement
UCP Checkout, as commonly implemented today, is designed for SKU-based commerce. It can represent line items, totals, and (via extensions) discounts and physical fulfillment. It does not provide a standard representation for appointment requirements such as:
- Location selection and its association with specific line items
- Staff or resource selection (optional or required, depending on the merchant)
- Start time selection and negotiation against availability
- Post-booking lifecycle actions (cancel, reschedule, retrieve booking state)
The protocol gap
Without an appointment representation inside UCP, an agent cannot reliably move from a service line item to a confirmed reservation. Implementers typically fall back to one of two approaches:
- Call platform-specific booking APIs directly from the agent surface (undermines interoperability).
- Rely on embedded or redirect flows for most bookings (limits agentic completion and increases drop-off). A services-oriented extension closes this gap by making appointment state a first-class, validated part of checkout.
The N×N integration bottleneck
In the absence of a standard, each agent surface must integrate separately with each booking backend. That N×N pattern slows adoption and creates duplicative effort across the ecosystem. A standardized Appointment extension collapses integration to a single capability per platform, replicating the efficiency UCP already provides for product commerce.
3. Services are not products: structural differences
The gap is not a presentation issue; it is a state and validation issue. Product checkouts can be expressed as SKUs with quantities. Appointment services are capacity reservations that introduce additional constraints and lifecycle operations.
Capacity reservation vs. inventory decrement
A product transaction typically decrements inventory and selects a fulfillment method. An appointment transaction reserves time-bound capacity and must satisfy multiple constraints simultaneously:
- Time window: a start time (and derived end time based on duration).
- Resources: staff members, rooms, equipment, or other constrained assets.
- Location: availability varies by venue; some services are location-specific.
- Policies: cancellation windows, deposits, no-show fees, and rescheduling rules.
Payment semantics and policy coupling
Service payments often include deposits, delayed capture, and policy-driven fees. These rules can affect what can be modified after booking (for example, rescheduling constraints after a deposit). A practical protocol needs a way to gate completion on policy requirements and support fallback when UI-level consent is necessary.
4. Architectural motivation and Adoption Drivers
This section explains why appointments should be modeled as a UCP extension for Services, and what makes the extension attractive for a services platform to ship.
Why model Appointments as an UCP Extension
UCP supports domain-specific augmentation of Checkout through independently versioned extensions. Fulfillment extends Checkout with a structured selection model; discounts extend Checkout with code submission and applied discount reporting. Appointments are structurally similar: they introduce selection state and validation that must be satisfied before completion. A UCP extension of Checkout is the highest-leverage integration point because appointment slots must reference the services being purchased (line items), and completion must be gated until required scheduling fields are satisfied.
Technical design requirements
To be deployable across platforms, the extension should meet the following requirements:
- Composable schema: additive fields under Checkout without changing core line-item or totals semantics.
- Capability negotiated: active only when both the agent and the business advertise support.
- Partial automation with handoff: allow escalation when policies require human consent or unsupported flows arise.
- Backend mapping feasibility: straightforward mapping to existing booking APIs.
- Operational correctness: idempotent booking creation and deterministic linkage between checkout and booking records.
- Multi-line-item support: multiple services and/or multiple appointment slots per checkout.
5. Proposed solution: the Appointment extension
The proposed Services Protocol is implemented as an Appointment extension to the Checkout object. It adds scheduling state to Checkout in a way that is capability negotiated, backward compatible, and consistent with existing UCP extension patterns.
Extension registration and capability advertisement
The extension is registered as a named capability that extends the Checkout capability. The reference implementation uses a reverse-domain namespace for the prototype:
{
"name": "com.viaschema.appointment",
"version": "2026-01-11",
"extends": "dev.ucp.shopping.checkout"
}
Once multiple implementations converge, the common semantics can be promoted into a shared namespace (for example, dev.ucp.services.appointment) without changing the underlying model. A sample schema is included in Appendix A for reference.
Object model: slots, options, and selection
The core design principle is to represent scheduling as slots bound to one or more line_item_ids, and to represent availability as selectable options within each slot. The selected option is tracked by selected_option_id. This is intentionally parallel to the Fulfillment extension's group/option/selection structure to reduce implementation and UI complexity.
| Concept | Fulfillment extension | Appointment extension |
|---|---|---|
| Grouping unit | FulfillmentGroupResponse | AppointmentSlotResponse |
| Groups by | Shipping method + destination | Location + time + staff |
| Options within group | FulfillmentOptionResponse | AppointmentOptionResponse |
| Selection state | selected_option_id | selected_option_id |
| Binds to | line_item_ids[] | line_item_ids[] |
Data model summary
The following types summarize the minimum fields required to support end-to-end booking while leaving room for platform-specific enrichment (for example, resource identifiers).
| Type | Key fields | Semantics |
|---|---|---|
| AppointmentResponse | slots: list[AppointmentSlotResponse] | Container added to Checkout response; holds all appointment slots. |
| AppointmentSlotResponse | id, line_item_ids[], location, options[], selected_option_id, notes | Scheduling unit grouping one or more line items at a location with selectable time/staff options. |
| AppointmentOptionResponse | id, start_time, end_time?, staff_id?, staff_name?, duration_minutes? | Concrete availability option presented to the agent/user. |
| AppointmentRequest | slots: list[AppointmentSlotRequest] | Agent-submitted request to set or update appointment details. |
| AppointmentSlotRequest | id?, line_item_ids[], location_id, staff_id?, start_time, notes? | Agent-submitted slot configuration for create and update. |
| AppointmentCheckoutResponse | CheckoutResponse + appointment: AppointmentResponse? | Checkout response composed when the capability is active. |
Implementation note: the reference models use Pydantic extra="allow" to permit vendor-specific metadata without breaking the contract.
Sample Conceptual Checkout Shape
The snippet below illustrates a Checkout response containing a single service line item scheduled into one slot.
{
"id": "chk_123",
"line_items": [
{
"id": "li_1",
"item": { "id": "svc_var_abc", "title": "Haircut" },
"quantity": 1
}
],
"appointment": {
"slots": [
{
"id": "slot_1",
"line_item_ids": ["li_1"],
"location": { "id": "loc_123", "name": "Market St" },
"options": [
{
"id": "opt_1",
"start_time": "2026-02-15T10:00:00Z",
"staff_id": "staff_7",
"duration_minutes": 30
}
],
"selected_option_id": "opt_1",
"notes": "Please use fragrance-free products."
}
]
}
}
Scheduling semantics and validation
Create and update
Agents can set appointment details either inline at add-to-checkout time (for simple single-service flows) or via an explicit appointment update (for multi-service flows). Servers may accept the requested configuration, return alternative options if the request conflicts with availability, or return an incomplete state that identifies missing required fields.
Readiness gating
Checkout must not transition to ready_for_complete unless both conditions are met:
- Buyer identity/contact is present (at minimum, an email address).
- Every service line item is associated with a scheduled appointment slot with a selected option.
Idempotency and linkage
Booking creation should be idempotent and linked deterministically to the Checkout completion. On retry, the system should avoid double-booking and return the same booking identifiers when appropriate.
Cancellation support
Appointments require post-booking operations. In the prototype, cancellation and retrieval are exposed as separate agent tools backed by the booking provider API. Longer term, these operations can be standardized under an order/booking management capability.
6. End-to-end flow
This section describes the expected sequence for a fully native agent-driven booking, including the points where the Appointment extension is read and written.
Sequence: Discovery → Availability → Schedule → Complete
User
-> Agent Surface
-> Business UCP Profile (capabilities negotiated)
-> Checkout Create (line_items)
-> Availability Search (business/backend)
-> Checkout Update (appointment.slots + selected_option_id)
-> Start Payment (readiness validation)
-> Complete Checkout (payment + booking creation)
<- Confirmation (booking_ids + receipt/order)
Availability search can be implemented either as an explicit tool call (as in the reference implementation) or as a server-driven response that returns options during checkout updates. The protocol should support both patterns.
Considerations for Adoption
Platform adoption decisions tend to balance distribution upside against integration cost and operational risk. The Appointment extension is designed to score well on both axes:
Distribution upside
- Cross-surface reach via one integration: a single UCP capability can be reused across multiple agent surfaces.
- Higher conversion through fewer handoffs: native scheduling and completion reduce dependence on redirects.
Integration cost and risk controls
- Incremental rollout: platforms can begin with embedded fallback and expand native support over time.
- Non-breaking extensibility: agents that do not negotiate the capability still operate against core Checkout.
- Bounded adapter layer: the extension maps cleanly to existing booking APIs (availability search and booking creation).
7. Reference Implementation based on Square API
The reference implementation demonstrates an end-to-end flow using the Square Bookings API. It is structured as a set of additive layers so that existing product flows are unaffected when the Appointment capability is not negotiated.
Architecture overview
| Layer | Key artifacts | Responsibility |
|---|---|---|
| Data models | appointment_types.py | Defines slot/option/request/response models and Checkout extensions. Models allow vendor metadata via extra="allow". |
| Square client | square_client.py (~800 LOC) | Wraps Square SDK for locations, staff, service catalog lookup, availability, booking CRUD, and customer management. |
| Service store | store.py | Owns Checkout lifecycle: attach appointment slots, validate readiness, and create bookings on completion. |
| Agent tools | agent.py | Exposes service-native tools: availability search, checkout CRUD, appointment setting, booking management. |
| Frontend | App.tsx + components | Displays service cards and appointment details when appointment data is present; core checkout UI remains unchanged otherwise. |
Negotiated typing: composable Checkout models
The Checkout response type is composed dynamically based on active capabilities. This makes the extension additive rather than intrusive.
def get_checkout_type(ucp_metadata) -> type[Checkout]:
active = {cap.name for cap in ucp_metadata.ucp.capabilities}
selected = []
if "dev.ucp.shopping.fulfillment" in active:
selected.append(FulfillmentCheckout)
if "dev.ucp.shopping.discount" in active:
selected.append(DiscountCheckout)
if "com.levro.appointment" in active:
selected.append(AppointmentCheckout)
if not selected:
return Checkout
# Compose selected base models...
Agent tool surface
The key tools follow the booking workflow:
- search_shopping_catalog(service_query): find service variations to add as line items.
- list_locations(): return bookable locations.
- list_staff(location_id?): return staff members and their assigned locations.
- search_availability(start_date, end_date, location_id?, staff_id?, service_variation_id?): return available options.
- add_to_checkout(service_variation_id, location_id?, staff_id?, start_time?, notes?): add a service line item and optionally attach scheduling fields.
- set_appointment(slots[]): bulk set slot configurations for multi-service checkouts.
- start_payment(checkout_id): validate readiness gating.
- complete_checkout(checkout_id): complete payment and create bookings.
- get_bookings() / cancel_booking(booking_id): minimal post-booking operations.
Payment readiness gating
The store validates that buyer information is present and that all service line items are scheduled before allowing payment to proceed.
def start_payment(checkout_id):
checkout = get_checkout(checkout_id)
errors = []
if checkout.buyer is None:
errors.append("Provide a buyer email address")
scheduled = set()
for slot in (checkout.appointment.slots or []):
scheduled.update(slot.line_item_ids)
unscheduled = [li for li in checkout.line_items if li.id not in scheduled]
if unscheduled:
errors.append("Some services don't have appointments scheduled")
if errors:
return "\n".join(errors)
checkout.status = "ready_for_complete"
return checkout
Booking creation with idempotency
On completion, the store creates a booking for each scheduled service line item using an idempotency key.
def create_booking(location_id, start_time, service_variation_id, staff_id=None, ...):
service_variation = get_service_variation(service_variation_id)
body = {
"idempotency_key": str(uuid4()),
"booking": {
"start_at": start_time.isoformat(),
"location_id": location_id,
"appointment_segments": [{
"service_variation_id": service_variation_id,
"service_variation_version": service_variation.version,
"duration_minutes": service_variation.duration_seconds // 60
}]
}
}
if staff_id:
body["booking"]["appointment_segments"][0]["team_member_id"] = staff_id
return square.bookings.create_booking(body=body)
8. Technical considerations
Flexibility by design
The extension is intentionally minimal and composable. The following design points improve applicability across scheduling systems:
- Slots group line items: a single slot can cover multiple services in one session, or services can be scheduled independently.
- Multiple options per slot: supports agent-driven comparison and negotiation.
- Optional staff: staff_id can be omitted to represent 'any available provider'.
- Vendor metadata: extensions can attach platform-specific fields without breaking clients.
Ease of adoption: Mapping API to UCP
The example implantation demonstrates a straightforward mapping between UCP appointment concepts and services booking APIs such as Square:
| UCP / Appointment concept | Sample API Equivalent | Adapter complexity |
|---|---|---|
| Service variation | CatalogItemVariation (APPOINTMENTS_SERVICE) | Light: price normalization and duration conversion. |
| Location | Location | Trivial: direct field mapping. |
| Staff | TeamMember (+ assigned locations) | Light: name composition and location filtering. |
| Availability slot | SearchAvailability response | Moderate: iterate segments and enrich with staff/location metadata. |
| Booking create | Bookings.create + idempotency_key | Moderate: customer upsert and variation version lookup. |
| Booking cancel | Bookings.cancel + booking_version | Trivial: version-based cancellation. |
Frontend integration impact
On the client side, the appointment UI is rendered conditionally when the appointment field is present on Checkout. This keeps product-commerce and service-commerce flows isolated: payment and order confirmation remain unchanged, while service-specific components render availability options and the selected appointment details.
9. Further Extensions
The Appointment extension provides a minimal foundation. The following extensions and capabilities would address common requirements in service commerce:
Deposits and partial payments
Model deposit requirements, authorization vs capture, refund policies, and the relationship between booking state and payment state.
Cancellation/no-show policies and consent
Standardize policy disclosure (human-readable and structured fields) and buyer consent capture when policies must be accepted before completion.
Multi-segment appointments and bundles
Support bookings that consist of multiple segments with ordering, spacing constraints, and per-segment resource assignment.
Rescheduling as a first-class operation
Provide protocol-level operations for rescheduling, including policy checks and availability negotiation.
Recurring appointments
Support series creation and management for recurring bookings.
10. Conclusion
An Appointment extension to UCP Checkout is a small, composable change that enables interoperable agent-driven service booking. It follows established UCP patterns for extension negotiation and selection state, and it maps cleanly to existing scheduling and booking APIs. The sample prototype based on the Square API shows that the integration can be implemented as a bounded adapter layer while keeping product-commerce flows intact. Next steps are to validate the model across additional booking backends, tighten the specification of readiness, idempotency, and post-booking operations, and converge on a shared namespace.
Appendix A: Sample Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://viaschema.com/schemas/shopping/appointment.json",
"name": "com.viaschema.appointment",
"title": "Appointment Extension",
"description": "Extends Checkout with appointment/booking support for services.",
"$defs": {
"appointment_option": {
"$ref": "types/appointment_option.json"
},
"appointment_slot": {
"$ref": "types/appointment_slot.json"
},
"appointment": {
"$ref": "types/appointment.json"
},
"staff": {
"$ref": "types/staff.json"
},
"dev.ucp.shopping.checkout": {
"title": "Checkout with Appointment",
"description": "Checkout extended with appointment booking.",
"allOf": [
{
"$ref": "https://ucp.dev/schemas/shopping/checkout.json"
},
{
"type": "object",
"properties": {
"appointment": {
"$ref": "#/$defs/appointment",
"description": "Appointment details.",
"ucp_request": {
"create": "optional",
"update": "optional",
"complete": "omit"
}
}
}
}
]
},
"com.viaschema.appointment": {
"platform_schema": {
"title": "Appointment Capability (Platform)",
"description": "Platform-level appointment capability configuration",
"allOf": [
{
"$ref": "https://ucp.dev/schemas/capability.json#/$defs/platform_schema"
},
{
"properties": {
"config": {
"type": "object",
"description": "Platform appointment configuration"
}
}
}
]
},
"business_schema": {
"title": "Appointment Capability (Business)",
"description": "Business-level appointment capability configuration",
"allOf": [
{
"$ref": "https://ucp.dev/schemas/capability.json#/$defs/business_schema"
},
{
"properties": {
"config": {
"type": "object",
"description": "Business appointment configuration"
}
}
}
]
}
}
},
"embedded": {
"methods": [
{
"name": "ec.appointment.change",
"summary": "Appointment details changed",
"description": "Merchant notifies host that checkout.appointment has changed (slot selected, time updated).",
"params": [
{
"name": "checkout",
"required": true,
"schema": {
"$ref": "https://ucp.dev/schemas/shopping/checkout.json",
"description": "Current checkout state with updated appointment."
}
}
]
},
{
"name": "ec.appointment.slot_selection_request",
"summary": "Request appointment slot selection",
"description": "Merchant requests host to present appointment slot selection UI.",
"params": [
{
"name": "checkout",
"required": true,
"schema": {
"$ref": "https://ucp.dev/schemas/shopping/checkout.json",
"description": "Current checkout state with appointment context."
}
}
],
"result": {
"name": "slotSelectionResult",
"schema": {
"type": "object",
"description": "Checkout state with updated appointment slot selection.",
"required": ["checkout"],
"properties": {
"checkout": {
"type": "object",
"description": "Partial checkout update with appointment slot selection.",
"properties": {
"appointment": {
"$ref": "types/appointment.json",
"description": "Appointment with updated selected_option_id."
}
}
}
}
}
}
}
]
}
}