Commerce Protocols · Recommended · Medium impact
Machine-readable booking action
For a service business the agent's task is to book, not to browse — a declared action is what turns a contact page into a completed job.
- Recommended
- Medium impact
- Draft standard
| Property | Value |
|---|---|
| Check id | booking-action |
| Dimension | Commerce Protocols (15%) |
| Tier | Recommended — pool 20 pts |
| Weight inside tier | 2 |
| Impact | Medium |
| Maturity | Draft — A published specification that is not yet ratified. |
| Conditional | No |
| Publishing skill | booking-action |
| Standards | schema.org/ReserveAction |
What the scanner requests
-
/contact -
/book -
/appointment
It also reads documents it has already fetched: $home.
What is measured
This check declares no probe path, so the scanner walks the JSON-LD of the already fetched homepage / for a potentialAction whose @type is ReserveAction (schema.org) and inspects its target. Pass when the target is an EntryPoint carrying a urlTemplate, an RFC 6570 URI template with at least one variable for the agent to fill in such as {date} or {service}, so the booking can be completed by expanding a URL instead of scraping a contact page; partial when ReserveAction is present but target is a bare URL with no urlTemplate, or the template holds no variables and is therefore just a link; fail when no ReserveAction appears anywhere in the graph and the only booking path is a ContactPoint, a tel: link or a free-text form. This profile-scoped check is reported as not scored and excluded from the denominator unless the services profile has been positively identified.
Reproduce it yourself
You do not have to take our word for any of this. Run the same measurement against your own domain:
DOMAIN="https://example.com"
curl -sL "$DOMAIN/" | perl -0777 -ne 'while (m{<script[^>]*application/ld\+json[^>]*>(.*?)</script>}gsi) { print "$1\n" }' | jq -r '.. | objects | select(([."@type"?] | flatten | any(. == "ReserveAction"))) | (.target // empty | if type=="array" then .[0] else . end) | if type=="object" then [(."@type" // "NO EntryPoint"), (.urlTemplate // "NO urlTemplate")] else ["target is a bare URL", tostring] end | @tsv'
The fix
This is the artifact to ship. It is the complete change, not a description of one.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Boiler service and safety check",
"serviceType": "Boiler maintenance",
"areaServed": { "@type": "City", "name": "Bristol" },
"provider": { "@type": "LocalBusiness", "name": "Harbourside Heating", "telephone": "+44-117-496-0000" },
"potentialAction": {
"@type": "ReserveAction",
"name": "Book a boiler service",
"target": {
"@type": "EntryPoint",
"urlTemplate": "https://www.example.com/book?service={service}&date={date}&postcode={postcode}",
"contentType": "text/html",
"actionPlatform": ["https://schema.org/DesktopWebPlatform", "https://schema.org/MobileWebPlatform"],
"httpMethod": "GET"
},
"result": { "@type": "Reservation", "name": "Boiler service appointment" }
}
}
</script>
Standards referenced
-
schema.org/ReserveAction
Related checks
| Check | Tier | Impact | Same skill |
|---|---|---|---|
product-schema — Product/catalog schema | Essential | Critical | — |
payment-protocols — Agent payment standard support (AP2/ACP/x402/UCP) | Recommended | High | — |
agent-purchase — Agent purchase policy | Recommended | High | — |
ucp-manifest — UCP manifest (/.well-known/ucp) | Emerging | Medium | — |
returns — Return and shipping terms machine-readable | Recommended | Medium | — |
agent-analytics — Agent traffic measurement | Emerging | Low | — |
inventory-freshness — Inventory and price freshness | Recommended | High | — |
seller-identity — Seller identity declared | Recommended | Medium | — |