LivStay
An open protocol for personal agents to find, ask about and book independent homes.
Draft 0.2 · 24 October 2026 · Extension livstay.org/stays, version "0", revision "0.2" ·
Builds on Personal Agent Protocol (Poppy) draft 0.2 · Licensed under the Apache License 2.0
Status: draft. This document is a working draft for implementers and reviewers. Until 1.0 a revision can break compatibility with earlier drafts; §4.3 says how both sides tell revisions apart. LivAround is the reference implementation; the protocol is not owned by it. Feedback: open an issue. Changes from draft 0.1 are listed in Appendix D.
1. Introduction
Travellers are starting to plan and book stays through personal agents: assistants that act for one person. Today those agents mostly reach homes through online travel agencies (OTAs), which own the listing, the guest relationship and the price. Independent hosts and the platforms that serve them have no standard way to be found by an agent, answer its questions or take its booking directly.
LivStay is that standard. It is an extension of Poppy: Poppy defines how a personal agent discovers a company, signs a user in, holds a session and talks to the company; LivStay defines what a stay is on top of that: the home, its facts, its availability, its terms, how an agent asks the host a question and how a booking is proposed, approved and completed.
1.1 Goals
- Recommendation before booking. An agent can tell a traveller why a home fits, from facts the host stands behind, before any booking call is made.
- The traveller approves; the price is binding. No agent can commit a traveller's money to a price the traveller has not seen, and the price shown is the price charged.
- The host's rules hold on every channel. An agent sees the same terms the host publishes everywhere else, and the host decides who can book instantly.
- A human where it matters. An agent can put a question to the host, a person, before booking, and get an answer it can cite.
- Start with what you have. A host platform can publish facts alone and add prices, enquiries and bookings later (§3.3).
- Reuse, don't reinvent. Identity, sign-in, sessions, conversations and confirm-before-act are Poppy's. LivStay adds only what is specific to stays.
1.2 Non-goals
- Search ranking. LivStay does not say how agents rank homes.
- Rate parity. LivStay takes no position on any contract between a host and an OTA.
- Payment rails. LivStay does not define a payment protocol (§11).
- Hotels. Nothing prevents a hotel from implementing LivStay, but the objects are shaped for homes rented by their owners or managers: one calendar, one host, house rules, a deposit.
- Member-only exchange. Home swaps, points and circles are out of scope for this draft.
1.3 Relationship to Poppy
LivStay is a third-party Poppy extension, named under a domain its authors control, as Poppy 3.3 requires. It depends on:
| Poppy feature | Used for | Needed from |
|---|---|---|
Discovery (/.well-known/poppy.json) |
Finding a Host Platform and its LivStay endpoint | Level 0 |
| Sessions, signed out | Enquiries (§8) and reports (§6.6) | Level 2 |
| Sign-in and account scopes | Bookings and the traveller's data (§5) | Level 3 |
| OpenAPI and MCP | The LivStay endpoints and tools | Level 1 |
| Conversations | Enquiries (§8) and the booking conversation the agent opens (§12.3) | Level 2 |
operations extension |
Every booking, change and cancellation (§9, §10, §12) | Level 3 |
Web browsing (web.browser_session_endpoint) |
Identity checks and payment pages in the traveller's Session (§9.5, §11) | Level 3 |
feedback |
Stay tasks the Host Platform cannot do (§6.6) | Level 2 |
Where this document and Poppy disagree, Poppy governs everything Poppy defines.
2. Conventions and terms
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
JSON examples are illustrative. Field tables and the JSON Schemas (§16) are normative; where a schema and the text disagree, the text governs and the schema is a defect.
| Term | Meaning |
|---|---|
| Traveller | The person the stay is for. Poppy's User. |
| Personal Agent | Software acting for the traveller. As in Poppy. |
| Host | The person or business that rents the home and answers for it. |
| Host Platform | The service that publishes homes under LivStay for one or more hosts. Poppy's Company. A host with their own site and a host platform serving thousands of hosts are both Host Platforms. |
| Home | A unit with one calendar that is booked as a whole. A room rented separately is its own Home. |
| Party | Who is staying: adults, children with their ages, and pets (§2.2). |
| Stay | A Home, a check-in date, a check-out date and a Party. |
| Facts | Plain-language statements about a Home that the host stands behind (§6.2). |
| Terms | The rules a stay is sold under: price adjustments, cancellation, deposit, who may book instantly (§6.3). |
| Quote | A priced Stay, signed and binding until it expires. It holds nothing (§7.2). |
| Booking operation | A Poppy operations operation whose action is reserving a Stay (§9). |
| Hold | The period in which a booking operation blocks the calendar (§9.2). |
| Requirements | What must happen after confirmation for a booking to succeed: payment, identity verification (§9.5). |
| Attestation | A signed claim about a Home, Host or Traveller by any issuer (§13.5). |
| Receipt | A signed record of something the Host Platform did for or to a booking (§13.3). |
2.1 Dates, times and money
- Dates (
check_in,check_out, availability days) are ISO 8601 calendar dates in the Home'stime_zone(§6.1.1). Times of day (check-in from, check-out by) are local to the same zone. - Timestamps (
expires_at,complete_by,at) are RFC 3339 in UTC with aZsuffix. - Money is a Money object
{ "amount": string, "currency": string }: currencyis an ISO 4217 alphabetic code.amountis a decimal string with exactly as many fraction digits as the currency's ISO 4217 minor unit:"129500.00"for INR,"18500"for JPY,"12.500"for KWD. It matches^-?(0|[1-9][0-9]*)(\.[0-9]+)?$, with no exponent, sign+, spaces or group separators.- Negative amounts appear only where a field says they may (discounts, price differences).
- Rounding. A Host Platform computes each Quote line in whatever precision it likes, then rounds that line to the minor unit before anything is summed. It may choose the rounding method; it MUST round lines before reconciling them, never the total alone.
- Reconciliation. In every Quote, and in the
livstayterms of every booking operation: 1. every Money value has the Quote'scurrency; 2. the sum of theamountof every line within_total: trueequalstotalexactly; 3. the sum ofpayment_scheduleamounts equalstotalexactly; 4. everycancellationrefund is between zero andtotal, and refunds never increase as the deadline gets later; 5.due_nowequals the sum ofpayment_scheduleentries due at booking.
An agent MUST NOT confirm a booking whose Quote fails any of these, and SHOULD report it with a mismatch report (§6.6). - Signed objects contain no fractional numbers. Quotes, Receipts and signed answers carry money as strings and counts as integers, so their canonical form (§13.1) is exact.
2.2 The Party
{ "adults": 2, "children": [{ "age": 7 }, { "age": 1 }], "pets": 1 }
| Field | Req. | Description |
|---|---|---|
adults |
Yes | Integer, at least 1. People aged 18 or over. |
children |
Yes | Array of { "age" }, integer ages 0–17 on the check-in date. Empty when none. An age below the Home's infant_max_age is an infant. |
pets |
Yes | Integer, 0 or more. |
A Host Platform MUST price and check eligibility from the whole Party: a stay that breaks
max_guests, max_infants, pets.max or min_adult_age fails with stay_invalid, and a price that
depends on children's ages or pets MUST show the difference as Quote lines.
Agents SHOULD send only the ages, never names or dates of birth.
3. Overview
3.1 The parties
Traveller ──▶ Personal Agent ──Poppy──▶ Host Platform ──▶ Host
│ │
│ read: homes, quotes │ facts, calendar, terms,
│ ask: conversation │ answers to enquiries,
│ book: operations │ booking messages
│ follow: booking events │
The Host Platform speaks for the Host. Conversations reach the Host as a person (§8.2).
3.2 A stay, in six steps
- Find. The agent reads
poppy.json, then lists Homes and reads their Facts. No token. - Price. The agent asks for a Quote for exact dates and Party. No token. The Quote is binding for up to 24 hours and holds nothing.
- Ask (optional). In a signed-out Session, the agent opens a conversation and puts the traveller's question to the Host.
- Book. The traveller signs in. The agent proposes a booking from the Quote; the Host Platform
answers with a Poppy operation in state
proposed, which holds the calendar for up to 15 minutes. The agent shows the traveller the summary; the traveller approves; the agent confirms. - Complete. The traveller pays, and verifies identity if the Host requires it, before a deadline the Host Platform states. The operation then succeeds.
- Stay. The agent follows the booking's event feed and opens a conversation with the Host when needed. Changes and cancellations are new operations. Every write and every money movement carries a Receipt.
3.3 Levels
A Host Platform declares the highest level it supports. Each level includes the ones below.
| Level | Name | What an agent can do | Poppy needed | Sections |
|---|---|---|---|---|
| 0 | Listed | Read Homes, Facts, Terms, Protection | poppy.json only |
§4, §6 |
| 1 | Priced | Read availability; get binding Quotes | poppy.json, apis |
§7, §13.1–§13.2 |
| 2 | Reachable | Ask the Host questions | Signed-out Sessions, conversations | §8 |
| 3 | Bookable | Book, complete, change and cancel | Sign-in, operations |
§9–§12 |
Levels 0 and 1 need no Poppy Session and no token. Level 0 can be served from static files. A Host Platform SHOULD publish Level 0 before anything else: it is what lets an agent recommend a home.
4. Advertising support
4.1 Host Platform
A Host Platform advertises LivStay in the extensions object of its poppy.json:
{
"protocol_version": "0.2",
"organization": { "name": "LivAround", "domain": "stay.livaround.com" },
"auth": {
"issuer": "https://stay.livaround.com",
"direct": { "scopes": ["poppy:read", "livstay:book", "livstay:share_contact", "livstay:share_trip_context"] },
"custom_scopes": {
"livstay:book": "Book, change and cancel stays you approve",
"livstay:share_contact": "Give the host your phone number and email",
"livstay:share_trip_context": "Tell the host the purpose of your trip and when you arrive"
}
},
"apis": [
{ "type": "openapi", "url": "https://stay.livaround.com/livstay/v0/openapi.json" },
{ "type": "mcp", "url": "https://stay.livaround.com/livstay/v0/mcp" }
],
"agent": { "protocols": [{ "type": "poppy", "endpoint": "https://stay.livaround.com/poppy/conversations" }] },
"web": { "browser_session_endpoint": "https://stay.livaround.com/poppy/browser-session" },
"extensions": {
"operations": { "version": "1", "endpoint": "https://stay.livaround.com/poppy/operations" },
"livstay.org/stays": {
"version": "0",
"revision": "0.2",
"endpoint": "https://stay.livaround.com/livstay/v0",
"level": 3,
"keys": "https://stay.livaround.com/.well-known/livstay-keys.json",
"terms_of_service": "https://livaround.com/terms",
"facts_site": "https://booking.livaround.com"
}
}
}
Example 1. A Host Platform's poppy.json.
| Field | Req. | Description |
|---|---|---|
version |
Yes | "0". The extension's major version, as Poppy 3.3 defines. Stays "0" until 1.0. |
revision |
Yes | The exact draft this Host Platform implements: "0.2" for this document. See §4.3. |
revisions_supported |
No | Every revision the Host Platform serves, if more than one. Includes revision. |
endpoint |
Yes | Base URL of the LivStay endpoints. HTTPS; plain http://localhost or http://127.0.0.1 only for local testing. The same applies to every URL in this document. |
level |
Yes | Highest level supported, 0–3 (§3.3). |
keys |
Yes at level ≥1 | URL of a JWK Set [RFC7517] of the keys that sign Quotes, Receipts and answers (§13.2). |
terms_of_service |
Yes at level 3 | The Host Platform's terms the traveller books under. |
facts_site |
No | Origin where each Home's Facts are also published as HTML (§6.5). |
A Host Platform at level 3 MUST list operations with version "1", agent.protocols with a
poppy entry, and auth.direct (or another sign-in type) able to grant livstay:book.
4.2 Personal Agent
A Personal Agent that supports LivStay lists it in its client metadata document:
"extensions": {
"operations": { "version": "1" },
"livstay.org/stays": { "version": "0", "revisions": ["0.2"] }
}
revisions is REQUIRED: the drafts the agent implements. A Host Platform MUST NOT return a
booking operation or LivStay conversation data to an agent that does not list livstay.org/stays
with a revision the Host Platform serves; it answers extension_required (Poppy 3.3). Levels 0 and 1
are public (§5.1) and need no client metadata.
4.3 Revisions
The extension version stays "0" for every draft, so before 1.0 it cannot tell drafts apart.
revision does.
- An agent MUST send the header
LivStay-Revision: <revision>on every request to a LivStay endpoint, naming one revision both sides support. A Host Platform MUST answer in that revision and echo the header. - A request without the header is answered in the Host Platform's
revision, with the header set. An agent MUST check it before relying on the response. - A request naming a revision the Host Platform does not serve fails with
revision_unsupported(400), whose body listssupported. - In a conversation, LivStay
datacarries"revision"alongside"type". - From 1.0,
revisionchanges only for compatible additions, andversionchanges for breaking ones, as Poppy 3.3 defines.
5. Access and scopes
5.1 What needs what
Poppy's scopes are account scopes: a signed-out Session Token carries none (Poppy 4.2). LivStay therefore names three kinds of access.
| Access | Token | Endpoints |
|---|---|---|
| Public | None | Homes, a Home, availability, Quotes, tier issuers, the OpenAPI document |
| Session | A Session Token, signed out or in, with no scope required | Enquiry conversations (§8), mismatch reports (§6.6), Poppy feedback |
| Account | A signed-in Session Token with the scope named in §5.2 | Everything about bookings (§9–§12) |
- A Host Platform MUST serve public endpoints without a token. It MAY accept a Session Token on them, signed in or out, and use it only to set rate limits and to resolve a signed-in traveller's tier (§7.2); it MUST NOT require one or check its scope.
- A Host Platform MUST accept a signed-out Session Token for enquiries and reports, and MUST NOT ask for sign-in to answer a pre-booking question.
5.2 Account scopes
Every write is a named scope the traveller sees at sign-in. A Host Platform MUST NOT require
poppy:write for any LivStay action.
| Scope | Grants |
|---|---|
poppy:read |
Listing the traveller's bookings, reading their events and requirements (§9.5, §12) |
livstay:book |
Proposing, confirming, countering, changing and cancelling bookings (§9–§12) |
livstay:share_contact |
Releasing the traveller's phone and email to the Host (§12.4) |
livstay:share_trip_context |
Sending trip purpose and arrival notes to the Host (§9.4) |
Without livstay:share_contact, Host and traveller talk through the Host Platform's relay and
neither sees the other's direct details.
6. The Home
6.1 Listing Homes
GET {endpoint}/homes (public)
| Parameter | Req. | Description |
|---|---|---|
near |
No | lat,lng |
radius_km |
No | With near. |
area |
No | A place name the Host Platform publishes (e.g. north-goa). |
check_in, check_out |
No | If both are given, only Homes with that range available. |
adults, children_ages, pets |
No | The Party, as in §2.2; children_ages is comma-separated (7,1). |
cursor |
No | From a previous page. |
The response is { "homes": [Home summary], "next_cursor": string | null }. A Home summary is the
Home object (§6.1.1) without facts.faq and attestations.
GET {endpoint}/homes/{home_id} (public) returns one Home.
6.1.1 Home object
| Field | Req. | Description |
|---|---|---|
home_id |
Yes | URL-safe, prefixed home_. Stable for the Home's life. |
name |
Yes | The Host's name for the Home. |
url |
Yes | The Home's public page (§6.5). |
time_zone |
Yes | IANA time zone of the Home, e.g. Asia/Kolkata. Every date and local time about this Home is in it. |
description |
Yes | A schema.org Accommodation object [SCHEMA]: type, bedrooms, beds, bathrooms, occupancy, amenities. |
location |
Yes | { "area", "country", "lat", "lng", "precision_m" }. country is ISO 3166-1 alpha-2. precision_m MUST be at least 500 until a booking for the traveller succeeds. |
host |
Yes | Host object (§6.1.2). |
facts |
Yes | Facts object (§6.2). |
terms |
Yes | Terms object (§6.3). |
protection |
Yes | Protection object (§6.4). |
photos |
Yes | Array of { "url", "caption", "taken_on" }. At least one. |
photos_updated_at |
Yes | When the set last changed. |
currency |
Yes | Currency the Home is priced in. |
attestations |
No | Array of compact JWS Attestations (§13.5). |
updated_at |
Yes | When any field last changed. |
6.1.2 Host object
| Field | Req. | Description |
|---|---|---|
display_name |
Yes | A name a person would use. Not a company placeholder. |
host_since |
Yes | Year the Host started hosting. |
languages |
Yes | BCP 47 tags. |
response_time_p50_minutes |
No | Median time to first human reply over the last 90 days. |
homes_count |
No | Homes this Host publishes on this Host Platform. |
6.2 Facts
Facts are what an agent quotes when it tells a traveller why a Home fits. Every Fact is a statement the Host stands behind; none is generated by the Host Platform without the Host's review.
| Field | Req. | Description |
|---|---|---|
facts_version |
Yes | Integer. Increments on any change. |
summary |
Yes | Two to four sentences: what the Home is and who it suits. |
ideal_for |
Yes | Array of short phrases ("families with small children"). |
not_for |
Yes | Array of short phrases ("guests who need step-free access"). May be empty only if the Host says so. |
accessibility |
Yes | Array of statements; empty means not stated, never "accessible". |
check_in |
Yes | { "from", "until", "how" }. from and until are local times HH:MM. how is plain language ("the host meets you"). |
check_out |
Yes | { "by" }, a local time HH:MM. |
included |
Yes | Array ("daily cleaning", "airport pickup"). |
not_included |
Yes | Array. |
power_backup, water_backup, internet |
No | Plain-language statements. |
parking, pets, smoking, noise, children |
Yes | Plain-language statements. |
house_rules |
Yes | Array of rules. |
cancellation |
Yes | One sentence a traveller can rely on. MUST agree with terms.cancellation. |
when_things_go_wrong |
Yes | What the Host does if something breaks, and how the traveller reaches them. |
faq |
No | Array of { "q", "a" }. |
Agents SHOULD cite Facts by home_id and facts_version when they rely on them.
6.3 Terms
| Field | Req. | Description |
|---|---|---|
terms_version |
Yes | Integer. Increments on any change. A Quote is bound to the version it was made under. |
decision_mode |
Yes | instant (a valid proposal is held and can be confirmed), request (the Host decides, §10) or auto_counter (the Host Platform may counter within bounds the Host set, §10.3). |
instant_min_tier |
No | Lowest traveller tier that may book instantly (§13.6). Below it, decision_mode is treated as request. |
id_required |
Yes | Whether the traveller must verify identity before the booking succeeds (§9.5). |
min_nights, max_nights |
Yes | Defaults; availability may override per date. |
max_guests |
Yes | Most adults and children together, not counting infants unless infants_count is true. |
infant_max_age |
Yes | Children younger than this are infants. Commonly 2. |
infants_count |
Yes | Whether infants count toward max_guests. |
max_infants |
Yes | Most infants. 0 if the Home cannot take infants. |
min_adult_age |
No | Lowest age of the booking adult, if above 18. |
pets |
Yes | { "allowed": boolean, "max": integer, "note": string }. |
discounts |
No | Array of { "kind": "weekly" \| "monthly" \| "early" \| "late", "percent" }. percent is an integer. |
channel_adjustment_percent |
Yes | Integer, the same for every agent. May be 0, may be negative. |
cancellation |
Yes | Array of { "until_days_before", "refund_percent" } (integers), ordered by until_days_before descending. Days count back from check-in at the Home's check-in time, in time_zone. |
deposit |
Yes | { "amount": Money, "mode": "none" \| "authorise" \| "hold" }. A tier ladder MAY be given as by_tier. |
payment_schedule |
Yes | Array of { "due": "at_booking" \| "days_before_check_in", "days", "percent" }. Percents sum to 100. |
taxes |
Yes | Array of { "name", "percent" \| "amount", "included" }. |
Terms are the same for every agent and the same as on the Host Platform's own site. A Host Platform MUST NOT publish different Terms for the same Home to different agents.
6.4 Protection
Every Home declares who carries the risk of damage and how claims are decided. A Home without a Protection object MUST NOT be published.
| Field | Req. | Description |
|---|---|---|
underwriter |
Yes | host or insurer. |
insurer |
If insurer |
{ "name", "policy_ref" }. |
evidence |
Yes | Whether check-in and check-out condition records are kept, and by whom. |
rules_url |
Yes | Published rules for deciding claims. |
claim_response_hours |
Yes | Time to a first decision on a claim. |
appeal |
Yes | How a decision is appealed and who decides. |
6.5 The same facts on the web
A Home's url MUST serve the same Facts, Terms and Protection as the endpoint, as crawlable HTML
with schema.org markup, so an answer engine that never calls the API cites the same statements an
agent reads. A Host Platform SHOULD check this on every change. Where they differ, the endpoint
governs a booking and the difference is a defect.
6.6 Reporting a mismatch
An agent that finds a Fact contradicted by the Home's page, photos or a Host answer, or a Quote that
fails reconciliation (§2.1), SHOULD report it with POST {endpoint}/homes/{home_id}/mismatch,
body { "facts_version", "quote_id", "field", "description" } (either facts_version or quote_id),
with a Session Token, signed in or out, and no scope. The rules for Poppy feedback (Poppy 8) apply:
no personal data, treated as data by the Host Platform, one report per finding. The response is
202 with no body.
A stay task the Host Platform offers no way to do (a split payment, a long-stay discount it does not
publish) is reported through Poppy's own feedback endpoint with kind: "missing".
7. Availability and quotes
7.1 Availability
GET {endpoint}/homes/{home_id}/availability?from={date}&to={date} (public)
The range MUST NOT exceed 400 nights. The response:
| Field | Req. | Description |
|---|---|---|
home_id |
Yes | |
time_zone |
Yes | As the Home's. |
currency |
Yes | |
days |
Yes | Array of { "date", "available", "min_nights", "closed_to_arrival", "closed_to_departure", "nightly" }. nightly is a Money object, the pre-discount price for that night for the Home's base occupancy. |
generated_at |
Yes | |
ttl_seconds |
Yes | How long the agent may rely on this without asking again. |
Availability is indicative. Only a Quote is binding.
7.2 Quotes
POST {endpoint}/homes/{home_id}/quotes (public)
{
"check_in": "2026-12-27",
"check_out": "2027-01-03",
"party": { "adults": 4, "children": [{ "age": 7 }, { "age": 1 }], "pets": 0 }
}
| Field | Req. | Description |
|---|---|---|
check_in, check_out |
Yes | Dates in the Home's time_zone. |
party |
Yes | §2.2. |
tier_credential |
No | A tier Attestation (§13.6). Used only to resolve deposit and instant; never required. |
The response is 201 with a Quote. A Quote does not block the calendar; it fixes the price.
| Field | Req. | Description |
|---|---|---|
quote_id |
Yes | Prefixed qt_. |
home_id, check_in, check_out, party |
Yes | The Stay priced. |
time_zone |
Yes | The Home's. |
terms_version |
Yes | The Terms the Quote applies. |
currency |
Yes | |
lines |
Yes | Array of Line objects: { "kind", "label", "amount": Money, "in_total": boolean }. kind is one of nights, discount, cleaning, extra_guest, pet_fee, channel_adjustment, tax, platform_fee, agent_fee, other. A line with in_total: false is informational, such as a tax already contained in nights. |
total |
Yes | Money. What the traveller pays for the stay. Reconciles with lines (§2.1). |
payment_schedule |
Yes | Array of { "due_at": timestamp \| null, "at_booking": boolean, "amount": Money }. Sums to total. |
cancellation |
Yes | Array of { "until": timestamp, "refund": Money }, earliest first, then a final entry { "until": null, "refund": Money } for any later cancellation. |
deposit |
Yes | { "amount": Money, "mode" }, resolved for assumed_tier. |
assumed_tier |
Yes | The tier deposit and instant were resolved for: the tier from a verified tier_credential, a signed-in traveller's known tier, or new. |
instant |
Yes | Whether a booking from this Quote can be confirmed without the Host deciding (§6.3). |
created_at |
Yes | |
expires_at |
Yes | At most 24 hours after created_at. |
signature |
Yes | A compact JWS over the Quote (§13.1), typ livstay-quote+jws. |
Binding. A Host Platform MUST honour a Quote's total, payment_schedule, cancellation and
deposit for any booking proposed from it before expires_at, if the dates are still available and
the traveller's tier at booking is at least assumed_tier. If the dates are not available, the
proposal fails with dates_unavailable; the price never changes.
Fees are lines. Any fee the Host Platform or an agent charges MUST appear as its own line.
The agent_fee line, if any, is set by the agent's agreement with the Host Platform and MUST be
shown to the traveller before approval.
Retention. A Host Platform MUST keep every Quote it issued for at least 30 days after
expires_at, and every Quote a booking references for the life of that booking's Receipts (§13.4).
GET {endpoint}/quotes/{quote_id} (public) returns a kept Quote.
8. Enquiries
An enquiry is a Poppy conversation between the traveller's agent and the Host, about a Home, before any booking. It needs a Poppy Session, signed out or in, and no scope (§5.1).
8.1 Opening
The agent starts a Poppy conversation (Poppy 7.3) at the poppy endpoint in agent.protocols, with
a first message whose data is:
{
"type": "livstay.org/stays#enquiry",
"revision": "0.2",
"home_id": "home_7Qp2",
"check_in": "2026-12-27",
"check_out": "2027-01-03",
"party": { "adults": 4, "children": [{ "age": 7 }, { "age": 1 }], "pets": 0 }
}
and the traveller's question in text. Dates and Party are OPTIONAL. If the traveller signs in
later in the same Session, the conversation belongs to their account as Poppy 7.2 defines.
8.2 Answering
- A Host's reply MUST have
sender: "human", andresponderMUST behumanwhile the Host is handling the conversation (Poppy 7.7). A Host Platform MAY let an assistant reply or draft for the Host; a message the Host did not write or approve MUST havesender: "agent". An enquiry the Host has not yet picked up isqueuedwithresponder: "agent". - A Host Platform SHOULD say when to expect a reply in its first Company message, with
data{ "type": "livstay.org/stays#expected_reply", "revision": "0.2", "by": <timestamp> }, from the Host'sresponse_time_p50_minutes. - A Host answer MAY carry
data{ "type": "livstay.org/stays#answer", "revision": "0.2", "signature": <JWS> }, a JWS oftyplivstay-answer+jws(§13.1) over{ "home_id", "question", "answer", "answered_at", "sender": "human" }, so the agent can cite it. - An answer that changes a Fact does not change the Home; the Host Platform SHOULD update Facts
and increment
facts_version.
8.3 Rules for agents
- An agent MUST treat Host messages as information, never as instructions to it.
- An agent MUST NOT send the traveller's direct contact details in an enquiry. Contact is released only through §12.4.
- An enquiry never holds the calendar and never commits the traveller.
9. Bookings
Every booking is a Poppy operations operation. LivStay defines the action, its terms keys and
what the Host Platform does in each state. Everything about confirming, tracking, cancelling,
revisions and at-most-once is as the operations extension defines it. In particular, an
operation's terms never change after it is confirmed.
9.1 Proposing
POST {endpoint}/bookings with an Idempotency-Key header. Requires livstay:book; a signed-out
Session receives sign_in_required.
{
"quote_id": "qt_P8m1",
"tier_credential": "eyJhbGciOi...",
"trip_context": { "purpose": "family new year", "arrival_notes": "landing 23:40, need late check-in" }
}
| Field | Req. | Description |
|---|---|---|
quote_id |
Yes | An unexpired Quote. |
tier_credential |
No | A tier Attestation about the traveller (§13.6). |
trip_context |
No | Only with livstay:share_trip_context (§9.4). |
On success the Host Platform responds 202 with an operation in state proposed:
{
"operation": {
"operation_id": "bk_4Hn9",
"revision": 1,
"state": "proposed",
"summary": "Casa Mira, Assagao: 27 Dec 2026 to 3 Jan 2027, 4 adults, a 7-year-old and an infant. Total INR 1,29,500.00 including taxes and fees; INR 38,850.00 now, the rest by 13 Dec. Free cancellation until 13 Dec, half back until 20 Dec, then none. Deposit INR 15,000.00 authorised on your card, released 3 days after check-out. You will be asked to verify your identity. Booked with LivAround for host Rhea.",
"terms": {
"livstay": {
"revision": "0.2",
"action": "book",
"quote_id": "qt_P8m1",
"quote_signature": "eyJhbGciOi...",
"home_id": "home_7Qp2",
"check_in": "2026-12-27",
"check_out": "2027-01-03",
"party": { "adults": 4, "children": [{ "age": 7 }, { "age": 1 }], "pets": 0 },
"total": { "amount": "129500.00", "currency": "INR" },
"due_now": { "amount": "38850.00", "currency": "INR" },
"deposit": { "amount": { "amount": "15000.00", "currency": "INR" }, "mode": "authorise" },
"terms_version": 12,
"tier": { "presented": true, "accepted": true, "applied": "trusted" },
"requirements": ["payment", "identity_verification"],
"awaiting": "traveller",
"proposed_by": "traveller_agent"
}
},
"expires_at": "2026-10-24T10:15:00Z",
"user_approval_required": true,
"url": "https://stay.livaround.com/b/bk_4Hn9",
"confirmation": null,
"result": null
}
}
Example 2. A proposed instant booking. The calendar is held until expires_at.
terms.livstay field |
Req. | Description |
|---|---|---|
revision |
Yes | The LivStay revision. |
action |
Yes | book, change or cancel. |
quote_id, quote_signature |
Yes for book and change |
The Quote this revision is bound to (§9.3). |
home_id, check_in, check_out, party |
Yes | The Stay. |
total, due_now |
Yes for book and change |
Money. Equal to the bound Quote's. |
deposit |
Yes for book |
As the Quote's. |
terms_version |
Yes | |
tier |
Yes for book |
{ "presented", "accepted", "applied" }: whether a tier credential was presented, whether it was accepted (§13.6), and the tier applied. An unaccepted credential is not an error; the booking proceeds at applied. |
requirements |
Yes for book and change |
What completion will need (§9.5): any of payment, identity_verification. May be empty. |
awaiting |
Yes | traveller (the traveller may confirm) or host (§10.1). |
proposed_by |
Yes | traveller_agent, host or host_agent. |
reason, counter_no |
For counters | §10.2. |
price_difference, refund |
For change, cancel |
§12.5. |
If tier.applied is lower than the Quote's assumed_tier, the Host Platform MUST NOT apply the
Quote's tier-dependent terms: it fails the proposal with tier_mismatch, and the agent asks for a new
Quote at the lower tier.
9.2 Holds
While a booking operation is proposed with awaiting: "traveller", or in_progress, the Host
Platform MUST block the Stay's dates against every other channel it controls. That period is the
hold.
- For an instant booking,
expires_atMUST be at most 15 minutes after the proposal. - After confirmation the hold continues until the operation is final or
complete_bypasses (§9.5). It is never extended by a revision. - When the operation becomes
expired,cancelledorfailed, the hold ends. - A Host Platform MUST limit open holds per traveller and per
client_id(§15.3).
A traveller who wants time to decide uses a Quote (24 hours, binding price, no hold), not a hold.
9.3 Approval
- A Host Platform MUST set
user_approval_required: trueon any booking operation whose terms move money or authorise a deposit. A standing permission is never enough to spend a traveller's money on a stay. - The
summaryMUST state the Home, dates, Party, total, what is due now, the cancellation terms in dated form, the deposit, any identity verification, and the Host Platform and Host names. A traveller who reads only thesummaryMUST have everything needed to decide. - Every
bookorchangerevision is bound to exactly one Quote, named byquote_idandquote_signature. For revision 1 it is the Quote the booking was proposed from; a counter revision carries a new Quote for its own terms (§10.2). The revision'scheck_in,check_out,party,total,due_nowanddepositMUST equal that Quote's. - Before confirming, an agent MUST verify
quote_signature(§13.2) and reconcile the Quote (§2.1), and MUST NOT confirm if either fails, if the revision's terms differ from its Quote, or if the Quote differs from the one the traveller was shown for this revision. - The agent confirms with
approved_by: "user"as theoperationsextension defines.
9.4 Trip context
trip_context lets the Host prepare (an airport pickup, a cot, a late arrival). It is OPTIONAL and
MUST NOT be required to book.
| Field | Description |
|---|---|
purpose |
Free text, e.g. "anniversary". |
arrival_notes |
Free text. |
A Host Platform MUST share trip_context only with the Host of that booking and MUST NOT use
it for advertising. It MUST delete it:
- within 24 hours after the proposing operation ends
failed,cancelledorexpired; or - no later than 90 days after check-out, or after the booking is cancelled.
9.5 Completion: payment and identity
Poppy operations cannot change after confirmation, so what happens between confirmation and success is reported beside the operation, not in it.
When the traveller's agent confirms a revision whose requirements is not empty, the Host Platform
moves the operation to in_progress and creates a completion record:
GET {endpoint}/bookings/completion/{operation_id} (account, poppy:read)
{
"operation_id": "bk_4Hn9",
"complete_by": "2026-10-24T10:42:00Z",
"requirements": [
{ "type": "payment", "status": "pending", "url": "https://stay.livaround.com/pay/bk_4Hn9",
"amount": { "amount": "38850.00", "currency": "INR" } },
{ "type": "identity_verification", "status": "done", "url": null }
]
}
| Field | Req. | Description |
|---|---|---|
operation_id |
Yes | |
complete_by |
Yes | When the hold ends if the requirements are not all done. At most 30 minutes after confirmation, or 60 minutes if identity_verification is listed. Fixed at confirmation; never extended. |
requirements |
Yes | Array of { "type", "status", "url", "amount" }. type is payment or identity_verification. status is pending, done or failed. url is the Host Platform's page for this step while pending. amount is Money, for payment. |
- The pages open in the traveller's browser joined to their Poppy Session where the Host Platform
lists
web.browser_session_endpoint(Poppy 5), so the traveller is already identified. The operation'surlSHOULD lead to the same steps. - While requirements are pending, the operation's Retry-After (operations extension, Tracking)
SHOULD be short, and a Host Platform MAY also send an
action_neededevent (§12.2). - When every requirement is
done, the operation MUST move tosucceeded. - If
complete_bypasses first, or a requirement isfailed, the operation MUST move tofailed, the hold ends, anything taken is returned, andresult.summarysays so. - An agent MUST NOT collect card, bank or identity-document details itself.
A revision with empty requirements goes from confirmed to succeeded as soon as the Host Platform
books it.
9.6 States
| Operation state | Calendar | Money | Meaning for the stay |
|---|---|---|---|
proposed |
Held if awaiting: "traveller" and instant; not held while awaiting: "host" |
Nothing | Awaiting the traveller or the Host (§10) |
in_progress |
Held until complete_by |
Payment or authorisation underway | Confirmed; completion pending (§9.5) |
succeeded |
Booked | As payment_schedule |
Booked. result.data.livstay carries the booking (§12.1). |
failed |
Released | Anything taken is returned; result.summary says what |
Not booked |
cancelled |
Released | Nothing taken | Declined or withdrawn before confirmation |
expired |
Released | Nothing taken | Not confirmed in time |
10. Requests and counter-offers
10.1 Requests
When the Quote's instant is false, proposing a booking makes a request: an operation in state
proposed with awaiting: "host", which does not hold the calendar and cannot yet be confirmed. Its
expires_at is at most 72 hours ahead. Confirming it fails with awaiting_host.
The Host either:
- accepts: the Host Platform issues a new revision with the same terms and
awaiting: "traveller", holds the calendar, and setsexpires_atat most 15 minutes ahead, or at most 24 hours ahead if it also sends anaction_neededevent (§12.2); - counters: a new revision with different terms (§10.2); or
- declines: the operation becomes
cancelled.
The agent learns of the Host's decision by reading the operation (operations extension, Tracking).
10.2 Counter-offers
A counter is a new revision of the same operation, issued before confirmation. Each counter revision MUST carry:
| Field | Description |
|---|---|
proposed_by |
host, host_agent or traveller_agent. |
reason |
Plain language. REQUIRED when proposed_by is host_agent: which of the Host's rules produced this counter. |
counter_no |
1, 2 or 3. |
quote_id, quote_signature |
A new Quote (§7.2) the Host Platform issues for this revision's Stay and total. Its expires_at is no earlier than the revision's. It replaces the earlier Quote for this operation only. |
Rules:
- Only kinder. A counter from
hostorhost_agentMUST NOT be worse for the traveller than the Home's published Terms for the same Stay: no higher total, no stricter cancellation, no larger deposit. - At most three counters per operation, from either side. After the third, no party may counter again: a revision awaiting the Host can still be accepted or declined as in §10.1, and a revision awaiting the traveller can be confirmed or cancelled. An acceptance is not a counter.
- A traveller's agent proposes a counter with
POST {endpoint}/bookings/{operation_id}/counter(account,livstay:book) carrying any changedcheck_in,check_out,partyor a requestedtotal, and anote. The Host Platform answers with the operation, unchanged or as a new revision withproposed_by: "traveller_agent"andawaiting: "host". - Every revision needs the traveller's fresh approval before confirmation (§9.3), and is checked against its own Quote, never the one the operation started from.
10.3 Countering within bounds
If decision_mode is auto_counter, the Host Platform MAY issue counters as host_agent without
the Host, only within bounds the Host set (for example a lowest acceptable total, shorter dates, a
split stay). The bounds themselves are not published. Each such counter MUST carry a reason
and MUST be visible to the Host in the same record.
11. Payment
Poppy leaves payments as an open topic, so this draft defines only the hand-off, through the completion record (§9.5).
due_nowis taken during completion, on the Host Platform's own page.- An agent MUST NOT collect card or bank details from the traveller for the Host Platform.
- Later instalments follow
payment_schedule. The Host Platform announces each with apayment_dueevent (§12.2) carrying the amount, the date and a payment page URL. A missed instalment is handled as the Terms say; it MUST NOT cancel the booking without acancelled_by_hostevent that states why and what is refunded. - Every amount collected, authorised, captured, released or refunded produces a Receipt (§13.3).
When Poppy defines payments, a later draft will adopt it and this section will become a fallback.
12. After booking
12.1 The booking
When a booking operation succeeds, result.data.livstay carries:
| Field | Req. | Description |
|---|---|---|
revision |
Yes | The LivStay revision. |
booking_id |
Yes | Prefixed bkg_. MAY equal operation_id. |
home_id, check_in, check_out, party |
Yes | The Stay booked. |
location |
Yes | { "address", "lat", "lng" }, exact. |
check_in_instructions |
Yes | When they will be sent, or the instructions. |
receipt |
Yes | A Receipt for the confirmation (§13.3). |
GET {endpoint}/bookings (account, poppy:read) lists the signed-in traveller's bookings with this
Host Platform. GET {endpoint}/bookings/{booking_id} returns one, with its current dates, Party,
status and the IDs of operations that changed it.
12.2 Booking events
Every booking has an event feed. It is how the agent learns what the Host or the Host Platform did.
GET {endpoint}/bookings/{booking_id}/events?cursor={cursor}&wait={seconds} (account, poppy:read)
The response is { "events": [Event], "cursor": string, "has_more": boolean }, with the same
reading rules as Poppy conversation events (Poppy 7.5): cursor is the last event returned, wait
(0–30) holds the request open until an event arrives, and has_more: true means read again at once.
| Event field | Req. | Description |
|---|---|---|
event_id |
Yes | Prefixed evt_. |
booking_id |
Yes | |
type |
Yes | One of the types below. |
at |
Yes | Timestamp. |
summary |
Yes | Plain language a traveller can read. |
data |
No | Type-specific fields below. |
receipt |
When listed | A Receipt (§13.3). |
type |
Receipt | data |
|---|---|---|
booked |
Yes | operation_id |
action_needed |
No | operation_id, what (confirm, payment, identity_verification), by |
payment_due |
No | amount, due_at, url |
payment_received |
Yes | amount, reference |
refund_issued |
Yes | amount, reference, reason |
deposit_authorised, deposit_captured, deposit_released |
Yes | amount, reference; for deposit_captured, claim_id |
changed |
Yes | operation_id |
cancelled_by_traveller |
Yes | operation_id, refund |
cancelled_by_host |
Yes | reason, refund, rebooking_help |
check_in_details |
No | instructions |
checked_in, checked_out |
No | |
claim_opened |
Yes | claim_id, amount, description, evidence_url |
claim_decided |
Yes | claim_id, decision (upheld, partly_upheld, rejected), amount, appeal_by |
message_waiting |
No | The Host has written in a booking conversation the agent has not opened (§12.3) |
contact_shared, contact_revoked |
Yes | fields |
Poppy has no push channel yet (Poppy path to 1.0). Agents read the feed with wait.
12.3 The booking conversation
Poppy conversations are started by the Personal Agent (Poppy 7.3), so the agent opens the booking conversation; the Host Platform never does.
- The agent starts a Poppy conversation with a first message whose
datais{ "type": "livstay.org/stays#booking", "revision": "0.2", "booking_id": "bkg_…" }, in a signed-in Session whose account holds the booking. A Host Platform MUST reject it withbooking_not_found(404) otherwise. - The conversation reaches the Host as a person, under the rules of §8.2.
- A booking has at most one open booking conversation per
client_id. Opening another returnsconversation_exists(409) with itsconversation_id. - When the Host writes and no booking conversation is open, the Host Platform keeps the message, sends
a
message_waitingevent, and delivers the message as the first Company message once the agent opens the conversation. - Machine events stay in the feed (§12.2); a Host Platform SHOULD NOT duplicate them as messages.
12.4 Contact
The Host and the traveller talk through the Host Platform's relay by default.
PUT {endpoint}/bookings/{booking_id}/contact (account, livstay:share_contact)
{ "share": ["phone", "email"] }
The Host Platform releases those of the traveller's details it holds to the Host of this booking,
and returns 200:
{
"shared": ["phone", "email"],
"host_contact": { "name": "Rhea", "phone": "+91…", "email": null },
"receipt": "eyJhbGciOi..."
}
host_contact holds the Host's details the Host has agreed to share with booked guests; a field the
Host has not agreed to is null. GET on the same path returns the current state; DELETE revokes:
- after revocation the Host Platform MUST NOT release the traveller's details again, MUST tell
the Host that the traveller withdrew them, and returns
200with{ "shared": [], "receipt" }; - details already released cannot be recalled; the response says so in
note; - both
PUTandDELETEproduce a Receipt and acontact_sharedorcontact_revokedevent.
12.5 Changes and cancellations
- Change:
POST {endpoint}/bookings/{booking_id}/changes(account,livstay:book) with the newcheck_in,check_outorparty. The Host Platform issues a Quote for the changed Stay and answers with a new operation (action: "change") bound to it, whose terms addprice_difference(Money, may be negative) and state the new cancellation deadlines. A price difference due is collected through completion (§9.5). - Cancel:
POST {endpoint}/bookings/{booking_id}/cancellation(account,livstay:book). The Host Platform answers with an operation (action: "cancel") whose terms addrefund(Money), computed from the cancellation terms the traveller approved: those of the Quote bound to the booking's confirmed revision, or of a later confirmed change. A Host Platform MUST NOT apply a stricter policy published after booking. It MAY apply a more generous one. - Both need the traveller's approval like a booking.
- A Host-initiated cancellation is not an operation the agent confirms: it arrives as a
cancelled_by_hostevent with a Receipt, the reason and the refund.
13. Signatures, receipts and attestations
13.1 Signed objects
Quotes, Receipts and signed Host answers are compact JWS [RFC7515].
- Algorithms. A Host Platform MUST sign with
ES256(ECDSA P-256, SHA-256) [RFC7518] and MAY also offerEdDSAwith Ed25519 [RFC8037]. Agents MUST supportES256.none, and any symmetric (HS*) algorithm, MUST be rejected. - Header.
alg,kidandtypare REQUIRED.kidnames a key in the Host Platform'skeysJWK Set.typislivstay-quote+jws,livstay-receipt+jwsorlivstay-answer+jws. No other header parameter is used;jku,x5uandjwkMUST be ignored. - Payload. The UTF-8 bytes of the JSON Canonicalization Scheme [RFC8785] form of the signed object. Because signed objects contain no fractional numbers (§2.1), that form is: object members sorted by key (by UTF-16 code units), no insignificant whitespace, strings escaped as RFC 8785 specifies, integers in plain decimal.
- A Quote's payload is the Quote object with its
signaturemember removed. A Receipt's and an answer's payload is the claims object itself.
13.2 Verifying
To verify a signed object, an agent:
- splits the JWS, decodes the header, and rejects it unless
algis allowed,typis the one expected, andkidis present; - fetches
keysfrom the Host Platform'spoppy.json(it MAY use a cached copy no older than the response's cache lifetime, and MUST refetch once on an unknownkid); - selects the JWK whose
kidmatches, and rejects the JWS unless the key'salg, if present, equals the header'salg; - verifies the signature;
- decodes the payload and treats it as the authoritative object. For a Quote, it also canonicalizes
the Quote it received, without
signature, and rejects the Quote unless the two byte strings are equal.
A Quote that fails verification is not a Quote: an agent MUST NOT show it to the traveller as a price or propose a booking from it.
13.3 Receipts
A Receipt is a signed object of typ livstay-receipt+jws. A Host Platform MUST produce one:
- for every write it performs for an agent: proposal, confirmation, counter, change, cancellation, contact grant and revocation; and
- for every event the event table (§12.2) marks: payments, refunds, deposit authorisation, capture and release, claims and their decisions, and Host-initiated cancellations.
| Claim | Req. | Description |
|---|---|---|
iss |
Yes | The Host Platform's auth.issuer. |
revision |
Yes | The LivStay revision. |
receipt_id |
Yes | Prefixed rcp_. Unique. |
action |
Yes | For writes: propose, confirm, counter, change, cancel, share_contact, revoke_contact. For events: the event type. |
initiated_by |
Yes | traveller_agent, host, host_agent or host_platform. |
sub |
Yes | The traveller's Poppy sub. |
client_id |
When an agent acted | The agent. Omitted for Host or Host Platform actions. |
scopes |
When an agent acted | Scopes the token carried. |
booking_id |
Once a booking exists | |
operation_id, revision_no |
For operation writes | The operation and the revision the write produced or confirmed. |
terms_hash |
For operation writes | Base64url SHA-256 of the canonical form (§13.1) of that revision's terms.livstay. |
confirmed |
For operation writes | true when the write confirmed a revision; false for a proposal or counter. |
approved_by |
For operation writes | The confirmation's value when confirmed is true; otherwise null. |
event_id |
For events | |
amount |
When money moves | Money. |
reference |
When money moves | The Host Platform's payment reference. |
iat |
Yes | Integer seconds since the epoch. |
A Host Platform MUST NOT put a value in approved_by for a write that confirmed nothing. The Host
sees the same records on the booking. A Receipt proves what the Host Platform did and on whose stated
approval; like a Poppy confirmation, it does not prove what the traveller saw.
13.4 Keys and retention
- Publishing.
keysis a JWK Set. Each signing key haskid,kty,use: "sig"andalg. The Set is served with an HTTP cache lifetime of at most 24 hours. - Rotation. A new key MUST be published at least 24 hours before it signs anything. A retired
key stops signing at once but MUST stay in the Set, with the LivStay member
"livstay_retired_at": <timestamp>, for as long as anything it signed is retained. - Compromise. A key believed compromised is removed from the Set at once. Objects it signed can no longer be verified; the Host Platform SHOULD reissue Receipts for live bookings with a new key and send them as events.
- Retention. A Host Platform MUST keep, and keep verifiable, every Receipt and every Quote a booking references for at least 24 months after that booking's check-out or cancellation, and longer where law requires. Agents SHOULD keep the Quotes they showed and the Receipts they received for the same period, together with the JWK they verified against.
13.5 Attestations
An Attestation is a signed claim about a Home, Host or traveller, as a W3C Verifiable Credential
[VC-DATA-MODEL] in JWT form. Anyone may issue one: a Host Platform, a review service, an insurer, an
inspection company. Agents decide whose signatures they trust. A Host Platform MAY carry
Attestations about its Homes in attestations and MUST NOT alter them.
13.6 Traveller tiers
A traveller tier is an Attestation about the traveller whose credentialSubject includes tier (one
of new, verified, trusted, established) and that is bound to the traveller's Poppy sub at
this Host Platform or to a key the agent proves. A Host Platform decides which issuers it accepts and
MUST publish that list at GET {endpoint}/tier-issuers (public). An unaccepted, unbound, expired
or absent credential means new; it is never an error (§9.1, tier).
14. Errors
Errors use Poppy's error format: a JSON body { "error": <code> } plus the fields listed.
| Error | Status | Meaning |
|---|---|---|
revision_unsupported |
400 | The LivStay-Revision requested is not served. Body: supported. |
home_not_found |
404 | Unknown Home, or not published to this client. |
stay_invalid |
400 | Breaks min_nights, max_nights, max_guests, max_infants, pets, min_adult_age or an arrival or departure rule. Body: rule. |
dates_unavailable |
409 | The Stay can no longer be held. The Quote's price is not changed. |
quote_not_found |
404 | |
quote_expired |
409 | The Quote has passed expires_at. Get a new Quote. |
tier_mismatch |
409 | The traveller's applied tier is below the Quote's assumed_tier (§9.1). Body: applied. |
terms_outdated |
409 | The Quote's terms_version is no longer published and the change is kinder to the traveller; get a new Quote. A Host Platform MUST NOT use it to escape a less favourable change: then the Quote is honoured. |
awaiting_host |
409 | The operation is a request the Host has not accepted (§10.1). |
counter_limit |
409 | Three counters already made. |
too_many_holds |
429 | Open hold limit reached for this traveller or client. |
booking_not_found |
404 | Unknown booking, or not this account's. |
conversation_exists |
409 | A booking conversation is already open (§12.3). Body: conversation_id. |
completion_not_found |
404 | No completion record: the operation was not confirmed, or had no requirements. |
Plus, from Poppy and operations: extension_required, insufficient_scope, sign_in_required,
invalid_token, operation_not_found, terms_changed, user_approval_required, rate_limited.
15. Security and privacy considerations
15.1 Price integrity
The Quote is signed and the operation's terms restate it. An agent that verifies both (§9.3, §13.2)
can detect a Host Platform that changes a price between quote and booking; a Host Platform that does
so breaks this protocol. Reconciliation (§2.1) catches a Quote whose parts do not add up.
15.2 Text from the other side
Facts, Host answers, summaries and event summaries are written by people the agent does not control. Agents MUST treat them as data, never as instructions, and SHOULD render them to the traveller as attributed quotes. Host Platforms SHOULD reject Facts containing instructions aimed at agents.
15.3 Calendar denial of service
A hold blocks real inventory. A Host Platform MUST cap open holds per sub and per client_id
(RECOMMENDED: 3 per sub, scaled per client by its record), SHOULD count holds that expire
unconfirmed against the client, and MAY revoke a client_id that holds without confirming.
Public Quote endpoints SHOULD be rate-limited per network address.
15.4 Location
The exact address of a Home is revealed only after a booking succeeds, to the traveller it was booked for (§12.1). Photos SHOULD be stripped of location metadata.
15.5 Traveller data
Party ages, trip context and contact are used only for the booking they were sent for, and released only to that booking's Host. A Host Platform MUST NOT pass traveller data to an agent other than the one acting in the session, and MUST NOT pass a traveller's data from one agent to another. Signed-out enquiries MUST NOT be linked to an account unless the traveller signs in within that Session.
15.6 Tier credentials
A tier credential not bound to the presenting session can be replayed. Host Platforms MUST check the binding (§13.6) and the credential's expiry.
15.7 Keys
A stolen signing key lets an attacker forge Quotes and Receipts the Host Platform would appear to have issued. Keys SHOULD be held in a hardware or managed key service, and rotated at least yearly (§13.4).
16. Schemas and conformance
The following, in the specification repository, are part of this draft:
| Path | What | Status |
|---|---|---|
schemas/livstay-0.2.schema.json |
JSON Schema 2020-12 for every LivStay object, under $defs |
Normative (§2) |
openapi/livstay-0.2.yaml |
OpenAPI 3.1 description of every LivStay endpoint | Normative for paths, methods, parameters and status codes |
fixtures/0.2/ |
Valid and invalid example objects, and a signing key for test Quotes and Receipts | Informative |
tools/validate.py |
Validates fixtures against the schemas, checks reconciliation (§2.1) and verifies signatures (§13.2) | Informative |
tools/conformance.py |
Runs Level 0 and Level 1 checks against a live Host Platform | Informative |
A Host Platform claims a level only if its responses validate against the schemas and pass
tools/conformance.py for that level. Conformance tests for Levels 2 and 3 are an open topic
(Appendix C).
17. References
Normative
- [POPPY] Personal Agent Protocol, draft 0.2. https://personalagentprotocol.org/docs/introduction
- [POPPY-OPS] Personal Agent Protocol, Operations extension, version 1. https://personalagentprotocol.org/docs/0.2/extensions/operations
- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels.
- [RFC8174] Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words.
- [RFC7515] JSON Web Signature. [RFC7517] JSON Web Key. [RFC7518] JSON Web Algorithms. [RFC8037] CFRG Elliptic Curve Signatures in JOSE. [RFC8785] JSON Canonicalization Scheme.
- [RFC3339] Date and Time on the Internet. [ISO4217] Currency codes. [IANA-TZ] Time Zone Database.
- [JSON-SCHEMA] JSON Schema 2020-12. [OPENAPI] OpenAPI Specification 3.1.
- [VC-DATA-MODEL] W3C Verifiable Credentials Data Model 2.0.
Informative
- [SCHEMA] schema.org
Accommodation,LodgingBusiness. - [BCP47] Language tags. [ISO3166] Country codes.
Appendix A. A complete booking, end to end
- The agent fetches
https://stay.livaround.com/.well-known/poppy.json:livstay.org/staysrevision0.2, level 3, andoperationsversion 1. - With no token,
GET /livstay/v0/homes?area=north-goa&check_in=2026-12-27&check_out=2027-01-03&adults=4&children_ages=7,1withLivStay-Revision: 0.2. - The agent reads three Homes' Facts and tells the traveller why Casa Mira fits, citing
facts_version9. - With no token,
POST /homes/home_7Qp2/quotes→qt_P8m1, total INR 1,29,500.00, expires in 24 hours. The agent verifies the signature and reconciles the lines. The traveller wants to sleep on it. - Next morning, in a signed-out Session, the agent opens an enquiry: "Is the pool fenced? Our youngest is one." Host Rhea answers in 40 minutes, signed.
- The traveller says book it. The agent signs them in with
livstay:book,poppy:readandlivstay:share_trip_context. POST /livstay/v0/bookingswithqt_P8m1→202, operationbk_4Hn9proposed, calendar held 15 minutes,requirements: ["payment", "identity_verification"].- The agent checks the revision against the Quote, shows the
summary; the traveller approves. POST /poppy/operations/bk_4Hn9/confirm{ "revision": 1, "approved_by": "user" }→in_progress.GET /livstay/v0/bookings/completion/bk_4Hn9→complete_byin 60 minutes, two pending steps with page URLs.- The traveller verifies identity and pays INR 38,850.00 in their browser Session. The operation
→
succeeded;result.data.livstaycarriesbkg_4Hn9, the address and a Receipt. The feed showspayment_receivedwith its Receipt, thenbooked. - A week later the feed shows
message_waiting. The agent opens the booking conversation and reads Rhea's question about the airport pickup. - Twelve days before arrival the feed shows
payment_due; on the day,check_in_details; three days after check-out,deposit_releasedwith a Receipt.
Appendix B. How LivAround implements this draft
Informative. LivAround is the reference Host Platform.
| LivStay | LivAround |
|---|---|
| Host Platform issuer | stay.livaround.com, one OAuth server per operator domain |
| Home | Property (linked rooms are separate Homes; occupancy computed across the group) |
| Facts | Facts page on booking.livaround.com, host-reviewed |
| Terms | LivinbnbListingTerms money subset; decision_mode from decisionMode |
| Protection | Deposit ladder, evidence protocol, published claim rules; underwriter host in v1 |
| Enquiries, booking conversation | The Masked Relay; Host replies over WhatsApp |
auto_counter |
AUTO_COUNTER_WITHIN_BOUNDS with autoCounterFloorPct and autoCounterMoves |
| Tier credential | livscore guest credential |
| Fees | Flat 5% platform_fee line; agent_fee as its own line |
Appendix C. Open topics on the path to 1.0
- Conformance for Levels 2 and 3. An executable suite needs a test Personal Agent with a Poppy client identity, sign-in and DPoP. Planned alongside the reference implementation.
- Payments. Adopt Poppy's payment design when it exists (§11).
- Push. Booking events need Poppy push notifications; until then, the event feed with
wait. - Attachments. Receipts and check-in packs as Poppy attachments when defined.
- Multi-home stays. One booking across linked Homes (a villa and its cottage).
- Points and exchange. A member-authenticated extension for swaps and points, if ever.
- Search. Whether
GET /homesneeds a shared query vocabulary across Host Platforms. - Directory. How agents find Host Platforms beyond domains they already know.
- Governance. Moving the spec to a neutral home once there are independent implementers.
Appendix D. Changes from draft 0.1
Draft 0.2 is not compatible with 0.1. Both sides now say which they speak (§4.3).
- Access matches Poppy. Signed-out Poppy tokens carry no scopes, so homes, availability and Quotes
are public with no token, and enquiries need a signed-out Session with no scope.
livstay:enquireis removed. Levels 0 and 1 need no Poppy Session (§3.3, §5). - The agent opens the booking conversation. Poppy conversations are agent-started. Host and Host
Platform updates move to a booking event feed (§12.2); Host messages wait for the agent, announced
by
message_waiting(§12.3). - Completion replaces hold extension. Confirmed operations cannot be revised, so payment and
identity verification run against a fixed
complete_byin a completion record beside the operation (§9.5). Revisions no longer extend holds. - Wire format. JSON Schemas, an OpenAPI document, fixtures, a validator and a Level 0–1
conformance runner (§16). Signing is specified: ES256,
kid,typ, RFC 8785 payloads, verification steps, rotation and retention (§13.1–§13.4). - Revisions.
revisioninpoppy.json,revisionsin client metadata, and theLivStay-Revisionheader (§4.3). - Lifecycle.
- Each Home has an IANA
time_zone. - Money has fixed precision, rounding and reconciliation rules (§2.1). -guestsbecomesparty, with children's ages and pets (§2.2); Terms gain infant and pet rules. - Contact sharing and revocation have endpoints (§12.4). - Trip context is deleted after a failed, cancelled or expired proposal. - Receipts cover payments, refunds, deposits, claims and Host cancellations (§13.3). -tier_not_acceptedis removed: tier outcome is reported interms.livstay.tier, and a Quote made for a higher tier fails withtier_mismatch.