Stage tells Dataverse where in the pipeline your plugin runs. Execution mode tells it when the caller gets control back. Every plugin step is registered as either synchronous or asynchronous, and the choice has a real, felt effect on how fast the app feels to the person using it.
- Synchronous plugins run inside the same transaction and block the caller until they finish
- Asynchronous plugins are queued as a system job and run after the response has already returned
- Stages 10 (pre-validation) and 20 (pre-operation) only support synchronous execution
- Only stage 40 (post-operation) can be either synchronous or asynchronous
Waiting at the counter vs. dropping off a form
A synchronous plugin is like waiting at a bank counter: the teller processes your transaction while you stand there, and you don't leave until it's done — or until you're told it failed. An asynchronous plugin is like dropping a form in a box and getting a text later once someone's processed it: you walk away immediately, and the outcome catches up with you afterward, separately from the moment you handed the form over.
Synchronous execution
A synchronous plugin runs in-line with the user's request, as part of the same database transaction that's saving the record. The caller — a user clicking Save, or a process calling the Web API — waits for it to finish before getting a response back. If a synchronous plugin throws an exception, the entire operation can be rolled back: nothing gets saved. This is the only option that can reliably stop a bad save before it happens, which is why pre-validation and pre-operation are synchronous-only — there'd be no point validating or adjusting a record after the caller has already been told it saved.
Asynchronous execution
An asynchronous plugin doesn't run immediately. Instead, Dataverse drops a system job onto a queue, returns control to the caller right away, and a background process picks the job up and runs your plugin afterward — seconds later, sometimes longer under load. By the time it runs, the original transaction has already committed and can't be rolled back because of anything your async plugin decides. This makes async the right home for anything that isn't time-critical: sending a notification, writing an audit entry, calling a slower external API that shouldn't hold up the user's save.
| Synchronous | Asynchronous | |
|---|---|---|
| When it runs | Inside the transaction, before the response returns | After the response returns, as a queued system job |
| Can block/roll back the save | Yes | No — the save has already committed |
| Available stages | 10, 20, or 40 | 40 only |
| Effect on save time | Adds directly to how long the save takes | Doesn't slow the save down |
| Good for | Validation, field calculations that must complete first | Notifications, external calls, non-critical follow-up work |
Choosing wrong in the "too synchronous" direction is one of the most common — and most avoidable — ways a Dynamics 365 CE environment starts to feel slow. A plugin calling a third-party API synchronously ties every affected save to that API's speed and uptime, even on days it's having a bad one. If your logic doesn't need to finish before the user sees a result, asynchronous is almost always the better default.
Setting it at registration
Execution mode isn't set in code — it's chosen when you register the plugin step (covered fully in Chapter 7), alongside the message, table, and stage. The same compiled plugin class can even be registered twice — once synchronously at pre-operation to validate, and once asynchronously at post-operation to notify — since each registration is an independent step pointing at the same Execute method.