hospitable-api

SKILL.md

Hospitable.com Public API (v2) Skill

πŸ“ Source Files β€” Read These First

Before writing any code, the agent MUST read the following files:

File Purpose
types.ts All TypeScript interfaces and enums for API entities
client.ts Ready-to-use typed HTTP client β€” always use this, never raw fetch()

Agent rules:

  • Do NOT redefine types. Import from types.ts.
  • Do NOT write custom fetch wrappers. Use client.ts.
  • Do NOT manually implement pagination loops. Use the built-in listAll() helpers.
  • Always pass crypto.randomUUID() as the idempotencyKey for all POST/PATCH/PUT calls.

πŸ”‘ Authentication & Setup

  • Base URL: https://public.api.hospitable.com/v2
  • Auth: OAuth 2.0 or Personal Access Token (PAT)
  • Required headers on every request:
    Authorization: Bearer <ACCESS_TOKEN>
    Accept: application/json
    Content-Type: application/json
    
  • Environment variable: HOSPITABLE_ACCESS_TOKEN in .env
  • Timezone: All dates are UTC / ISO 8601
  • IDs: All entity IDs are UUIDs, not integers

πŸ“¦ Pagination

All list endpoints return:

{
  "data": [...],
  "meta": { "current_page": 1, "last_page": 5, "per_page": 25, "total": 98 },
  "links": { "next": "https://...", "prev": null }
}
  • Always read from .data β€” never assume a flat array
  • Use listAll() helpers in client.ts to auto-paginate (per_page=100)
  • Single-resource endpoints return { "data": { ... } }

See types.ts β†’ PaginatedResponse<T>, SingleResponse<T>


πŸ”— Including Related Resources (?include=)

Embed related entities in one call to avoid N+1 requests.

Resource Available includes
properties listings, images, tags, user
reservations guest, listing, property, checkins, transactions, conversation, financials
inquiries property, guest
conversations guest, reservation
reviews reservation, guest

client.ts methods accept an include: string[] parameter β€” pass the desired relationships.


πŸ›‘ Rate Limits & Idempotency

Rate Limiting β€” inspect response headers:

  • X-RateLimit-Remaining: requests left; stop if 0
  • X-RateLimit-Reset: Unix timestamp when window resets
  • Client throws automatically on 429

Idempotency β€” required on all POST, PATCH, PUT:

import { randomUUID } from "crypto";
client.reservations.create(payload, randomUUID());

πŸ› οΈ Complete Resource Reference

1. User

See types.ts β†’ User, BillingInfo

GET /v2/user?include=billing    β†’ SingleResponse<User>

2. Properties

See types.ts β†’ Property, Address, Capacity, PropertyImage, Listing, Quote

GET  /v2/properties                          β†’ PaginatedResponse<Property>
GET  /v2/properties/search?q=...             β†’ PaginatedResponse<Property>
GET  /v2/properties/{uuid}                   β†’ SingleResponse<Property>
GET  /v2/properties/{uuid}/images            β†’ PaginatedResponse<PropertyImage>
GET  /v2/properties/{uuid}/quote?check_in=...&check_out=...&guests=...
POST /v2/properties/{uuid}/tags              body: { tag: string }

3. Calendar

See types.ts β†’ CalendarDay, CalendarUpdateItem, CalendarDayStatus

GET   /v2/properties/{uuid}/calendar?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD
PATCH /v2/properties/{uuid}/calendar         body: { days: CalendarUpdateItem[] }

Per-day fields: status (available|unavailable|booked), price, min_stay, check_in_allowed, check_out_allowed

4. Inquiries

Pre-booking inquiries. See types.ts β†’ Inquiry, InquiryStatus

GET  /v2/inquiries               β†’ PaginatedResponse<Inquiry>
GET  /v2/inquiries/{uuid}        β†’ SingleResponse<Inquiry>
POST /v2/inquiries/{uuid}/messages

Statuses: pending, pre_approved, declined, expired, withdrawn

5. Reservations

See types.ts β†’ Reservation, ReservationStatus, Platform, GuestCounts, ReservationFinancials

GET    /v2/reservations               β†’ PaginatedResponse<Reservation>
GET    /v2/reservations/{uuid}        β†’ SingleResponse<Reservation>
POST   /v2/reservations               Create manual reservation
PATCH  /v2/reservations/{uuid}        Update manual reservation
POST   /v2/reservations/{uuid}/cancel Cancel manual reservation

Status lifecycle: inquiry β†’ request β†’ pending verification β†’ accepted (may also become: cancelled, declined, withdrawn, expired, checkpoint, request for payment)

Financials (use ?include=financials): accommodation, cleaning_fee, linen_fee, management_fee, resort_fee, pet_fee, pass_through_taxes, other_fees[]{amount,label}

Common list filters: property_id, status, check_in_start, check_in_end

6. Messaging

See types.ts β†’ Message, MessageAttachment, SendMessagePayload

