Power Pages WebAPI with $pages Client API: Modern Dataverse CRUD Implementation Guide
If you have worked with Power Pages, you probably got stuck at some point using webapi.safeAjax for Dataverse operations. Microsoft introduced the $pages Client API to simplify how developers interact with forms, authentication, Dataverse data, and multilingual configuration directly from custom HTML and JavaScript pages.
Initialize Client API
The $pages Client API is not automatically available on page load. You must ensure it is initialized before calling its methods.
Microsoft.Dynamic365.Portal.onPagesClientApiReady(($pages) => { const forms = $pages.currentPage.forms.getAll(); console.log(`Found ${forms.length} forms on the page.`); });Promise or async/await-based readiness
let $pages = await Microsoft.Dynamic365.Portal.onPagesClientApiReady(); const forms = $pages.currentPage.forms.getAll();$pages Client API Objects
The Client API exposes the below objects
- currentPage
- user
- webAPI
- languages
currentPage Object
Provides access to forms and components available on the current page.
The below piece of code will get all the forms inside the current page
let forms = $pages.currentPage.forms.getAll();The below piece of code will get all the list inside the current page
let lists = $pages.currentPage.lists.getAll();user Object
Allows programmatic sign-in and sign-out operations.
The below line of code will sign the user into the site.
$pages.user.signInThe below line of code will sign the user into the site.
$pages.user.signOutweb API Object
Supports Create, Retrieve, and Retrieve Multiple operations in Dataverse.
Create
$pages.webAPI.createRecord('contacts', { firstName: 'John', lastName: 'Doe' });Retrieve
let record = await $pages.webAPI.retrieveRecord('accounts', 'aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb', '$select=name');Retrieve Multiple
let records = await $pages.webAPI.retrieveMultipleRecords('accounts','$select=name&$top=3');languages Object
Retrieves the list of configured languages for the website.
let allLanguages = $pages.languages.getAll();Demonstration: Custom Appointment Form
A Bootstrap-based custom HTML page submits form data into the Appointment table in Dataverse. Choice values are fetched from the StringMaps table using $pages.webAPI.retrieveMultiple and records are created using $pages.webAPI.createRecord
Below is the HTML code of the page
<div class="container mt-2">
<h2 class="text-center">Book Appointment</h2>
<hr />
<div class="row mt-2">
<div class="col-lg-4 col-md-6">
<div class="input-group mb-3">
<span class="input-group-text rounded-0"><i class="bi bi-person"></i></span>
<input type="text" class="form-control rounded-0" id="mw_firstname" placeholder="Firstname" />
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="input-group mb-3">
<span class="input-group-text rounded-0"><i class="bi bi-person"></i></span>
<input type="text" class="form-control rounded-0" id="mw_lastname" placeholder="Lastname" />
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="input-group mb-3">
<span class="input-group-text rounded-0"><i class="bi bi-telephone"></i></span>
<input type="text" class="form-control rounded-0" id="mw_mobilenumber" placeholder="Mobile Number" />
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="input-group mb-3">
<span class="input-group-text rounded-0"><i class="bi bi-envelope"></i></span>
<input type="text" class="form-control rounded-0" id="mw_emailaddress" placeholder="Email Address" />
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="input-group mb-3">
<span class="input-group-text rounded-0"><i class="bi bi-pencil"></i></span>
<textarea class="form-control rounded-0" id="mw_subject" placeholder="Subject" rows="1"></textarea>
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="input-group mb-3">
<span class="input-group-text rounded-0"><i class="bi bi-calendar-event"></i></span>
<input type="date" class="form-control rounded-0" id="mw_appointment_date" placeholder="Appointment Date" />
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="input-group mb-3">
<span class="input-group-text rounded-0"><i class="bi bi-clock"></i></span>
<select name="appointmentSlot" id="mw_appointment_slot" class="form-control rounded-0">
<option value="null" disabled selected>Select your slot</option>
</select>
</div>
</div>
<div class="col-lg-4 col-md-6">
<button class="btn btn-outline-info rounded-0 w-100" id="bookAppointment">Book Appointment</button>
</div>
</div>
</div>Below is the JS Code
Microsoft.Dynamic365.Portal.onPagesClientApiReady(async ($pages) => {
const firstNameField = document.getElementById("mw_firstname");
const lastNameField = document.getElementById("mw_lastname");
const emailAddressField = document.getElementById("mw_emailaddress");
const mobileNumberField = document.getElementById("mw_mobilenumber");
const subjectField = document.getElementById("mw_subject");
const aptDateField = document.getElementById("mw_appointment_date");
const slotField = document.getElementById("mw_appointment_slot");
const bookAptBtn = document.getElementById("bookAppointment");
// retrieve multiple stringmaps records for fetching options of slot choice
let slotOptions = await $pages.webAPI.retrieveMultipleRecords('stringmaps', "$select=attributename,attributevalue,displayorder,objecttypecode,value&$filter=attributename eq 'mw_appointment_slot' and objecttypecode eq 'mw_appointment'&$orderby=displayorder asc");
slotOptions.value.forEach((opt) => {
let newSlotOption = document.createElement("option");
newSlotOption.text = opt.value;
newSlotOption.value = opt.attributevalue;
slotField.appendChild(newSlotOption);
});
bookAptBtn.addEventListener("click", function () {
try {
// create an appointment record
$pages.webAPI.createRecord('mw_appointments', {
"mw_firstname": firstNameField.value,
"mw_lastname": lastNameField.value,
"mw_mobilenumber": mobileNumberField.value,
"mw_emailaddress": emailAddressField.value,
"mw_appointment_date": aptDateField.value,
"mw_appointment_slot": slotField.value,
"mw_subject": subjectField.value,
"mw_appointment_status": 2000,
}).then(
function success(){
// show alert after appointment booked
alert(`Appointment Booked on ${aptDateField.value} and the your slot is ${slotField.options[slotField.selectedIndex].text}`);
// refresh the current page
history.go(0);
}
);
} catch (err) {
console.error(err.message);
}
});
});Below is the image after the appointment is booked
References
- Power Pages Client APIs Overview (preview) | Microsoft Learn
- Power Pages Client API supported controls (preview) | Microsoft Learn
Have a great day!
Originally published at https://www.tamilarasu.blog on February 15, 2026.
