# How reservation and guest data flows
> Source: /guides/reservation-and-guest-data-flow
> From a PMS booking to a member's points balance: ingestion, linking, escrow and notifications.

Knowing this pipeline answers most "why can't I see it yet" and "why didn't
that earn" questions, and it explains why polling the API faster does not help.

## The pipeline [#the-pipeline]

<Steps>
  <Step title="Your PMS to object storage">
    Journey collects from your property management system two ways: **scheduled
    pulls** run as jobs against the PMS API, and **PMS webhooks** push changes
    as they happen. Which applies depends on the PMS.

    Either way the raw records land as newline-delimited JSON in Google Cloud
    Storage, partitioned by chain, date and time. Files are written
    create-only, so a replay cannot overwrite history.
  </Step>
  <Step title="Staging and modelling with dbt in BigQuery">
    BigQuery external tables read those files in place. dbt models stage each
    PMS into a common shape and then union them into a single reservations
    mart.

    Staging uses a lambda pattern: a small view over the most recent files for
    freshness, and a historical table behind it. Rows are deduplicated to the
    latest version of each PMS record.
  </Step>
  <Step title="Import into Core">
    A scheduled job reads the reservations mart and upserts into Core's
    operational database, keyed on the PMS reservation id together with the
    host. It runs on a multi-hour cycle with an overlapping lookback window, so
    a late-arriving record is picked up on a subsequent pass.

    The import is idempotent — re-running it does not duplicate reservations.
  </Step>
  <Step title="Downstream processing">
    Further scheduled jobs handle guest processing, arrival evaluation for the
    front-desk feed, and upkeep: retrying orphaned records, converting pending
    points to valid after checkout, and expiring escrow.
  </Step>
</Steps>

<Note>
  **This is why the Partner API refreshes on a multi-hour rhythm.** The API
  reads Core, and Core is fed by a batch import. Polling
  `GET /v1/partner/reservations` every minute returns the same rows as polling
  it every few hours.
</Note>

## What travels with a reservation [#what-travels-with-a-reservation]

The import carries a wide record, including identifiers (reservation id,
parent reservation, PMS guest id and a hashed form, host, PMS, property,
confirmation code), booking details (booked-at, channel and channel type,
status, check-in and check-out), guest details (name, email, phone, party
counts, rooms, notes, promotion code), money (charges, fees, discounts, taxes,
payments, refunds, commissions, totals and currency) and processing
timestamps.

**Channel classification matters.** Each booking carries a channel type of
`direct`, `ota` or `other`, and that single field decides how the stay earns.

## How a stay earns [#how-a-stay-earns]

### The gates [#the-gates]

A stay only earns when all of these hold:

- the guest identity is linked to a member;
- the booking status is confirmed;
- the property exists and is eligible;
- the price is at least US$1;
- the stay falls after the property's go-live date.

The earn base is the **net price** — net of taxes and fees — floored to a whole
number.

### Award contexts [#award-contexts]

| Context | Meaning |
|---|---|
| `UNCLAIMED` | No member linked. Base points are held in escrow. |
| `MEMBER_DIRECT` | Member, direct booking. Earns. |
| `MEMBER_INDIRECT` | Member, OTA or other channel. Does not earn; escrow is voided. |
| `MEMBER_ENROLLED` | Member joined after the stay. One-time signup bonus. |
| `MEMBER_INELIGIBLE` | Member or property is ineligible. |

The rate itself resolves through layered configuration, where the **most
specific configured layer wins even when it is lower** — see
[program configuration](/guides/patterns/program-configuration).

## Escrow and linking [#escrow-and-linking]

<Steps>
  <Step title="Unlinked stays go to escrow">
    Points for a stay with no linked member are held rather than discarded.
  </Step>
  <Step title="Checkout converts pending to valid">
    A couple of days after checkout, pending points are burned and valid points
    are minted. Cancelling a stay reverses the award.
  </Step>
  <Step title="The claim window closes">
    Escrowed points for an unlinked stay expire after **30 days**. This is the
    deadline that makes [guest invites](/guides/patterns/guest-invites) worth
    sending early.
  </Step>
</Steps>

### How linking decides [#how-linking-decides]

The reservation's guest email or phone is matched against the member's
**verified** contacts, and then the name is compared with fuzzy matching. The
outcome is an automatic link, a queue for human review (name mismatch, or
phone and email pointing at different members), or no link at all.

Linking is re-evaluated at import, when a member verifies a new contact, when a
guest follows a claim link or claims by id, and when an administrator resolves
a queued case — so a stay can link well after it happened.

## Notifications [#notifications]

Partners do not configure member messaging. Journey sends member-facing
notifications; lifecycle and bulk messaging are driven from Journey's own
stack rather than from the Partner API.

The exception you control directly is the **guest invite**, which sends a
transactional email or SMS at your request. SMS links are short-lived.

## What you can see through the API [#what-you-can-see-through-the-api]

- `GET /v1/partner/reservations` — reservations at your venues, filtered and
  cursor-paginated. See [roster sync](/guides/patterns/roster-sync).
- `GET /v1/partner/reservations/{externalReservationId}` — a single stay.
- `GET /v1/partner/guests` — a roster derived from reservations. It reflects
  the guests on your stays; it is not identity resolution.
- `GET /v1/partner/arrivals` — the front-desk feed of owed benefits.

Everything is bounded to your organization and venue reach, and guest contact
details are masked unless your credential carries the guest PII permission.
