Kiosk Integration API
Weave Living · Microsoft Dynamics 365 CRM · Korea (Seoul) properties · v1.0
Engineers building the self check-in / check-out kiosk software for Weave Living's Korea properties. It describes every CRM endpoint the kiosk needs to call, in the order the guest journey uses them.
Introduction #
This guide documents the REST endpoints the kiosk uses to talk directly to Weave Living's Microsoft Dynamics 365 (Dataverse) CRM. The CRM holds the property booking, the guests on that booking, the room and room-readiness state, and the guests' identification records.
There is no intermediate middleware for the CRM calls: the kiosk authenticates against Microsoft Entra ID, receives an access token, and calls the Dataverse Web API directly. The API is standard OData v4, so all the usual $select, $filter, $expand and $top query options are available.
What the kiosk is expected to do
Scope
This revision is scoped to the Korea (Seoul) properties. The majority of Korea guests are international visitors, so passport capture is the primary identification path and the documentation is written around it. Korean domestic documents are supported too — see Korea / Seoul Notes.
Card payment and invoice handling are a separate phase and are not documented here. The check-in and check-out flows below show where payment sits so the screen sequence makes sense, but no payment endpoints are included in this revision.
Three things in this API are easy to get wrong and will silently corrupt data: the *timezoneindependent date fields, the way lookup fields are written on PATCH, and URL-encoding of + in e-mail addresses. They are all covered in Conventions & Gotchas.
Environments & Base URLs #
Two environments are provided. Both host the same Korea properties — only the hostname changes between them. Build against UAT first; production access is granted once UAT sign-off is complete.
| Environment | CRM base URL | Purpose |
|---|---|---|
| UAT | https://weaveuat.crm5.dynamics.com | Integration and acceptance testing. Safe to write to. |
| Production | https://weaveliving.crm5.dynamics.com | Live. Only after UAT sign-off. |
All Dataverse calls in this document are relative to that base, e.g.
https://weaveuat.crm5.dynamics.com/api/data/v9.2/acwapp_propertybookings
Use /api/data/v9.2/ on every request. It is the current version and the only one you should target.
Authentication #
Dataverse is called with an OAuth 2.0 client-credentials bearer token issued by Microsoft Entra ID. There is no user sign-in; the kiosk authenticates as an application.
The tenantId, clientId and clientSecret for the kiosk application registration are supplied to you out of band by Weave Living — they are deliberately not reproduced in this document. Ask your Weave Living contact if you have not received them.
Get Access Token
The token is valid for 3,599 seconds (about one hour). Cache it and refresh it before it expires — do not request a new token on every call. A simple approach is to store the token alongside its expiry timestamp and renew when fewer than five minutes remain.
Request headers
| Header | Value |
|---|---|
Content-Type | application/x-www-form-urlencoded |
Request body (form-urlencoded)
| Field | Required | Value |
|---|---|---|
grant_type | Required | client_credentials |
client_id | Required | {{clientId}} — supplied by Weave Living |
client_secret | Required | {{clientSecret}} — supplied by Weave Living |
resource | Required | https://weaveuat.crm5.dynamics.com (UAT)https://weaveliving.crm5.dynamics.com (Production) |
curl -X POST "https://login.windows.net/{{tenantId}}/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id={{clientId}}" \
-d "client_secret={{clientSecret}}" \
-d "resource=https://weaveuat.crm5.dynamics.com"
{
"token_type": "Bearer",
"expires_in": "3599",
"ext_expires_in": "3599",
"expires_on": "1768282179",
"not_before": "1768278279",
"resource": "https://weaveuat.crm5.dynamics.com",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
}
Using the token
Send the token on every subsequent CRM request:
Authorization: Bearer {{access_token}}
Content-Type: application/json; odata.metadata=minimal
Bearer
Send Authorization: Bearer eyJ0eXAi... — not the raw token on its own. The Bearer prefix is part of the header value and is required.
{
"error": {
"code": "0x80048306",
"message": "ValidateAccessToken: Invalid or expired token."
}
}
Conventions & Gotchas #
Date and time fields
Dataverse exposes two flavours of date column. This integration uses the timezone-independent columns, which are all suffixed timezoneindependent.
Z suffix
This is counter-intuitive but it is how the existing integration works and it must be preserved. For a 15:30 check-in at a Seoul property, send "2026-01-12T15:30:00.000Z" — the 15:30 is the local Seoul time, and the trailing Z is required by the payload format but does not mean the value has been converted to UTC.
For Korea, use UTC+9 (Asia/Seoul). Do not convert to UTC. Singapore properties use UTC+8.
Fields you will write:
| Field | Entity | Meaning |
|---|---|---|
acwapp_actualstaystartdatetimezoneindependent | Booking Occupant | Actual stay start, local wall clock |
acwapp_actualstayenddatetimezoneindependent | Booking Occupant | Actual stay end, local wall clock |
acwapp_checkintimetimezoneindependent | Booking Occupant | Check-in date & time, local wall clock |
acwapp_checkouttimetimezoneindependent | Booking Occupant | Check-out date & time, local wall clock |
Lookup (relationship) fields
Lookups are read as _<logicalname>_value and written using the @odata.bind annotation. On write you must use the schema name with its original capitalisation, and the value is the singular collection path plus the record GUID in brackets.
{
"acwapp_Nationality@odata.bind": "/acwapp_nationalities(52ba15a5-3796-ed11-aad1-000d3a85c990)",
"acwapp_PropertyBooking@odata.bind": "/acwapp_propertybookings(dbc67724-351c-f111-8342-70a8a50132c4)"
}
| Entity | Read as | Write as |
|---|---|---|
| Property Booking | _acwapp_propertybooking_value | acwapp_PropertyBooking@odata.bind |
| Booking Occupant | _acwapp_primarybookingoccupant_value | acwapp_Primarybookingoccupant@odata.bind |
| Bedroom Offer | _acwapp_bedroomoffer_value | acwapp_BedroomOffer@odata.bind |
| Room Type | _acwapp_roomtype_value | acwapp_RoomType@odata.bind |
| Room | _acwapp_room_value | acwapp_Room@odata.bind |
| Property Unit | _acwapp_propertyunit_value | acwapp_PropertyUnit@odata.bind |
| Contact | _acwapp_contact_value | acwapp_Contact@odata.bind |
| Nationality | _acwapp_nationality_value | acwapp_Nationality@odata.bind |
| Current Living Location | _acwapp_currentlivinglocation_value | acwapp_CurrentLivingLocation@odata.bind |
| Country | _acwapp_countrylookup_value | acwapp_CountryLookup@odata.bind |
In this data model a "Room" record is an acwapp_propertyunit record. acwapp_Room@odata.bind therefore points at /acwapp_propertyunits(...), which looks wrong but is correct. acwapp_propertyunit is the apartment/unit record; acwapp_room is the lettable room inside it.
Escaping in filters
- Single quotes in a filter value must be doubled:
O'Brienbecomes'O''Brien'. +in e-mail addresses must be percent-encoded as%2B, otherwise it is decoded as a space and the lookup silently fails.occto888+steve@gmail.combecomesoccto888%2Bsteve@gmail.com.- Use straight quotes (
') in filters. Copy-pasting from a word processor can substitute curly quotes (‘ ’) and the query will fail. - Query options are separated by
&and must be percent-encoded in the request line where your HTTP client requires it.
Response envelope
Collection queries return a value array. A query that matches nothing still returns 200 OK with an empty value array — it does not return 404. Always check the array length rather than relying on the status code.
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_propertybookings",
"value": []
}
Useful request headers
| Header | Purpose |
|---|---|
Content-Type: application/json; odata.metadata=minimal | Use on every request. Keeps the response payload small. |
Prefer: return=representation | Optional. On POST/PATCH, returns the created or updated record instead of 204 No Content. |
If-Match: * | Optional. Recommended on PATCH to bypass optimistic-concurrency checks when you have not read an @odata.etag. |
OData-MaxVersion: 4.0 / OData-Version: 4.0 | Recommended if your HTTP client is strict about OData versions. |
Error Handling #
Dataverse uses standard HTTP status codes. The body carries a code and a human-readable message.
| Status | Meaning | What the kiosk should do |
|---|---|---|
200 | Success. May still contain an empty value array. | Check the array length, not just the status. |
201 | Created. | Read the new record id from the response. |
204 | No content. Typical for a successful PATCH. | Treat as success. |
400 | Bad request — malformed body, unknown field, or a business rule rejected the write. | Show a friendly message; do not retry unchanged. |
401 | Token missing, expired or wrong resource. | Refresh the token and retry once. |
403 | Authenticated but not permitted. | Log and alert; do not retry. |
404 | Record or endpoint does not exist. | Treat as not found. |
429 | Too many requests. | Back off exponentially and retry. |
500 | Server error. | Retry with backoff, then surface an error. |
Error response format
{
"error": {
"code": "0x80040265",
"message": "BookingOccupantCheckExceedLimit of Error Message: \r\n \t Max Occupancy Met: Max Occupancy: 2, Booking Occupant Count: 3"
}
}
Retry only 429 and 5xx, and only for idempotent calls (GET). Never blind-retry a POST that creates a Booking Occupant — you will create duplicates. Re-query first (see List Booking Occupants) and only then create.
Korea / Seoul Notes #
Everything below is specific to the Korea properties and must be handled by the kiosk.
Identification — passports first
Most Korea guests are international visitors arriving with a passport. The passport is the primary identification document and the primary way a guest finds their booking.
- Scanning a passport yields the guest's full name (and usually passport number, nationality and date of birth). Use the name to locate the booking — see Get Booking Data and Get Booking Data via View.
- Store the passport number in
acwapp_idnumberon the Contact, and set the identification type toPassport(557130000) — see Get or Update Identification Type. - Nationality is a lookup on the Contact (
_acwapp_nationality_value) resolved through Get Nationality. - Passport expiry is not stored. There is no field for it, and one is not required. Read the date if you need it for your own validation, but do not attempt to send it.
- Passport data may be captured for guests of any age, including under-18s.
Accepted identification types
The acwapp_identificationtype option set supports these values. The ones you are most likely to use at a Korea kiosk are marked.
| Value | Label | Notes |
|---|---|---|
557130000 | Passport | Primary Use for all international guests. |
557130001 | HKID | Hong Kong |
557130002 | Two-way Permit (CN) | Mainland China |
557130003 | National Registration (SG) | Singapore |
557130004 | Individual Number (JP) | Japan — My Number |
557130005 | Resident Registration (JP) | Japan |
557130006 | Resident Registration (KR) | Korea 주민등록증 |
557130007 | Driver's License (SG) | Singapore |
557130008 | Driver's License (JP) | Japan |
557130009 | Driver's License (KR) | Korea 운전면허증 |
557130010 | Business Registration (KR) | Korea 사업자등록증 — corporate stays |
There is no separate option for a Korean Alien Registration Card (외국인등록증). Use one of the existing values above — in practice Passport for international guests.
Time zone
Korea is UTC+9 (Asia/Seoul) with no daylight saving. All *timezoneindependent values must be written as Seoul local wall-clock time — see Conventions. The check-out date check compares the guest's acwapp_contractenddatetimezoneindependent value directly; there is no separate business-date or cut-off rule to apply.
Currency and tax
Korea amounts are in KRW and are held in the CRM as VAT inclusive. When you display an amount, read the currency from the expanded transactioncurrencyid rather than assuming — see Get Booking Data. Do not apply your own tax calculation; display the value the CRM returns.
Telephone numbers
Korean mobile numbers are 010-0000-1111. Store the country calling code 82 in acwapp_countrycallingcode and the local subscriber number in telephone1. The kiosk's "phone number, last 4 digits" lookup key is matched against the stored number.
Language
The acwapp_language option set is used for acwapp_preferredlanguage. For a Korea kiosk the two values you will set most often are 557130003 (Korean) and 557130000 (English). Full list in Option Sets.
Room readiness must be checked before check-in
Before letting a guest complete check-in, call Get Room Status and confirm the room is Vacant Clean or Vacant Inspected. If it is not, stop the flow and ask the guest to speak to a member of staff. This check is required and is easy to overlook.
Check-in Flow #
This maps each kiosk screen to the calls it needs. Screen names are the ones used in the kiosk designs.
Reserved guest
| Kiosk screen | What happens | API calls |
|---|---|---|
| Idle → Language → Main menu | Guest taps CHECK-IN. | — |
| Search Reservation By Typing / Passport / Mobile App (QR) |
Guest identifies their booking. Passport scan is the usual path for international guests. |
Get Booking Data — by acwapp_contractno, wvlv_ota_booking_id, or guest nameGet Booking Data via View — alternative single-call lookup Get Contact by Email — when matching on e-mail Check for an Existing Contact — duplicate check |
| Guest Information | Show and confirm guest name, phone, e-mail, nationality, passport number, stay dates, room type, guest count, total amount. Guest may edit name / phone / e-mail. If no room is assigned, the guest selects one. |
List Booking Occupants — the people on the booking Get Contact Details — the contact record Get Nationality — resolve nationality name Get Room Type — selling name & max occupancy Update Contact Details — save edits Get or Update Identification Type — write passport / ID type Create Booking Occupant / Create Booking Occupant (Existing Contact) — add an accompanying guest Update Purpose of Stay & Update Hear About Us |
| Room readiness (before allowing check-in) |
Confirm the room is Vacant Clean or Vacant Inspected. If not, stop and refer the guest to staff. | Get Room Status |
| Payment Phase 2 |
Show the amount due; prepaid guests skip this step. Out of scope for this phase — no endpoints are documented here. | Phase 2 |
| Receive Card Key | Guest chooses quantity, key is dispensed. | — (kiosk / door-lock hardware) |
| Check-in complete | Record the check-in, save any request the guest left for the Operations Team, upload the signed registration card and selfie, trigger the unit guide e-mail. |
Check In — write the check-in timestamp Add Comment — the guest's request for Operations Send Unit Guide Email Upload Registration Card Upload Check-in Selfie |
The whole reserved-guest flow below is in scope, including the registration card, selfie and Unit Guide e-mail steps.
Two of those steps need a secret supplied directly by your Weave Living contact — the blob storage SAS credentials for Upload Registration Card and Upload Check-in Selfie, and the Unit Guide workflow URL for Send Unit Guide Email. Ask your Weave Living contact if you have not received them. Everything else can be built immediately.
Walk-in guest
Deferred Out of scope for the first release.
The walk-in branch reuses the same endpoints. The differences are that no booking exists yet, the guest's details are captured from scratch, and a room must be chosen from available inventory. No endpoints exist today for walk-in room availability or for creating a Property Booking from the kiosk — see Scope & Open Items.
Check-out Flow #
| Kiosk screen | What happens | API calls |
|---|---|---|
| Select room key type | Guest chooses Card key or Mobile App (QR code). | — |
| Return card key | The returned card identifies the room. Confirm today is the departure date; if not, stop and refer to staff. If more than one card was issued, collect them all. |
Get Booking Data — resolve the booking List Booking Occupants — confirm the occupant Get Stay Type — short vs long stay |
| Payment only if a balance is due · Phase 2 |
Show room number and outstanding total, VAT inclusive. Out of scope for this phase. | Phase 2 |
| Checkout complete | Write the check-out timestamp. | Check Out — write the check-out timestamp |
GETGet Booking Data #
The first call in almost every flow. It returns the Property Booking plus the identifiers of everything hanging off it — the room type, the room, the bedroom offer (contract), the lead, and the contract start/end dates.
The booking number has the format PB-251126-001, which is also what a booking QR code contains. Bookings that originated on an OTA carry a separate wvlv_ota_booking_id.
Query parameters
| Parameter | Required | Description |
|---|---|---|
$top | Optional | Use 1 for a single-booking lookup. |
$filter | Required | Filter on the booking number, the OTA id, or both. |
$select | Recommended | The fields listed below. Trimming the projection keeps the response small. |
$expand | Recommended | transactioncurrencyid($select=isocurrencycode) to read the currency. |
Filter by booking number
$filter=acwapp_contractno eq 'PB-251126-001'
Filter by OTA booking id
$filter=wvlv_ota_booking_id eq 'E54545442342'
Search both in one call
$filter=contains(acwapp_contractno,'PB-260413-021') or contains(wvlv_ota_booking_id,'E54545442342')
Full example
curl -X GET "https://weaveuat.crm5.dynamics.com/api/data/v9.2/acwapp_propertybookings?\$top=1&\$filter=acwapp_contractno%20eq%20'PB-251126-001'&\$select=elpis_stayduration,acwapp_propertybookingid,_acwapp_lead_value,_acwapp_propertyunit_value,_acwapp_roomtype_value,_acwapp_room_value,_acwapp_bedroomoffer_value,acwapp_contractstartdatetimezoneindependent,acwapp_contractenddatetimezoneindependent,elpis_hotelcollect,acwapp_dailyrentincludegst&\$expand=transactioncurrencyid(\$select=isocurrencycode)" \
-H "Content-Type: application/json; odata.metadata=minimal" \
-H "Authorization: Bearer {{access_token}}"
Fields returned
| Field | Type | Meaning |
|---|---|---|
acwapp_propertybookingid | GUID | Primary key of the booking. Needed by almost every later call. |
acwapp_contractno | string | Human-readable booking number, e.g. PB-251126-001. |
wvlv_ota_booking_id | string | OTA booking reference, when the booking came from a channel. |
_acwapp_lead_value | GUID | Lead record. Required for Update Purpose of Stay and Update Hear About Us. |
_acwapp_roomtype_value | GUID | Room type → Get Room Type. |
_acwapp_room_value | GUID | Room (a acwapp_propertyunit record) → Get Room Status. |
_acwapp_propertyunit_value | GUID | Apartment / unit record. |
_acwapp_bedroomoffer_value | GUID | Bedroom offer (contract line). |
acwapp_contractstartdatetimezoneindependent | datetime | Contract start (local wall clock). |
acwapp_contractenddatetimezoneindependent | datetime | Contract end (local wall clock). |
elpis_stayduration | boolean | false = Short Stay, true = Long Stay. See Get Stay Type. |
elpis_hotelcollect | boolean | When true, payment is collected at the property. Used by the Phase 2 payment work; no action needed in this phase. |
acwapp_dailyrentincludegst | money | Daily rent, tax inclusive. |
transactioncurrencyid.isocurrencycode | string | Currency, e.g. KRW. Read it; do not assume. |
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_propertybookings(...)",
"value": [
{
"@odata.etag": "W/\"409774077\"",
"elpis_stayduration": false,
"elpis_hotelcollect": false,
"acwapp_dailyrentincludegst": 404.7500000000,
"acwapp_propertybookingid": "d624f82c-3a0e-f111-8407-7ced8dff82c4",
"acwapp_contractno": "PB-260220-001",
"_acwapp_roomtype_value": "9e9861de-50d9-ef11-8ee9-00224819b8e1",
"_acwapp_room_value": "b1071fca-58d9-ef11-8ee9-6045bd58db72",
"_acwapp_propertyunit_value": "346bb80b-4ad9-ef11-8ee9-6045bd5897bf",
"_acwapp_bedroomoffer_value": "1dbd7f1a-3a0e-f111-8407-7ced8dff82c4",
"_acwapp_lead_value": "64899436-330e-f111-8407-7ced8dff83b0",
"acwapp_contractstartdatetimezoneindependent": "2026-02-20T15:00:00Z",
"acwapp_contractenddatetimezoneindependent": "2026-02-25T11:00:00Z",
"transactioncurrencyid": {
"isocurrencycode": "KRW",
"transactioncurrencyid": "b40f206f-5245-ed11-bba3-000d3a85685d"
}
}
]
}
Returns 200 OK with "value": []. There is no 404. Always check the array length.
{
"error": {
"code": "0x80048306",
"message": "ValidateAccessToken: Invalid or expired token."
}
}
GETGet Booking Data via View #
An alternative to Get Booking Data that queries a pre-defined CRM view instead of listing columns explicitly. Useful when you want a stable, curated column set without maintaining a long $select. Dataverse resolves the view's columns for you.
Reference: Retrieve and execute predefined queries.
The view is a Shared View on the Property Booking table named "Weave Suites – Sunyu Parkside", with userQuery id ad3660e7-fed0-f111-a05b-6045bd584bf7.
The userQuery GUID differs between UAT and Production. The id above is the one supplied for this integration — confirm it against the environment you are building on, and treat it as configuration rather than a hard-coded constant.
This endpoint is optional. Get Booking Data needs no view and covers the same ground with an explicit $select. Use whichever suits you.
Filtering within the view
$filter=wvlv_ota_booking_id eq '1128148567166155' or acwapp_contractno eq '1128148567166155'
Any column exposed by the view can then be selected. For kiosk use, only wvlv_ota_booking_id and acwapp_contractno are used as lookup keys.
{
"@odata.context": "https://weaveliving.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_propertybookings(...)",
"value": [
{
"@odata.etag": "W/\"635976614\"",
"statecode": 0,
"acwapp_contractno": "PB-260703-014",
"acwapp_name": "PB-260703-014 | Yin Ling Choi",
"wvlv_ota_booking_id": "1128148567166155",
"wvlv_lead_source": "OTA Trip.com (SG)",
"acwapp_contractstatus": 557130002,
"elpis_stayduration": false,
"elpis_hotelcollect": false,
"acwapp_dailyrentincludegst": 0.0000000000,
"acwapp_propertybookingid": "71571fa3-8176-f111-ab0f-002248eda67a",
"_acwapp_lead_value": "e5e01c4e-d468-f111-ab0d-000d3a813cf7",
"_acwapp_roomtype_value": "1eb0e0fb-5ddc-ef11-a731-6045bd569c57",
"_acwapp_room_value": "bac8f3d2-5edc-ef11-a732-0022481a1935",
"_acwapp_propertyunit_value": "c95aa555-ffd9-ef11-a730-00224816074b",
"_acwapp_bedroomoffer_value": "a00a1451-d468-f111-ab0c-002248ee2bb9",
"acwapp_contractstartdatetimezoneindependent": "2026-07-31T15:00:00Z",
"acwapp_contractenddatetimezoneindependent": "2026-08-03T11:00:00Z",
"createdon": "2026-07-03T01:50:59Z"
}
]
}
GETGet Stay Type #
Returns whether the booking is a short stay or a long stay. The kiosk uses this to decide which flow to present.
Query parameters
$filter=acwapp_contractno eq 'PB-251126-001'&$select=elpis_stayduration,acwapp_propertybookingid&$top=1
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_propertybookings(elpis_stayduration,acwapp_propertybookingid)",
"value": [
{
"@odata.etag": "W/\"358897516\"",
"elpis_stayduration": false,
"acwapp_propertybookingid": "3cc241de-75ca-f011-8544-6045bd568dff"
}
]
}
| Value | Meaning |
|---|---|
false | Short Stay |
true | Long Stay |
If you have already called Get Booking Data you do not need a separate call — elpis_stayduration is in the projection. This endpoint exists for flows that only need the stay type.
GETGet Room Type #
Resolves the room type's selling name and maximum occupancy, for display on the Guest Information screen and for enforcing the occupancy limit.
Query parameters
$top=1&$filter=acwapp_roomtypeid eq '{{_acwapp_roomtype_value}}'&$select=acwapp_sellingname,acwapp_maximumoccupancy
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_roomtypes(acwapp_sellingname,acwapp_maximumoccupancy)",
"value": [
{
"@odata.etag": "W/\"264990959\"",
"acwapp_roomtypeid": "bc69f400-9870-ed11-81ac-000d3a85c990",
"acwapp_maximumoccupancy": 2,
"acwapp_sellingname": "Central West Urban Suite"
}
]
}
| Field | Meaning |
|---|---|
acwapp_sellingname | Guest-facing room type name. |
acwapp_maximumoccupancy | Maximum number of occupants. Exceeding it causes Create Booking Occupant/Create Booking Occupant (Existing Contact) to fail with 400. |
GETGet Room Status #
Returns the housekeeping status of the room. Only Vacant Clean and Vacant Inspected may proceed to check-in. Any other status means the room is not ready and the guest should be asked to contact staff.
Query parameters
$filter=acwapp_propertyunitid eq '{{_acwapp_room_value}}'&$select=acwapp_cleaningstatus
Wrap GUIDs in straight single quotes ('). Curly quotes (‘...’), which word processors insert automatically, are not valid OData delimiters and the query will be rejected.
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_propertyunits(acwapp_cleaningstatus)",
"value": [
{
"@odata.etag": "W/\"437493634\"",
"acwapp_cleaningstatus": 557130000,
"acwapp_propertyunitid": "b1071fca-58d9-ef11-8ee9-6045bd58db72"
}
]
}
Cleaning status values
| Value | Label | Allow check-in? |
|---|---|---|
557130000 | Vacant Clean | Yes |
557130001 | Vacant Inspected | Yes |
557130002 | Occupied Dirty | No |
557130003 | Occupied Clean | No |
557130004 | Vacant Dirty | No |
557130005 | Vacant Dirty OOO | No |
406530000 | Occupied Inspected | No |
GETList Booking Occupants #
Returns everyone attached to a Property Booking, with their Booking Occupant id and their Contact id. This is the bridge between a booking and the guest records.
GET
It takes query parameters and returns a collection, so it is a GET — not a POST. The same applies to Get Contact Details.
Query parameters
| Parameter | Required | Description |
|---|---|---|
$filter | Required | Filter by the booking number, e.g. wvlv_propertybookingno eq 'PB-250123-006'. |
$select | Recommended | See the field table below. |
$filter=wvlv_propertybookingno eq 'PB-250123-006'
$select=_acwapp_contact_value,acwapp_bookingoccupantid,acwapp_firstname,acwapp_lastname,acwapp_email,acwapp_preferredlanguage
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_bookingoccupants(...)",
"value": [
{
"@odata.etag": "W/\"295609373\"",
"acwapp_bookingoccupantid": "0ede9310-5fd9-ef11-8ee9-000d3a8289bb",
"acwapp_firstname": "Moses",
"acwapp_lastname": "QQQ",
"acwapp_email": "moses1qqq@a.com",
"acwapp_preferredlanguage": 557130001
},
{
"@odata.etag": "W/\"281228086\"",
"acwapp_bookingoccupantid": "a687ab8d-8634-f011-8c4d-6045bd565ad1",
"acwapp_firstname": "JJ",
"acwapp_lastname": "Lin",
"acwapp_email": "weavelivingtesting888+jjlin@gmail.com",
"acwapp_preferredlanguage": null
}
]
}
| Field | Meaning |
|---|---|
acwapp_bookingoccupantid | Booking Occupant id. Required by Check In, Check Out, Upload Registration Card. |
_acwapp_contact_value | Contact id. Required by Get Contact Details, Update Contact Details, Get or Update Identification Type, Upload Check-in Selfie. |
acwapp_firstname / acwapp_lastname | Guest name as held on the occupant record. |
acwapp_email | Guest e-mail. |
acwapp_preferredlanguage | Option set value — see Option Sets. May be null. |
_acwapp_contact_value may be null
Guest details are maintained on the Contact record, not the occupant record. If an occupant has no linked Contact, Get Contact Details cannot be called for that guest.
Creating a Booking Occupant for a guest who has no Contact creates a new Contact automatically, matched on the e-mail address you supply in acwapp_email. Make sure that address is correct — it is what the new Contact is built from.
GETCheck for an Existing Contact #
Before creating a new accompanying guest, check whether a Contact already exists with that e-mail address. Running this first keeps the Contact table clean and lets you reuse an existing guest record.
Query parameters
$filter=emailaddress1 eq 'jane.doe@example.com'&$select=contactid
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#contacts(contactid)",
"@Microsoft.Dynamics.CRM.totalrecordcount": -1,
"@Microsoft.Dynamics.CRM.totalrecordcountlimitexceeded": false,
"value": [
{
"@odata.etag": "W/\"440821850\"",
"contactid": "85899436-330e-f111-8407-7ced8dff83b0"
}
]
}
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#contacts(contactid)",
"value": []
}
If a contact is returned, you can reuse it — create the occupant with Create Booking Occupant using that guest's details.
If value is empty, create the occupant with Create Booking Occupant as a new guest.
This check is a recommendation, not a hard requirement. Duplicate e-mail addresses and duplicate Booking Occupants are permitted by the CRM, so nothing breaks if you skip it — you will simply end up with more than one Contact for the same person.
GETGet Contact by Email #
Fetches the full Contact record for a guest so you can read their country, calling code and telephone number. Use this when the guest is identified by e-mail rather than by a scanned document.
Query parameters
| Parameter | Required | Description |
|---|---|---|
$filter | Required | Filter on emailaddress1. |
$select | Recommended | See example. |
$top | Optional | Use 1. |
$select=fullname,firstname,lastname,emailaddress1,acwapp_countrycallingcode,telephone1,_acwapp_countrylookup_value
$filter=emailaddress1 eq 'occto888%2Bstevesmith@gmail.com'
$top=1
+ as %2B
An unescaped + is decoded as a space by the query-string parser, so occto888+stevesmith@gmail.com would be searched as occto888 stevesmith@gmail.com and return nothing. Always percent-encode it.
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#contacts(...)",
"value": [
{
"@odata.etag": "W/\"244886719\"",
"contactid": "e9bd5143-b7a7-ed11-aad1-000d3a81b0d8",
"firstname": "Steve",
"lastname": "Smith",
"fullname": "Steve Smith",
"emailaddress1": "occto888+stevesmith@gmail.com",
"acwapp_countrycallingcode": "65",
"telephone1": "6090 1145",
"_acwapp_countrylookup_value": "f5dad402-c96f-ed11-81ac-000d3a85685d"
}
]
}
| Field | Meaning |
|---|---|
contactid | Contact GUID. Needed by Update Contact Details, Get or Update Identification Type, Upload Check-in Selfie. |
acwapp_countrycallingcode | Dialling code without +, e.g. 82 for Korea. |
telephone1 | Subscriber number. |
_acwapp_countrylookup_value | Country lookup → resolve via Get Country. |
POSTCreate Booking Occupant #
Creates a Booking Occupant for a guest who does not yet exist as a Contact. Use this for the primary guest and for any accompanying guest whose e-mail is not found by Check for an Existing Contact.
Request body
| Field | Required | Notes |
|---|---|---|
acwapp_firstname | Required | From the passport / guest input. |
acwapp_lastname | Required | |
acwapp_email | Required | Primary identifier for duplicate detection. |
acwapp_preferredlanguage | Required | Option set — see Option Sets. |
acwapp_primary | Required | true for the lead guest, false for accompanying guests. |
acwapp_actualstaystartdatetimezoneindependent | Required | Local wall clock — see Conventions. |
acwapp_actualstayenddatetimezoneindependent | Required | Local wall clock. |
acwapp_PropertyBooking@odata.bind | Required | From Get Booking Data. |
acwapp_BedroomOffer@odata.bind | Required | From Get Booking Data. |
acwapp_PropertyUnit@odata.bind | Required | From Get Booking Data. |
acwapp_RoomType@odata.bind | Required | From Get Booking Data. |
acwapp_Room@odata.bind | Required | From Get Booking Data. Binds to acwapp_propertyunits. |
{
"acwapp_firstname": "Jane",
"acwapp_lastname": "Doe",
"acwapp_email": "jane.doe@example.com",
"acwapp_preferredlanguage": 557130000,
"acwapp_primary": false,
"acwapp_actualstaystartdatetimezoneindependent": "2026-02-20T15:00:00.000Z",
"acwapp_actualstayenddatetimezoneindependent": "2026-02-25T11:00:00.000Z",
"acwapp_PropertyUnit@odata.bind": "/acwapp_propertyunits(346bb80b-4ad9-ef11-8ee9-6045bd5897bf)",
"acwapp_BedroomOffer@odata.bind": "/acwapp_bedroomoffers(1dbd7f1a-3a0e-f111-8407-7ced8dff82c4)",
"acwapp_RoomType@odata.bind": "/acwapp_roomtypes(9e9861de-50d9-ef11-8ee9-00224819b8e1)",
"acwapp_Room@odata.bind": "/acwapp_propertyunits(b1071fca-58d9-ef11-8ee9-6045bd58db72)",
"acwapp_PropertyBooking@odata.bind": "/acwapp_propertybookings(d624f82c-3a0e-f111-8407-7ced8dff82c4)"
}
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_bookingoccupants/$entity",
"@odata.etag": "W/\"358834313\"",
"acwapp_bookingoccupantid": "0ede9310-5fd9-ef11-8ee9-000d3a8289bb",
"wvlv_unit_number": "2601",
"wvlv_main_entrance_image_url": "https://wlbooking.blob.core.windows.net/guides/cw-entrance-2024-11.jpg"
}
{
"error": {
"code": "0x80040265",
"message": "BookingOccupantCheckExceedLimit of Error Message: \r\n \t Max Occupancy Met: Max Occupancy: 2, Booking Occupant Count: 3"
}
}
Creating an occupant with an e-mail that already exists will succeed and produce a duplicate Contact. This is accepted behaviour, not an error — the CRM does not enforce uniqueness on e-mail address.
Calling Check for an Existing Contact first is recommended so you reuse an existing guest rather than creating another, but it is not required and nothing will fail if you skip it.
POSTCreate Booking Occupant (Existing Contact) #
Creates a Booking Occupant for a guest who already exists as a Contact — the branch you take when Check for an Existing Contact returns a match.
The request body is the same shape as Create Booking Occupant, with the guest's details taken from the existing Contact record.
{
"acwapp_firstname": "Winky",
"acwapp_lastname": "Chong",
"acwapp_email": "winky.chong@example.com",
"acwapp_preferredlanguage": 557130000,
"acwapp_primary": false,
"acwapp_actualstaystartdatetimezoneindependent": "2026-03-14T15:00:00.000Z",
"acwapp_actualstayenddatetimezoneindependent": "2026-03-20T11:00:00.000Z",
"acwapp_PropertyUnit@odata.bind": "/acwapp_propertyunits(346bb80b-4ad9-ef11-8ee9-6045bd5897bf)",
"acwapp_BedroomOffer@odata.bind": "/acwapp_bedroomoffers(5923c5e4-341c-f111-8342-70a8a50132c4)",
"acwapp_RoomType@odata.bind": "/acwapp_roomtypes(0a3dc604-4fd9-ef11-8ee9-6045bd5897bf)",
"acwapp_Room@odata.bind": "/acwapp_propertyunits(3277fe09-5ad9-ef11-8ee9-00224819b8e1)",
"acwapp_PropertyBooking@odata.bind": "/acwapp_propertybookings(dbc67724-351c-f111-8342-70a8a50132c4)"
}
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_bookingoccupants/$entity",
"@odata.etag": "W/\"447792011\"",
"acwapp_bookingoccupantid": "a687ab8d-8634-f011-8c4d-6045bd565ad1",
"wvlv_unit_number": "1218",
"wvlv_main_entrance_image_url": null
}
The distinguishing feature of this endpoint is that the new occupant is bound to the existing Contact record. However, the payload documented so far does not include an acwapp_Contact@odata.bind value, so it is currently identical to Create Booking Occupant and would create a second Contact rather than reuse the first.
Until the correct binding field is confirmed, do not use this endpoint. Call Check for an Existing Contact to check for an existing Contact, then create the occupant with Create Booking Occupant either way.
GETGet Contact Details #
Reads the full Contact record for a guest, including nationality, identity number, calling code and current living location. Take the Contact id from List Booking Occupants.
Query parameters
$filter=contactid eq '{{_acwapp_contact_value}}'
$select=firstname,lastname,emailaddress1,_acwapp_nationality_value,acwapp_idnumber,acwapp_countrycallingcode,telephone1,_acwapp_currentlivinglocation_value
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#contacts(...)",
"@Microsoft.Dynamics.CRM.totalrecordcount": -1,
"value": [
{
"@odata.etag": "W/\"438985911\"",
"contactid": "85899436-330e-f111-8407-7ced8dff83b0",
"firstname": "Winky",
"lastname": "Chong",
"emailaddress1": "winky.chong@example.com",
"acwapp_idnumber": null,
"acwapp_countrycallingcode": "82",
"telephone1": "010-0000-1111",
"_acwapp_nationality_value": "50ba15a5-3796-ed11-aad1-000d3a85c990",
"_acwapp_nationality_value@OData.Community.Display.V1.FormattedValue": "Korea, Republic of",
"_acwapp_nationality_value@Microsoft.Dynamics.CRM.lookuplogicalname": "acwapp_nationality",
"_acwapp_currentlivinglocation_value": "61c104e3-c86f-ed11-81ac-000d3a85685d",
"_acwapp_currentlivinglocation_value@OData.Community.Display.V1.FormattedValue": "Korea, Republic of",
"_acwapp_currentlivinglocation_value@Microsoft.Dynamics.CRM.lookuplogicalname": "acwapp_country"
}
]
}
| Field | Meaning |
|---|---|
acwapp_idnumber | Identity document number — for international guests, the passport number. |
_acwapp_nationality_value | Nationality lookup → Get Nationality. |
_acwapp_currentlivinglocation_value | Country of residence lookup → Get Country. |
acwapp_countrycallingcode | Dialling code without +. |
Adding the Prefer: odata.include-annotations="*" header makes Dataverse return @OData.Community.Display.V1.FormattedValue annotations alongside lookup GUIDs, so you can display the label without a second call.
PATCHUpdate Contact Details #
Writes guest edits back to the Contact record — birthdate, nationality, preferred language.
Request body
{
"birthdate": "1990-02-28",
"acwapp_Nationality@odata.bind": "/acwapp_nationalities(52ba15a5-3796-ed11-aad1-000d3a85c990)"
}
Including preferred language
{
"acwapp_preferredlanguage": 557130003,
"birthdate": "1990-02-28",
"acwapp_Nationality@odata.bind": "/acwapp_nationalities(52ba15a5-3796-ed11-aad1-000d3a85c990)"
}
Note acwapp_Nationality@odata.bind — capital N. The plain field name acwapp_nationality will be rejected. Scalar fields such as birthdate and acwapp_preferredlanguage stay lower case.
(empty body)
GETGet or Update Identification Type #
Reads or writes the guest's identity document type on the Contact record. For international guests in Korea this is almost always Passport (557130000).
Get
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#contacts(acwapp_identificationtype)",
"value": [
{
"@odata.etag": "W/\"438985911\"",
"contactid": "85899436-330e-f111-8407-7ced8dff83b0",
"acwapp_identificationtype": 557130000
}
]
}
Update
{
"acwapp_identificationtype": 557130000
}
(empty body)
Accepted values are listed in Korea / Seoul Notes and Option Sets. Remember that there is no Alien Registration Card option today.
This integration uses acwapp_identificationtype. A second picklist, wvlv_identification_type, also exists on the same table and is not used by the kiosk. Do not write to it by mistake.
PATCHUpdate Purpose of Stay #
Writes the guest's reason for staying. This field lives on the Lead, not the Contact — take _acwapp_lead_value from Get Booking Data.
{
"acwapp_noofrooms": 1,
"wvlv_purpose_of_stay": 557130004
}
(empty body)
Values for wvlv_purpose_of_stay are in Option Sets.
Both Update Purpose of Stay and Update Hear About Us write to the same Lead record and can be sent as one PATCH.
PATCHUpdate Hear About Us #
Writes how the guest heard about Weave Living. Also on the Lead.
{
"acwapp_noofrooms": 1,
"acwapp_howdidyouhearaboutus": 557130004
}
(empty body)
Values for acwapp_howdidyouhearaboutus are in Option Sets. For Korea, 557130028 (Naver) is likely to be used often.
GETGet Country #
Resolves a country name from a country lookup value — used for the guest's country and country of residence.
Query parameters
$select=acwapp_name
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_countries(acwapp_name)/$entity",
"@odata.etag": "W/\"13116914\"",
"acwapp_countryid": "f5dad402-c96f-ed11-81ac-000d3a85685d",
"acwapp_name": "Korea, Republic of"
}
{
"error": {
"code": "0x80040217",
"message": "The record does not exist."
}
}
GETGet Nationality #
Resolves a nationality. Same shape as Get Country, but the table also supports searching by name, which is useful when a passport scan returns a nationality you need to match.
By id
$filter=acwapp_nationalityid eq {{_acwapp_nationality_value}}&$select=acwapp_name
By name
$filter=contains(acwapp_name, 'Korean')&$select=acwapp_name
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_nationalities(acwapp_name)",
"value": [
{
"@odata.etag": "W/\"20095678\"",
"acwapp_nationalityid": "50ba15a5-3796-ed11-aad1-000d3a85c990",
"acwapp_name": "Chinese (Hong Kong)"
}
]
}
{
"@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#acwapp_nationalities(acwapp_name)",
"value": [
{ "acwapp_name": "Chinese", "acwapp_nationalityid": "4eba15a5-3796-ed11-aad1-000d3a85c990" },
{ "acwapp_name": "Chinese (Hong Kong)", "acwapp_nationalityid": "50ba15a5-3796-ed11-aad1-000d3a85c990" },
{ "acwapp_name": "Chinese (Macau)", "acwapp_nationalityid": "52ba15a5-3796-ed11-aad1-000d3a85c990" }
]
}
contains matching returns every partial match, so "Chinese" also returns Hong Kong and Macau. Prefer an exact eq match on the full label where you can, and cache the option list at kiosk start-up rather than querying per guest.
PATCHCheck In #
Records the moment the guest checks in, against the Booking Occupant. Call this once per occupant who is present.
Request body
{
"acwapp_checkintimetimezoneindependent": "2026-01-12T15:30:00.000Z"
}
Value format
The timestamp is the Seoul local wall-clock time with a Z suffix — not a UTC conversion. See Conventions. For a 15:30 check-in send 15:30, not 06:30.
(empty body)
{
"error": {
"code": "0x0",
"message": "An unexpected error occurred while processing the request."
}
}
PATCHAdd Comment #
Writes the guest's request into the booking's Special Request field. This is the field the Operations Team reads, so use it for anything the guest wants acted on during their stay.
Request body
{
"acwapp_specialrequest": "No cleaning please!"
}
(empty body)
acwapp_specialrequest is a plain text field with no append behaviour. A PATCH replaces whatever is already there, including notes the Operations Team has added in the CRM. If the kiosk needs to add to an existing request, read the current value from Get Booking Data (adding acwapp_specialrequest to the projection) and concatenate before writing.
POSTSend Unit Guide Email #
Triggers the "Unit Guide" e-mail — a welcome message containing house rules, the room number and the room's Wi-Fi details. It is a Power Automate flow invoked over HTTP, so the request shape is the flow's trigger rather than a Dataverse entity.
There is a separate flow URL for UAT and for Production, and each contains a signature query parameter (sig) that acts as a shared secret. These are deliberately not reproduced here. Store the URL as configuration, never in source control.
Ask your Weave Living contact if you have not received them.
Request body
{
"acwapp_bookingoccupant": "{{acwapp_bookingoccupantid}}"
}
Request headers
| Header | Value |
|---|---|
Content-Type | application/json |
(empty body)
202 only means the flow was queued, not that the e-mail was delivered. If the guest has no e-mail address on the Contact record the flow may complete without sending anything — check that emailaddress1 is populated before calling.
PATCHUpload Registration Card #
After the guest signs the registration card and you have uploaded the image to blob storage, write the resulting URL back onto the Booking Occupant.
Request body
{
"wvlv_registration_card_url": "https://wlbookinguat.blob.core.windows.net/kiosk/2026-04-14/registration-card-test.png"
}
(empty body)
This endpoint only records the URL. The blob storage container and the SAS credentials needed to write to it are provided separately by your Weave Living contact. Store them as configuration, never in source control, and do not assume the UAT container accepts production uploads.
Ask your Weave Living contact if you have not received them.
PATCHUpload Check-in Selfie #
Writes the URL of the guest's check-in selfie onto the Contact record. Note this is the Contact, not the Booking Occupant.
Request body
{
"wvlv_checkin_selfie_url": "https://wlbookinguat.blob.core.windows.net/kiosk/test.jpg"
}
(empty body)
A successful PATCH with odata.metadata=minimal returns 204 No Content. If you need the updated record in the response, send Prefer: return=representation.
Uploads to blob storage use the same container and SAS credentials as Upload Registration Card. Ask your Weave Living contact if you have not received them.
PATCHCheck Out #
Records the check-out moment, against the Booking Occupant. Structurally the same call as Check In.
Request body
{
"acwapp_checkouttimetimezoneindependent": "2026-01-12T11:00:00.000Z"
}
Seoul is UTC+9; Singapore is UTC+8. A 11:00 check-out in Seoul is sent as 11:00 with a Z suffix — do not convert to UTC. Getting this wrong shifts every check-out by nine hours.
(empty body)
The kiosk flow stops check-out if today is not the guest's departure date. Compare the current date against acwapp_contractenddatetimezoneindependent from Get Booking Data — match the timezone-independent date directly. Both values are Seoul local wall-clock dates, so no conversion or business-date cut-off is involved.
Option Sets #
All picklist values verified against the live CRM metadata. Cache these at kiosk start-up rather than querying them per guest.
acwapp_language — preferred language
Used by acwapp_preferredlanguage on both Contact and Booking Occupant. Referenced by Create Booking Occupant, Create Booking Occupant (Existing Contact), Update Contact Details.
| Value | Label | Kiosk UI language? |
|---|---|---|
557130000 | English | Yes |
557130001 | Japanese | Yes |
557130002 | Traditional Chinese | Yes |
557130003 | Korean | Yes |
557130004 | Simplified Chinese | Yes |
The kiosk offers Korean, English, Japanese and Chinese. Traditional and Simplified Chinese both map to the Chinese UI — store the script variant the guest selected.
acwapp_identificationtype — identity document type
Used by acwapp_identificationtype on Contact. Referenced by Get or Update Identification Type.
| Value | Label | Region |
|---|---|---|
557130000 | Passport | International — use by default |
557130001 | HKID | Hong Kong |
557130002 | Two-way Permit (CN) | Mainland China |
557130003 | National Registration (SG) | Singapore |
557130004 | Individual Number (JP) | Japan |
557130005 | Resident Registration (JP) | Japan |
557130006 | Resident Registration (KR) | Korea — 주민등록증 |
557130007 | Driver's License (SG) | Singapore |
557130008 | Driver's License (JP) | Japan |
557130009 | Driver's License (KR) | Korea — 운전면허증 |
557130010 | Business Registration (KR) | Korea — 사업자등록증 |
There is no separate option for a Korean Alien Registration Card (외국인등록증). Use Passport (557130000) for international guests, or one of the Korean values above.
wvlv_purpose_of_stay — purpose of stay
Used by wvlv_purpose_of_stay on the Lead. Referenced by Update Purpose of Stay.
| Value | Label |
|---|---|
557130000 | Study or Internship |
557130001 | Relocation (from overseas or within city) |
557130002 | Home renovation or waiting for new home |
557130003 | Work assignment / Project stay |
557130004 | Business trip |
557130005 | Healthcare / Medical treatment nearby |
557130006 | Short-term stay (travel, visiting friends/family) |
557130007 | Looking for a serviced living option |
557130008 | Other |
acwapp_howdidyouhearaboutus — how did you hear about us
Used by acwapp_howdidyouhearaboutus on the Lead. Referenced by Update Hear About Us.
| Value | Label | Value | Label |
|---|---|---|---|
557130000 | Company referral | 557130015 | House 730 |
557130001 | Friend and family | 557130016 | 28Hse |
557130002 | Google search | 557130017 | Public Transportation |
557130003 | 557130018 | Other news and media | |
557130004 | 557130019 | Others | |
557130005 | 557130020 | Agoda | |
557130006 | Little Red Book | 557130021 | AirBnB |
557130007 | University website | 557130022 | Booking.com |
557130008 | Honeycomber | 557130023 | Expedia |
557130009 | Expat Living | 557130024 | Meituan |
557130010 | Property Guru | 557130025 | Trip.com |
557130011 | Cosmopolitan | 557130026 | Property listing websites |
557130012 | Sassy | 557130027 | E-Housing Japan |
557130013 | Spacious | 557130028 | Naver Korea |
557130014 | Carousell |
Note the numbering is not contiguous — 557130026, 557130027 and 557130028 are out of label order. Key on the value, never on the position.
acwapp_cleaningstatus — room readiness
Used by acwapp_cleaningstatus on Property Unit. Referenced by Get Room Status.
| Value | Label | Check-in allowed |
|---|---|---|
557130000 | Vacant Clean | Yes |
557130001 | Vacant Inspected | Yes |
557130002 | Occupied Dirty | No |
557130003 | Occupied Clean | No |
557130004 | Vacant Dirty | No |
557130005 | Vacant Dirty OOO | No |
406530000 | Occupied Inspected | No |
Note 406530000 — a different publisher prefix. Do not assume the 5571300xx pattern.
Data Dictionary #
The CRM entities the kiosk touches. All names verified against live metadata.
Entity map
| Entity | Logical name | What it is |
|---|---|---|
| Property Booking | acwapp_propertybooking | The reservation. Root of everything. |
| Booking Occupant | acwapp_bookingoccupant | A guest on a booking. Holds check-in/check-out times. |
| Contact | contact | Standard Dataverse contact. Holds identity, nationality, language. |
| Lead | lead | Standard Dataverse lead. Holds purpose of stay and marketing source. |
| Property Unit | acwapp_propertyunit | An apartment/unit. Also serves as the Room record. |
| Room Type | acwapp_roomtype | Selling name and max occupancy. |
| Bedroom Offer | acwapp_bedroomoffer | Contract line for the room. |
| Country | acwapp_country | Country lookup values. |
| Nationality | acwapp_nationality | Nationality lookup values. |
Key fields by entity
Property Booking — acwapp_propertybooking
| Field | Type | Notes |
|---|---|---|
acwapp_propertybookingid | GUID | Primary key |
acwapp_contractno | string | Booking number, PB-yymmdd-nnn |
wvlv_ota_booking_id | string | OTA / channel reference |
acwapp_name | string | Display name, e.g. PB-260703-014 | Yin Ling Choi |
acwapp_specialrequest | memo | Guest's request for the Operations Team — see Add Comment |
acwapp_contractstartdatetimezoneindependent | datetime | Local wall clock |
acwapp_contractenddatetimezoneindependent | datetime | Local wall clock |
elpis_stayduration | boolean | false Short / true Long |
elpis_hotelcollect | boolean | Payment collected at property |
acwapp_dailyrentincludegst | money | Tax-inclusive daily rent |
acwapp_contractstatus | picklist | Booking status |
statecode | int | 0 = Active |
Booking Occupant — acwapp_bookingoccupant
| Field | Type | Notes |
|---|---|---|
acwapp_bookingoccupantid | GUID | Primary key |
acwapp_firstname / acwapp_lastname | string | Guest name |
acwapp_email | string | Duplicate-detection key |
acwapp_preferredlanguage | picklist | acwapp_language |
acwapp_primary | boolean | Lead guest flag |
acwapp_checkintimetimezoneindependent | datetime | Local wall clock — see Check In |
acwapp_checkouttimetimezoneindependent | datetime | Local wall clock — see Check Out |
acwapp_actualstaystartdatetimezoneindependent | datetime | Local wall clock |
acwapp_actualstayenddatetimezoneindependent | datetime | Local wall clock |
acwapp_idnumber | string | Identity document number |
wvlv_registration_card_url | string | Signed registration card — see Upload Registration Card |
wvlv_unit_number | string | Room number |
wvlv_main_entrance_code | string | Building entry code |
wvlv_main_entrance_image_url | string | Entrance photo |
acwapp_contact | lookup | Linked Contact — read as _acwapp_contact_value |
acwapp_PropertyBooking | lookup | Parent booking |
Contact — contact
| Field | Type | Notes |
|---|---|---|
contactid | GUID | Primary key |
firstname / lastname / fullname | string | Guest name |
emailaddress1 | string | Primary e-mail |
telephone1 | string | Phone number |
birthdate | date | Date of birth |
acwapp_idnumber | string | Passport / ID number |
acwapp_identificationtype | picklist | See Get or Update Identification Type |
acwapp_preferredlanguage | picklist | acwapp_language |
acwapp_countrycallingcode | string | Dialling code, no + |
acwapp_nationality | lookup | → acwapp_nationality |
acwapp_currentlivinglocation | lookup | → acwapp_country |
acwapp_countrylookup | lookup | → acwapp_country |
wvlv_checkin_selfie_url | string | Selfie — see Upload Check-in Selfie |
A second picklist wvlv_identification_type exists on Contact but is not used by this integration.
Lead — lead
| Field | Type | Notes |
|---|---|---|
leadid | GUID | Primary key |
wvlv_purpose_of_stay | picklist | See Update Purpose of Stay |
acwapp_howdidyouhearaboutus | picklist | See Update Hear About Us |
acwapp_noofrooms | integer | Number of rooms |
Property Unit / Room — acwapp_propertyunit
| Field | Type | Notes |
|---|---|---|
acwapp_propertyunitid | GUID | Primary key |
acwapp_cleaningstatus | picklist | Room readiness — see Get Room Status |
wvlv_has_checkin_kiosk | boolean | Whether this property has a kiosk |
wvlv_main_entrance_code | string | Building entry code |
wvlv_main_entrance_image_url | string | Entrance photo |
acwapp_shortstaycheckintime | string | Property check-in time |
acwapp_shortstaycheckouttime | string | Property check-out time |
wvlv_self_checkout_email_template_id | string | Folio e-mail template |
Scope & Open Items #
What is in this phase, what has been left out, and the small number of things still outstanding.
In scope
Deferred to a later phase
These are acknowledged but not required for the first release. They are listed so they are not lost.
| Item | Status | Detail |
|---|---|---|
| Payments | Phase 2 | Card payment, retrieving the outstanding invoice and reporting the payment result. This is a separate phase and no payment endpoints are included in this revision. The flows in this document show where the payment step sits so the screen sequence is clear. |
| Occupant with an existing Contact | Deferred | The Create Booking Occupant (Existing Contact) endpoint is not usable as documented: the payload needs a binding to the existing Contact record that has not been defined. Use Create Booking Occupant for all occupant creation. |
| Walk-in check-in | Deferred | Requires endpoints that do not exist yet — creating a booking from the kiosk and querying live room availability. Build the reserved-guest flow first. |
| Partial booking matching | Deferred | The kiosk design allows a guest to search on a partial value (phone last 4 digits, e-mail local part). The API supports exact filters only. To be resolved if it causes a problem during testing. |
Still outstanding
These need something from Weave Living before the related work can be completed.
| Item | Status | Detail |
|---|---|---|
| Kiosk application credentials | To be provided | tenantId, clientId and clientSecret for the UAT and Production application registrations. Nothing can be called without these, so make sure you have them before you start. They are supplied directly, never in this document. Ask your Weave Living contact if you have not received them. |
| Blob storage SAS credentials | To be provided | Container and SAS credentials for the registration card and selfie uploads — Upload Registration Card and Upload Check-in Selfie. These are in scope for the first release; only the credentials are outstanding. Ask your Weave Living contact if you have not received them. |
| Unit Guide workflow URL | To be provided | The Power Automate flow URL for Send Unit Guide Email, separately for UAT and Production. It carries a signature parameter that acts as a shared secret, so it is supplied directly rather than published here. Ask your Weave Living contact if you have not received it. |
| Korea properties in scope | To confirm | The list of Korea properties this kiosk rollout covers. Needed to confirm the CRM view covers them all. |
| Rate limits | To confirm | Whether any throttling policy applies to the service principal. Affects retry and back-off design. |
Decisions already taken
Answers to questions raised during specification. Recorded here so they do not have to be asked again.
| Topic | Decision |
|---|---|
| Duplicate e-mail addresses | Permitted. The CRM does not enforce uniqueness on e-mail. Duplicate Contacts and duplicate Booking Occupants are acceptable. Calling Check for an Existing Contact first is recommended so you reuse an existing guest, but it is not required. |
| Occupant with no linked Contact | A new Contact is created automatically from the e-mail address supplied in acwapp_email. Make sure that address is correct. |
| Booking not found | The API returns 200 OK with an empty value array. It does not return 404. Display your own "not found" message — the wording is yours to choose. |
| Passport expiry date | Not stored. No field exists and none is required. Read the date if you need it for your own validation, but do not try to send it. |
| Guest age | Passport data may be captured for guests of any age, including under-18s. There is no minimum-age restriction. |
| Korean identification types | There is no separate Alien Registration Card option. Use Passport (557130000) or one of the existing Korean values — see Korea / Seoul Notes. |
| Check-out date check | Match the timezone-independent date directly. Compare today's date against acwapp_contractenddatetimezoneindependent from Get Booking Data. No conversion or business-date cut-off is involved. |
| CRM view for booking lookup | Provided: the "Weave Suites – Sunyu Parkside" shared view, userQuery id ad3660e7-fed0-f111-a05b-6045bd584bf7. See Get Booking Data via View. Confirm the id for the environment you build on. |
Revision History #
| Version | Date | Summary |
|---|---|---|
| 1.0 | 26 Oct 2026 | First Korea-scoped revision. Covers booking lookup, guest and contact management, check-in and check-out for the Seoul properties. Payments deferred to Phase 2. |
Questions on anything here should go to your Weave Living contact.
This document is confidential and intended solely for the named recipient. It is marked noindex, nofollow and must not be published or indexed. It contains no credentials; those are provisioned separately.