AB-400 replaces PL-400 on 16 October 2026. Same certification, new exam, and most of what you already studied still counts.
By Vlad CatrinescuMicrosoft MVP and MCT · Pluralsight Author
The AB-400 Study Guide helps you prepare for Exam AB-400: Extending Microsoft Power Platform Solutions with Code and AI. If you came here looking for PL-400, you are in the right place. It is the same certification, Microsoft Certified: Power Platform Developer Associate, and only the exam code changed. Registration for PL-400 closes on 16 October 2026, which is the day registration opens for AB-400, and if you registered for PL-400 on or before that date you can still schedule and sit it through 30 October 2026.
Everything here lines up with the skills Microsoft measures: free Microsoft Learn paths and my own study notes for self-study, plus the courses and practice tests I recommend when you want more. No exam dumps, ever.
Exam length
100 min
Passing score
700 / 1000
Skills measured
4 domains, 88 skills
Guide reviewed
September 2026
Resources by the way you like to study
16 hand-picked, free and paid
On-Demand Video Training
3 resources
On-demand training lets you learn at your own pace, on your own schedule: expert-led video courses from Pluralsight or Udemy, or hands-on modules from Microsoft Learn, whenever you need them.
Not all platforms are the same. Pluralsight relies on vetted authors and curated content, while marketplaces vary in quality. I only recommend courses that are highly rated and closely aligned with the skills you need, and I still encourage reading the course details and reviews before enrolling. Every course below was built for PL-400, and because the two exams share most of their content it still covers the majority of AB-400. Each description says what it does not reach yet.
Recommended
Microsoft Power Platform Developer (PL-400) Certification Path
Practice test includedFree trial
The certification path that covers Dataverse extensibility end to end: plug-ins, the Power Apps component framework, client scripting, platform APIs and application lifecycle management. It was built to the PL-400 skills measured, so it does not reach code apps or Microsoft Foundry agents yet, but it covers almost all of the heaviest AB-400 domain.
A follow-up course for anyone who has already studied the PL-200 material, covering the extra development content the developer exam adds. Current to the last PL-400 update, so check the course notes for AB-400 content before you rely on it for the newest skills.
A free community playlist on building low-code, automated and intelligent solutions with Power Apps, Power Automate and Copilot Studio. Useful background and product context rather than exam-aligned training, and it predates the AB-400 update.
These are practice exams, not dumps. Dumps ruin the value of a certification for everyone. Practice tests are a great way to check you are ready once you have studied everything in this guide.
All three below were written for PL-400, and Microsoft itself still points the AB-400 study guide at the PL-400 practice assessment. Treat a good score as evidence that you know the carried-over material, not the new code app and agent skills.
Recommended
Practice Assessment for Exam PL-400: Microsoft Power Platform Developer
Free
Microsoft's own free practice assessment, and the one the AB-400 study guide page still links to. It is the closest you will get to the real style, wording and difficulty of the questions, and it tells you which skill areas need more work.
Timed practice exams built to feel like the real thing, covering developing end-to-end Power Platform solutions and extending the platform beyond its out-of-the-box features. Written against the PL-400 skills measured.
Exam PL-400: Microsoft Power Platform Developer Associate
Nine practice quizzes written to mirror the real exam, sold on their own or bundled with the video course. A reasonable second opinion once you have worked through the Microsoft practice assessment, and again written for PL-400.
Microsoft Learn is a great free way to learn the AB-400 content. It is mostly text-based articles, with small quizzes at the end of every module.
The paths below were published for PL-400 and still cover the plug-in, component framework, integration and application lifecycle management skills that carried over. Microsoft has not yet published paths for Power Apps code apps or Microsoft Foundry agent integration, so use the product documentation linked from each skill in my study notes for those two areas.
Introduction to developing with Microsoft Power Platform
Free
Where to start if you are new to extending Power Platform: the extensibility points, how configuration and code fit together, and the developer tooling. It maps to the design decisions in domain 1.
Plug-ins, the event execution pipeline, custom APIs and the Plug-in Registration Tool. This is the single most valuable free path for the exam, because domain 3 is 35-40% of it and this is most of domain 3.
Extend the user experience with client scripting and command bar customization
Free
The Client API object model, form and column events, and modern commands with Power Fx and JavaScript. It covers the first skill group of domain 2 almost exactly.
Build basic code components with the Power Apps Component Framework
Free
The component manifest, the lifecycle methods and packaging a component into a solution. Work through it with the CLI open rather than just reading, because the exam asks about the commands too.
Service endpoints, webhooks, Azure Service Bus and Azure Functions listening to Dataverse events. It covers the Azure Functions group in domain 3 and the first group of domain 4.
Get started with custom connectors for Microsoft Power Platform
Free
Building a connector from an OpenAPI definition, the authentication types, policy templates and custom code. Custom connectors moved into the integrations domain on AB-400, and this path still covers them.
Automate a business process with expressions and Dataverse actions using Power Automate
Free
Flow expressions, error handling and calling Dataverse actions from a cloud flow. It maps to the cloud flows skill group in domain 3, minus the new Copilot Studio workflow material.
Basic application lifecycle management in Microsoft Power Platform
Free
Solutions, layers, dependencies, environment variables and moving a solution between environments. Application lifecycle management is a named skill group in domain 1 and this path covers it.
Use advance techniques in canvas apps to perform custom updates and optimization
Free
Advanced canvas app formulas, patching and performance tuning. AB-400 dropped the advanced canvas app skills that PL-400 measured, so this is background reading for real projects rather than exam preparation now.
This is the Microsoft Official Course, which you can schedule at a Microsoft learning partner. The classes are presented by Microsoft Certified Trainers. It is the best way to learn any topic, since you can ask a live instructor questions, and also the most expensive one.
Course AB-400T00-A: Extend Microsoft Power Platform Solutions with code and AI
Instructor-led
The official Microsoft instructor-led course for AB-400, delivered by a Microsoft Certified Trainer at a learning partner. It works through extending Power Platform with code, plug-ins and AI, the same ground the exam measures.
Some links on this page are affiliate links. If you use them, I may earn a commission at no extra cost to you.
Skills measured and study notes
88 skills, free to study here
0 of 88 studied
AB-400 replaces PL-400 on 16 October 2026, and the certification you earn is still Microsoft Certified: Power Platform Developer Associate. If you have already been studying for PL-400, most of that work still counts. Plug-ins, the Power Apps component framework, client scripting, platform APIs, Azure Functions, Dataverse events and custom connectors carry over almost unchanged. What left the exam is the canvas app depth and the standalone troubleshooting domain. What arrived is Power Apps code apps, Microsoft Foundry agents, Model Context Protocol servers and Copilot Studio workflows, and those four are where your new study time should go.
These notes are written to the skills measured as of 16 October 2026, the edition that takes effect the day AB-400 replaces PL-400. I have broken down every skill Microsoft measures below, with an explanation, the facts the exam can actually ask, and a Microsoft Learn link for each one. Here is how the four domains break down by weight:
Tip: Domain 3 is 35-40% of the exam on its own, and it is almost entirely classic Dataverse extensibility: plug-ins, platform APIs and Azure Functions. Get that domain solid first, then spend your remaining time on the code apps and agent material in domains 1 and 2, which is the genuinely new ground.
Domain 1Build Microsoft Power Platform Solutions15-20% of the exam0 / 22 studied
This domain tests the decisions you make before you write anything: which component type fits the requirement, where business logic belongs, when Dataverse security features change your design, and how the solution moves between environments. It is the lightest domain by weight, but it is the one that decides whether the rest of your build is sensible.
Design the technical architecture
01
Analyze the technical architecture to identify solution components and their implementation approach
Studied
A Power Platform solution is a container of components, and almost everything customizable is a component: tables, columns, forms, views, apps, flows, plug-in assemblies, custom APIs and connectors. Before you design anything you need to know what the moving parts are and how they nest, because dependencies between them govern the order you build and deploy in.
What you need to know
A solution has a maximum size of 95 MB, so large file and image resources do not belong inside one
Components nest: a table contains its columns, forms, views, charts, relationships and business rules, and a dependent component cannot exist outside its parent
The SolutionComponent table's ComponentType options list is the authoritative list of what can be added to a solution
A typical integration architecture splits into three layers: the Power Platform layer (apps, Dataverse, plug-ins, flows), the Azure layer (Service Bus, Functions, Key Vault, managed identity), and the external system
Architecture analysis covers environments and their data location, platform limits, and the high availability and disaster recovery story, not just the component list
Design authentication and authorization for solution components
Studied
Any code that talks to Dataverse without a signed-in human needs an application user, which is a Dataverse user record bound to a Microsoft Entra ID app registration. This is the single most common integration design question, and the exam tests whether you know the pieces and the order you create them in.
What you need to know
An application user is created from a Microsoft Entra ID app registration, and is linked through the SystemUser columns ApplicationId, ApplicationIdUri and AzureActiveDirectoryObjectId
An application user is unlicensed and non-interactive, does not count against the non-interactive user cap, and must be assigned a security role before it can do anything
pac admin create-service-principal --environment {id} creates the Entra ID app and registers it with Power Platform in one command
Only one application user per registered app per environment is allowed
In multi-tenant ISV scenarios the security role ships inside a managed solution, because the application user record itself can never be included in a solution
The Dataverse connector in Power Automate supports "Connect with service principal" instead of an interactive sign-in
Exam tip: A service principal connecting to Power Platform admin APIs is treated as holding the Power Platform Administrator role, and granular roles cannot scope it down. If a question asks you to limit what an automation can do, the answer is the Dataverse security role on the application user, not the app registration.
Determine whether requirements can be met with out-of-the-box functionality
Studied
The exam rewards the developer who reaches for code last. Dataverse already gives you business rules, real-time workflows, calculated and rollup columns, row level security, field level security and auditing, and a requirement that one of those covers does not justify a plug-in.
What you need to know
Business rules run on both canvas and model-driven apps when they are defined at the table level, and need no code
Calculated and rollup columns cover derived values and aggregates without custom logic
Dataverse gives you row level security, field level security profiles and audit logging natively, which is the usual reason to choose it over SharePoint or SQL
Document generation has an out-of-the-box path through Word and PDF document templates, which an administrator can edit without a developer
Command bar configuration and business process flows cover most user interface guidance without custom UI
Determine where to implement business logic including cloud services, client-side processing, business rules, plug-ins, and automation
Studied
Every logic option has a place where it runs and a set of callers it applies to, and that is what decides the answer. Logic that must hold no matter who calls belongs in the platform, and logic that only improves the experience of one screen belongs on the client.
What you need to know
Option
Runs where
Applies to
Business rule
Client and server, no code
All apps using that table or form
Client scripting and Power Fx
Browser, in the app only
One model-driven form or command
Plug-in
Dataverse, inside the transaction
Every caller: apps, flows, API, imports
Power Automate cloud flow
Cloud, asynchronous
Whatever triggers it, across connectors
Azure Function
Azure, outside the pipeline
Called from a webhook, Service Bus or flow
A plug-in is the right answer when the rule must be enforced regardless of the caller, because an error in a synchronous plug-in rolls the whole transaction back
A cloud flow does not block the app, and can run with elevated connection permissions so the interactive user needs fewer privileges
Azure Functions provide compute a plug-in cannot host, since plug-in assemblies must be self-contained and cannot reference external libraries
A custom API is better than chaining event-driven plug-ins when one business event spans several tables and should be callable on demand
Determine when to use standard tables, virtual tables, elastic tables, or connectors
Studied
Dataverse now offers three table kinds and they behave differently enough that picking the wrong one is expensive to undo. The exam asks this as a scenario: high volume and changing shape points at elastic, live external data points at virtual, and relational integrity points at standard.
What you need to know
Table kind
Storage
Reach for it when
Standard
Dataverse relational store
You need relationships, transactions, strong consistency
Elastic
Azure Cosmos DB behind Dataverse
Schema changes, huge volume, horizontal scale
Virtual
Stays in the external system
Data must not be copied and is read live
An elastic table's default page size is 500 rows, against 5,000 for a standard table
Elastic tables do not support business rules, charts, business process flows, alternate keys, rollup and calculated columns, or N:N relationships with standard tables, and they have no multi-record transaction, so a failing synchronous plug-in does not roll back the created row
Virtual tables support only organization-owned ownership, so there is no row level or field level security, no auditing, no Dataverse search and no charts
A table can never be converted between virtual and standard after it is created
CreateMultiple, UpdateMultiple and DeleteMultiple give roughly ten times the throughput on elastic tables within the same API limits
Exam tip: If the scenario mentions telemetry, sensor readings or logs arriving in the tens of millions, the answer is an elastic table. If it mentions a system of record that must stay authoritative elsewhere, the answer is a virtual table.
Assess the impact of Microsoft Power Platform security features on solution components, including managed environments, data policies, security roles, teams, business units, and row sharing
Studied
Security features are not a separate workstream from development, because each of them can stop your component from running. A data policy can block the connector your app depends on, and a security role can hide the table your plug-in reads.
What you need to know
A security role grants privileges (Create, Read, Write, Delete, Append, Append To, Assign, Share) at one of five access levels: None, User, Business Unit, Parent: Child Business Unit, Organization
Access is cumulative across every role a user holds, so the most permissive assignment wins
Owner teams own records and hold security roles; access teams never own records or hold roles and grant access purely through row sharing
Row sharing is an exception mechanism, one record at a time, and Microsoft documents it as lower performing and harder to troubleshoot than roles and teams
Data policies classify every connector as Business, Non-Business or Blocked, and a resource cannot mix a Business and a Non-Business connector; no policy exists by default and the most restrictive combination wins
Managed Environments is a licensing entitlement that adds environment groups, sharing limits, pipelines, solution checker enforcement, IP firewall and Application Insights export
Determine when to use deterministic or non-deterministic methods to implement business logic
Studied
This skill is new language for a decision developers now make constantly: does this step need to produce the same answer every time, or is it a judgement call a model should make. Validation, calculation and approvals are deterministic. Interpreting a free-text request is not.
What you need to know
Copilot Studio documents topics and agent flows as deterministic, meaning the same inputs always produce the same output
Plug-ins, business rules and cloud flows are deterministic by nature, which is what makes them auditable
An agent reasoning over a prompt is non-deterministic, so it is the wrong place for anything that must be reproducible or defensible
A single agent can combine both: deterministic tools and workflows for the actions, model reasoning for the decision about which action to take
Agent flows cannot be copied, shared or given co-owners the way Power Automate cloud flows can, and they bill through Copilot Studio consumption rather than a per-flow licence
Determine when to use code apps, generative pages, Power Apps component frameworks (PCFs), web resources, interactive agents, and Model Context Protocol (MCP) app widgets to implement the user experience
Studied
Six ways to build a custom interface, and the exam wants the one that fits the requirement. The short version: a component inside an app is PCF, a whole app in your own framework is a code app, and a web resource is the legacy option you keep for existing code.
What you need to know
A PCF code component renders in the same context and loads at the same time as every other component on the form, which an HTML web resource does not
PCF gives you device features (camera, location, microphone), Web API access and platform dialogs, none of which a web resource has
Web resources are static files only (HTML, CSS, JScript, XML, PNG, JPG, GIF, ICO, XSL, SVG, RESX) with no server-side execution, and their size ceiling is the Organization.MaxUploadFileSize setting, 5 MB by default
A code app is a full application you build locally with your own framework and publish into Power Apps; the environment feature must be switched on by an administrator and users need a Power Apps Premium licence
Generative pages are AI-generated React pages inside a model-driven app, built from a description plus up to six linked Dataverse tables
A component whose manifest declares external-service-usage becomes premium and requires a Power Apps licence
Determine when to use Microsoft Foundry or Microsoft Copilot Studio to build agents
Studied
Both build agents, and Microsoft positions them as complementary rather than competing. Copilot Studio is the low-code studio with Power Platform governance built in. Microsoft Foundry is the code-first service for developers who need their own framework, their own model and container-level control.
What you need to know
Copilot Studio: graphical authoring, hundreds of prebuilt and custom connectors, and governance through the Power Platform admin center using the same environments, data policies and security roles as apps and flows
Microsoft Foundry: two agent types, prompt agents (instructions plus model plus tools, with Foundry running them) and hosted agents (your own code and framework, shipped as a container or zip with a managed endpoint and its own Entra identity)
Foundry hosted agents support Agent Framework, LangGraph, the OpenAI Agents SDK, the Anthropic Agent SDK and the GitHub Copilot SDK
A Copilot Studio agent can bring its own model hosted in Foundry, and can call a Foundry agent; a Foundry agent can call a Copilot Studio agent
Copilot Studio agents get native Power Platform application lifecycle management through pipelines and solutions, which is often the deciding factor for a business solution
Design Power Apps reusable components including generative pages, code components (PCF), and client scripting
Studied
Reuse in Power Platform means one definition consumed in many places, and the shape of that definition differs by component type. A PCF component is a packaged project, a generative page is solution-aware, and client scripting is a shared JavaScript web resource referenced from multiple handlers.
What you need to know
A PCF project has three parts: the manifest (ControlManifest.Input.xml), the TypeScript implementation, and the resource files
The same PCF definition works in both model-driven and canvas apps, and only the configuration differs
pac pcf init --namespace {namespace} --name {name} --template field|dataset scaffolds the project, and --framework react scaffolds a React control
In canvas apps a code component is embedded as a copy inside the app definition, so two apps in one environment can run different versions, and a maker has to accept an "Update code components" prompt to move forward
jQuery must not be used in form scripts or commands; only the documented Client API is supported
Generative pages created during the original preview must be opened once in the app designer to migrate to the solution-aware data model before they can be added to a solution
Design custom connectors and MCP servers for use with Microsoft Power Platform solutions
Studied
A custom connector wraps a REST API so makers can use it like any other connector. An MCP server does something related but different: it exposes tools an agent can call, and Dataverse now ships one of its own.
What you need to know
A custom connector is built from an OpenAPI definition, a Postman collection, or from scratch, and can be shared inside one tenant or submitted to Microsoft for certification
Certified connectors are Premium tier only, and certification takes roughly three to five business days plus another seven to ten for global rollout
Dataverse exposes an MCP server at https://{orgName}.crm.dynamics.com/api/mcp
The Dataverse MCP server is enabled by default only for the Copilot Studio client; other clients such as VS Code, GitHub Copilot CLI and Claude must be allowed per environment
Governing MCP client access through advanced connector policies requires a Managed Environment
A connector title submitted for certification has a 30 character limit and cannot contain "API", "Connector" or a Power Platform product name
Design Dataverse code components, including plug-ins and custom APIs
Studied
The design question is whether your logic reacts to something or is invoked deliberately. A plug-in step reacts to a message on a table. A custom API is your own message, which anything can call on demand.
What you need to know
A custom API is a developer-defined Dataverse message, usually backed by a plug-in, and it replaces the older custom process action
Custom APIs support typed request and response parameters: Boolean, DateTime, Decimal, Entity, EntityCollection, EntityReference, Float, Integer, Money, Picklist, String, StringArray and Guid
They can be created four ways: the Plug-in Registration Tool designer, the Power Apps solution forms, code against the CustomAPI tables, or solution XML with pac solution init
Unique Name, Allowed Custom Processing Step Type, Binding Type and Bound Entity Logical Name are locked once saved, so a mistake means delete and recreate
Low-code Power Fx plug-ins run in the same Dataverse transaction as .NET plug-ins and can return validation failures with Error()
Custom API table data cannot be surfaced in a model-driven app
Design automations, including Power Automate cloud flows and Copilot Studio workflows
Studied
Copilot Studio now has its own automation surface, so "which automation tool" has a third answer. Cloud flows still live in Power Automate, and the Copilot Studio surfaces exist so an agent can call deterministic logic.
What you need to know
Copilot Studio has two automation surfaces: workflows, on the redesigned visual canvas, and agent flows, which author like Power Automate but run natively in Copilot Studio
To be callable as an agent tool, a flow needs the "When an agent calls the flow" trigger and a "Respond to the agent" action, must be published, and must respond synchronously
An agent-callable flow has a hard 100 second response limit
Copilot Studio's Flows page lists only flows created there; an existing cloud flow can be converted to an agent flow, and that conversion is one way
Microsoft's guidance is to keep heavy data transformation out of cloud flows and push it into Dataverse logic, a custom connector over an Azure Function, or a dataflow
Design inbound and outbound integrations using Dataverse and Microsoft Azure
Studied
Outbound means Dataverse tells Azure something happened. Inbound means an external system reads or writes Dataverse. The exam wants you to match the pattern to the volume, the latency and whether a listener is guaranteed to be there.
What you need to know
Three native outbound patterns exist: webhooks, Azure Service Bus and Azure Event Hubs, all registered as service endpoints
A webhook is the lightweight option, an HTTP POST with a JSON payload, and it is the only one of the three that supports synchronous steps
Azure Service Bus is the high-scale option with a real queuing mechanism, and it supports asynchronous steps only
Azure Event Hubs is the streaming option for high-throughput telemetry, registered with a body format of XML or JSON
Inbound integration uses the Dataverse Web API or Organization service directly, a virtual table for live external data, or a custom connector
Azure Functions usually sit on the receiving end of an outbound pattern, or are called from a flow for work a plug-in has no time for
Design custom interfaces by using Power Apps code apps
Studied
A code app is the answer when the interface you need is a real front-end application rather than a configured app. You build it locally in your own framework, wire Power Platform data in through the CLI, and publish it into the environment.
What you need to know
Code apps are CLI-driven: pa app init wires the project to an environment, pa app run serves it locally and pa app push publishes it
The "Power Apps code apps" environment feature must be switched on in the Power Platform admin center before anything can be published
End users need a Power Apps Premium licence to run a code app
Code apps do not support Git integration, Power BI native data integration, SharePoint forms integration, or Power Apps for Windows
Code apps do inherit connector consent, sharing limits, app quarantine, data policy enforcement at launch and per-app Conditional Access
Because code apps authenticate through Microsoft Entra ID, network restriction is done with Conditional Access rather than SAS IP restriction
Exam tip: Code apps are new enough that the limitation list is itself examinable. If a scenario requires source control integration or a SharePoint form, a code app is the wrong answer.
Design custom agents that integrate with Microsoft Power Platform
Studied
An agent that is going to act on Power Platform data needs a route in, a governed identity and somewhere a human can see what it did. Microsoft's current answer to all three is an MCP server plus the agent feed in a model-driven app.
What you need to know
The Power Apps MCP Server is the surface an agent uses to automate repetitive app tasks, with the agent feed inside the app for human review and handoff
Connecting an agent means building it in Copilot Studio, adding the Power Apps MCP Server tool, setting credentials to maker-provided, adding a Dataverse trigger, and selecting Add to feed in the app designer
Agent feed supervision works only in model-driven apps
Default access to the agent feed is limited to the System Administrator and System Customizer roles
A custom agent built outside Copilot Studio integrates through the Dataverse MCP server or through a Foundry agent connected from Copilot Studio
Design monitoring and alerting, including Azure Monitor Application Insights and Microsoft Power Platform monitor alerts
Studied
Two monitoring surfaces exist and they do not overlap completely. Power Platform Monitor is in-product and needs no Azure subscription. Application Insights is deeper, queryable and alertable, and it needs a managed environment for flow telemetry.
What you need to know
Power Automate exports telemetry to exactly two Application Insights tables: Requests for cloud flow runs, and Dependencies for triggers and actions
That export works only on Managed Environments
A "Failed requests" alert maps to flow run failures, and a "Dependency call failures" alert maps to trigger or action failures
Alerts are ordinary Azure Monitor alert rules wired to an action group, which can email or call a webhook back into a flow
A custom log search alert can use Kusto against customDimensions['environmentId'] and customDimensions['resourceProvider'] to scope an alert to one specific flow
Application Insights also covers Dataverse platform telemetry, canvas app telemetry and Copilot Studio agent telemetry
A dependency is Dataverse telling you that one component needs another, and it is what blocks your import or your delete. Knowing the two kinds and how to see them is most of this skill.
What you need to know
Solution-level dependency: a managed solution can require a base managed solution, and Dataverse blocks both installing without it and uninstalling the base while a dependent solution is present
Component-level dependency comes in two kinds: internal dependencies, such as a form on its table, which cascade automatically, and published dependencies, such as a lookup column on a form, which you must remove by editing the component and publishing
Four messages read dependencies: RetrieveDependentComponents, RetrieveRequiredComponents, RetrieveDependenciesForDelete and RetrieveDependenciesForUninstall
The Show dependencies command lists both dependent and required components and lets you open or remove them
If the unmanaged layer depends on a managed component, that dependency has to go before the managed solution can be uninstalled
Environment variables are how one solution carries different values in development, test and production without anyone editing components. The exam tests the type list and the difference between the default value and the current value.
What you need to know
Six data types: Decimal number, Text, JSON, Two options, Data source and Secret
A Secret type variable stores its value in Azure Key Vault; a client secret stored as Text is unencrypted and is the wrong answer
A variable has two value rows: the Default Value, which ships with the solution, and the Current Value, which is the environment-specific override
Removing the Current Value before export lets the target environment keep or supply its own
Dataverse data sources need no environment variable, but SharePoint, Microsoft Entra ID authenticated SQL Server and code app data sources do
Custom connectors can reference them with @environmentVariables("name"), but the connector must be re-saved after a value changes because the new value is not read live
Layering is how Dataverse decides which version of a customization wins when several solutions touch the same component. There is one unmanaged layer and a stack of managed layers under it, and the top one wins.
What you need to know
There is a single unmanaged layer holding all unmanaged customizations, and a stack of managed layers in install order with the system layer at the base
Within one managed solution, sub-layers can include the base layer, patches stacked above it with the most recent on top, and a pending upgrade layer
Microsoft explicitly does not recommend patches
Merge is top wins for almost every component type, with model-driven app, form and site map using special merge logic instead
Applying an upgrade flattens everything into a new base, and unlike a patch, an upgrade can delete components that are no longer in the new version
A managed component cannot be edited directly; adding it to an unmanaged solution creates an unmanaged layer that then blocks uninstalling the managed solution
Exam tip: If a customization is not taking effect, the answer is nearly always an unmanaged layer sitting on top of the managed one. Look at the component's Solution Layers view before anything else.
Implement and extend Microsoft Power Platform Pipelines
Studied
Pipelines are the in-product deployment path, and the "extend" half of this skill is new: pipelines now raise business events that a cloud flow can handle, which is how you add approvals and validation gates.
What you need to know
A pipeline can have up to seven deployment stages, and needs at least one development environment and one stage before it can run
Target environments other than the host must be Managed Environments
Deployments run in strict sequential stage order for a solution version, so a stage cannot be skipped
Extension points are Dataverse business events surfaced as Power Automate triggers: OnDeploymentRequested, OnApprovalStarted, OnPreDeploymentStarted, OnDeploymentCompleted and others
Three gated extension settings exist: Pre-export step required (first stage only), Is delegated deployment (runs under a service principal or the stage owner), and Pre-deployment step required
pac pipeline deploy and pac pipeline list drive a pipeline from the CLI, and personal pipelines created in Power Apps cannot be extended
Create Continuous Integration/Continuous Deployment (CI/CD) automations using Microsoft Power Platform Build Tools
Studied
Build Tools is the Azure DevOps extension that turns solution export and import into pipeline tasks. The exam asks about the task names, the order and the authentication.
What you need to know
The Power Platform Tool Installer task must run before any other Build Tools task
The standard source control flow is Export, then Unpack to XML for the repository, then later Pack and Import
Unpack is recommended as Unmanaged and Pack as Managed
The Power Platform Checker task runs static analysis against an exported solution file, not against unpacked source
Large solutions need asynchronous import, because the synchronous import task times out after roughly four minutes
Authentication uses an Azure DevOps service connection, either PowerPlatformSPN with a service principal, which is the documented preference, or PowerPlatformEnvironment with a username and password
A deployment settings file supplies connection references and environment variable values for the target, so nobody types them into the UI
Domain 2Extend the User Experience25-30% of the exam0 / 22 studied
This domain is where the exam changed most visibly. Client scripting and the Power Apps component framework are almost exactly as they were on PL-400, and then two entirely new skill groups appear for Power Apps code apps: building them, and deploying and managing them. If you are coming from PL-400, the first two groups are revision and the last two are new material.
Apply business logic in model-driven apps by using client scripting
23
Build code that targets the Client API object model
Studied
Client scripting in a model-driven app talks to the form through a documented object model rather than the browser DOM. Getting the entry points right is the whole skill, because everything else hangs off them.
What you need to know
The object model has four root objects: the execution context, the form context, the grid context and the global Xrm object
executionContext.getFormContext() is how you get the form context, and it is the supported replacement for the deprecated Xrm.Page
formContext has two main branches: data (with attributes, entity and process) and ui (with formSelector, navigation, process, tabs and controls)
formContext.data.entity gives you getId(), getEntityName() and save()
The Xrm.Internal namespace and anything in the HTML DOM outside the documented object model are unsupported and can change without notice
Getting the form context inside a ribbon or command action works differently from getting it in a form script
Exam tip:Xrm.Page still works for backward compatibility but is deprecated. Any answer that writes new code against Xrm.Page instead of executionContext.getFormContext() is the wrong one.
Registering a handler is a configuration step, not a code step, and the exam asks about the event list, the limits and the execution context checkbox that trips everybody up.
What you need to know
The registrable events are OnLoad and OnSave on the form, TabStateChange on a tab, OnChange on a column, and OnReadyStateComplete on an IFRAME
Each element supports up to 50 event handlers
Registration takes the library web resource, the function name, an optional comma-separated parameter list, an Enabled checkbox and the "Pass execution context as the first parameter" option
An On Save handler can declare table column dependencies so the event fires for changes to those columns
Event handlers are not called in bulk edit mode by default; enabling one means setting BehaviorInBulkEditForm to Enabled in the form XML, and that is currently supported for OnLoad events only
formContext.ui.addOnLoad and removeOnLoad manage the OnLoad collection programmatically as an alternative to the designer
Create client scripting that targets the Dataverse Web API
Studied
From inside a model-driven app you never construct your own authenticated request. Xrm.WebApi does it for you, because the script is already running in an authenticated context.
What you need to know
Xrm.WebApi.online exposes createRecord, retrieveRecord, retrieveMultipleRecords, updateRecord, deleteRecord, execute and executeMultiple
retrieveMultipleRecords accepts OData options ($select, $top, $filter, $expand, $orderby) or a FetchXML string; OData options must be URL encoded and FetchXML must not be
Without an explicit maxPageSize, the default is 5,000 records for standard tables and 500 for elastic tables, and the response carries a nextLink when there is more
execute calls bound and unbound actions and functions, such as WhoAmI or CalculateRollupField
Associate accepts multiple related records in one call, but Disassociate handles only one at a time
Use Associate and Disassociate for collection-valued relationships, and updateRecord for single-valued navigation properties such as lookups
Configure commands and buttons by using Power Fx and JavaScript
Studied
Modern commands replaced ribbon XML for most scenarios, and they brought Power Fx with them. The exam tests which of the two languages can do what, and what modern commands still cannot do.
What you need to know
Modern commands support both "Run formula" with Power Fx and "Run JavaScript"; classic commands support JavaScript only
Dataverse is currently the only data source supported for Power Fx in commands
Choosing Power Fx the first time creates a command component library to hold the formulas, and you can still use JavaScript afterwards
A single command cannot call multiple JavaScript libraries or multiple functions
Command visibility is either Show or "Show on condition from formula", which replaces most classic visibility rules with one declarative expression
Modern commands are app-scoped by default, unlike classic commands, which apply across apps unless restricted
Modern commands cannot populate a flyout dynamically from code, customize the global application header, or customize obsolete ribbon locations
Implement navigation, dialogs, and notifications by using the Client API
Studied
Anything that opens something or tells the user something goes through Xrm.Navigation or the notification methods on the form and control. The exam asks which method opens what, and which notification blocks a save.
Opening a main form as a modal dialog requires navigateTo with pageType of entityrecord and target of 2; openForm cannot do it
openConfirmDialog returns a promise resolving to an object with a confirmed boolean
formContext.ui.setFormNotification(message, level, uniqueId) takes a level of ERROR, WARNING or INFO, and is cleared with clearFormNotification(uniqueId)
control.setNotification blocks the record from being saved; control.addNotification does not
The deprecated Xrm.Utility dialog methods map to Xrm.Navigation equivalents, and getBarcodeValue and getCurrentPosition moved to Xrm.Device
Demonstrate the use of the different lifecycle events
Studied
A code component is driven by four methods the framework calls for you. Knowing which one runs when, and what each one is allowed to do, is the most reliably tested part of the whole component framework.
The four lifecycle methods, in order:
init(context, notifyOutputChanged, state, container): runs once; context carries configuration, parameters and platform APIs, notifyOutputChanged is the callback you raise when outputs change, state is whatever a previous setControlState saved, and container is the div you render into
updateView(context): runs every time a bound property changes, including field values, datasets, container size, offline status and metadata such as label or visibility
getOutputs(): optional, called by the framework after you raise notifyOutputChanged, returning only the values that changed
destroy(): runs when the component leaves the DOM; close sockets and remove handlers you added outside the container here
Dataset values cannot be initialized in init, so read them in updateView
updateView can be called with property values temporarily null, so the component has to handle that without throwing
A code component must never reach for the host's formContext; it communicates changes back through notifyOutputChanged and getOutputs
The manifest is the contract between your component and the platform, and almost every configuration question on this topic is really a question about one of its elements or attributes.
What you need to know
The file is ControlManifest.Input.xml, and the build output renames it to ControlManifest.xml
The control element carries namespace, constructor, version, display-name-key and control-type, where control-type is standard or virtual
A property element needs name, display-name-key, a type through of-type or of-type-group, and a usage of bound, input or output
The resources element is required and must contain at least one code resource, and can also list css, img and resx files
Device and Utility APIs must be declared in a feature-usage element with uses-feature children; when required is true and the host does not support it, the component fails to load at runtime, and when false the API is simply null
React components set control-type="virtual" and declare platform-library entries for React and Fluent, which keeps bundle.js smaller because those libraries are shared
The external-service-usage element marks a canvas component as premium
There are two interfaces to implement, and which one you pick decides whether you render the DOM yourself or hand React elements back to the platform.
What you need to know
A standard component implements ComponentFramework.StandardControl, typed with the generated input and output interfaces, and works in model-driven apps, canvas apps and Power Pages
A React component implements ComponentFramework.ReactControl, typed the same way, and works in model-driven and canvas apps but not Power Pages
A React component's init has no container parameter, and its updateView returns a ReactElement instead of drawing into the DOM
IInputs and IOutputs are generated from the manifest's property definitions, and bound values are read through context.parameters.{propertyName}
pac pcf init --framework react scaffolds a React control project
A component either uses platform libraries or bundles everything itself; mixing the two approaches is not supported
Code components must not use window.localStorage or window.sessionStorage, and custom authentication inside a component is not supported in canvas apps
Getting a component into an environment has a fast path for development and a proper path for release, and the exam distinguishes them.
What you need to know
A .pcfproj holds exactly one component; a .cdsproj solution project can reference many, created with pac solution init and pac solution add-reference
pac pcf push --publisher-prefix {prefix} is the fast development path; without a solution name it deploys to a temporary PowerAppsTools_{prefix} unmanaged solution
The release path builds the .cdsproj with msbuild and imports the resulting solution, manually or through Build Tools
Always build in production mode for deployment, because development builds are larger and can be rejected on import for size
Using code components in canvas apps requires an administrator to enable the Power Apps component framework feature for the environment
Local debugging against a deployed component uses an AutoResponder rule to redirect the deployed bundle.js to your local build
Configure and use Device, Utility, and Web API features in component logic
Studied
The context object gives a component three sets of platform capabilities, and each one is available in a different set of hosts. The exam asks which works where.
What you need to know
context.device provides captureAudio, captureImage, captureVideo, getBarcodeValue, getCurrentPosition and pickFile
Every Device API method requires a matching uses-feature declaration in the manifest, and this is enforced
context.utility provides getEntityMetadata, hasEntityPrivilege and lookupObjects, and is available in model-driven apps only
context.webAPI provides the five record operations and is available in model-driven apps and Power Pages, but not in canvas apps
Power Pages does not support the Device methods or the Utility namespace at all
Start network and metadata requests in init rather than waiting for updateView, and show a loading state if updateView arrives first
Configure code apps to use Microsoft Power Platform connections
Studied
A code app reaches data through the same connectors everything else in Power Platform uses, but you wire them up from the command line and the tooling generates typed code for you.
What you need to know
Code apps can call Power Platform data sources and the full connector catalogue directly from JavaScript
pa connector list lists connectors and their identifiers, such as shared_office365
pa connection create --connector {id} creates a connection and returns the connection ID you use afterwards
pa app add data-source --connector {id} --connection-id {id} adds the connection to the project
Adding a data source generates strongly typed TypeScript model and service files under the generated folder, for example Office365UsersModel.ts and Office365UsersService.ts
Excel Online (Business) and Excel Online (OneDrive) are not yet supported for code apps
Dataverse is added to a code app the same way any other data source is, and the generated service is what your components call. The limitation list here is short and worth memorizing, because it is exactly the kind of thing a scenario question turns on.
What you need to know
pa app add data-source --connector dataverse --table {table logical name} adds a table and generates its model and service files
The generated service covers create, retrieve, retrieve multiple, update and delete, with delegation for filter, sort and top, plus paging
Formatted values for choice columns are available, so you can show the label rather than the stored value
System-managed columns such as the primary key and the owner columns must be excluded from a create payload
Polymorphic lookups, entity metadata CRUD, FetchXML and alternate keys are not supported for code apps
Perform table and metadata operations on Dataverse data
Studied
Sometimes a code app needs to know about the shape of a table rather than its rows: which columns exist, what a column is called in the user's language, what privileges the user has. That is a metadata call, and it is expensive enough to be worth caching.
What you need to know
The generated service exposes getMetadata, a typed wrapper over the Web API's table definition queries
The options object takes a metadata array for entity-level properties such as DisplayName, Privileges and IsCustomizable, and a schema object for columns and relationships
Requesting relationships returns OneToManyRelationships, ManyToOneRelationships and ManyToManyRelationships collections alongside Attributes
Properties defined only by derived attribute types, such as the option list on a choice column, cannot be requested this way
Microsoft's guidance is to cache metadata once per session, request a specific column list rather than all columns, and access nested labels defensively
A code app can call any Dataverse message, not just read and write rows. You add the operation by name from the command line and the tooling generates a typed service from the Dataverse metadata document.
What you need to know
pa app find-dataverse-api --search {term} finds available operations, and pa app add dataverse-api --api-name {name} adds one
The command reads the operation definition from the Dataverse $metadata document and writes a schema file plus a generated service class
Re-running the command overwrites the operation schema and regenerates the data source list, while preserving referenced table schemas
GUID parameters are typed as strings, and a lookup parameter referencing a table is typed as a generic record
A bound action takes the target record's ID as its first argument
An operation with no return value is typed as a promise of void, and a scalar return is typed to that scalar
Configure code apps to use agents built in Copilot Studio
Studied
Calling an agent from a code app goes through the Copilot Studio connector, and there are three similar-looking operations of which only one is the right answer.
What you need to know
The connector identifier is shared_microsoftcopilotstudio, added as a data source like any other connection
ExecuteCopilotAsyncV2 is the recommended operation, because it waits for the agent and returns the response
ExecuteCopilot is fire and forget and returns only a conversation ID, and ExecuteCopilotAsync can return a 502 error
The call takes a message, an agentName and a notificationUrl, and the notification URL is a required placeholder that is unused in synchronous mode
The agent name is its internal name, found in Copilot Studio under Channels and the web app connection string
The agent must be published in Copilot Studio before a code app can invoke it
The response carries responses, conversationId, lastResponse and completed
Two different things are called environment variables around code apps, and telling them apart is the skill. One is a Dataverse solution component for application lifecycle management. The other configures the CLI on your machine.
What you need to know
Solution environment variables are Dataverse records, and they are what makes a code app move cleanly between development, test and production
The CLI's own PA_CLI_* operating system variables, such as PA_CLI_ENVIRONMENT_ID and PA_CLI_SOLUTION_ID, are a separate mechanism that configures the tooling
A data source argument references a solution variable by prefixing its schema name with @envvar:, for example --dataset "@envvar:crd1b_SharepointSiteVar"
The @envvar: reference itself is stored in power.config.json, not the resolved value, so the target environment supplies its own
pa app list-environment-variables shows what is available to the app
The environment variables must already exist in the solution, and the app must already be initialized, before the reference will resolve
Because a code app is your own front-end code, the platform puts a content security policy in front of it by default. Anything your app calls that is not on the allowed list is blocked, and this is the first thing to check when a code app half works.
What you need to know
CSP is configured per environment and applies to every code app in it, under Settings, Product, Privacy and Security in the Power Platform admin center
CSP is enforced by default; code apps are not opt-in
Several directives default to 'none', including connect-src, form-action, child-src, manifest-src and worker-src
Custom values are merged with the default for a directive, except where the default is 'none', in which case your values replace it
A reporting endpoint can be configured to collect violation reports, and it works independently of enforcement
Programmatic configuration uses the Power Platform API settings PowerApps_CSPEnabledCodeApps, PowerApps_CSPReportingEndpoint and PowerApps_CSPConfigCodeApps
An environment without a Dataverse instance has to be configured through the API, because the admin center path needs Dataverse
Exam tip:connect-src defaults to 'none', so any code app that calls an external API, including Application Insights, needs that host added before the call will succeed.
Implement monitoring by using Azure Application Insights
Studied
A code app sends telemetry to Application Insights through a logger you register at startup, and the platform hands you its own load metrics to forward.
What you need to know
Setup is an Application Insights resource in Azure, the @microsoft/applicationinsights-web package, and a setConfig call supplying a logMetric callback
The platform currently delivers two built-in metric types to that callback: sessionLoadSummary and networkLoadSummary
Application Insights only captures telemetry after the app successfully loads, so it never sees a startup failure
Startup failures surface in Power Platform Monitor instead, which is why the two tools are complementary rather than alternatives
If CSP is enforced, telemetry calls can be blocked, and the fix is adding the Application Insights host to connect-src
CSP changes can take several minutes to take effect
Publishing a code app is one command, and everything examinable sits in the options around it: which solution it lands in, how a service principal publishes it, and who can run it afterwards.
What you need to know
pa app push packages the build and publishes it, optionally with --solution-id to pin the target solution, which must be a GUID because solution names are not accepted
Publishing to an environment without Dataverse works but skips the solution step
Putting the app in a solution makes it portable through normal application lifecycle management, including pipelines
pa app share --principal {emails or object ids} --access play|edit shares it, and play is the default
A service principal that needs to run pa app push must be shared with edit access, using the enterprise application object ID rather than the app registration object ID
Service principal authentication uses PA_CLI_SP_CLIENT_ID, PA_CLI_SP_CLIENT_SECRET, PA_CLI_SP_TENANT_ID and PA_CLI_USE_SP_AUTH
Code apps do not support Power Platform Git integration
Troubleshoot code apps by using Monitor and other browser-based debugging tools
Studied
Troubleshooting a code app means knowing which tool sees which failure. Monitor sees the app open. The browser console sees what the policy blocked. Application Insights sees everything after a successful load.
What you need to know
Power Platform Monitor tracks five code app metrics: time to interactive, app session count, app open success rate, data request success rate and data request latency
Monitor metrics are aggregated from daily event logs, so they are not real time
Monitor lives under Monitor, Products, Power Apps in the Power Platform admin center, and tenant-level analytics must be switched on for any metrics to appear
The browser developer tools console, filtered to errors, is where a content security policy violation appears, with the wording "violates the following Content Security Policy directive"
Application Insights and Monitor are complementary: a failure before the app loads appears only in Monitor
Performance work on a code app is the same discipline as any front-end application, with two platform signals to measure against and one Dataverse habit that matters more than the rest.
What you need to know
Time to interactive and data request latency are the two Monitor metrics that track performance directly
The networkLoadSummary metric in Application Insights lets you analyse request duration per URL, and sessionLoadSummary covers load
Limiting returned columns with $select is Dataverse's own documented performance practice, and the generated services sit on the Web API, so it applies here too
Fewer and larger data calls beat many small ones, which is the same guidance Monitor gives canvas apps
Ship a production build rather than a development build, for the same size and speed reasons that apply to code components
Domain 3Extend Microsoft Power Platform35-40% of the exam0 / 30 studied
The heaviest domain on the exam, and the one that changed least from PL-400. Plug-ins, platform APIs and Azure Functions are the same material they have always been, with a new skill group at the end for building Microsoft Foundry agents that integrate with Power Platform. If your study time is limited, this is the domain to be sure of.
Create a Dataverse plug-in
45
Demonstrate the use of the different event execution pipeline stages
Studied
The event execution pipeline is the single most tested concept on this exam. Dataverse runs your registered code at defined points around the main database operation, and which point you choose decides what you can see, what you can change and what happens when you throw.
The four stages, with their numbers:
PreValidation (10): runs before the main operation and before security checks, normally outside the database transaction
PreOperation (20): runs inside the transaction, before the main operation, and can modify the request
MainOperation (30): the platform's own operation; only custom API plug-ins and custom virtual table data providers run here
PostOperation (40): runs inside the transaction, after the main operation, and can act on the results
Only PostOperation steps can be registered asynchronously; PreValidation and PreOperation must be synchronous
A synchronous exception at any in-transaction stage rolls the entire transaction back, so cancelling in PreValidation avoids an expensive rollback
PreValidation participates in a transaction when it is triggered from within another already-transactional operation; check IExecutionContext.IsInTransaction rather than assuming
Several steps on the same message, stage and table run in ascending execution order, and ties are not guaranteed to be stable
Exam tip: Validation that should reject the operation belongs in PreValidation. Changing values before they are written belongs in PreOperation. Reacting to what was written belongs in PostOperation.
The execution context is how a plug-in knows what just happened: which message, which table, which user, and what data came in. Everything a plug-in does starts by reading it.
What you need to know
IPluginExecutionContext is obtained from the service provider with serviceProvider.GetService(typeof(IPluginExecutionContext))
The properties you use most are MessageName, PrimaryEntityName, InputParameters, OutputParameters, PreEntityImages, PostEntityImages, Depth, UserId and InitiatingUserId
UserId is the user the plug-in runs as; InitiatingUserId is the user who caused the event, and they differ when impersonation is in play
Depth is the infinite loop protection counter, and the platform aborts execution once it reaches its maximum of 8
SharedVariables passes data between plug-ins in the same pipeline execution, and CorrelationId ties the whole execution together in the trace log
Later interface versions add members: IPluginExecutionContext6 adds the environment and tenant ID, and IPluginExecutionContext7 adds IsApplicationUser
Exam tip: Microsoft explicitly warns against branching your business logic on a specific Depth value. If a question offers "check Depth equals 1" as a way to stop recursion, it is the trap answer.
A plug-in is one class with one method, and most of the rules about writing one are about what you must not do: no state, no authentication, no Web API calls.
What you need to know
A plug-in implements IPlugin and its single Execute(IServiceProvider) method
The services you pull from the provider are IPluginExecutionContext, IOrganizationServiceFactory and ITracingService
Plug-in classes are instantiated once and reused across many executions and threads, so they must be stateless and thread safe
Never authenticate the user in plug-in code, because the call is already authenticated
A plug-in must use the Organization service, not the Web API; calling the Web API from a plug-in is unsupported
ITracingService.Trace writes to the PluginTraceLog table, and trace logging has to be enabled for the environment before anything is recorded
Cancel an operation by throwing InvalidPluginExecutionException; in an asynchronous plug-in the exception is logged to the system job instead of shown to the user
An image is a snapshot of the row registered alongside your step, so you can see values your message did not include. It is also the cheapest performance win in plug-in development, because it replaces a retrieve.
What you need to know
A pre-image holds column values before the operation, and a post-image holds them after; both are read through context.PreEntityImages["alias"] and context.PostEntityImages["alias"]
Only ten messages support images: Assign, Create, Delete, DeliverIncoming, DeliverPromote, Merge, Route, Send, SetState and Update
A pre-image is not available on Create, because the row does not exist yet
A post-image is not available on Delete, because the row no longer exists
A post-image can only be defined on a step registered in the PostOperation stage, since the final values are not known until the operation completes
Register only the columns you need; the tooling's default of all columns costs performance
Exam tip: An Update plug-in only receives the columns that actually changed in its Target. If you need the value of a column the user did not touch, that is exactly what a pre-image is for.
Perform operations in plug-ins by using the Organization service
Studied
Inside a plug-in you do not create a connection. You ask the factory for a service, and the user you pass it decides whose privileges the operations run under.
What you need to know
IOrganizationServiceFactory.CreateOrganizationService(Guid? userId) creates the service; pass context.UserId for the calling user's privileges, or a specific user ID to impersonate
IOrganizationService provides Create, Retrieve, Update, Delete, Execute, Associate and Disassociate
Every message also exists as a request and response pair, such as CreateRequest and CreateResponse, sent through Execute
Table data in InputParameters and OutputParameters is late bound; cast it with the generic ToEntity method
Never assign a new early-bound instance back into InputParameters, because it throws a serialization exception
Passing null as the user ID runs the operation in the system user context
Plug-ins run inside somebody's save, so slow plug-in code is slow user experience. Dataverse also enforces a hard ceiling, and knowing that number is worth an exam mark on its own.
What you need to know
A Dataverse message operation, including every plug-in registered on it, must complete within 2 minutes or Dataverse throws a timeout and rolls back
Microsoft's guidance is to keep an individual plug-in under 2 seconds, and to register asynchronously when that is not achievable
Use images instead of extra retrieve calls
Use filtering attributes on Update steps so the step does not fire for irrelevant changes, and never include the primary key in the filter, because it is always present and defeats the filter entirely
Set an explicit timeout on any outbound HTTP call, because the default .NET HttpClient timeout of 100 seconds is dangerously close to the platform's own ceiling
Dataverse can terminate a plug-in that exceeds CPU, memory or handle thresholds, and that execution fails while later ones run normally
A custom API is your own Dataverse message. Getting the configuration right matters more than the code, because several of its properties cannot be changed after you save.
What you need to know
A custom API is defined by the CustomAPI table plus CustomAPIRequestParameter for inputs and CustomAPIResponseProperty for outputs
BindingType is Global for unbound, Entity, which automatically creates a Target parameter, or EntityCollection
IsFunction decides the shape: a function is a Web API GET, and an action is a POST with a body, which handles special characters better
AllowedCustomProcessingStepType has three values: None, so only your own plug-in runs; Async Only, so others can react but not interfere, which is the business events pattern; and Sync and Async, so others can add synchronous steps and even cancel the operation
IsPrivate hides the API from the Web API metadata document and code generation tools, and should be set before shipping a managed solution unless external developers need it
Unique Name, Allowed Custom Processing Step Type, Binding Type and Bound Entity Logical Name are locked after saving
Register plug-in components by using the Plug-in Registration Tool
Studied
Registration is where your assembly becomes something Dataverse actually runs. The tool is the classic path and the CLI is the modern one, and the exam covers both plus the settings on a step.
What you need to know
The Plug-in Registration Tool ships through NuGet and can be launched with pac tool prt
For Dataverse online, an assembly is always registered with isolation mode Sandbox and location Database; the other options exist only for legacy on-premises deployments
Registering an assembly discovers every class implementing IPlugin as a plug-in type, and the assembly itself is stored in the PluginAssembly table
A step configures the message, primary table, filtering attributes, stage, execution mode, execution order, the user context it runs in, and the unsecure and secure configuration strings
The secure configuration string is excluded from solution export, which is why secrets go there rather than in the unsecure one
pac plugin init scaffolds a project and pac plugin push imports the built assembly
Assemblies and their steps are separate solution components, so adding a step does not automatically add its assembly
The plug-in behind a custom API reads and writes parameters you defined yourself, using the exact unique names from the configuration. It is ordinary plug-in code with a different contract.
What you need to know
Link the plug-in to the API through the CustomAPI record's PluginTypeId
Read InputParameters and write OutputParameters using the unique names from the request parameter and response property records
A late-bound caller builds new OrganizationRequest("prefix_UniqueName") and sets parameters by string key
An entity-bound custom API is invoked with the fully qualified name Microsoft.Dynamics.CRM.{UniqueName}
pac modelbuilder build generates early-bound request and response classes for a custom API
A private custom API still works when called directly, but is excluded from the metadata document and cannot be used within other plug-ins
A business event turns something that happens in Dataverse into something an external system or a flow maker can subscribe to, without either side writing a plug-in to expose it.
What you need to know
Business events are built on custom APIs plus two tables: Catalog, which is two levels deep with the top level as the solution and the second as a category, and CatalogAssignment, which places a message into a category
Cataloguing a message makes it appear in the Power Automate Dataverse connector's "When an action is performed" trigger
The standard Create, Update and Delete messages are deliberately excluded from that trigger, because "When a row is added, modified or deleted" already covers them
The documented external events pattern uses no plug-in on the main operation, AllowedCustomProcessingStepType of Async Only, and request parameters but no response properties
External subscribers need an application user with server-to-server authentication and a security role
Business events are additive: the underlying event framework is unchanged
Configure Microsoft Power Platform managed identity for plug-ins
Studied
Managed identity lets a plug-in get a token for an Azure resource without a stored secret anywhere. It replaces the old pattern of putting a client secret in the secure configuration string.
What you need to know
A plug-in acquires a token with IManagedIdentityService.AcquireToken(scopes), retrieved from the service provider like any other service
The setup is a user-assigned managed identity or app registration in Azure, a federated identity credential on it, and a managedidentities record in Dataverse carrying the application ID, tenant ID, credential source, subject scope and version
The federated credential issuer is https://login.microsoftonline.com/{tenantId}/v2.0 with an explicit subject identifier
Subject format version 2 is recommended for all plug-ins, because version 1 is based on the certificate common name and fails on non-ASCII characters or commas in the distinguished name
pac managed-identity upgrade-version moves an existing credential to version 2 without rebuilding the plug-in
The pac managed-identity commands are keyed by component type and component ID, covering plug-in assemblies, service endpoints and Copilot Studio components
The Web API is the OData endpoint every non-.NET caller uses. The exam focuses on batching, because that is where the difference between "several operations" and "one transaction" lives.
What you need to know
The Web API is OData version 4, at [org URI]/api/data/v9.2
Functions are GET requests with no side effects; actions are POST requests that change something; both can be bound or unbound
A $batch request groups up to 1,000 operations in one multipart request, and is the Web API equivalent of ExecuteMultiple
A $batch containing a change set is atomic, all or nothing, and is the equivalent of ExecuteTransaction; a GET is not allowed inside a change set
Batching also works around URL length limits, because a long FetchXML or OData query moves into the body
ETags with If-Match give optimistic concurrency, and Prefer: return=representation returns the full record on create and update
Perform operations by using the Organization service
Studied
The Organization service is the .NET path into Dataverse, and the exam mainly wants to know that you use the current client rather than the legacy one.
What you need to know
ServiceClient, in Microsoft.PowerPlatform.Dataverse.Client, is the current client and uses MSAL; it is the recommended choice for new development
CrmServiceClient, in Microsoft.Xrm.Tooling.Connector, is the legacy client, uses ADAL and runs on .NET Framework only
ServiceClient implements IOrganizationServiceAsync2, IOrganizationServiceAsync, IOrganizationService and IDisposable
One ServiceClient constructor takes a caller-supplied token provider function, which is how a web app passes its own token
OrganizationServiceContext gives you a LINQ queryable, change-tracking context with a SaveChanges method, generated by pac modelbuilder build
A plug-in never constructs a ServiceClient; it gets its service from IOrganizationServiceFactory
Dataverse protects itself with service protection limits, and any integration that ignores them will eventually fail in production. The three limits and the retry behaviour are directly examinable numbers.
The three service protection limits, per user, per web server, in a five minute sliding window:
Number of requests: 6,000
Combined execution time: 1,200,000 milliseconds, which is 20 minutes
Concurrent requests: 52
The Web API returns HTTP 429 with a Retry-After header in seconds; the SDK returns the same value as a TimeSpan in the fault's error details
The correct response is to wait the Retry-After interval and retry, not to back off arbitrarily or to give up
A concurrency error is returned immediately, while request count and execution time errors typically appear only after several minutes of heavy traffic
These limits do not apply to calls made from inside plug-ins and custom workflow activities, although their execution time counts towards the triggering external request
Many small parallel requests work better than a few very large ones, because the limits are about sustained demand
Exam tip: If a scenario describes an integration failing under load with HTTP 429, the answer is to honour Retry-After. Adding more threads or splitting across more users is the wrong answer.
Optimize solution components for performance, concurrency, transactions, and bulk operations
Studied
There are three different things people mean by "send several operations at once", and the exam tests whether you know which one is atomic and which one is fast.
What you need to know
Approach
What it gives you
ExecuteMultiple or $batch
Fewer round trips, partial success allowed
ExecuteTransaction or $batch with a change set
All or nothing, one transaction
CreateMultiple, UpdateMultiple, UpsertMultiple
Highest throughput, purpose-built bulk messages
DeleteMultiple is available for elastic tables only
The bulk messages are for external clients and cannot be called from inside a plug-in
On standard tables a bulk request fails entirely if any record is bad, while elastic tables support partial success through the bulk error details
Dataverse merges the single and bulk pipelines, so a single-row plug-in still runs once per row under a bulk message; only logic moved to the Multiple message gets the throughput benefit, and you should not register both
Start with small batches, around ten, and scale up while combining with parallelism
Implement and troubleshoot authentication by using OAuth
Studied
Every modern connection to Dataverse is an OAuth token. The exam separates the interactive case, where a user consents, from the server-to-server case, where an application user stands in for one.
What you need to know
An interactive application needs the "Access Dynamics 365 as organization users" delegated permission
A server-to-server application skips that permission and relies on a Dataverse application user instead
Multi-tenant is the ISV pattern: one Entra ID app in the publisher's tenant, and an application user created in each customer tenant after consent
Single-tenant applications connect only within their own tenant
Only one application user per registered app per environment can exist
ServiceClient authentication types include OAuth for interactive use, which supports Conditional Access and multifactor authentication, plus Certificate and ClientSecret for unattended use; the old Office365 username and password type is deprecated
Perform asynchronous logic by using background operations
Studied
Background operations are a newer way to run a custom API without making the caller wait, and they are distinct from the classic asynchronous plug-in. The important limitation is that they buy you a non-blocking caller, not more execution time.
What you need to know
The SDK uses ExecuteBackgroundOperation, and the Web API uses the Prefer: respond-async header
The wrapped operation must itself be a custom API
The Web API responds with HTTP 202 Accepted plus a background operation ID and a location header pointing at a status monitor
The backgroundoperation table tracks state and status, with status values including Waiting For Resources, In Progress, Succeeded, Failed and Canceled, and a default retention of 90 days
On failure Dataverse retries up to three times with exponential backoff, and an operation can only be cancelled before it starts
Completion can notify a callback URI, which carries status only, so the receiver still has to fetch the result
A background operation plug-in is still a plug-in and is still bound by the 2 minute timeout
Perform operations by using Dataverse SDKs for .NET and Python
Studied
Dataverse now ships an official Python SDK alongside the long-standing .NET one, and its arrival is exactly the kind of change an updated exam asks about.
What you need to know
The .NET path is ServiceClient from Microsoft.PowerPlatform.Dataverse.Client, which supersedes the legacy CrmServiceClient
The Python package is PowerPlatform-Dataverse-Client, installed from PyPI with pip
The Python SDK is built with agent scenarios in mind, with support for Model Context Protocol and agent-to-agent collaboration primitives
It provides a structured exception hierarchy with retry guidance, and opt-in HTTP diagnostic logging that redacts sensitive headers such as Authorization
Passing a list of records to the Python client's create method invokes the collection-bound CreateMultiple action and returns the created record IDs
Both SDKs are subject to the same service protection limits as any other external caller
Process workloads by using Microsoft Azure Functions
63
Process long-running operations by using Azure Functions for Microsoft Power Platform solutions
Studied
An Azure Function is where work goes when it will not fit inside a plug-in's 2 minutes. The catch, and the examinable part, is that a Function is outside the Dataverse transaction.
What you need to know
Azure Functions do not run inside the Dataverse event execution pipeline
A Function is invoked synchronously from Dataverse only through a webhook service endpoint step, which delivers a serialized remote execution context
Changes a Function makes do not roll back if a later synchronous plug-in in the same operation fails and the transaction is rolled back
Logic that genuinely needs the transaction, such as formatting a value before it is saved, belongs in a plug-in rather than a Function
The normal reason to choose a Function is offloading work that has no transactional requirement: long-running calculations, calls to slow external services, or heavy processing
Exam tip: When a scenario says "must complete within the same transaction" or "must roll back with the record", the answer is a plug-in, not an Azure Function, however long the work takes.
Implement scheduled and event-driven triggers in Azure Functions for Microsoft Power Platform solutions
Studied
A Function needs a trigger, and the two shapes that matter for Power Platform work are a schedule and an event. The exam asks about the timer trigger's syntax and behaviour.
What you need to know
A timer trigger uses an NCRONTAB expression, for example 0 */5 * * * * for every five minutes
The timer trigger uses a blob storage lock through the storage connection, so only one instance fires when the app scales out
A timer trigger does not retry on failure; it simply waits for the next scheduled time
An HTTP trigger is what the Dataverse webhook pattern uses, with a configurable authorization level, methods and route
Other relevant triggers are Event Grid, Event Hubs, Service Bus, Queue Storage and Cosmos DB
The usual event-driven design is a Dataverse plug-in or service endpoint posting to Service Bus or Event Hubs, with a Function triggered from there
Authenticate to Microsoft Power Platform by using managed identities
Studied
This is the opposite direction from managed identity for plug-ins: here an Azure resource such as a Function calls into Dataverse, and it uses its own identity rather than a stored secret.
What you need to know
Enable a system-assigned or user-assigned managed identity on the Azure resource, which creates a service principal in Microsoft Entra ID
That service principal must be registered in Dataverse as an application user with a security role, exactly like any other server-to-server caller
Code acquires a token with ManagedIdentityCredential, or with DefaultAzureCredential, which tries managed identity automatically when running in Azure
ManagedIdentityCredential takes a client ID to select a specific user-assigned identity, and defaults to the system-assigned one otherwise
ServiceClient accepts a caller-supplied token provider function, which is how you hand it a managed identity token
The point of the pattern is that no secret is stored or rotated anywhere
Configure Power Automate cloud flows and Copilot Studio workflows
66
Implement complex expressions in flow steps
Studied
Flow expressions are the Workflow Definition Language, shared between Power Automate and Azure Logic Apps. The exam asks about specific functions and the arguments they take.
What you need to know
Expressions use @ syntax and the same function library as Logic Apps, which is where they are documented
The families are string, collection, math, date and time, manipulation, and workflow runtime functions
substring(text, startIndex, length) takes a length as its third argument, not an end index
Runtime functions give you the data: triggerBody(), triggerOutputs(), body('Step'), outputs('Step'), variables(), parameters() and workflow()
workflow() returns run metadata, which is how you build a link back to a specific run
coalesce handles nulls, formatDateTime and addToTime handle dates, and toUpper and replace are case sensitive
Implement flow control actions including error handling
Studied
Error handling in a cloud flow is built from scopes and the run-after setting, and the exam asks you to assemble a try, catch and finally pattern from them.
What you need to know
The control actions are Condition, Switch, Apply to each, Do until and Scope
A Scope has its own aggregate status of Succeeded, Failed, Skipped or TimedOut, taken from the actions inside it
Configure run after has four checkboxes: is successful, has failed, is skipped and has timed out
A try, catch and finally pattern uses three Scope actions: catch runs after try has failed, is skipped or has timed out, and finally runs after all four states
The result() function, usually with a Filter array, extracts the error details from a failed scope or loop
Per-action retry policies are Default, None, Fixed Interval or Exponential Interval, with a configurable interval and retry count
Filtering at the trigger is better than branching inside the flow, because a flow that never starts costs nothing. The Dataverse trigger's filtering options are the examinable detail.
What you need to know
The Dataverse trigger filters on change type (Create, Update or Delete), select columns, filter rows, and scope
Select columns applies to Update only, since Create and Delete always fire for the whole row
Lookup columns are not supported in select columns and are silently ignored
The trigger fires when a selected column is present in the update, even if its value did not actually change
A filter expression is OData style, such as firstname eq 'John', evaluated after the save, and must not include the literal $filter=
Scope options are User, Business Unit, Parent: Child business unit and Organization
Each Dataverse trigger creates a CallbackRegistration record, and if that record is missing the flow silently stops firing
Exam tip: If a flow stopped triggering with no error and nothing in the run history, check that its callback registration still exists before you look at anything else.
A child flow is a flow another flow calls, and it is how you avoid copying the same eight actions into six places. The rules about solutions and connections are what the exam tests.
What you need to know
Both the parent and the child must be in the same solution, and Microsoft recommends creating both directly in that solution rather than importing afterwards
The child normally uses the "Manually trigger a flow" trigger and returns data with "Respond to a Power App or flow"
The parent calls it with the "Run a Child Flow" action and waits for it to finish
The wait limit is up to one year for built-in connections and Dataverse, and 30 days for any other connector
A child flow cannot use run-only or invoker connections; every non-built-in connector must use an embedded connection or the call fails
A child flow does not inherit its parent's flow group capacity and has to be added to the group explicitly
Implement authentication for the Dataverse connector by using service principals
Studied
A flow that runs as a service principal does not depend on any individual's account, which is what makes it safe for production integrations. Setting it up is the same application user pattern as everywhere else.
What you need to know
Create the Entra ID app registration with a client secret, create a matching Dataverse application user, and give it a security role
In the flow designer, choose "Connect with service principal" on a Dataverse action rather than signing in, and supply the application ID, client secret and tenant ID
Service principal owned flows draw on separate pooled, non-licensed tenant allocations rather than a user's licence
A flow can be assigned a licensee through the Process table's licensee lookup so premium connector usage is covered by a specific user's licence
A Dataverse connection keeps working after its token expires as long as the service principal is enabled and still has permission; to stop it you turn the flow off or revoke the Dataverse permission
Build Microsoft Foundry agents by using code that integrates with the Microsoft Power Platform
71
Integrate custom agents by using Microsoft Power Platform MCP servers
Studied
Model Context Protocol is how an agent discovers and calls tools, and Dataverse now publishes its own MCP server. This skill group is entirely new on AB-400.
What you need to know
The Dataverse MCP server endpoint is https://{orgName}.crm.dynamics.com/api/mcp
Its tools cover data and schema: searching data, creating, updating and deleting records, creating, updating and deleting tables, running a read query, and describing the schema
Supported clients include Copilot Studio, VS Code with GitHub Copilot, the GitHub Copilot CLI, Claude Desktop and Claude Code
It is enabled by default only for Copilot Studio; every other client must be enabled per environment under Settings, Product, Features
It respects ordinary Dataverse security roles and row level security, so there is no separate permission model to configure
Record and table deletion through the MCP server requires explicit user approval
Governing client access through advanced connector policies requires a Managed Environment
Agents increasingly call other agents, and Copilot Studio can connect to three different kinds. The exam asks what each connection requires on the far end.
Connecting to another Copilot Studio agent: the target must be in the same environment, must be published, and must have "Let other agents connect to and use this one" enabled in its settings. A toggle controls whether conversation history is passed, and the connected agent's description does not sync automatically.
Connecting to a Microsoft Foundry agent: the Foundry agent must have its Activity protocol endpoint enabled. New Foundry agents expose only Responses and A2A by default, and enabling Activity currently requires the REST API or the Python SDK rather than the portal.
Connecting to a generic agent-to-agent endpoint: this is built on custom connector infrastructure, discovers the agent's name and description from an agent card at /.well-known/agent.json, and supports no authentication, an API key, or OAuth 2.0.
A missing Activity endpoint shows up as a 400 error saying the endpoint does not support activity
A2A payloads carry a context ID, message IDs, locale and the chat history
Integrate custom agents with Dataverse data by using Work IQ
Studied
Work IQ is the layer that gives an agent context about a person's work, and its Dataverse integration is how a custom agent reaches business data through the same governed path as Microsoft 365 content.
What you need to know
Work IQ has three layers: data, which spans Microsoft Graph content and Dataverse; context, including memory, activity signals and the semantic index; and skills and tools
Work IQ's MCP surface collapses a very large number of operations into a small set of generic verb-based tools over resource paths
Authorization uses a policy engine evaluating the resource path, method, user identity and content on every request
Every action is scoped to the signed-in user rather than to the agent, and every tool invocation is logged
Custom MCP servers can be built over connectors, Graph APIs, Dataverse custom APIs and arbitrary REST endpoints, then added to agents in Copilot Studio or Microsoft Foundry
Exam tip: Work IQ's Dataverse capabilities are still arriving, so study the model (data, context, tools, with per-user authorization and full logging) rather than memorizing a tool list that is likely to move.
Consume Copilot Studio agents by using the Copilot Studio client library
Studied
When your own application needs to talk to a Copilot Studio agent, the client library is the supported route, with Direct Line as the fallback for the cases it does not cover.
What you need to know
The Copilot Studio client ships as part of the Microsoft 365 Agents SDK, with .NET, JavaScript and Python packages
The two integration methods are the Copilot Studio client through the Agents SDK, which is the recommended one, and Direct Line
The Agents SDK does not support service principal tokens, so a service principal scenario has to use Direct Line
Interactive sign-in requires the Power Platform API delegated permission CopilotStudio.Copilots.Invoke
Configuration needs the environment ID, the agent's schema name, the tenant ID and the application client ID
Passing context to the agent is event based: post a custom event and handle it in an OnEventActivity topic after the first agent message, because context applies after the greeting rather than at connection
Domain 4Develop Integrations10-15% of the exam0 / 14 studied
The lightest domain, and the one that carried over from PL-400 with almost no change at all. Dataverse events, data synchronization and custom connectors are the same skills, in the same words, with custom connectors moved here from the platform domain. If you studied this material for PL-400, you are already done with it.
Publish and consume Dataverse events
75
Publish a Dataverse event by using the IServiceEndpointNotificationService
Studied
This is the code path for sending a Dataverse event to Azure. A plug-in asks the platform for the notification service and hands it the execution context plus the endpoint to post to.
What you need to know
IServiceEndpointNotificationService lives in Microsoft.Xrm.Sdk and posts the plug-in execution context to Azure Service Bus
Its method is Execute(EntityReference serviceEndpoint, IExecutionContext context), where the first argument points at the registered service endpoint record
The method returns a string, but only a two-way or REST listener actually returns anything usable; queue, topic and one-way contracts do not
If the post fails, the service retries for a set period, and when every attempt fails the related system job is set to an Error status
A plug-in calling this service must be registered to run in the sandbox
For an asynchronous step, a retry re-runs the whole plug-in, so an Azure-aware plug-in should contain only context modification and the post, nothing else
The remote execution context carries OperationId and OperationCreatedOn, which the receiving system uses to de-duplicate retried messages
Publish a Dataverse event by using the Plug-in Registration Tool
Studied
Most of the time you write no code at all. Dataverse ships an Azure-aware plug-in, and you configure it by registering a service endpoint and a step against it.
What you need to know
Register the endpoint with Register, then Register New Service Endpoint, pasting the Azure Service Bus connection string
Configure the designation type, the message format, which is .NET Binary, XML or JSON, and optionally whether user information is sent
Then register a step on the endpoint, mapping a message and table combination to it
Registered service endpoint steps support only port 80 for HTTP and port 443 for HTTPS
Microsoft's guidance is to register the step asynchronously for best system performance
Testing means checking System Jobs for a job named after the service endpoint, with a status of Succeeded, Waiting or Failed
You can also write your own Azure-aware plug-in instead of using the out-of-the-box one, but it must run in the sandbox
Register service endpoints including webhooks, Azure Service Bus, and Azure Event Hub
Studied
Three destinations, several contract types, and a set of differences the exam loves: which one supports a synchronous step, which one has a queue, and what happens to a large payload.
What you need to know
Destination
Step mode
Reach for it when
Webhook
Synchronous or asynchronous
A simple HTTP listener, or an Azure Function
Azure Service Bus
Asynchronous only
High scale with real queuing and retries
Azure Event Hubs
Asynchronous only
High-throughput event streaming
Service Bus contract types are Queue, Topic, One-way, Two-way, REST and Event Hub; only two-way and REST return a string to the caller
A one-way contract needs an actively listening endpoint, and Dataverse retries in growing intervals until the system job fails
A webhook has three authentication options: HttpHeader, WebhookKey, which sends a code query string value and suits Azure Functions, and HttpQueryString
A webhook times out after 60 seconds, and a response outside the 2xx range fails, except for 502, 503 and 504, which get one extra retry
A Service Bus payload over 192 KB has its parent context, input parameters and images stripped, and errors if it is still too large; a webhook sets the x-ms-dynamics-msg-size-exceeded header when its payload exceeds 256 KB
Event Hubs registration permits SAS authorization only, with a body format of XML or JSON
Exam tip: Webhooks are the only one of the three that can be registered on a synchronous step. If the scenario needs the external call to happen before the record is committed, it is a webhook.
Recommend options for listening to Dataverse events
Studied
The recommendation question is really a sizing question. How much volume, how much latency can you accept, and does anything need to happen before the save completes.
What you need to know
Microsoft documents five ways to relay Dataverse event data: Power Automate flows, Azure Service Bus, Azure Event Hubs, webhooks and plug-ins
Service Bus is for high-scale processing with real queuing; a webhook only scales as far as the web service you host
Webhooks support synchronous and asynchronous steps, Service Bus supports asynchronous only, and webhooks are simpler to secure
Power Automate is the low-code listener, and the guidance is to filter at the trigger with change type, select columns and filter rows rather than with a condition inside the flow
Business events built on custom APIs notify only when the operation completes successfully, unlike a synchronous plug-in step, which can still cancel it
The event framework remains the answer for synchronous, in-transaction logic; everything else here is for asynchronous external consumption
Perform data synchronization by using change tracking
Studied
Change tracking is how an external system asks Dataverse "what changed since last time" without querying everything. You get a token back, you keep it, and you send it with the next request.
What you need to know
Enable it per table with the Track Changes option under advanced options, or by setting ChangeTrackingEnabled on the table metadata, and once enabled it cannot be disabled
Not every table can be enabled; check the CanChangeTrackingBeEnabled managed property first
The SDK message is RetrieveEntityChangesRequest, and its DataVersion property carries the token from the previous call
The response's token is at EntityChanges.DataToken, which you store and pass back next time
The first call, with no token, returns every record as new, and deleted records are never returned on that first call
Tokens expire after a default of seven days, controlled by the organization's ExpireChangeTrackingInDays setting, and an expired token throws, leaving a full re-extract as the only recovery
One table per call, results are capped at 5,000 items per response, and the caller needs organization-level read privilege on the table; the Web API equivalent uses the Prefer: odata.track-changes header, and $filter, $orderby, $expand and $top are not supported with it
Exam tip: Store the token every time and check its age before you use it. An integration that runs weekly with a seven day default will eventually fail on an expired token.
An alternate key lets you address a Dataverse row by a value the other system already knows, such as an order number, instead of by a GUID it has never seen. It is the foundation of most synchronization code.
What you need to know
A table supports up to 10 alternate key definitions, and virtual tables support none
Only six column types are valid in a key: Decimal, Whole Number, Single Line of Text, Date and Time, Lookup and Choice
A key is limited to 900 bytes and 16 columns because it becomes a SQL index; columns with field level security enabled cannot be used, and a null value in a key column disables uniqueness enforcement for that row
The KeyAttributes collection on Entity and EntityReference is how code identifies a record by alternate key instead of by ID
Web API syntax puts the key in the URL, such as /accounts(accountnumber='ABC123'), and a multi-part key lists both columns
The index is created asynchronously, and EntityKeyIndexStatus moves through Pending, In Progress and Active, or Failed; a failed index is retried with the ReactivateEntityKey message
Synchronize data by using the UpsertRequest message
Studied
Upsert is one request that creates the record if it is missing and updates it if it is there. It is the message that makes a re-runnable synchronization job possible.
What you need to know
UpsertRequest takes a Target entity whose KeyAttributes carry the alternate key values used to find the record
UpsertResponse has RecordCreated, a boolean telling you which happened, and Target, an entity reference to the result
Dataverse uses Entity.Id if it is supplied, and otherwise looks the record up by the key attributes
Do not put the alternate key columns in the Attributes collection as well; on update they are stripped, and on create they are copied in automatically, so mismatched values give unpredictable results
The caller needs both Read and Write privileges on the table
In the Web API an upsert is a PATCH to the key URL, and the default 204 response cannot tell you which happened; add Prefer: return=representation to get 201 for a create and 200 for an update
UpsertMultipleRequest does the same in bulk, and each result still carries its own RecordCreated
A custom connector's authentication type is fixed when you build it, and it decides what the user is asked for when they create a connection. The exam tests the four types and the OAuth details.
What you need to know
The four types are No authentication, Basic authentication, API Key based authentication and OAuth 2.0
With API Key authentication the user supplies only the value; you choose whether it is sent in a header or a query string
OAuth 2.0 requires an identity provider, where Generic OAuth 2.0 and Microsoft Entra ID are the two general-purpose options alongside service-specific ones such as GitHub, Google, Dropbox and Salesforce
OAuth 2.0 configuration needs a client ID, client secret, authorization URL, token URL and refresh URL, and the client credentials grant type is not supported
Managed identity is available as an alternative to a client secret for Microsoft Entra ID, using federated credentials, and the app must be single tenant
Multiple authentication options on one connector are configured through connectionParameterSets in apiProperties.json, and the maker portal wizard cannot create them; the paconn CLI can
Bump the connector version whenever the client secret changes
Configure policy templates to modify connector behavior at runtime
Studied
Policy templates change a request or a response without you writing any code. They are the first thing to reach for when the backend API almost fits but not quite.
What you need to know
Set host URL replaces the connector's host at runtime using a URL template, which is how one connector serves many tenants or regions
Set HTTP header adds or overrides a header on the request or the response
Set query string parameter adds or updates a value, with an exists action of override, skip or append
Set property adds or updates a property in the request or response body
Route request sends the call to a different relative path on the same service, which is how you version an operation
Conversion templates reshape data: array to object, object to array, and a delimited string into an array of objects
Every policy has a "Run policy on" setting of Request or Response, and can be scoped to specific operations or left to apply to all, with parameters accepting the @headers, @queryParameters and @connectionParameters expressions
Exam tip: Do not confuse a runtime policy template with a data policy. A data policy is the tenant governance feature that classifies connectors as Business, Non-Business or Blocked, and it has nothing to do with modifying a request.
Import definitions from existing APIs including Open API definitions, Azure services, and GitHub
Studied
You rarely describe an API by hand. The wizard imports a definition, and several Azure services can generate one or export a connector directly.
What you need to know
A connector is created from an OpenAPI definition, a Postman collection, or from scratch in the wizard
Custom connectors use OpenAPI 2.0, formerly called Swagger; OpenAPI 3.0 definitions are not supported
A Postman collection must be exported in Collection v1 format and is capped at 1 MB, and the wizard converts it to generatedApiDefinition.swagger.json
Azure API Management exports an API straight to Power Platform as a connector, from the APIs blade
An Azure Logic Apps Consumption workflow that starts with a Request trigger can be exported to Power Apps from its Overview page
The microsoft/PowerPlatformConnectors repository on GitHub holds the definition files of every certified and independent publisher connector, which is both where you submit for certification and a source of reusable definitions
A connector created in Power Automate is available in Power Apps and Copilot Studio, but one created in Logic Apps is not shared across them
Azure services have shortcuts into Power Platform that save you writing a definition, and the exam asks which service offers which route.
What you need to know
In Azure API Management, choose APIs, then Power Platform, then Create a connector, picking the API, the target environment and a display name
The export can create a subscription key connection parameter, and can take OAuth 2.0 server details when the backend is protected by Microsoft Entra ID
Testing an exported connector from the Power Apps test console needs a CORS policy in API Management plus a Set HTTP header policy in the connector setting an Origin header
Logic Apps export to Power Apps is available for Consumption workflows only, not Standard, and only when the workflow starts with a Request trigger
Microsoft names three products for building the API behind a connector: Azure Functions, Azure Web Apps and Azure API Apps
A connector can reach a private API through the on-premises data gateway, but API Key authentication is not supported over the gateway
Develop an Azure Function to be used in a custom connector
Studied
A Function is the usual backend when there is no existing API to wrap. The work is making it describable, because a connector needs an OpenAPI definition.
What you need to know
Start from an HTTP trigger with an authorization level of Function, which gives you a function key
To use the Function as a Dataverse event listener instead, register a webhook in the Plug-in Registration Tool whose endpoint URL is the Function URL and whose WebhookKey value is the function key
Enter only the key value in the registration tool, not the code= prefix
To expose the Function as a connector-ready API, integrate the Function App with Azure API Management, or add the OpenAPI extension for Azure Functions so the app describes itself
Either route gives you a definition to download and import, or you can export straight from API Management
Function Apps in any supported language can integrate with API Management for this
Extend the Open API definition for a custom connector
Studied
The base OpenAPI document describes the API. Microsoft's x-ms- extensions describe how it should look and behave inside Power Automate and Power Apps, and that is what turns a functional connector into a usable one.
What you need to know
summary is the operation title in sentence case, and x-ms-summary is the parameter or response title in Title Case
x-ms-visibility takes important, which shows the parameter first, advanced, which hides it behind the advanced toggle, or internal, which hides it from the user entirely
An internal parameter that is also required must have a default value
x-ms-dynamic-values populates a dropdown from another operation's output, and x-ms-dynamic-list is its successor, required alongside it when the values reference properties inside parameters
x-ms-trigger and x-ms-trigger-hint mark an operation as a trigger, and x-ms-notification-content and x-ms-notification-url support webhook-style triggers
x-ms-api-annotation carries operation versioning through family, revision and replacement
You can always download the effective OpenAPI definition from the connector, even after editing through the wizard
Develop code for a custom connector to transform data
Studied
When a policy template is not enough, a connector can run your own C# script against the request or the response. The limits on that script are tight and very examinable.
What you need to know
The code goes in a Visual C# Script file, script.csx, containing a class named exactly Script that inherits ScriptBase and overrides ExecuteAsync
When code is present it takes precedence over the codeless policy template definition
Only one script file per connector is allowed, it must complete within 2 minutes, the file cannot exceed 1 MB, and only a restricted subset of .NET Standard 2.0 namespaces is supported
Call the backend with this.Context.SendAsync rather than creating your own HttpClient, which Microsoft states will be blocked in future
Custom code is not supported with the on-premises data gateway
The scriptOperations list in apiProperties.json scopes which operations run through the script, and leaving it empty means all of them do
The script is deployed as one of the connector's definition files, through paconn with --script-file or pac connector create with --script-file
Power Platform admin center > Environments > Settings > Product > Features
Configure Content Security Policy for code apps
Power Platform admin center > Environments > Settings > Product > Privacy + Security
Create an environment variable
Power Apps > Solutions > solution > New > More > Environment variable
Inspect solution layers
Power Apps > Solutions > component > Advanced > See solution layers
Set up a deployment pipeline
Power Platform admin center, or the Deployment Pipelines app
Configure data policies
Power Platform admin center > Policies > Data policies
Create an application user
Power Platform admin center > Environments > Settings > Users + permissions > Application users
Build or edit a custom connector
Power Apps or Power Automate > Data > Custom connectors
Export an API as a connector
Azure portal > API Management > APIs > Power Platform
Enable the Dataverse MCP server for a client
Power Platform admin center > Environments > Settings > Product > Features
Check code app health metrics
Power Platform admin center > Monitor > Products > Power Apps
Review plug-in trace logs
Power Apps advanced find > Plug-in Trace Log
Watch a running app or flow
Power Apps or Power Automate > Monitor
Take the free practice assessment
The AB-400 study guide page on Microsoft Learn
Additional tips
The best thing you can do after reading this guide is to start a free Power Apps Developer Plan and build the things in it yourself. Register a plug-in, watch it fire in the wrong stage, then fix it. That hour teaches you more than any amount of reading about the event execution pipeline.
A free environment of your own with Dataverse, Power Apps and Power Automate, made for exactly this. It is where you should register your first plug-in, push your first code component and try the Power Platform CLI.
A Microsoft 365 E5 developer sandbox with 25 licences, 16 sample users and preloaded Teams, mail and calendar data, useful when you want to test integrations that reach beyond Power Platform into Microsoft 365 services and Copilot Studio. There is no charge for the sandbox, but Microsoft will not provision one until you link a valid billing account, and that requirement cannot be bypassed.
What is the difference between the PL-400 and the AB-400 exam?
AB-400 is the replacement for PL-400, and the certification you earn is identical: Microsoft Certified: Power Platform Developer Associate. The skills measured were rewritten to reflect how Power Platform developers build today, so the canvas app depth and the standalone troubleshooting domain are gone, and Power Apps code apps, Microsoft Foundry agents, Model Context Protocol servers and Copilot Studio workflows are new. Everything in between, plug-ins, the Power Apps component framework, client scripting, platform APIs, Azure Functions, Dataverse events and custom connectors, carried over largely unchanged.
Should I take PL-400 before it closes, or wait for AB-400?
Registration for PL-400 closes on 16 October 2026, and if you register on or before that date you can still schedule and sit it through 30 October 2026. AB-400 becomes available on 16 October 2026 and registration is already open. If you are close to ready now, sitting PL-400 is the lower-risk choice because the study material has been stable for years. If you are starting from scratch, go straight to AB-400, since you would otherwise be learning content that is about to leave the exam.
Do my PL-400 study materials still work for AB-400?
For most of the exam, yes. The heaviest domain is still classic Dataverse extensibility, and a PL-400 course or practice test covers it just as well as it did before. What no PL-400 material covers is Power Apps code apps, Microsoft Foundry agent integration, Model Context Protocol servers and Copilot Studio workflows, so plan to pick those up from Microsoft Learn and my study notes above. That is also why every course listed in this guide is labelled with what it does and does not cover yet.
How long should I study for the AB-400?
If you already write .NET and JavaScript against Dataverse day to day, four to six weeks of focused study is a realistic target, with most of that time spent on the plug-in and platform API material that makes up the largest domain. If you are new to Power Platform development, plan for two to three months and start with the Microsoft Learn paths in this guide before touching anything else. Add a couple of weeks on top for the code apps and agent content, which almost nobody has production experience with yet.
Do I need hands-on development experience to pass?
Yes, more than for most Microsoft exams. This is an associate-level developer exam that expects working knowledge of C#, JavaScript, TypeScript, Power Fx, RESTful web APIs and the Power Platform CLI, and the questions assume you have actually registered a plug-in and debugged a code component. Sign up for a free Power Apps Developer Plan and build the things in this guide in your own environment, because reading about the event execution pipeline is not the same as watching a step fire in the wrong stage.
Maintained by Vlad Catrinescu, reviewed September 2026 · All study guides
Subscribe to the Vlad Talks Tech Newsletter
Don't miss out on the latest Microsoft and certification news, conference and
certification discounts, tutorials featuring some of your favorite
Microsoft MVPs, and much more!
Not sure yet? Subscribe and find out why THOUSANDS around the globe subscribe.