Workflow management operations with DaprWorkflowClient
DaprWorkflowClient provides the following operations in SDK 1.19.0. Use a Dapr 1.19 runtime for the complete
surface, particularly listing, history inspection, and rerunning from an event. Register or construct a client as
described in client registration.
Schedule a new workflow instance
ScheduleNewWorkflowAsync returns an instance ID, not the workflow result. Provide a registered workflow name and
optional input. Omitting instanceId generates a GUID. A scheduled start in the future delays execution; a past
or omitted start time starts it immediately.
var instanceId = await client.ScheduleNewWorkflowAsync(
name: "OrderProcessingWorkflow",
instanceId: null,
input: new { Product = "Paperclips", Quantity = 1000 },
startTime: DateTimeOffset.UtcNow.AddMinutes(5),
cancellation: cancellationToken);
The cancellation-capable overload takes name, instanceId, input, DateTimeOffset? startTime, and
cancellation. The three-argument convenience overload and legacy DateTime? overload do not take a token.
Cancellation stops the client request; it does not terminate an instance that was already scheduled.
When name-based versioning is enabled, prefer the canonical name for new instances so the target app can choose its latest registered version.
Retrieve the status of a workflow instance
GetWorkflowStateAsync returns a WorkflowState snapshot:
var state = await client.GetWorkflowStateAsync(
instanceId, getInputsAndOutputs: true, cancellation: cancellationToken);
if (!state.Exists)
{
throw new InvalidOperationException($"Workflow {instanceId} does not exist.");
}
var status = state.RuntimeStatus;
| Member | Meaning |
|---|---|
Exists, WorkflowName | Whether an instance was found and its workflow name. |
RuntimeStatus | Current WorkflowRuntimeStatus, including pending, running, suspended, terminal, or unknown state. |
IsWorkflowRunning | True only for Running, not for pending or suspended instances. |
IsWorkflowCompleted | True for any terminal state: completed, failed, or terminated. It does not imply success. |
CreatedAt, LastUpdatedAt | UTC timestamps exposed as DateTimeOffset. |
FailureDetails | Failure type, message, and optional stack trace when available. |
ReadInputAs<T>(), ReadOutputAs<T>(), ReadCustomStatusAs<T>() | Deserialize captured values using the configured workflow serializer. |
Set getInputsAndOutputs: false to avoid fetching input/output data when polling status. Keep custom status small;
the SDK processes custom status returned by the runtime even when this flag is false. Typed readers do not fetch
missing data later. With data references, metadata reads can also
download binary objects.
A missing instance, including gRPC NotFound, produces Exists == false. Other gRPC failures propagate as
RpcException; do not treat transport failures as missing workflows. Access to failure details depends on the
data included in the runtime response.
Wait for start or completion
WaitForWorkflowStartAsync waits until the instance leaves pending state. WaitForWorkflowCompletionAsync waits
for completed, failed, or terminated state. Both return WorkflowState, can return immediately when the target
state was already reached, and throw InvalidOperationException when the runtime reports no instance.
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeout.CancelAfter(TimeSpan.FromMinutes(10));
var finalState = await client.WaitForWorkflowCompletionAsync(
instanceId, getInputsAndOutputs: true, cancellation: timeout.Token);
if (finalState.RuntimeStatus != WorkflowRuntimeStatus.Completed)
{
throw new InvalidOperationException(
$"Workflow ended with {finalState.RuntimeStatus}: {finalState.FailureDetails?.ErrorMessage}");
}
var result = finalState.ReadOutputAs<string>();
Waits retry gRPC Unavailable and DeadlineExceeded with backoff until they succeed or the caller cancels.
Always set a cancellation/deadline appropriate to the application: an eternal workflow might never finish.
Canceling a wait does not cancel the workflow.
Raise an event to a running workflow instance
await client.RaiseEventAsync(instanceId, "Approval", true, cancellation: cancellationToken);
The workflow receives the value through WaitForExternalEventAsync<T>("Approval"). Events can be buffered before
the workflow starts waiting. Submission does not acknowledge application processing; do not rely on this call to
confirm that a missing or already-completed target consumed an event.
Suspend a running workflow instance
await client.SuspendWorkflowAsync(instanceId, reason: "Manual review", cancellation: cancellationToken);
Suspension pauses workflow progression until resumed; it is not cancellation of an activity’s external side effects.
Resume a suspended workflow instance
await client.ResumeWorkflowAsync(instanceId, reason: "Review complete", cancellation: cancellationToken);
Terminate a workflow instance
await client.TerminateWorkflowAsync(
instanceId, output: new { Reason = "Canceled by operator" }, cancellation: cancellationToken);
output is an optional serializable object, not just a string. The request recursively terminates child
workflows, but does not stop in-flight activity executions or independent detached workflows. Request completion
does not mean every affected workflow has already transitioned; query or wait for the resulting state.
Purge a workflow instance
The public method is PurgeInstanceAsync, not PurgeWorkflowAsync:
bool purged = await client.PurgeInstanceAsync(instanceId, cancellation: cancellationToken);
Only terminal instances can be purged. The SDK requests recursive deletion of child instances and their histories; the Boolean result reports whether the runtime deleted any instances. Runtime errors still propagate. Purging is irreversible and does not delete binary payload objects. Follow the payload retention and cleanup guidance.
List instance IDs with pagination
ListInstanceIdsAsync returns a WorkflowInstancePage containing InstanceIds and an optional
ContinuationToken. Pass the token back unchanged to obtain the next page:
string? continuationToken = null;
do
{
var page = await client.ListInstanceIdsAsync(
continuationToken, pageSize: 100, cancellation: cancellationToken);
foreach (var id in page.InstanceIds)
{
Console.WriteLine(id);
}
continuationToken = page.ContinuationToken;
}
while (!string.IsNullOrEmpty(continuationToken));
pageSize, when supplied, must be greater than zero. This API lists IDs, not full states; it does not expose
status/date/name filters. Fetch individual states as needed and bound request concurrency for large listings.
Inspect execution history
var events = await client.GetInstanceHistoryAsync(instanceId, cancellation: cancellationToken);
foreach (var historyEvent in events)
{
Console.WriteLine($"{historyEvent.EventId}: {historyEvent.EventType} at {historyEvent.Timestamp:O}");
}
The returned IReadOnlyList<WorkflowHistoryEvent> contains event ID, event type, and timestamp. It does not
expose raw serialized inputs, outputs, or the full protobuf history. Event types distinguish activity scheduling,
completion/failure, child workflows, timers, events, suspension/resumption, continuation, and detached creation.
Rerun from a history event
RerunWorkflowFromEventAsync creates a new workflow instance from a source history and event ID. It does not
reset the source instance. Select a runtime-supported rerun point from history; an event ID is not a list index,
and not every event is a valid rerun boundary.
using Dapr.Workflow.Client;
// Pick a rerun point: the first scheduled activity in the source history.
var history = await client.GetInstanceHistoryAsync(instanceId, cancellationToken);
var scheduledEvent = history.First(e => e.EventType == WorkflowHistoryEventType.TaskScheduled);
var rerunId = await client.RerunWorkflowFromEventAsync(
sourceInstanceId: instanceId,
eventId: (uint)scheduledEvent.EventId,
options: new RerunWorkflowFromEventOptions
{
NewInstanceId = null,
OverwriteInput = true,
Input = new { Quantity = 3 }
},
cancellation: cancellationToken);
The eventId argument is uint, while WorkflowHistoryEvent.EventId is int, so cast when passing an ID from
history. An activity can also capture its own rerun point via WorkflowActivityContext.ScheduledEventId.
Omit options to retain recorded input and let the runtime allocate an ID.
NewInstanceId optionally chooses the new ID. Set OverwriteInput = true when supplying Input; otherwise a
non-null input is rejected. The override is applied at the next activity event at the rerun point, not as a
general replacement for all earlier history.
Keep the workflow/activity implementations and any referenced payload objects needed by the copied history. Rerunning is not compensation: earlier external side effects remain, and executing work again still requires idempotent activities.