Kiosk Integration API

Weave Living · Microsoft Dynamics 365 CRM · Korea (Seoul) properties · v1.0

Who this document is for

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.

Payments are out of scope for this phase

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.

Read the Conventions section before you start

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.

EnvironmentCRM base URLPurpose
UAThttps://weaveuat.crm5.dynamics.comIntegration and acceptance testing. Safe to write to.
Productionhttps://weaveliving.crm5.dynamics.comLive. 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
API version

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.

Credentials are provisioned separately

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.

POST https://login.windows.net/{{tenantId}}/oauth2/token

Request headers

HeaderValue
Content-Typeapplication/x-www-form-urlencoded

Request body (form-urlencoded)

FieldRequiredValue
grant_typeRequiredclient_credentials
client_idRequired{{clientId}} — supplied by Weave Living
client_secretRequired{{clientSecret}} — supplied by Weave Living
resourceRequiredhttps://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"
200 OK Response
{
  "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
Always prefix the token with Bearer

Send Authorization: Bearer eyJ0eXAi... — not the raw token on its own. The Bearer  prefix is part of the header value and is required.

401 Error — token expired or invalid
{
  "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.

Send the building's local wall-clock time, formatted with a 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:

FieldEntityMeaning
acwapp_actualstaystartdatetimezoneindependentBooking OccupantActual stay start, local wall clock
acwapp_actualstayenddatetimezoneindependentBooking OccupantActual stay end, local wall clock
acwapp_checkintimetimezoneindependentBooking OccupantCheck-in date & time, local wall clock
acwapp_checkouttimetimezoneindependentBooking OccupantCheck-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)"
}
EntityRead asWrite as
Property Booking_acwapp_propertybooking_valueacwapp_PropertyBooking@odata.bind
Booking Occupant_acwapp_primarybookingoccupant_valueacwapp_Primarybookingoccupant@odata.bind
Bedroom Offer_acwapp_bedroomoffer_valueacwapp_BedroomOffer@odata.bind
Room Type_acwapp_roomtype_valueacwapp_RoomType@odata.bind
Room_acwapp_room_valueacwapp_Room@odata.bind
Property Unit_acwapp_propertyunit_valueacwapp_PropertyUnit@odata.bind
Contact_acwapp_contact_valueacwapp_Contact@odata.bind
Nationality_acwapp_nationality_valueacwapp_Nationality@odata.bind
Current Living Location_acwapp_currentlivinglocation_valueacwapp_CurrentLivingLocation@odata.bind
Country_acwapp_countrylookup_valueacwapp_CountryLookup@odata.bind
Room and Property Unit are the same table

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

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

HeaderPurpose
Content-Type: application/json; odata.metadata=minimalUse on every request. Keeps the response payload small.
Prefer: return=representationOptional. 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.0Recommended 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.

StatusMeaningWhat the kiosk should do
200Success. May still contain an empty value array.Check the array length, not just the status.
201Created.Read the new record id from the response.
204No content. Typical for a successful PATCH.Treat as success.
400Bad request — malformed body, unknown field, or a business rule rejected the write.Show a friendly message; do not retry unchanged.
401Token missing, expired or wrong resource.Refresh the token and retry once.
403Authenticated but not permitted.Log and alert; do not retry.
404Record or endpoint does not exist.Treat as not found.
429Too many requests.Back off exponentially and retry.
500Server 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 guidance

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.

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.

ValueLabelNotes
557130000PassportPrimary Use for all international guests.
557130001HKIDHong Kong
557130002Two-way Permit (CN)Mainland China
557130003National Registration (SG)Singapore
557130004Individual Number (JP)Japan — My Number
557130005Resident Registration (JP)Japan
557130006Resident Registration (KR)Korea 주민등록증
557130007Driver's License (SG)Singapore
557130008Driver's License (JP)Japan
557130009Driver's License (KR)Korea 운전면허증
557130010Business 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 screenWhat happensAPI 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 name
Get 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
First-release scope

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 screenWhat happensAPI 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.

GET /api/data/v9.2/acwapp_propertybookings

Query parameters

