Structured Data Guide
Shopify Booking & Appointment Product Schema: ReserveAction & BookAction for AI Agents
Shopify powers thousands of stores selling bookable services — haircuts, personal training, consultations, photography sessions, car detailing. AI shopping agents answering "book a [service] near me" don't know your listing is bookable unless your structured data says so. potentialAction with BookAction is that signal.
potentialAction with @type: BookAction to your Service JSON-LD. The target is an EntryPoint with urlTemplate pointing to your booking flow (Shopify product URL or third-party booking app URL). Use ReserveAction for free/no-payment reservations; BookAction for paid appointment bookings. Tag bookable Shopify products with schema-bookable and output the action block conditionally in product.liquid.
Why Booking Schema Matters for AI Agent Discovery
The query "book a [service]" is fundamentally different from "buy a [product]." Shoppers asking to book have high commercial intent and expect to interact with a scheduling flow — not just see a product page. AI agents are increasingly learning to distinguish between:
- Informational results (what is this service?)
- Product recommendations (where can I buy this?)
- Bookable service recommendations (where can I book this right now?)
The potentialAction property on Service declares that this listing has an actionable next step — in this case, a booking flow. AI agents that encounter a BookAction in a service's structured data can:
- Surface your listing specifically for booking-intent queries
- Display a "Book now" deep link in AI-generated results
- Feed your service into booking aggregators that use structured data as input
- Distinguish your bookable service from static informational pages about the same service type
ReserveAction vs. BookAction: Which to Use
| Action Type | Use When | Payment | Examples |
|---|---|---|---|
ReserveAction |
Holding or reserving a slot without immediate payment | No payment at booking | Free consultation, restaurant reservation, trial class, waitlist sign-up |
BookAction |
Purchasing a booking upfront through the booking flow | Payment at booking | Haircut appointment, personal training session, photography booking, car detailing |
Most Shopify bookable services collect payment at checkout (because Shopify's cart and checkout are the payment flow). Use BookAction for these. If you offer free bookings or consultations where no payment is taken when scheduling, use ReserveAction.
Booking Schema Properties Reference
| Property | On Type | Description |
|---|---|---|
potentialAction |
Service, Product | Array of actions the user can take — include BookAction or ReserveAction here |
@type |
Action | BookAction or ReserveAction |
target |
Action | EntryPoint describing the booking URL |
urlTemplate |
EntryPoint | The URL template for the booking flow |
inLanguage |
EntryPoint | Language of the booking page ("en") |
actionStatus |
Action | PotentialActionStatus (available to take), CompletedActionStatus (booking confirmed) |
scheduledTime |
ReserveAction / BookAction | ISO 8601 DateTime for the booked appointment (if known at structured data time) |
provider |
ReserveAction / BookAction | Who fulfills the booking (Organization or Person) |
BookAction JSON-LD Example: Hair Salon Appointment
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Balayage Color & Style — Full Service",
"serviceType": "Hair Coloring",
"description": "Full balayage color service with toner, blow-dry, and style finish. 3–4 hours. Book online and choose your stylist.",
"provider": {
"@type": "Organization",
"name": "Luminary Hair Studio",
"url": "https://luminary-hair.myshopify.com",
"telephone": "+1-512-555-0199"
},
"areaServed": {
"@type": "City",
"name": "Austin",
"containedInPlace": {"@type": "State", "name": "Texas"}
},
"availableChannel": {
"@type": "ServiceChannel",
"serviceUrl": "https://luminary-hair.myshopify.com/products/balayage-full-service",
"serviceLocation": {
"@type": "Place",
"name": "Luminary Hair Studio — South Congress",
"address": {
"@type": "PostalAddress",
"streetAddress": "1204 S Congress Ave",
"addressLocality": "Austin",
"addressRegion": "TX",
"postalCode": "78704"
}
}
},
"offers": {
"@type": "Offer",
"price": "245.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"url": "https://luminary-hair.myshopify.com/products/balayage-full-service"
},
"potentialAction": {
"@type": "BookAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "https://luminary-hair.myshopify.com/products/balayage-full-service",
"inLanguage": "en"
},
"actionStatus": "https://schema.org/PotentialActionStatus",
"provider": {
"@type": "Organization",
"name": "Luminary Hair Studio"
}
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "87",
"bestRating": "5"
}
}
ReserveAction JSON-LD Example: Free Consultation Call
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Free 30-Minute Strategy Consultation",
"serviceType": "Business Consulting",
"description": "A free 30-minute video call to audit your Shopify store's AI readiness and identify the top 3 structured data gaps. No payment required. Book via Calendly.",
"provider": {
"@type": "Organization",
"name": "CatalogScan",
"url": "https://catalogscan.com"
},
"areaServed": "Online",
"availableChannel": {
"@type": "ServiceChannel",
"serviceUrl": "https://catalogscan.com/products/free-consultation"
},
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"url": "https://catalogscan.com/products/free-consultation"
},
"potentialAction": {
"@type": "ReserveAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "https://calendly.com/catalogscan/strategy-call",
"inLanguage": "en"
},
"actionStatus": "https://schema.org/PotentialActionStatus"
}
}
Shopify Liquid Template: BookAction Implementation
{% comment %} product.liquid — BookAction for bookable service products {% endcomment %}
{% assign is_bookable = false %}
{% if product.tags contains 'schema-bookable' %}
{% assign is_bookable = true %}
{% endif %}
{% assign booking_url = product.metafields.seo.booking_url.value %}
{% unless booking_url %}
{% assign booking_url = "https://" | append: shop.domain | append: product.url %}
{% endunless %}
Required metafields for bookable service products
| Metafield key | Type | Example value |
|---|---|---|
seo.booking_url | Single line text (URL) | https://bookapp.example.com/book/haircut (or leave blank to use product URL) |
seo.service_type | Single line text | "Hair Coloring" / "Personal Training" / "Photography" |
seo.area_served | Single line text | "Austin, Texas" / "Online" / "United States" |
Booking Schema for Common Shopify Service Categories
| Service Category | Action Type | urlTemplate Target | areaServed |
|---|---|---|---|
| Hair salon, spa, beauty | BookAction | Sesami / Booksy / Shopify product URL | City + State |
| Personal training, fitness | BookAction | Gym app / Shopify product URL with variant for session | City or "Online" |
| Photography / videography | BookAction | Honeybook / HoneyFable / Shopify product URL | City + region served |
| Home services (cleaning, repair) | BookAction | Housecall Pro / Shopify product URL | City + surrounding area |
| Consulting / strategy call | ReserveAction (free) / BookAction (paid) | Calendly / Acuity link stored in metafield | "Online" |
| Car detailing / auto service | BookAction | Shopify product URL or external scheduling app | City |
| Tutoring, coaching | BookAction | Calendly / Zoom link / Shopify product | "Online" or local city |
Common Booking Schema Mistakes on Shopify
| Mistake | Effect | Fix |
|---|---|---|
| Using Product @type instead of Service for bookable listings | potentialAction BookAction is semantically inconsistent on Product; AI agents deprioritize it | Use Service @type for all bookable service listings |
| Missing target EntryPoint on potentialAction | Structured data validator error; AI agents can't extract the booking URL | Always include target: EntryPoint with urlTemplate on any action |
| Using external booking URL without storing it in a metafield | Hardcoded booking URL breaks if you switch booking apps | Store booking URL in seo.booking_url metafield; update the metafield when switching apps |
| Omitting actionStatus | Agents don't know if the booking action is currently available | Add actionStatus: https://schema.org/PotentialActionStatus |
| Using BookAction for a free reservation (no payment) | Semantic mismatch; ReserveAction is correct for no-payment holds | Switch to ReserveAction when product.price == 0 (auto-detect in Liquid) |
CatalogScan Booking Schema Checks
CatalogScan's AI Readiness scan identifies Shopify products that appear to be bookable services (by tag, product_type, or keywords like "session", "appointment", "booking", "consultation") and checks whether they include a potentialAction BookAction or ReserveAction in their structured data. Bookable service products without a booking action receive a booking-signal gap warning. The scan also validates that the target EntryPoint has a non-empty urlTemplate that resolves to a real page.
Related guides: Service product schema for Shopify · Local pickup and BOPIS schema · Store location LocalBusiness schema · Shopify schema markup overview
FAQ
What schema.org types are used for bookable appointments on Shopify?
Use Service @type with a potentialAction containing either BookAction (paid booking) or ReserveAction (free/hold reservation). The BookAction has a target EntryPoint with urlTemplate pointing to your booking flow URL — either the Shopify product URL or an external booking app URL stored in a metafield.
What Shopify booking apps work best with BookAction schema?
Apps that create dedicated product pages per service (Sesami, Cowlendar, BookedUp) work best because each service has a product URL you can add potentialAction to. For external booking apps (Calendly, Acuity), store the booking URL in a seo.booking_url metafield and use it as the urlTemplate in EntryPoint.
How does BookAction schema help AI shopping agents?
BookAction signals to AI agents that this listing has an active booking flow — not just an informational page. Agents use this to surface your service in booking-intent queries ("book a massage near me") and may generate "Book now" deep links directly in AI-generated results. Without BookAction, agents treat your service as a static product page.
What is the difference between ReserveAction and BookAction?
ReserveAction is for holding a slot without immediate payment (free consultation, restaurant reservation). BookAction is for paying-at-booking scenarios. For most Shopify service businesses that collect payment via Shopify checkout, BookAction is correct. Use ReserveAction when product.price == 0 (auto-detect this in Liquid).
How do I implement BookAction in Shopify Liquid?
Tag bookable products with 'schema-bookable'. Store the booking URL in a seo.booking_url metafield (falls back to the product URL if not set). In product.liquid, detect the tag and add a potentialAction block with @type: BookAction (or ReserveAction if price == 0), target: EntryPoint, urlTemplate: the booking URL, and actionStatus: PotentialActionStatus.