AutoMate Technisch Help

Workflows — Execution Model

Dit document beschrijft het execution model van Workflows 2.0: de database-entiteiten, statusovergangen, hoe execution results worden opgebouwd en hoe het Management Portal de status opvraagt.

Database-entiteiten

Het execution model bestaat uit drie primaire tabellen:

Workflows_Executions

Elke workflow execution heeft één rij in deze tabel. De kolommen:

Kolom

Type

Beschrijving

Id

GUID

Unieke execution identifier

TenantId

GUID

Tenant waarvoor de execution loopt

DefinitionId

GUID

Verwijzing naar Workflows_Definitions

VersionId

GUID

Gelocked versie op moment van aanmaak

HangfireBatchId

string

ID van de Hangfire Batch die alle jobs groepeert

TriggerType

string

EmployeeOnboarding, EmployeeUpdated, EmployeeOffboarded

TriggerData

JSON

Employee-object + changes + trigger metadata

ExecutionStatus

string

Zie statusovergangen

IsTerminal

bit

1 als de execution klaar is (Completed/Failed/Cancelled)

StartedAtUtc

datetime

Gezet wanneer status overgaat naar Running

CompletedAtUtc

datetime

Gezet wanneer IsTerminal = 1

ErrorMessage

string

Foutmelding bij Failed status

De VersionId wordt bij aanmaken van de execution bepaald en nooit meer gewijzigd. Dit garandeert dat een lopende execution altijd de workflow definitie gebruikt die gold bij de start — ook als de workflow nadien wordt aangepast en opnieuw gepubliceerd.

Workflows_ExecutionJobs

Per node in de workflow die wordt uitgevoerd, wordt één rij aangemaakt. De kolommen:

Kolom

Type

Beschrijving

Id

GUID

Unieke job identifier

ExecutionId

GUID

Verwijzing naar Workflows_Executions

NodeId

GUID

Verwijzing naar de node-ID in de workflow definitie

ActivityType

string

TriggerActivity, ActionActivity, ConditionActivity, DelayActivity

ActivityName

string

Leesbare naam (label uit de node data)

InputPayload

JSON

Geserialiseerde NodeDto — bevat de volledige node configuratie

OutputData

JSON

Resultaat van de activity (bijv. email MessageId, webhook response)

HangfireJobId

string

ID van de bijbehorende Hangfire job

JobStatus

string

Zie statusovergangen

StepIndex

int

Volgorde binnen de execution (incrementeel)

IsSkipped

bit

1 bij conditionele tak die niet werd genomen

StartedAtUtc

datetime

Begin van uitvoering

CompletedAtUtc

datetime

Einde van succesvolle uitvoering

FailedAtUtc

datetime

Tijdstip van fout

DurationMs

long

Uitvoerduur in milliseconden

ErrorMessage

string

Foutmelding

ErrorStackTrace

string

Stack trace bij fout

RetryCount

int

Automatisch verhoogd bij elke Failed status update

ScheduledForUtc

datetime

Tijdstip waarvoor de job is gepland (bij hire date of delay)

Workflows_Definitions en Workflows_Versions

De workflow definitie en versies zijn twee aparte entiteiten:

  • Workflows_Definitions — bevat naam, trigger type, IsActive, en een verwijzing naar ActiveVersionId.

  • Workflows_Versions — bevat de daadwerkelijke JSON payload met nodes en edges. Een definitie heeft maximaal één draft-versie en één actieve versie.

Bij publiceren van een draft wordt in een SQL-transactie:

  1. De huidige actieve versie op IsActive = 0 gezet.

  2. De draft-versie gepubliceerd (IsPublished = 1, IsActive = 1).

  3. De ActiveVersionId van de definitie bijgewerkt.

Statusovergangen van een execution

CreateExecutionAsync

WorkflowDispatcherJob (directe trigger)

WorkflowDispatcherJob (hire date in toekomst)

WorkflowTriggerActivityJob start

alle jobs afgerond zonder fouten

een of meer jobs mislukt

handmatige annulering via API

Pending

Running

Scheduled

Completed

Failed

Cancelled

De UpdateExecutionStatusAsync in de repository handelt de StartedAtUtc en CompletedAtUtc automatisch af:

UPDATE [dbo].[Workflows_Executions] SET [ExecutionStatus] = @Status, [IsTerminal] = @IsTerminal, [StartedAtUtc] = CASE WHEN [StartedAtUtc] IS NULL AND @Status = 'Running' THEN SYSUTCDATETIME() ELSE [StartedAtUtc] END, [CompletedAtUtc] = CASE WHEN @IsTerminal = 1 THEN SYSUTCDATETIME() ELSE [CompletedAtUtc] END WHERE [Id] = @ExecutionId

Statusovergangen van een job

CreateExecutionJobWithStepIndexAsync

ExecuteJobAsync start

delay of hire date scheduling

Hangfire scheduled job start

activiteit geslaagd

exception gegooid

conditionele tak niet genomen

Pending

Running

Scheduled

Completed

Failed

Skipped

Jobs die zijn aangemaakt voor nodes op een niet-genomen conditietak worden direct op Skipped gezet (IsSkipped = 1). Ze ontvangen geen Hangfire job ID en worden nooit uitgevoerd.

De RetryCount kolom wordt verhoogd bij elke transitie naar Failed:

[RetryCount] = CASE WHEN @Status = 'Failed' THEN [RetryCount] + 1 ELSE [RetryCount] END

Opbouw van execution results

De output van een activity job wordt opgeslagen in OutputData als JSON. Elk activity type produceert een eigen formaat:

WorkflowTriggerActivityJob

Slaat de volledige TriggerData op als output — het employee-object inclusief alle Graph-properties. Downstream action jobs lezen dit via GetTriggerNodeForExecutionAsync:

// WorkflowActionActivityJob.cs var triggerNode = await _workflowsRepo.GetTriggerNodeForExecutionAsync(executionId, ct); var triggerData = JsonDocument.Parse(triggerNode.OutputData).RootElement;

WorkflowActionActivityJob — Email

{ "Action": "Email", "To": "jan.de.vries@contoso.com", "Subject": "Welkom bij Contoso", "MessageId": "msg_abc123", "StatusCode": 202, "SentAt": "2026-03-24T10:00:00Z" }

Bij geen geldige ontvangers na template-resolutie:

{ "Action": "Email", "To": "[employee.manager.email]", "Subject": "...", "Skipped": true, "Reason": "No valid recipients after template resolution", "SkippedAt": "2026-03-24T10:00:00Z" }

WorkflowActionActivityJob — Webhook

{ "Action": "Webhook", "Url": "https://api.example.com/notify", "Method": "POST", "StatusCode": 200, "Response": "{\"ok\":true}", "CalledAt": "2026-03-24T10:00:00Z" }

WorkflowConditionActivityJob

{ "ConditionResult": true, "EvaluatedAt": "2026-03-24T10:00:00Z", "Rules": [ { "Field": "employee.department", "Operator": "Equals", "Value": "IT" } ] }

Status opvragen — API endpoints

GET /api/workflows/executions//status

De GetExecutionStatusCommandHandler bouwt een volledig statusoverzicht op. Het response bevat:

  • Execution metadata (status, tijdstippen, employee info)

  • De gelocked versie ID en het versienummer

  • Een geordende lijst van ExecutionStepInfo per node (topologisch gesorteerd)

De stappen worden via een topologische sortering van de graph bepaald, niet via de volgorde in de database. Dit zorgt voor een correcte volgorde ook bij parallelle paden.

Voor jobs met status Pending en een Hangfire job ID wordt ook de Hangfire-staat gecontroleerd om de ScheduledForUtc te tonen:

using var connection = jobStorage.GetConnection(); var jobData = connection.GetJobData(job.HangfireJobId); if (jobData?.State == "Scheduled") { var stateData = connection.GetStateData(job.HangfireJobId); if (stateData?.Data.TryGetValue("ScheduledAt", out var scheduledAt) == true) stepInfo.ScheduledForUtc = DateTime.Parse(scheduledAt).ToUniversalTime(); }

GET /api/workflows/executions/active

Haalt alle executions op waar IsTerminal = 0, geordend op aanmaaktijdstip.

GET /api/workflows/executions/history

Haalt alle executions op waar IsTerminal = 1, geordend op afrondingstijdstip.

Real-time updates via SignalR

Het Management Portal ontvangt real-time statusupdates via de WorkflowExecutionsHub SignalR hub. Clients subscriben op tenant-niveau:

// WorkflowExecutionsHub.cs public async Task SubscribeToTenant(Guid tenantId) { var groupName = $"tenant_{tenantId}"; await Groups.AddToGroupAsync(Context.ConnectionId, groupName); }

De WorkflowNotificationService stuurt drie event types via IHubContext<WorkflowExecutionsHub>:

Event

Trigger

Payload

ExecutionCreated

Nieuwe execution aangemaakt

ExecutionId, DefinitionId, WorkflowName

ExecutionUpdated

Execution status gewijzigd

ExecutionId, Status

JobUpdated

Job status gewijzigd

ExecutionId, JobId, Status

Elke activity job stuurt JobUpdated events bij de overgang naar Running, Completed en Failed. Dit stelt het Management Portal in staat om per stap de voortgang te tonen zonder polling.

Toegangscontrole in de hub:

  • Eigen tenant: altijd toegestaan.

  • Cross-tenant: alleen voor MSP-tenants met de rol Automate365.Customers.ReadWrite, en alleen voor tenants waarbij PartnerTenantId overeenkomt.

Retry mechanisme

Een mislukte execution kan worden herstart via POST /api/workflows/executions/{id}/retry. De retry maakt een nieuwe execution aan met de originele trigger data, aangevuld met retry metadata:

// RetryExecutionCommandHandler.cs triggerData["IsRetry"] = true; triggerData["OriginalExecutionId"] = execution.Id.ToString(); triggerData["RetriedBy"] = currentUserName; triggerData["RetriedAt"] = DateTime.UtcNow; var batchId = BatchJob.StartNew(b => { b.Enqueue<WorkflowDispatcherJob>( job => job.RunAsync(tenantId, newExecutionId, CancellationToken.None)); }, $"Workflow Retry: {workflow.Name}");

Beperkingen:

  • Alleen executions met status Failed kunnen worden herstart.

  • De retry-window is 7 dagen na de fout.

  • De workflow moet nog actief zijn en een gepubliceerde versie hebben.

  • De nieuwe execution gebruikt de huidige actieve versie, niet de versie waarmee de originele execution liep.

Cancel mechanisme

Een lopende execution kan worden geannuleerd via POST /api/workflows/executions/{id}/cancel. De handler:

  1. Zet de execution status op Cancelled (IsTerminal = true).

  2. Zoekt alle enqueued, scheduled en processing Hangfire jobs op die bij de execution horen via argument matching op executionId.

  3. Verwijdert alle gevonden jobs met BackgroundJob.Delete.

  4. Stuurt een ExecutionUpdated SignalR event.

// CancelExecutionCommandHandler.cs var monitoring = JobStorage.Current.GetMonitoringApi(); var enqueuedJobs = monitoring.EnqueuedJobs(DefaultQueueName, 0, 10000); foreach (var job in enqueuedJobs) { if (IsJobPartOfExecution(job.Value.Job, command.ExecutionId, execution.HangfireBatchId)) jobsToDelete.Add(job.Key); } foreach (var jobId in jobsToDelete) BackgroundJob.Delete(jobId);

Jobs die al worden verwerkt (Processing) worden gevonden via monitoring.ProcessingJobs maar kunnen niet altijd worden gestopt — de Hangfire worker heeft de job al opgepakt. In dat geval voltooit de individuele job nog wel, maar de execution zelf heeft status Cancelled.

Execution lifecycle — volledig overzicht

WorkflowTriggerService

Laad VersionId + definitie

Nee

Ja

Hire datum bereikt

action node

condition node

delay node

na vertraging

geen actieve jobs meer

Nee

Ja

API: TriggerWorkflowById of automatische trigger

BatchJob.StartNew

Execution rij: status Pending

WorkflowDispatcherJob draait

Hire date vertraging?

Execution: Running

Execution: Scheduled

WorkflowTriggerActivityJob

Graph traversal: ScheduleNextNodesAsync

WorkflowActionActivityJob

WorkflowConditionActivityJob

HandleDelayNodeAsync - wacht op vertraging

Volgende activity job

CheckAndCompleteExecutionAsync

Failed jobs?

Execution: Completed

Execution: Failed

SignalR: ExecutionUpdated Completed

SignalR: ExecutionUpdated Failed

Last modified: 24 March 2026