ParameterRequiredDescription
$topOptionalUse 1 for a single-booking lookup.
$filterRequiredFilter on the booking number, the OTA id, or both.
$selectRecommendedThe fields listed below. Trimming the projection keeps the response small.
$expandRecommendedtransactioncurrencyid($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

FieldTypeMeaning
acwapp_propertybookingidGUIDPrimary key of the booking. Needed by almost every later call.
acwapp_contractnostringHuman-readable booking number, e.g. PB-251126-001.
wvlv_ota_booking_idstringOTA booking reference, when the booking came from a channel.
_acwapp_lead_valueGUIDLead record. Required for Update Purpose of Stay and Update Hear About Us.
_acwapp_roomtype_valueGUIDRoom type → Get Room Type.
_acwapp_room_valueGUIDRoom (a acwapp_propertyunit record) → Get Room Status.
_acwapp_propertyunit_valueGUIDApartment / unit record.
_acwapp_bedroomoffer_valueGUIDBedroom offer (contract line).
acwapp_contractstartdatetimezoneindependentdatetimeContract start (local wall clock).
acwapp_contractenddatetimezoneindependentdatetimeContract end (local wall clock).
elpis_staydurationbooleanfalse = Short Stay, true = Long Stay. See Get Stay Type.
elpis_hotelcollectbooleanWhen true, payment is collected at the property. Used by the Phase 2 payment work; no action needed in this phase.
acwapp_dailyrentincludegstmoneyDaily rent, tax inclusive.
transactioncurrencyid.isocurrencycodestringCurrency, e.g. KRW. Read it; do not assume.
200 OK Response
{
  "@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"
      }
    }
  ]
}
Booking not found

Returns 200 OK with "value": []. There is no 404. Always check the array length.

401 Error — access token expired
{
  "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.

GET /api/data/v9.2/acwapp_propertybookings?userQuery=ad3660e7-fed0-f111-a05b-6045bd584bf7

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 view id is environment-specific

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.

200 OK Response
{
  "@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.

GET /api/data/v9.2/acwapp_propertybookings

Query parameters

$filter=acwapp_contractno eq 'PB-251126-001'&$select=elpis_stayduration,acwapp_propertybookingid&$top=1
200 OK Response
{
  "@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"
    }
  ]
}
ValueMeaning
falseShort Stay
trueLong Stay
This field is already returned by 1a

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.

GET /api/data/v9.2/acwapp_roomtypes

Query parameters

$top=1&$filter=acwapp_roomtypeid eq '{{_acwapp_roomtype_value}}'&$select=acwapp_sellingname,acwapp_maximumoccupancy
200 OK Response
{
  "@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"
    }
  ]
}
FieldMeaning
acwapp_sellingnameGuest-facing room type name.
acwapp_maximumoccupancyMaximum 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.

GET /api/data/v9.2/acwapp_propertyunits

Query parameters

$filter=acwapp_propertyunitid eq '{{_acwapp_room_value}}'&$select=acwapp_cleaningstatus
Use straight quotes

Wrap GUIDs in straight single quotes ('). Curly quotes (‘...’), which word processors insert automatically, are not valid OData delimiters and the query will be rejected.

200 OK Response
{
  "@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

ValueLabelAllow check-in?
557130000Vacant CleanYes
557130001Vacant InspectedYes
557130002Occupied DirtyNo
557130003Occupied CleanNo
557130004Vacant DirtyNo
557130005Vacant Dirty OOONo
406530000Occupied InspectedNo

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 /api/data/v9.2/acwapp_bookingoccupants
This is a 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

ParameterRequiredDescription
$filterRequiredFilter by the booking number, e.g. wvlv_propertybookingno eq 'PB-250123-006'.
$selectRecommendedSee 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
200 OK Response
{
  "@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
    }
  ]
}
FieldMeaning
acwapp_bookingoccupantidBooking Occupant id. Required by Check In, Check Out, Upload Registration Card.
_acwapp_contact_valueContact id. Required by Get Contact Details, Update Contact Details, Get or Update Identification Type, Upload Check-in Selfie.
acwapp_firstname / acwapp_lastnameGuest name as held on the occupant record.
acwapp_emailGuest e-mail.
acwapp_preferredlanguageOption 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.

GET /api/data/v9.2/contacts

Query parameters

$filter=emailaddress1 eq 'jane.doe@example.com'&$select=contactid
200 OK Contact exists
{
  "@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"
    }
  ]
}
200 OK No matching contact
{
  "@odata.context": "https://weaveuat.crm5.dynamics.com/api/data/v9.2/$metadata#contacts(contactid)",
  "value": []
}
Then what?

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.

