Introduction
I documented the existing HTTP booking endpoints in llms.txt so agents could discover an offer, request available times, and submit a booking. Before this change, agents could discover and evaluate offers through llms.txt, but they couldn't complete a booking themselves. The slots endpoint had no rate limit, and client-IP detection used the first network hop rather than the intended client address.
The interface now exposes GET /slots and POST /booking directly, without a dedicated Model Context Protocol (MCP) server. Slot responses include a flattened openSlots array, giving agents less response structure to navigate. I also added request limits and input restrictions to the public routes.
Client-IP detection now uses CloudFront-Viewer-Address. This is the viewer address reported by CloudFront, not necessarily the ultimate originating client. That distinction matters because the slots limit uses this address. The implementation changes are established, but their effectiveness under real traffic—and the ingress configuration needed to trust the address header—remain unverified.
The Rule That Shipped
I capped the slots route at 30 requests per minute per IP address. It previously had no rate limit. The booking route has two email-based limits: at most five booking attempts per hour per email address, and at most one email-only booking per day per email address. The hourly rule counts attempts; the daily rule limits successful bookings in the email-only category.
The supplied evidence doesn't establish exactly what makes a request “email-only.” I can't equate it with a missing phone number, company value, or any other particular field. The daily cap is explicit, but the classification that triggers it still requires inspection.
Booking requests also have a 16 KB JSON-body limit. Line breaks are stripped from the name, email, and company fields before further processing. This is a normalization step, not an established general injection defense or proof of protection at any particular downstream destination. Disposable email domains are rejected, while standard webmail providers remain allowed. No CAPTCHA or user-agent blocking was added.
Client-IP detection reads CloudFront-Viewer-Address rather than the first network hop. Trusting that value requires trusted CloudFront ingress and appropriate header handling. A proxy upstream of CloudFront can itself be the viewer, so this change doesn't universally recover an individual user's address. I haven't verified ingress restrictions or address parsing.
Worked Example
Consider a client repeatedly submitting POST /booking with the same email address within one hour. Some or all of the requests rely only on the email as an identifier and fall into the implementation's email-only category. This is a worked interpretation of the configured rules, not an executed test.
The first five attempts are processed under the booking route's other checks. The sixth attempt using that email address in the same hour is rejected by the hourly limit. Processing an attempt doesn't mean it creates a booking: any of the earlier requests can still fail another applicable check.
If all five processed attempts are email-only, at most one such booking can succeed for that email during the day. After one succeeds, later email-only requests can't create another booking for that address that day, even if the hourly allowance hasn't been exhausted. If the requests mix categories, the daily restriction applies to email-only bookings, while the hourly attempt cap still applies to reuse of the email address.
The example distinguishes processing an attempt from successfully creating a booking. It doesn't establish the exact email-only predicate, the order of every validation check, or the response format returned on rejection. Those details can't be inferred from the numeric limits.
Challenges and Tradeoffs
I chose to document the HTTP API directly rather than add an MCP server. Discovery and endpoint instructions stay in llms.txt, while booking operations stay in the HTTP interface. A dedicated protocol server would add another interface to deploy and keep aligned with the booking behavior. Plain documentation, however, doesn't provide MCP-specific discovery for clients that require it. This is an interface tradeoff, not evidence that every agent can successfully use the documented endpoints.
Flattening available times into openSlots reduces navigation through the response. That simpler shape doesn't, by itself, establish correct agent behavior. An agent still needs to interpret availability and submit an acceptable booking request.
The abuse controls have limits too. An IP-based quota can group legitimate callers who share an address, including callers behind a proxy. An email-based quota limits reuse of one identifier, but it doesn't establish that different addresses belong to different people. Distributed callers or callers using multiple email addresses can therefore fall outside the protection offered by any single counter.
Leaving out CAPTCHA and user-agent blocking avoids those interaction requirements, but neither contributes any additional screening. Disposable-domain rejection adds a separate filter while preserving access for standard webmail users. Its reliability can't be assessed without knowing the list's source, maintenance process, and completeness. The body limit and newline normalization are concrete input constraints, not substitutes for validating the rest of a booking request.
Verification Process
The evidence supports an implementation-level account: endpoint documentation, a flattened slot response, configured quotas, a body limit, field normalization, disposable-domain rejection, and a changed IP source. I haven't verified these controls through an executed test run, production logs, or measured false-positive rates. The configured values are not the same as demonstrated runtime enforcement, nor do they show that the values fit actual usage.
A focused verification pass would check both sides of each boundary: the thirtieth and thirty-first slots requests within a minute, the fifth and sixth booking attempts for one email within an hour, and a second email-only booking after a successful first booking that day. Testing the daily cap first requires finding the actual email-only predicate rather than inventing a request shape.
Payload checks would cover bodies below, at, and above the configured limit, plus line breaks in each of the three normalized fields. Domain checks would include a rejected disposable domain and an allowed standard webmail domain. Ingress verification would separately examine whether requests can bypass trusted CloudFront handling and how the viewer-address value is parsed.
These are verification steps still to perform, not completed repairs. The specific values for the booking-window check and scope-call limits mentioned alongside the rate limits are also not established.
Conclusion
The change makes booking actions available through documented HTTP endpoints without adding an MCP server. I paired that interface with explicit slots and booking limits, bounded request bodies, field normalization, disposable-domain rejection, and a CloudFront-reported address source.
Several details still need verification. Runtime enforcement hasn't been demonstrated here, and trusted ingress and address parsing remain unchecked. The email-only predicate is unknown. The disposable-domain list's provenance, maintenance, and completeness are unestablished, and the booking-window and scope-call values are unspecified. Keeping those uncertainties explicit separates what changed from what still needs checking.