Classic workflows
xrmverse runs the classic processes that a solution brings: workflows (category 0, real-time or background),
business rules (category 2, Entity scope only) and actions (category 3). It reads their XAML with its own
interpreter: no XAML runtime and no Windows Workflow Foundation runs it. A process that uses a step or an expression
xrmverse cannot run faithfully is imported, but it fails explicitly when it would run, never silently. Custom workflow
activities (CodeActivity) run through CoreWF once you install the Microsoft workflow SDK yourself.
The design and its rulings (W1–W25, with the amendments made during execution) are in
plan 2.14. What still has to be checked against a real
Dataverse is in the checklist. Microsoft's own description of these
features is on Learn: workflow table,
real-time workflows (and, for their
stages, rank and runas in code, the on-premises developer page
Create real-time workflows),
asynchronous service,
business rules,
ExecuteWorkflow and the
workflow activities namespace.
What runs
| Category | Runs | How |
|---|---|---|
| 0 Workflow, mode 1 (real-time) | yes | inside the triggering request, at its stage, in its transaction |
| 0 Workflow, mode 0 (background) | yes | as a system job (asyncoperation, operationtype 10) queued by the triggering request |
| 0 Workflow, on demand | yes | ExecuteWorkflow (below) |
| 0 Workflow, child | yes | queued as a background job by a "start child workflow" step |
| 2 Business rule, Entity scope | yes | real-time, stage 20 of Create and Update of its table |
| 2 Business rule, Form scope | no | imported and stored; it runs only in a form (the UI is phase 3) |
| 3 Action | yes | as the main operation (stage 30) of its own message |
| 1 Dialog, 4 Business process flow, 5 Modern flow, 6 Desktop flow, 7 AI flow | no | kept for export, never run; the import job log says so |
A process is one definition row (type 1) in the workflow table. Activating it creates an activation row
(type 2, a copy with parentworkflowid set to the definition), and the definition's activeworkflowid points to it;
triggers, jobs and asyncoperation.workflowactivationid use the activation. The tables are described in
Tables in every organization.
Triggers
| Message | Triggers a workflow when |
|---|---|
| Create | triggeroncreate is true |
| Update | a column of triggeronupdateattributelist is in the request, even with an unchanged value |
| Assign | the list holds ownerid (an Assign is an update of the owner) |
| SetState | the list holds statecode or statuscode |
| Delete | triggerondelete is true |
Business rules run on every Create and Update of their table, whatever columns change, and never on Assign, SetState or Delete. Writes that the core makes as a cascade (the child rows of a cascading Delete) trigger nothing; the record Update that commits a file column upload is a write of that column and does trigger.
Scope. scope limits automatic triggers by the owner of the record (before the operation) compared with the owner of
the workflow: 1 the same user, 2 the same business unit, 3 the workflow owner's business unit or one below it, 4 any. A
record without an owner matches every scope. Business rules, on-demand runs and child workflows ignore scope.
Stages and order with plug-ins
A real-time workflow runs at the stage of its message: createstage, updatestage (20 pre-operation or 40
post-operation) or deletestage (20 only; 40 is refused at activation). An empty stage means 40 for Create and Update and
20 for Delete. In a stage, synchronous plug-in steps and real-time workflows form one list ordered by rank, then plug-ins
before workflows at equal rank, then id. Nothing runs at stage 10 or on read messages.
The primary record at stage 20. An "update the primary record" step of a pre-operation workflow writes into the
message's Target, as a pre-operation plug-in would, instead of sending an Update; reads of the primary record see the
stored row with the Target laid over it. At stage 40 and in background runs it is a nested Update.
Identity
| Run | Runs as |
|---|---|
Real-time, runas 1 (Calling User) |
the caller of the triggering request |
Real-time, runas 0 (Owner) |
the workflow's owner; a team owner fails the request with 400 |
| Background | the workflow's owner, who owns the job (createdby is the caller); a team owner fails the triggering request with 400 |
On demand (ExecuteWorkflow) |
the user who starts it, who also owns the job |
| Action | the caller of the action's message, whatever runas says |
| Child workflow | the child workflow's owner |
The owner of a process is the user who imported it (for a queued import, the owner of the import job); a later import keeps the owner the process has. So the scope of an imported process and the user its background jobs run as are the importer's until the process is assigned.
Every step sends its request through the pipeline at depth + 1 as that user: privileges, record access, field security,
plug-ins, auditing, duplicate detection and change tracking apply to it as to any request. Reads of columns are Retrieve
requests, so Retrieve plug-in steps fire on them. At depth 8 the next level fails with 0x80044182 (infinite loop), so a
workflow that triggers itself stops.
Transactions
Real-time workflows and actions run in the triggering transaction: a failing step rolls back the request and every nested write. A background job runs each step in a transaction of its own, so the steps before a failure stay committed. A job is queued at post-operation inside the triggering transaction, so the job of a rolled-back write never exists.
Getting processes in, activating and running them
Processes arrive in two ways. Solution import (xrmd solution import, ImportSolution, pac with Entra; see
Solutions) brings the processes of a file. Authoring as code writes them from YAML: xrmd workflow apply, the authoring API and the MCP tools compile a file to the same designer XAML and store it in the same row (see
Workflow authoring as code). A raw Create or Update of workflow through the SDK, SOAP or the Web
API is accepted under the same rules (a Draft definition of category 0, 2 or 3, its XAML parsed and validated, no
server-written column); An Update re-validates the XAML only when it sends a definition column (the XAML, mode, trigger or stage columns), so an imported Draft with unsupported steps, or of category 1, 4 or 5, can still be renamed. A Create or Update of processtrigger is 400 ("its rows are written by solution import only").
What a client can do:
| Operation | SDK / SOAP | Web API | Rule |
|---|---|---|---|
| Activate | SetStateRequest (State 1, Status 2) or an Update of statecode/statuscode |
PATCH workflows(<id>) with "statecode": 1, "statuscode": 2 |
needs Write on the definition, the activation privileges below; the XAML is parsed and validated (below) |
| Deactivate | SetStateRequest (State 0, Status 1) |
PATCH with "statecode": 0, "statuscode": 1 |
deletes the activation row, unless a job that has not ended still uses it: then it is deleted when that job ends |
| Assign | AssignRequest |
not available: a PATCH of ownerid is an Update of a column other than the state, so it is 400 with the refusal above; use AssignRequest through the SDK or SOAP |
assigning a definition also reassigns its current activation, in the same transaction; background jobs queued from then on, runas 0 runs and the scope use the new owner |
| Delete | DeleteRequest |
DELETE workflows(<id>) |
400 while the process is Activated; a Draft definition takes its activations with it and cancels (3/32) the jobs still waiting on them |
| Run on demand | ExecuteWorkflowRequest (WorkflowId, EntityId) |
POST workflows(<id>)/Microsoft.Dynamics.CRM.ExecuteWorkflow with { "EntityId": "<id>" } |
see below |
Any other column in the activating Update, or a SetState of an activation row, is 400. xrmd workflow (apply, export, list,
activate, deactivate) and the matching MCP tools work on processes by name or id; there is no web admin page, and the
generic record commands and the Web API work on workflows.
Activation privileges. Activating or deactivating a definition, from any surface, also needs: being its owner or holding
prvActOnBehalfOfAnotherUser (a definition owned by a team is not the caller's own); prvActivateSynchronousWorkflow for a
real-time workflow (mode 1) and for an action; prvActivateBusinessRule for a business rule. A background workflow needs
neither. System Administrator holds them by its template, System Customizer at Global, Basic User holds none; a solution
import is exempt. Details: Security.
Activation checks. The XAML is parsed and checked; any of these is a 400 naming the reason: XAML that is not well formed
or not an Activity, a category that does not run, a mode other than 0 or 1, a wait in a real-time workflow, an action or a
business rule, an update trigger whose column list is empty, a stage that does not fit the trigger, a business rule without a
table, an action without a unique name. A process whose only problems are steps xrmverse does not run is activated: the
steps are listed in xrmverse_unsupportedsteps (one "path (display name): reason" per line) and every trigger of it fails
(see What fails explicitly).
ExecuteWorkflow. WorkflowId may be the definition or its current activation. Checks, in order: both ids given (400);
the process exists (404); it is a definition or an activation (400); category 0 (400); no InputArguments (400; an empty
collection counts as none); Activated, and a named activation is the current one (400; an activation that a deactivation
removed is 404); ondemand true (400); the record exists (404); the caller can read it (403). The caller needs Read on the
process and the privilege prvWorkflowExecution (System Customizer and Basic User hold it at Global; System Administrator
by its template). A background workflow is queued and the response Id is the job id; a real-time one runs at once, in the
request's transaction, and Id is Guid.Empty. Either runs as the user who started it.
Steps and expressions
The XAML is read element by element. Every element, referenced activity type and expression shape the parser knows is an
entry of ActivityCatalog (src/Xrmd.Workflows/Xaml/ActivityCatalog.cs); anything else is an unsupported step. The steps
of the designer that run:
| Step | What it does on xrmverse |
|---|---|
| Create record | Create; empty values are left out. A created activity gets isworkflowcreated true |
| Update record (primary or related through an N:1 lookup) | Update; an empty value clears the column; at stage 20 on the primary record, see above |
| Assign record | Assign to a user or a team |
| Change status | SetState |
| Send email | creates the email, then SendEmail with IssueSend true; with no email provider the email stays Pending Send (state 1, status 6), as described in Activities and notes |
| Check condition, conditional branch, default action | the first true branch runs, otherwise the default branch |
| Wait condition, timeout | background only; see Waits |
| Start child workflow | queues a background job of the child (below) |
| Perform action | sends the action's message through the pipeline (an action, a custom API or a custom action); its outputs can be read by later steps |
| Stop workflow | Succeeded ends the run normally; Canceled ends it as canceled (below) |
| Custom workflow activity | runs the CodeActivity (below) |
| Business rule: set field value, set default value, clear field value, show error message | server-side effects (below) |
| Business rule: show or hide, enable or disable, set requirement level, recommendation | no-ops on the server |
Values. Literals of every column type, columns of the primary record and of N:1 related records, the execution time,
records created by earlier steps, the action's own input arguments, outputs of earlier steps, arithmetic (Add,
Subtract, Multiply, Increment, Decrement, Set) on numbers and dates, text concatenation and a "first non-empty
value" choice. A value keeps its type: only two conversions exist (a Guid primary key to a reference of its table, and a
reference to a party list with one party); any other mismatch, for example a Money column read where the XAML expects a
Decimal, fails the run with a 400 naming the column and both types. These rules are ours until the checklist confirms them:
- an empty operand makes arithmetic empty; a date plus or minus a number moves it by that many days; the result keeps the first operand's type, and a result out of range fails;
- text comparisons ignore case, and an empty text counts as empty;
- with an empty operand, Equal is false, Not Equal true, the ordering and text operators false, except Does Not Contain, which is true;
- concatenation takes only text and the names of references (numbers, dates and choices would need the user's formats); only empty parts give an empty result;
- reading an action input that was not given, or an output before its step ran, gives an empty value.
No general Visual Basic expression is evaluated: any other expression text, x:Code or unknown function is an unsupported
step.
The catalog
Source says where the entry comes from: export was seen in the real parity export
(tests/fixtures/solutions/RealExports/xrmdWfTest_1_0_0_2.zip), learn is a public type name of the Learn reference that the
export does not use, and assumed is the shape we expect the designer to write; every assumed entry is a line of checklist
item 1g or 1h. ClientOnly entries are business rule actions that do nothing on the server; Unsupported entries make
the process's step unsupported, with the reason shown. The mxswa: prefix stands for the namespace
Microsoft.Xrm.Sdk.Workflow.Activities, mcwc: for Microsoft.Crm.Workflow.ClientActivities; names without a prefix
or namespace are System.Activities elements; full type names are ActivityReference targets. The table is generated from
the code, and ActivityCatalogDocTests keeps the two equal.
| Entry | Kind | Status | Source | Reason |
|---|---|---|---|---|
mxswa:Workflow |
Root | Supported | export | |
mxswa:CreateEntity |
Create | Supported | export | |
mxswa:UpdateEntity |
Update | Supported | export | |
mxswa:AssignEntity |
Assign | Supported | learn | |
mxswa:SetState |
SetState | Supported | learn | |
mxswa:SendEmail |
SendEmail | Supported | export | |
mxswa:StartChildWorkflow |
ChildWorkflow | Supported | export | |
mxswa:Postpone |
Wait | Supported | learn | |
mxswa:GetEntityProperty |
GetValue | Supported | export | |
mxswa:SetEntityProperty |
SetValue | Supported | export | |
mxswa:RetrieveEntity |
Retrieve | Supported | learn | |
mxswa:ActivityReference |
Reference | Supported | export | |
mxswa:ReferenceLiteral |
TypeLiteral | Supported | export | |
mxswa:SendEmailFromTemplate |
SendEmailFromTemplate | Unsupported | learn | sending an email from a template is not supported: xrmverse has no email templates |
mxswa:WebActivityReference |
WebActivityReference | Unsupported | learn | web activity references are not supported |
mxswa:GetPrimaryEntity |
GetPrimaryEntity | Unsupported | learn | GetPrimaryEntity is not part of the shapes xrmverse runs |
Sequence |
Sequence | Supported | export | |
Assign |
EntityBookkeeping | Supported | export | |
Persist |
Persist | Supported | export | |
Variable |
Variable | Supported | export | |
TerminateWorkflow |
Stop | Supported | export | |
Microsoft.Crm.Workflow.Activities.Composite |
Composite | Supported | export | |
Microsoft.Crm.Workflow.Activities.StepComposite |
StepComposite | Supported | assumed | |
Microsoft.Crm.Workflow.Activities.ConditionSequence |
Condition | Supported | export | |
Microsoft.Crm.Workflow.Activities.ConditionBranch |
ConditionBranch | Supported | export | |
Microsoft.Crm.Workflow.Activities.EvaluateCondition |
EvaluateCondition | Supported | export | |
Microsoft.Crm.Workflow.Activities.EvaluateLogicalCondition |
EvaluateLogicalCondition | Supported | assumed | |
Microsoft.Crm.Workflow.Activities.EvaluateExpression |
EvaluateExpression | Supported | export | |
Microsoft.Crm.Workflow.Activities.ConvertCrmXrmTypes |
ConvertTypes | Supported | assumed | |
Microsoft.Crm.Workflow.Activities.ExecuteAction |
ActionCall | Supported | assumed | |
mcwc:SetAttributeValue |
SetAttributeValue | Supported | export | |
mcwc:SetMessage |
ClientSetMessage | Supported | export | |
Microsoft.Crm.Workflow.Activities.SetDefaultValue |
SetDefaultValue | Supported | assumed | |
Microsoft.Crm.Workflow.Activities.SetMessage |
ShowError | Supported | assumed | |
Microsoft.Crm.Workflow.Activities.SetVisibility |
SetVisibility | ClientOnly | assumed | |
Microsoft.Crm.Workflow.Activities.SetDisplayMode |
SetDisplayMode | ClientOnly | assumed | |
Microsoft.Crm.Workflow.Activities.SetFieldRequiredLevel |
SetFieldRequiredLevel | ClientOnly | assumed | |
Shape:EntityBookkeeping |
Assign New Entity("x") to a #Temp key, its .Id, copies between entity dictionaries |
Supported | export | |
Shape:RecordKeys |
InputEntities("primaryEntity"), InputEntities("related_<lookup>#<entity>"), CreatedEntities("<step>") |
Supported | export | |
Shape:StopException |
TerminateWorkflow Exception = New InvalidPluginExecutionException(OperationStatus.Canceled|Succeeded[, message]), Reason = the message |
Supported | export | |
Shape:DesignerSequence |
a Sequence with DisplayName and Sequence.Variables for each designer step | Supported | export | |
Shape:WaitBranch |
ConditionSequence Wait=True with one ConditionBranch: wait for its condition, then its Then | Supported | export | |
Shape:WaitTimeoutBranch |
ConditionSequence Wait=True with a second branch whose Then starts with Postpone | Supported | assumed | |
Shape:ElseBranch |
a last ConditionBranch whose Condition is [True]: the otherwise block | Supported | export | |
Shape:ChildInputs |
StartChildWorkflow InputParameters = a variable whose Default is an empty Dictionary(Of String, Object) | Supported | export | |
Shape:OutputAssign |
Assign To = [<output argument>]: sets an action's output argument |
Supported | export | |
Shape:ArgumentAttributes |
x:Property.Attributes Argument{Required,Target,Description,Direction,Entity}Attribute | Supported | export | |
Shape:LookupLiteral |
CreateCrmType { EntityReference, "entity", "name", <Guid literal variable>, "Lookup" } |
Supported | export | |
Shape:TargetType |
Get/SetEntityProperty and EvaluateExpression TargetType: the value's own type, a Guid primary key as EntityReference, an EntityReference as EntityCollection (one activity party) | Supported | export | |
Shape:StepLabel |
SetMessage.StepLabels with one mcwo:StepLabel (Description, LabelId, LanguageCode) | Supported | export | |
EvaluateExpression:CreateCrmType |
Literal | Supported | export | |
EvaluateExpression:RetrieveCurrentTime |
ExecutionTime | Supported | assumed | |
EvaluateExpression:SelectFirstNonNull |
FirstNonNull | Supported | export | |
EvaluateExpression:CustomOperationArguments |
ArgumentRead | Supported | export | |
EvaluateExpression:Add |
Arithmetic | Supported | export | |
EvaluateExpression:Subtract |
Arithmetic | Supported | assumed | |
EvaluateExpression:Multiply |
Arithmetic | Supported | assumed | |
EvaluateExpression:Increment |
Arithmetic | Supported | assumed | |
EvaluateExpression:Decrement |
Arithmetic | Supported | assumed | |
EvaluateExpression:Set |
Arithmetic | Supported | assumed | |
Vb:String.Concat |
Concat | Supported | assumed | |
Vb:DirectCast |
Cast to System.String or System.Decimal | Supported | export | |
Vb:DirectCast:OtherPropertyTypes |
Cast to the CLR type of another workflow property type | Supported | assumed | |
Vb:ActionOutput |
<perform action result>("<output>"): an output of a perform action step |
Supported | assumed | |
EvaluateCondition:Null |
ConditionOperator | Supported | export | |
EvaluateCondition:GreaterThan |
ConditionOperator | Supported | export | |
EvaluateCondition:LessThan |
ConditionOperator | Supported | export | |
EvaluateCondition:Equal |
ConditionOperator | Supported | assumed | |
EvaluateCondition:NotEqual |
ConditionOperator | Supported | assumed | |
EvaluateCondition:NotNull |
ConditionOperator | Supported | assumed | |
EvaluateCondition:Contains |
ConditionOperator | Supported | assumed | |
EvaluateCondition:DoesNotContain |
ConditionOperator | Supported | assumed | |
EvaluateCondition:BeginsWith |
ConditionOperator | Supported | assumed | |
EvaluateCondition:EndsWith |
ConditionOperator | Supported | assumed | |
EvaluateCondition:GreaterEqual |
ConditionOperator | Supported | assumed | |
EvaluateCondition:LessEqual |
ConditionOperator | Supported | assumed | |
EvaluateCondition:Between |
ConditionOperator | Supported | assumed | |
EvaluateCondition:NotBetween |
ConditionOperator | Supported | assumed | |
EvaluateLogicalCondition:And |
LogicalOperator | Supported | assumed | |
EvaluateLogicalCondition:Or |
LogicalOperator | Supported | assumed | |
EvaluateLogicalCondition:Not |
LogicalOperator | Supported | assumed |
Background jobs and waits
A background workflow is a system job with operationtype 10: name is the workflow's name, messagename the
triggering message, regardingobjectid the record, workflowactivationid the activation, depth the triggering depth + 1,
and data the run's state (where it stopped and its variables). The worker runs it as its owner.
| State | Meaning |
|---|---|
| 0/0 | ready |
| 2/20 | running |
| 1/10 | waiting: for postponeuntil, or for a write of the record its wait condition reads |
| 3/30 | succeeded; with asyncautodelete the job row is deleted instead |
| 3/31 | failed, with the fault's code and message; workflow jobs are not retried |
| 3/32 | canceled: a stop step with Canceled (with its message), the workflow was deleted, or a client canceled it |
Waits. A wait whose condition holds goes on at once. Otherwise the job suspends (1/10) and registers a row in
meta.workflow_wait; any core write of the record it waits on (Update, Assign, SetState, SendEmail, a lookup cleared by a
deleted parent, …) wakes it, it evaluates its condition again and suspends again while the condition is false. A "wait until
a date" step sets postponeuntil. When a timeout passes, its block runs and the rest of that wait's sequence is skipped. If
the record of a wait is deleted, the job fails (3/31, "The record of the wait was deleted."). A waiting job survives a restart:
its state is in data. Writes cost nothing extra while no job waits.
Cancelling. Update the job to statecode 3, statuscode 32 (PATCH asyncoperations(<id>)): allowed while it is ready
or waiting; its wait row goes with it. Pause and Resume are not supported (the usual 400).
Deactivating and deleting. Deactivating a workflow keeps its waiting jobs running to their end, on the kept activation. Deleting a Draft definition, or uninstalling the managed solution that holds it, cancels its waiting jobs (3/32, "The workflow was deleted.").
Child workflows. The child must be Activated, have subprocess true and run on the same table as the record passed;
otherwise the step fails (400). It is queued at depth + 1 as a background job of its own, owned by the child workflow's owner,
also when the parent is real-time (then inside the parent's transaction). A job above depth 8 fails with 0x80044182.
Business rules on the server
A business rule runs on the server when its processtriggerscope is 2 (Entity), as the export's <ProcessTriggerScope>
element sets it, or when one of its processtrigger rows has scope 2. It runs at stage 20 of every Create and Update of its
table, in rank order with the plug-ins:
- set field value and clear field value write into the message's
Target; - set default value applies on Create only, and only when the column is empty;
- show error message fails the request with 400
0x80040265(the plug-in abort code) and the rule's text; - show or hide, enable or disable, requirement level and recommendations do nothing on the server.
A Form-scope rule (scope 1) is stored and exported and never runs on the server.
Actions
Activating an action (category 3), or importing one in any state, creates its message: an sdkmessage named with the
publisher prefix, _ and the action's uniquename (an export's Recalculate of publisher xrmv becomes
xrmv_Recalculate; a name that already has the prefix keeps it). The message is created with the export's SdkMessageId
when that id is free, with a filter on the bound table (none for a global action). A name that clashes with a core message,
a custom API or another message is refused (400, and the import fails naming the process and the message). Deactivating
keeps the message, so plug-in steps registered on it stay; calling a Draft action's message is 400 "not activated". Deleting
the definition deletes the message, and is refused while a step is registered on it. The table and the category of an action
cannot change on a re-import.
Calling the message checks the request against the arguments of the XAML (x:Members): a missing required input, a value of
the wrong type, an undeclared parameter or a Target of another table is 400 0x80040203, as for custom APIs. Plug-in steps
on the message fire at stages 20 and 40 around the XAML, which runs at stage 30 in the caller's transaction, as the caller.
Output arguments the XAML set are in the response; outputs it did not set are left out. A stop with Canceled fails the
request with 400 0x80040265. Actions are reached through the SDK and SOAP (OrganizationRequest by name); on the Web API,
POST /<message> stays 404 for XAML actions. How actions compare with custom APIs: see Custom APIs.
Custom workflow activities
A plug-in type whose class derives from System.Activities.CodeActivity (checked by full name, without loading the
assembly) is a workflow activity: registration sets isworkflowactivity true, and an import marks the types that have a
WorkflowActivityGroupName. A type sent with isworkflowactivity true that is not a CodeActivity is 400.
Running one needs Microsoft.Xrm.Sdk.Workflow.dll, which xrmverse does not ship. xrmd sdk install downloads it with the
other SDK packages after the one license acceptance (--from-workflow <file.nupkg> offline); see
Installing the Microsoft SDK. The host starts without it. Without it, a step that names a custom activity is
an unsupported step with the reason "custom workflow activities need the Microsoft workflow SDK: run xrmd sdk install",
shown in the import log and xrmverse_unsupportedsteps; activation and import check again, so installing the SDK and
re-activating (or restarting) is enough.
The activity loads in the plug-in's collectible context, with CoreWF's System.Activities and the installed
Microsoft.Xrm.Sdk.Workflow shared from the host, and runs through CoreWF's WorkflowInvoker with a context
(IWorkflowContext), an organization service factory and a tracing service:
- Arguments. Inputs are
InArgument<T>with[Input], outputsOutArgument<T>with[Output]. A value must already be of the argument's type and anEntityReferencemust point to the table of[ReferenceTarget]; otherwise, and for an unset[RequiredArgument]input, an unset input with a[Default]or an empty value for a value type, the step fails with 400 naming the argument. - Context.
Depthis the triggering message's;StageNameisPreOperation(20),MainOperation(actions and real-time on-demand runs) orPostOperation(40 and background);Modeis 0 for real-time and actions, 1 for background,WorkflowModethe opposite;WorkflowCategorythe process's;ParentContextis null; a background run has empty input parameters and there are no entity images. A member xrmverse does not fill throwsNotSupportedExceptionnaming it. - Service.
CreateOrganizationService(null),Guid.Emptyor the run user's id act as the run user; another id acts as that user. Requests go through the pipeline as the workflow's own steps do; a failed call inside a transaction dooms it, as for plug-ins. - Errors and limits. As a plug-in:
InvalidPluginExecutionExceptionis 400 with its message, any other exception 500, the plug-in timeout applies, and traces go toplugintracelog(operationtype2, no step id) underplugintracelogsetting. A type without a parameterless constructor is 400.StopWorkflowExceptionwithSucceededends the run normally, withCanceledas a canceled stop, withFailedas the 400 abort (a background job fails 3/31);RetryandSuspendedare 400 "not supported". An instance is created per run.
What fails explicitly
| Where | What | How it shows |
|---|---|---|
| Import | a step or expression xrmverse does not run | a warning in the import job log naming the process and each step; the process keeps its state from the file and xrmverse_unsupportedsteps lists the steps; the rest of the solution imports |
| Import | a process that fails the activation checks | a warning, and the process stays Draft |
| Import | a category that does not run (1, 4–7) | a warning "kept for export but not run" |
| Import | a StateCode other than 0 or 1 |
a warning, and the process stays Draft |
Run time, real-time and ExecuteWorkflow |
a process with unsupported steps | 400 0x80040203 "Workflow '…' cannot run on xrmverse: …", nothing is written |
| Run time, background | the same | the job fails 3/31 with that text |
| Run time | a step error (privilege, missing record, value of the wrong type, …) | the step's fault: the request fails (real-time) or the job fails 3/31 (background) |
| Run time | a stop with Canceled in real time or in an action | 400 0x80040265 with the stop's message, or "The workflow '…' canceled the operation." when it has none |
| Run time | an activation whose XAML no longer parses | treated as a process with an unsupported step |
Known limits
Each item is refused with an error or stated here; none is a silent difference.
processtriggeris written by solution import only; a Create, Update or Delete of it is 400. A public Create or Update ofworkflowis accepted (see Workflow authoring as code), but not with the server-writtenxrmverse_unsupportedsteps. The limits of authoring are on that page.- No
workflowlog,processsessionor process stage tables. Reading them is the usual unknown-table error. A background job's progress and error are on itsasyncoperationrow (message,friendlymessage,errorcode). - No Web API for XAML actions:
POST /<message>is 404; call them through the SDK or SOAP. - No Pause or Resume of workflow jobs; cancel them instead.
- No Assign over the Web API.
PATCH workflows(<id>)withownerid@odata.bindis 400; assign a process withAssignRequest(SDK or SOAP). - Form-scope business rules do not run on the server (no forms until phase 3).
- "Send email from template" is an unsupported step: xrmverse has no email templates. Email is not sent until plan 4.1 (the email stays Pending Send).
- Custom workflow activities run on .NET: an API that exists only on .NET Framework fails with the plug-in "could not be
loaded" error. There is no isolation: the activity runs in the
xrmdprocess, as plug-ins do. primaryentityandprocesstrigger.primaryentitytypecodeare text columns (String 100) holding the table's logical name, not EntityName columns.- On-demand runs do not accept
InputArguments. - A lookup literal to a record of another organization (for example the exporting organization's currency in an email step) fails the step with a 404 naming the table and id; create a record with that id, or change the process, and run it again.
- At-least-once. A job interrupted by a crash, or by a stop in the middle of a run, runs again from its last saved state, so the steps after its last wait can run twice.
- Background workflows on Delete run after the record is gone: a step that reads it fails the job (404). Dataverse gives them the record's pre-image.
- Team owners. A background workflow, or a real-time one with
runas0, owned by a team fails its trigger with 400. - Not checked against Dataverse. Every
assumedcatalog entry, the expression rules above, the default stages, the order at equal rank, the job messages, the error codes of the refusals and the context values of custom activities are our reading until the checklist confirms them.