GET /api/data/v9.2/contacts

Query parameters

ParameterRequiredDescription
$filterRequiredFilter on emailaddress1.
$selectRecommendedSee example.
$topOptionalUse 1.
$select=fullname,firstname,lastname,emailaddress1,acwapp_countrycallingcode,telephone1,_acwapp_countrylookup_value
$filter=emailaddress1 eq 'occto888%2Bstevesmith@gmail.com'
$top=1
Escape + 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.

200 OK Response
{
  "@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"
    }
  ]
}
FieldMeaning
contactidContact GUID. Needed by Update Contact Details, Get or Update Identification Type, Upload Check-in Selfie.
acwapp_countrycallingcodeDialling code without +, e.g. 82 for Korea.
telephone1Subscriber number.
_acwapp_countrylookup_valueCountry 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.

POST /api/data/v9.2/acwapp_bookingoccupants

Request body

FieldRequiredNotes
acwapp_firstnameRequiredFrom the passport / guest input.
acwapp_lastnameRequired
acwapp_emailRequiredPrimary identifier for duplicate detection.
acwapp_preferredlanguageRequiredOption set — see Option Sets.
acwapp_primaryRequiredtrue for the lead guest, false for accompanying guests.
acwapp_actualstaystartdatetimezoneindependentRequiredLocal wall clock — see Conventions.
acwapp_actualstayenddatetimezoneindependentRequiredLocal wall clock.
acwapp_PropertyBooking@odata.bindRequiredFrom Get Booking Data.
acwapp_BedroomOffer@odata.bindRequiredFrom Get Booking Data.
acwapp_PropertyUnit@odata.bindRequiredFrom Get Booking Data.
acwapp_RoomType@odata.bindRequiredFrom Get Booking Data.
acwapp_Room@odata.bindRequiredFrom 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)"
}
201 Created
{
  "@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"
}
400 Maximum occupancy exceeded
{
  "error": {
    "code": "0x80040265",
    "message": "BookingOccupantCheckExceedLimit of Error Message: \r\n \t Max Occupancy Met: Max Occupancy: 2, Booking Occupant Count: 3"
  }
}
Duplicates are allowed

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.

POST /api/data/v9.2/acwapp_bookingoccupants

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)"
}
201 Created
{
  "@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
}
Deferred  Out of scope for the first release

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.

GET /api/data/v9.2/contacts

Query parameters

$filter=contactid eq '{{_acwapp_contact_value}}'
$select=firstname,lastname,emailaddress1,_acwapp_nationality_value,acwapp_idnumber,acwapp_countrycallingcode,telephone1,_acwapp_currentlivinglocation_value
200 OK Response
{
  "@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"
    }
  ]
}
FieldMeaning
acwapp_idnumberIdentity document number — for international guests, the passport number.
_acwapp_nationality_valueNationality lookup → Get Nationality.
_acwapp_currentlivinglocation_valueCountry of residence lookup → Get Country.
acwapp_countrycallingcodeDialling code without +.
Formatted values come back for free

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.

PATCH /api/data/v9.2/contacts({{contactid}})

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)"
}
Lookups must use the schema name with its capitalisation

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.

204 No Content
(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

GET /api/data/v9.2/contacts?$top=1&$filter=contactid eq '{{contactid}}'&$select=acwapp_identificationtype
200 OK Response
{
  "@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

PATCH /api/data/v9.2/contacts({{contactid}})
{
  "acwapp_identificationtype": 557130000
}
204 No Content
(empty body)

Accepted values are listed in Korea / Seoul Notes and Option Sets. Remember that there is no Alien Registration Card option today.

Two identification-type fields exist on Contact

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.

PATCH /api/data/v9.2/leads({{_acwapp_lead_value}})
{
  "acwapp_noofrooms": 1,
  "wvlv_purpose_of_stay": 557130004
}
204 No Content
(empty body)

Values for wvlv_purpose_of_stay are in Option Sets.

Can be combined with 3g

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.

PATCH /api/data/v9.2/leads({{_acwapp_lead_value}})
{
  "acwapp_noofrooms": 1,
  "acwapp_howdidyouhearaboutus": 557130004
}
204 No Content
(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.

GET /api/data/v9.2/acwapp_countries({{_acwapp_countrylookup_value}})

Query parameters

$select=acwapp_name
200 OK Response
{
  "@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"
}
404 Not Found
{
  "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.

GET /api/data/v9.2/acwapp_nationalities

By id

$filter=acwapp_nationalityid eq {{_acwapp_nationality_value}}&$select=acwapp_name

By name

$filter=contains(acwapp_name, 'Korean')&$select=acwapp_name
200 OK By id
{
  "@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)"
    }
  ]
}
200 OK By name
{
  "@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" }
  ]
}
Nationality values are country names, and some are ambiguous

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.

