Study guide
9.4 Upsert-Based Synchronization with the UpsertRequest Message
Microsoft Power Platform Developer Associate PL-400
Track your progress as you read
Finished sections get a check mark.
1 Introduction, Exam Overview & Developer Toolchain
0.1 PL-400 Exam Facts 0.2 Audience Profile & Required Developer Skills 0.3 The Power Platform Developer Toolchain 0.4 Extension-Point Map & AI in the Developer Workflow
2 Create a Technical Design
1.1 Analyzing Technical Architecture and Choosing Out-of-the-Box vs. Custom Solutions 1.2 Designing Authentication, Authorization, and Security Architecture 1.3 Deciding Where Business Logic Runs and Choosing the Right Table Type 1.4 Designing Reusable Power Apps Components and Custom Connectors 1.5 Designing Dataverse Code Components, Automations, and Integrations
3 Build Power Platform Solutions
2.1 Managing Power Platform Environments for Development 2.2 Dataverse Security Roles for Code Components, Least Privilege & Troubleshooting Operational Security 2.3 Solutions, Managing Dependencies & Solution Layers 2.4 Environment Variables & Connection References 2.5 Power Platform Pipelines & CI/CD with Power Platform Build Tools
4 Implement Power Apps Improvements
3.1 Complex Power Fx Formulas and Functions 3.2 Building Reusable Canvas Component Libraries 3.3 Invoking Power Automate Cloud Flows from a Canvas App 3.4 Troubleshooting Canvas and Model-Driven Apps with Monitor and Browser Debugging Tools 3.5 Optimizing Canvas and Model-Driven App Performance
5 Extend the User Experience
4.1 The Client API Object Model & JavaScript Form Scripting 4.2 Event Handler Registration & Navigation to Custom Pages via the Client API 4.3 Client Scripting Against the Dataverse Web API; Command Bar & Buttons with Power Fx and JavaScript 4.4 PCF Fundamentals: The Component Manifest & Lifecycle Events 4.5 Implementing PCF Component Interfaces; Device, Utility & Web API Features 4.6 Packaging, Deploying & Consuming a PCF Component
6 Extend the Platform: Dataverse Plug-ins & Custom APIs
5.1 Plug-in Fundamentals & the Event Execution Pipeline Stages 5.2 Execution Context & Implementing Business Logic in Plug-ins 5.3 Pre/Post Images & Organization Service Operations in Plug-ins 5.4 Registering Plug-ins with the Plug-in Registration Tool & Optimizing Plug-in Performance 5.5 Custom API Messages & Plug-ins That Implement a Custom API 5.6 Configuring Dataverse Business Events
7 Extend the Platform: Custom Connectors
6.1 Open API Definitions: Building and Extending a Custom Connector 6.2 Authentication and Policy Templates for Custom Connectors 6.3 Importing Definitions and Creating Connectors for Azure Services 6.4 Azure Functions in Custom Connectors and Data Transformation
8 Extend the Platform: Platform APIs & Azure Functions
7.1 Dataverse Web API Operations & OAuth Authentication 7.2 Organization Service Operations, Concurrency, Transactions & Bulk Operations 7.3 Service Protection API Limits & Retry Policies 7.4 Azure Functions for Long-Running, Scheduled & Event-Driven Workloads 7.5 Authenticating to Power Platform with Managed Identities
9 Extend the Platform: Power Automate Cloud Flows
8.1 Dataverse connector triggers/actions, trigger filters & retry policies 8.2 Complex expressions & flow control with error handling 8.3 Sensitive input/output parameters & Azure Key Vault 8.4 Reusable child flows & Microsoft Entra ID service principals
10 Develop Integrations
9.1 Publishing Dataverse Events: IServiceEndpointNotificationService & the Plug-in Registration Tool 9.2 Registering Service Endpoints: Webhooks, Azure Service Bus & Event Hub 9.3 Data Synchronization with Change Tracking & Alternate Keys 9.4 Upsert-Based Synchronization with the UpsertRequest Message
11 Exam Readiness & Practice
10.1 Cross-Domain "Where Do I Implement This?" Decision Framework 10.2 Exam-Day Strategy, PL-400 Item Styles & Study Plan
9.4 Upsert-Based Synchronization with the UpsertRequest Message
Key Takeaways Explain This Topic AI
Upsert combines Create and Update into a single idempotent operation: it updates the record if a match is found by primary key or alternate key, and creates a new record if not.
The SDK's UpsertRequest.Target uses Entity.KeyAttributes to identify the record by alternate key; the response's RecordCreated property reports whether a create or update actually happened.
In the Web API, a PATCH request to an alternate-key URL is treated as upsert by default; adding If-Match: * forces update-only, and If-None-Match: * forces create-only.
Don't include the alternate key's own column values in the request body — Dataverse strips or copies them automatically, and mismatches between the URL key and the body risk unexpected data.
Elastic tables apply Upsert directly without firing separate Create/Update events, so business logic tied to those events must also be duplicated into any Upsert-handling logic.
Last updated: July 2026
The previous section covered how a batch integration finds out what changed and which record it corresponds to. This section covers the write side: once you know a piece of external data needs to land in Dataverse, how do you save it without first checking whether the record already exists?
The Problem Upsert Solves
A naive integration loop would retrieve the record by its alternate key, check whether anything came back, and then branch to either Create or Update . That's an extra round trip for every single record, on top of whatever the actual write costs — expensive at scale and easy to get wrong under concurrent writes. The Upsert message collapses that into one call: Upsert = Update-if-it-exists, Create-if-it-doesn't , decided entirely on the server side.
- How UpsertRequest Works (SDK) Entity target = new Entity("account", "accountnumber", "A123");
- target["name"] = "Contoso West Region";
- var request = new UpsertRequest { Target = target };
- var response = (UpsertResponse)service.Execute(request);
- if (response.RecordCreated)
- Console.WriteLine("New record created.");
- else
- Console.WriteLine("Existing record updated.");
The Target entity typically identifies the row through Entity.KeyAttributes (an alternate key) rather than the primary GUID, since that's the whole point of an integration scenario — the caller usually doesn't know the GUID. On the server:
If a matching record is found (by primary key or alternate key), Dataverse strips any attributes that duplicate the key values, calls Update , and sets UpsertResponse.RecordCreated to false .
If no match is found , Dataverse copies the key attribute values into the entity's attribute collection (so they're saved as real column values, not just match criteria), calls Create , and sets RecordCreated to true .
UpsertResponse.Target always returns an EntityReference to the affected record either way, and RecordCreated is the one property that lets your integration branch its own logic — for example, kicking off a "new customer" onboarding flow only when RecordCreated is true , versus a "customer updated" notification otherwise.
How Upsert Works (Web API)
The Web API doesn't have a separate Upsert verb — it overloads PATCH :
PATCH to an alternate-key URL with no extra header → treated as upsert; creates the record if the key doesn't match anything, updates it if it does. Either way the response is 204 No Content , so the status code alone doesn't tell you which happened.
PATCH with an If-Match: * header → forces update-only . If no record matches the key, the operation fails rather than creating one.
PATCH with an If-None-Match: * header → forces create-only , blocking an update if a matching record already exists.
Add Prefer: return=representation to get a distinguishable response: 201 Created for a new record, 200 OK for an updated one — at the cost of an extra internal Retrieve , so scope the $select down to just the primary key if you use it.
- Header combination Behavior
- None Upsert: create or update, whichever applies
- If-Match: * Update only — fails if the record doesn't exist
- If-None-Match: * Create only — fails if the record already exists
- Prefer: return=representation Adds 201 / 200 distinction to any of the above, at a performance cost
- Practical Guidance
Don't put the alternate key's own column values in the request body — for the Web API that means not repeating them in the JSON payload, and for the SDK it means not duplicating them in Entity.Attributes . On an update, the server discards any attribute data that overlaps with the key columns anyway; the risk is on create , where mismatched values between the URL/ KeyAttributes and the body could produce a record whose key doesn't match what you expect.
Elastic tables behave differently. For a standard table, Upsert internally calls the real Create or Update message, so any plug-ins registered on those events still fire. For an elastic table , Upsert applies the change directly — there's no separate Create or Update event raised. If business logic needs to run on every insert or every change to an elastic table, that logic has to be included in whatever handles the Upsert path too, not just registered against Create / Update .
When not to use it: if you're certain the record doesn't already exist — for example, a first-time bulk load into an empty table — plain Create is cheaper, since Upsert carries a performance penalty from the extra existence check. Save Upsert for the case it's built for: an ongoing sync job, most often paired with the change-tracking delta and alternate-key lookup from the previous section, where you genuinely don't know ahead of time whether each incoming record is new or already there.