Skip to main content
Casa Layer’s public data model is the canonical guest context: what each source system said, resolved into one memory per guest. Everything below is exposed over both the REST API and the MCP server. List routes page results. See Pagination and Errors.

Tenancy

Data is organized under organization → property group → property. An organization (what users sign into) anchors exactly one property group — the highest-order data tenant. A property group owns properties: single operated entities like one hotel or venue. Every credential resolves to a property group. Send X-Property-Id to narrow one request to a property in that group. A property-pinned API key is already narrowed.

Entities

Guest profiles

GET /v1/guests · GET /v1/guests/{id} · reference A guest profile is one source system’s record of a guest — Mews’ view, Square’s view — kept exactly as ingested (names, contact details, preferences, tags), stamped with the integration_id it came from. The same real person appears once per source that knows them. Fetch one profile by its Casa Layer ID (GET /v1/guests/{id}). A miss is 404. Look up by PMS guest ID with source_id + integration_id on GET /v1/guests. That path returns a one-page list: one row, or data: [] when nothing matches. Some source systems do not expose a stable guest source_id. List integration IDs with GET /v1/integrations.

Master profiles

GET /v1/master-profiles · searchable by name, email, phone, exact sub-profile email, or Casa Layer reservation ID The golden record. Identity resolution merges guest profiles across source systems and properties into one master profile per real guest, with per-attribute provenance (attribute_sources records which system supplied each canonical field) and the full list of merged member sub-profiles (guest_profiles plus guest_profile_ids). Use email for an exact sub-profile email lookup. reservation_id is the Casa Layer reservation UUID (reservations.id), not the PMS reservation source_id. Resolve the PMS ID on GET /v1/reservations first, then send the returned id. A value that is not a UUID matches nothing. When you want “the guest”, not “what one system knows about the guest”, read these.

Reservations

GET /v1/reservations · GET /v1/reservations/{id} · filter by status, stay window, channel, guest, or source_id + integration_id Stays and bookings, linked to the guest profile that made them. Fetch one reservation by its Casa Layer ID (GET /v1/reservations/{id}). A miss is 404. Search by the source system’s reservation ID with source_id and the integration_id that owns it. That search is a keyset-paginated list. List integration IDs with GET /v1/integrations. Statuses are normalized to a canonical lifecycle (pending, confirmed, in_progress, completed, cancelled, declined, no_show) and channels to canonical categories (direct, ota, …) — the original vendor values remain in source_payload.

Transactions

GET /v1/transactions · filter by guest, reservation, consumption date, type Revenue lines shaped like Pace Transactions V2 — one row per source order item, with net_revenue and gross_revenue, optional reservation_id, and a canonical type (e.g. space, product). Guest spend is the sum of linked transactions.

Consents

GET /v1/consents · GET /v1/consents/{id} · filter by contact value/method, purpose, status, integration, observing guest Contact-grain marketing and compliance observations — one row per decisive source signal (granted, withdrawn, denied) for a contact method + value and purpose (email ≠ SMS). Default-unknown: no row means unknown / not eligible; connectors do not store coverage rows for vendor silence. Effective eligibility within a property group is the most restrictive status among matching stored observations.

Actions

GET /v1/actions Tracked guest actions — purchases, visits, requests — newest first, linked to a guest profile and property. Shape varies by source (name, type, status, requested_at, completed_at).

Guest event log

GET /v1/events · timeline or latest mode · filter by entity, guest profile, or master profile The append-only history of everything Casa Layer has observed: every change to every entity, in order, forever. timeline mode is keyset-paginated change history. latest mode returns one page — the latest delta slice per entity (next_cursor is null). The rollup alias is kept for compatibility and behaves the same. Filter to one sub-profile with guest_profile_id, or send master_profile_id to read history across clustered member sub-profiles. The event log does not accept a PMS source_id. Resolve the guest first, then send the Casa Layer ID. This is the replayable source of truth behind the canonical tables. See Retrieving the guest event log.

Reviews

GET /v1/reviews · GET /v1/reviews/summary Guest reviews from every connected channel (Booking.com, Google, …) with ratings, tags, and host responses. The summary endpoint aggregates them — average rating, rating distribution, per-channel breakdown, top positive/negative tags — for a scope and date window.

Loyalty

GET /v1/loyalty-programs · GET /v1/loyalty-members Loyalty program definitions and their memberships. Member search joins each membership with its guest (name, email) and program, ordered by points — filter by tier, program, or free-text guest match.

Channels & spaces

GET /v1/channels · GET /v1/spaces Reference resources: the booking channels reservations and reviews arrive through, and the bookable spaces (inventory types) they concern.

Integrations

GET /v1/integrations The mapping roster for your scope. Each row is one source system mapped to a property. id is the integration_id you send with source_id when you look up a guest or reservation. Public credentials (read:properties) see a thin row: id, provider, name, property_id, status, source_ref. The response is the full roster — it is not cursor-paginated. Connection credentials stay session-only.

Properties

GET /v1/properties The roster of properties in your group. The response is the full set ({ "data": [ … ] }) — it is not cursor-paginated. Use a property id as X-Property-Id to drill one request down.

Tables

GET /v1/tables · GET /v1/tables/{id} · GET /v1/tables/{id}/members · POST /v1/tables/preview · GET /v1/tables/{id}/filters · POST /v1/tables/{id}/preview · GET /v1/tables/{id}/runs · GET /v1/tables/{id}/destinations Group-scoped ops containers of master or guest members — static curation or lightweight attribute filters. Each table has a type: static (manual add/remove) or dynamic (membership from saved filters, updated when you click Save). member_type (master or guest) is fixed at create and never mixed within one table. Member responses use profile directory projections plus guest_profile_id, master_profile_id, and consents. id is the membership key. On a guest table it matches guest_profile_id. On a clustered master row it matches master_profile_id and guest_profile_id is null — load member guests from GET /v1/master-profiles/{id}. Member rows can still carry kind (clustered or singleton) — that field describes the profile row, not the table’s type. List, member, and preview routes use offset pagination, not keyset cursors. MCP read tools: tables_list, table_get, table_members (scope read:tables). Writes require write:tables on REST. Filter tooling: POST /v1/tables/preview tests an ad-hoc filter without saving it; /{id}/filters reads the saved filter, /{id}/preview previews its matches, /{id}/runs audits refreshes, and /{id}/destinations shows ESP bindings with consent-filter guidance. Tables are not marketing segments — campaign audiences live in downstream ESPs. See Creating a table of guests who have provided marketing consent.

What is not exposed

Integration connections, provider credentials, users, auth internals, and ad-hoc SQL are not part of the public data model. They are session-only, internal backend surfaces — personal API keys cannot read them. The public integration roster lists mappings only.