PATCH /api/data/v9.2/acwapp_bookingoccupants({{acwapp_bookingoccupantid}})

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.

204 No Content
(empty body)
400 Bad Request — unknown or malformed field
{
  "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.

PATCH /api/data/v9.2/acwapp_propertybookings({{acwapp_propertybookingid}})

Request body

{
  "acwapp_specialrequest": "No cleaning please!"
}
204 No Content
(empty body)
This overwrites the existing value

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.

POST {{unitGuideWorkflowUrl}}
URL is supplied directly and differs per environment

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

HeaderValue
Content-Typeapplication/json
202 Accepted
(empty body)
Fire and forget, but record the outcome

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.

PATCH /api/data/v9.2/acwapp_bookingoccupants({{acwapp_bookingoccupantid}})

Request body

{
  "wvlv_registration_card_url": "https://wlbookinguat.blob.core.windows.net/kiosk/2026-04-14/registration-card-test.png"
}
204 No Content
(empty body)
Storage access is supplied directly

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.

PATCH /api/data/v9.2/contacts({{contactid}})

Request body

{
  "wvlv_checkin_selfie_url": "https://wlbookinguat.blob.core.windows.net/kiosk/test.jpg"
}
204 No Content
(empty body)
Getting the record back

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.

PATCH /api/data/v9.2/acwapp_bookingoccupants({{acwapp_bookingoccupantid}})

Request body

{
  "acwapp_checkouttimetimezoneindependent": "2026-01-12T11:00:00.000Z"
}
Send the building's local time

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.

204 No Content
(empty body)
Deciding whether check-out is allowed

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.

ValueLabelKiosk UI language?
557130000EnglishYes
557130001JapaneseYes
557130002Traditional ChineseYes
557130003KoreanYes
557130004Simplified ChineseYes

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.

ValueLabelRegion
557130000PassportInternational — use by default
557130001HKIDHong Kong
557130002Two-way Permit (CN)Mainland China
557130003National Registration (SG)Singapore
557130004Individual Number (JP)Japan
557130005Resident Registration (JP)Japan
557130006Resident Registration (KR)Korea — 주민등록증
557130007Driver's License (SG)Singapore
557130008Driver's License (JP)Japan
557130009Driver's License (KR)Korea — 운전면허증
557130010Business Registration (KR)Korea — 사업자등록증
Alien Registration Card

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.

ValueLabel
557130000Study or Internship
557130001Relocation (from overseas or within city)
557130002Home renovation or waiting for new home
557130003Work assignment / Project stay
557130004Business trip
557130005Healthcare / Medical treatment nearby
557130006Short-term stay (travel, visiting friends/family)
557130007Looking for a serviced living option
557130008Other

acwapp_howdidyouhearaboutus — how did you hear about us

Used by acwapp_howdidyouhearaboutus on the Lead. Referenced by Update Hear About Us.

ValueLabelValueLabel
557130000Company referral557130015House 730
557130001Friend and family55713001628Hse
557130002Google search557130017Public Transportation
557130003Facebook557130018Other news and media
557130004Instagram557130019Others
557130005WeChat557130020Agoda
557130006Little Red Book557130021AirBnB
557130007University website557130022Booking.com
557130008Honeycomber557130023Expedia
557130009Expat Living557130024Meituan
557130010Property Guru557130025Trip.com
557130011Cosmopolitan557130026Property listing websites
557130012Sassy557130027E-Housing Japan
557130013Spacious557130028Naver Korea
557130014Carousell

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.

ValueLabelCheck-in allowed
557130000Vacant CleanYes
557130001Vacant InspectedYes
557130002Occupied DirtyNo
557130003Occupied CleanNo
557130004Vacant DirtyNo
557130005Vacant Dirty OOONo
406530000Occupied InspectedNo

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

EntityLogical nameWhat it is
Property Bookingacwapp_propertybookingThe reservation. Root of everything.
Booking Occupantacwapp_bookingoccupantA guest on a booking. Holds check-in/check-out times.
ContactcontactStandard Dataverse contact. Holds identity, nationality, language.
LeadleadStandard Dataverse lead. Holds purpose of stay and marketing source.
Property Unitacwapp_propertyunitAn apartment/unit. Also serves as the Room record.
Room Typeacwapp_roomtypeSelling name and max occupancy.
Bedroom Offeracwapp_bedroomofferContract line for the room.
Countryacwapp_countryCountry lookup values.
Nationalityacwapp_nationalityNationality lookup values.

Key fields by entity

Property Booking — acwapp_propertybooking

FieldTypeNotes
acwapp_propertybookingidGUIDPrimary key
acwapp_contractnostringBooking number, PB-yymmdd-nnn
wvlv_ota_booking_idstringOTA / channel reference
acwapp_namestringDisplay name, e.g. PB-260703-014 | Yin Ling Choi
acwapp_specialrequestmemoGuest's request for the Operations Team — see Add Comment
acwapp_contractstartdatetimezoneindependentdatetimeLocal wall clock
acwapp_contractenddatetimezoneindependentdatetimeLocal wall clock
elpis_staydurationbooleanfalse Short / true Long
elpis_hotelcollectbooleanPayment collected at property
acwapp_dailyrentincludegstmoneyTax-inclusive daily rent
acwapp_contractstatuspicklistBooking status
statecodeint0 = Active

Booking Occupant — acwapp_bookingoccupant

FieldTypeNotes
acwapp_bookingoccupantidGUIDPrimary key
acwapp_firstname / acwapp_lastnamestringGuest name
acwapp_emailstringDuplicate-detection key
acwapp_preferredlanguagepicklistacwapp_language
acwapp_primarybooleanLead guest flag
acwapp_checkintimetimezoneindependentdatetimeLocal wall clock — see Check In
acwapp_checkouttimetimezoneindependentdatetimeLocal wall clock — see Check Out
acwapp_actualstaystartdatetimezoneindependentdatetimeLocal wall clock
acwapp_actualstayenddatetimezoneindependentdatetimeLocal wall clock
acwapp_idnumberstringIdentity document number
wvlv_registration_card_urlstringSigned registration card — see Upload Registration Card
wvlv_unit_numberstringRoom number
wvlv_main_entrance_codestringBuilding entry code
wvlv_main_entrance_image_urlstringEntrance photo
acwapp_contactlookupLinked Contact — read as _acwapp_contact_value
acwapp_PropertyBookinglookupParent booking

Contact — contact

FieldTypeNotes
contactidGUIDPrimary key
firstname / lastname / fullnamestringGuest name
emailaddress1stringPrimary e-mail
telephone1stringPhone number
birthdatedateDate of birth
acwapp_idnumberstringPassport / ID number
acwapp_identificationtypepicklistSee Get or Update Identification Type
acwapp_preferredlanguagepicklistacwapp_language
acwapp_countrycallingcodestringDialling code, no +
acwapp_nationalitylookup→ acwapp_nationality
acwapp_currentlivinglocationlookup→ acwapp_country
acwapp_countrylookuplookup→ acwapp_country
wvlv_checkin_selfie_urlstringSelfie — see Upload Check-in Selfie

A second picklist wvlv_identification_type exists on Contact but is not used by this integration.

Lead — lead

FieldTypeNotes
leadidGUIDPrimary key
wvlv_purpose_of_staypicklistSee Update Purpose of Stay
acwapp_howdidyouhearaboutuspicklistSee Update Hear About Us
acwapp_noofroomsintegerNumber of rooms

Property Unit / Room — acwapp_propertyunit

FieldTypeNotes
acwapp_propertyunitidGUIDPrimary key
acwapp_cleaningstatuspicklistRoom readiness — see Get Room Status
wvlv_has_checkin_kioskbooleanWhether this property has a kiosk
wvlv_main_entrance_codestringBuilding entry code
wvlv_main_entrance_image_urlstringEntrance photo
acwapp_shortstaycheckintimestringProperty check-in time
acwapp_shortstaycheckouttimestringProperty check-out time
wvlv_self_checkout_email_template_idstringFolio 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.

ItemStatusDetail
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.

ItemStatusDetail
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.

TopicDecision
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 #

VersionDateSummary
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.
End of document

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.