> For the complete documentation index, see [llms.txt](https://docs.customer-alliance.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.customer-alliance.com/guests.md).

# Guests

Push guests into a property (write-only; grants no read of guest data).

## Create or update a guest at a property

> Requires \`guests:write\`. Push a guest into the Guests section — for a CRM/PMS/booking integration to feed guests in. Write-only: this scope grants no read of guest data. Identity is (property, email), so a repeat push of the same email to the same property updates that guest (\`200\`) rather than creating a duplicate (\`201\` on first create). Include an optional \`reservation\` to upsert the guest's stay in the same call (identity (property, reservation.external\_id)), so you can build audiences on stay status, booking channel, and recently-ended stays. A guest erased under GDPR is never repopulated (\`409\`).

```json
{"openapi":"3.1.0","info":{"title":"Customer Alliance API","version":"4.0.0"},"tags":[{"name":"Guests","description":"Push guests into a property (write-only; grants no read of guest data)."}],"servers":[{"url":"https://app.customer-alliance.com/api/v4","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"An API key created in Settings → API (`ca_live_…`)."}},"schemas":{"GuestPushInput":{"type":"object","required":["email"],"description":"A guest to create or update at one property. Identity is (property, email): a repeat push of the same email to the same property updates that guest rather than creating a duplicate. Optionally include a `reservation` to upsert the guest's stay in the same call.","properties":{"property_id":{"type":"string","format":"uuid","description":"The property (leaf node) to attach the guest to. Optional when the key is bound to a single property; required when the key is bound to a group. Must be a property within the key's node scope."},"email":{"type":"string","format":"email","maxLength":320,"description":"Required — normalized (trimmed + lower-cased) and used as the dedupe key."},"salutation":{"type":"string","maxLength":50},"first_name":{"type":"string","maxLength":200},"last_name":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":40},"country_code":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 (e.g. DE)."},"language_code":{"type":"string","maxLength":5,"description":"e.g. de or de-DE."},"custom_fields":{"type":"object","additionalProperties":{"type":["string","number","boolean"]},"description":"Merged into the guest's custom fields; omitted keys are preserved."},"marketing_consent":{"type":"boolean","description":"When set, records invitation-email consent. An explicit prior withdrawal is never overridden by a grant (GDPR)."},"reservation":{"$ref":"#/components/schemas/ReservationInput"}}},"ReservationInput":{"type":"object","required":["external_id"],"description":"A reservation (stay) to create or update for the guest, mirroring what a PMS integration syncs. Identity is (property, external_id): a repeat push of the same external_id updates the stay in place (e.g. a status change from booked to checked_out) rather than creating a duplicate. Pushing stays lets you build audiences on stay status, booking channel, and recently-ended stays.","properties":{"external_id":{"type":"string","minLength":1,"maxLength":255,"description":"Required — your booking reference; the stay's dedupe key."},"status":{"type":"string","enum":["booked","arrived","checked_out","cancelled","no_show"],"description":"Canonical stay status. Defaults to `booked` when omitted."},"raw_status":{"type":"string","maxLength":120,"description":"Your own status label, kept verbatim for display."},"check_in":{"type":"string","format":"date-time","description":"Stay start. ISO-8601 date or date-time."},"check_out":{"type":"string","format":"date-time","description":"Stay end. ISO-8601 date or date-time — drives the recently-ended-stay audience filter."},"checked_in_at":{"type":"string","format":"date-time"},"checked_out_at":{"type":"string","format":"date-time"},"channel":{"type":"string","maxLength":120,"description":"Booking origin, e.g. booking.com."},"custom_fields":{"type":"object","additionalProperties":{"type":["string","number","boolean"]},"description":"Merged into the stay's custom fields; omitted keys are preserved."}}},"Guest":{"type":"object","description":"A guest record as stored. `source` is `api` for guests created through this endpoint.","properties":{"id":{"type":"string","format":"uuid"},"property_id":{"type":"string","format":"uuid"},"source":{"type":"string"},"email":{"type":["string","null"]},"salutation":{"type":["string","null"]},"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"phone":{"type":["string","null"]},"country_code":{"type":["string","null"]},"language_code":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"reservation":{"$ref":"#/components/schemas/Reservation","description":"Present only when the push included a reservation."}}},"Reservation":{"type":"object","description":"A reservation (stay) as stored. Returned inside a `Guest` when the push included a `reservation`.","properties":{"id":{"type":"string","format":"uuid"},"external_id":{"type":"string"},"status":{"type":"string","enum":["booked","arrived","checked_out","cancelled","no_show"]},"raw_status":{"type":["string","null"]},"check_in":{"type":["string","null"],"format":"date-time"},"check_out":{"type":["string","null"],"format":"date-time"},"checked_in_at":{"type":["string","null"],"format":"date-time"},"checked_out_at":{"type":["string","null"],"format":"date-time"},"channel":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}},"responses":{"BadRequest":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks the required scope, or the account's plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/guests":{"post":{"summary":"Create or update a guest at a property","description":"Requires `guests:write`. Push a guest into the Guests section — for a CRM/PMS/booking integration to feed guests in. Write-only: this scope grants no read of guest data. Identity is (property, email), so a repeat push of the same email to the same property updates that guest (`200`) rather than creating a duplicate (`201` on first create). Include an optional `reservation` to upsert the guest's stay in the same call (identity (property, reservation.external_id)), so you can build audiences on stay status, booking channel, and recently-ended stays. A guest erased under GDPR is never repopulated (`409`).","operationId":"pushGuest","tags":["Guests"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestPushInput"}}}},"responses":{"200":{"description":"Existing guest updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Guest"}}}},"201":{"description":"Guest created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Guest"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"The guest has been erased (GDPR) and can no longer be updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"property_id is missing, out of scope, or not a property (leaf) node.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.customer-alliance.com/guests.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