GET  /v2/reservations/{uuid}/messages    β†’ PaginatedResponse<Message>
POST /v2/reservations/{uuid}/messages    body: { body: string }
POST /v2/inquiries/{uuid}/messages       body: { body: string }

Message fields: body, content_type, sender_type (host/guest/system), attachments[]{type, url}

7. Conversations (Inbox)

See types.ts β†’ Conversation

GET /v2/conversations    β†’ PaginatedResponse<Conversation>

Note: To send messages, use reservation or inquiry messaging endpoints above.

8. Transactions

Booking-level payment events. See types.ts β†’ Transaction

GET /v2/transactions          β†’ PaginatedResponse<Transaction>
GET /v2/transactions/{uuid}   β†’ SingleResponse<Transaction>

9. Payouts

Host disbursements. See types.ts β†’ Payout

GET /v2/payouts          β†’ PaginatedResponse<Payout>
GET /v2/payouts/{uuid}   β†’ SingleResponse<Payout>

10. Reviews

See types.ts β†’ Review, ReviewRatings, ReviewPublic, ReviewPrivate, RespondToReviewPayload

GET  /v2/reviews               β†’ PaginatedResponse<Review>
GET  /v2/reviews/{uuid}        β†’ SingleResponse<Review>
POST /v2/reviews/{uuid}/response   body: { response: string }

Ratings (1–5): cleanliness, communication, check_in, accuracy, location, value

11. Enrichable Shortcodes

Dynamic placeholders for automated messages (e.g. {{wifi_password}}). See types.ts β†’ EnrichableShortcode, SetEnrichableShortcodePayload

GET /v2/shortcodes                 β†’ PaginatedResponse<EnrichableShortcode>
GET /v2/shortcodes/{key}           β†’ SingleResponse<EnrichableShortcode>
PUT /v2/shortcodes/{key}           body: { value: string }

Pass ?property_id={uuid} to scope to a specific property; omit for account-level default.


πŸͺ Webhooks

All webhook topics in WebhookTopic:

Topic Triggered when
reservation.created New booking received
reservation.changed Booking details updated
reservation.status_changed Status transition (e.g. pending β†’ accepted)
message.create New message from host or guest
message.updated Message edited
inquiry.created New pre-booking inquiry
review.created Guest leaves a review
property.created New property added
property.deleted Property removed
property.merged Two properties merged into one
integration.disconnected OTA channel integration broke

Envelope (types.ts β†’ WebhookEvent<T>):

{ "id": "<ULID>", "action": "reservation.status_changed", "data": {...}, "version": "1.0", "created": "..." }

Security (types.ts β†’ WebhookSecurityInfo):

  • Verify the Signature header on every request
  • Whitelist inbound IP 38.80.170.0/24

πŸ‘¨β€πŸ’» Workflow Examples

List all accepted reservations with financials

import { createClient } from "./client";
const client = createClient(process.env.HOSPITABLE_ACCESS_TOKEN!);

const reservations = await client.reservations.listAll({
  status: "accepted",
  include: "guest,financials",
});
for (const r of reservations) {
  console.log(r.guest?.first_name, r.financials?.total, r.financials?.currency);
}

Create a manual reservation

import { createClient } from "./client";
import { randomUUID } from "crypto";
const client = createClient(process.env.HOSPITABLE_ACCESS_TOKEN!);

const { data } = await client.reservations.create(
  {
    property_id: "<uuid>",
    check_in: "2025-07-01",
    check_out: "2025-07-07",
    guests_count: 2,
    guest: { first_name: "Jane", last_name: "Doe", email: "jane@example.com" },
    total_price: 1200,
    currency: "USD",
  },
  randomUUID(),
);
console.log(data.id, data.status); // β†’ "accepted"

Block calendar dates

await client.calendar.update(
  "<property-uuid>",
  [
    { date: "2025-12-24", status: "unavailable" },
    { date: "2025-12-25", status: "unavailable" },
  ],
  randomUUID(),
);

Respond to a guest review

await client.reviews.respond(
  "<review-uuid>",
  {
    response:
      "Thank you for your kind words! We look forward to hosting you again.",
  },
  randomUUID(),
);

Set a property-level WiFi shortcode

await client.shortcodes.set(
  "wifi_password",
  { value: "BeachHouse2025!" },
  randomUUID(),
  "<property-uuid>",
);

Handle incoming webhook

import type { WebhookEvent, Reservation } from "./types";

async function handleWebhook(event: WebhookEvent<Reservation>) {
  if (event.action === "reservation.status_changed") {
    console.log("Status changed:", event.data.id, "β†’", event.data.status);
  }
  if (event.action === "property.merged") {
    // event.data contains the surviving property
  }
}

πŸ”— Official Documentation

Weekly Installs
1
First Seen
Mar 2, 2026
Installed on
windsurf1
amp1
cline1
trae1
opencode1
cursor1