Workflow Audit
Workflow Audit provides execution history tracking for workflows, allowing you to monitor workflow runs, view execution logs, and troubleshoot failed executions.
Enabling Audit
To enable audit logging for a workflow, set enableAudit: true in the workflow definition:
workflow:
workflowId: "2e28201d-704e-40b1-8568-7a87d198e255"
name: "Send Order Confirmation Email"
enableAudit: true # Enable audit logging
logLevel: "Debug" # Optional: Set log level for detailed logs
Workflow Attributes for Audit
| Attribute | Type | Default | Description |
|---|---|---|---|
enableAudit | boolean | false | Enable audit logging for the workflow |
logLevel | string | None | Logging level: None, Debug, Info, Warning, Error |
Storage Architecture
Workflow execution data is stored in two locations:
1. Database (PostgreSQL)
Execution metadata is stored in the WorkflowExecutionLogs table for fast queries:
| Field | Type | Description |
|---|---|---|
ExecutionId | UUID | Unique identifier for the execution |
WorkflowId | UUID | Reference to the workflow |
OrganizationId | int | Organization that owns the workflow |
UserId | string | User who triggered the execution |
ExecutedAt | DateTime | When the execution started |
ExecutionStatus | enum | Started, Success, or Failed |
DurationMs | long | Execution duration in milliseconds |
Outputs | jsonb | Sanitized workflow outputs, used to replay idempotent executeWorkflow calls |
When a caller supplies an executionId to executeWorkflow, the API reserves that ID in WorkflowExecutionLogs before the workflow runs. Repeating the same executionId for the same workflow and organization returns the original result instead of executing side effects again. A Started row means async work has been accepted or a sync execution is still in progress; failed rows can be retried and rerun.
2. S3 Storage
Detailed execution logs are stored in S3:
logs/workflows/orgs/{organizationId}/{workflowId}/{timestamp}-{executionId}-{userId}{-failed}.txt
logs/workflows/orgs/{organizationId}/{workflowId}/{timestamp}-{executionId}-{userId}{-failed}.json
File Types:
| Extension | Content |
|---|---|
.txt | Raw execution logs (Serilog output) |
.json | Structured metadata (inputs, outputs, exceptions, timing) |
Accessing Execution History
GraphQL API
Get Single Execution
query {
workflowExecution(organizationId: 1, executionId: "guid-here") {
executionId
workflowId
executedAt
executionStatus
durationMs
txtLogUrl # Pre-signed URL for .txt log file
jsonLogUrl # Pre-signed URL for .json log file
user {
fullName
email
}
}
}
List Executions with Pagination
query {
workflowExecutions(
organizationId: 1
filter: "workflowId:guid-here AND executionStatus:Failed" # Optional: Lucene filter
orderBy: "-executedAt" # Optional: Sort order (default: most recent first)
) {
items {
executionId
executedAt
executionStatus
durationMs
txtLogUrl
jsonLogUrl
user {
fullName
}
}
pageInfo {
hasNextPage
hasPreviousPage
}
}
}
The workflowId is no longer a dedicated argument. Pass it as a Lucene filter clause: filter: "workflowId:guid-here". This follows the standard grid filter pattern used across all list queries.
Filter Examples
| Filter | Description |
|---|---|
workflowId:guid-here | Show executions for a specific workflow |
executionStatus:Failed | Show only failed executions |
executionStatus:Success | Show only successful executions |
userId:[email protected] | Filter by user |
workflowId:guid-here AND executionStatus:Failed | Combine multiple conditions |
Search
The search parameter performs free-text matching across userId, executionId, and workflowId. It matches partial substrings (case-insensitive for userId):
query {
items {
executionId
executedAt
executionStatus
}
}
}
Use search for quick lookups when you have a partial value. Use filter when you need precise, field-scoped conditions.
Sort Options
| Order By | Description |
|---|---|
-executedAt | Most recent first (default) |
executedAt | Oldest first |
-durationMs | Longest duration first |
durationMs | Shortest duration first |
Idempotent Execution Replays
Clients that may retry ambiguous requests, such as offline sync queues, should pass a stable executionId with the executeWorkflow mutation:
mutation ExecuteWorkflow($executionId: UUID!) {
executeWorkflow(input: {
organizationId: 1
workflowId: "2e28201d-704e-40b1-8568-7a87d198e255"
executionId: $executionId
variables: { orderId: 12345 }
}) {
workflowExecutionResult {
executionId
isAsync
outputs
}
}
}
Use a new GUID for each business operation and reuse it only for retries of that same operation. The API rejects an executionId that was already used for a different workflow or organization.
Pre-signed URLs
Log file URLs are generated on-demand as pre-signed S3 URLs with time-limited access. These URLs are only generated when you request the txtLogUrl or jsonLogUrl fields in your GraphQL query.
Security Features:
- URLs expire after a configured time period
- Each URL is unique per request
- Access is controlled by organization permissions
JSON Log Structure
The .json log file contains structured execution data:
{
"executionId": "guid",
"organizationId": 1,
"workflowId": "guid",
"workflowName": "Send Order Confirmation Email",
"inputs": {
"orderId": 12345,
},
"outputs": {
"response": "Email sent successfully",
"statusCode": 200
},
"exception": null,
"isSuccess": true,
"startExecutionTime": "2025-01-18T10:00:00Z",
"endExecutionTime": "2025-01-18T10:00:05Z",
}
Disabling Audit Logging
Per-Workflow
Set enableAudit: false in the workflow definition:
workflow:
name: "High-Volume Workflow"
enableAudit: false # Disable audit for this workflow
Environment-Wide
Set the environment variable to disable all audit logging:
DISABLE_AUDIT_LOGGING=true
This is useful for local development or testing environments.
Best Practices
-
Enable audit for critical workflows - Always enable audit for workflows that process important business data.
-
Use appropriate log levels - Use
Debugfor development/troubleshooting,InfoorWarningfor production. -
Monitor failed executions - Regularly review failed executions using the
executionStatus:Failedfilter. -
Clean up old logs - Implement S3 lifecycle policies to archive or delete old execution logs.
-
Protect sensitive data - Be aware that inputs and outputs are logged. Avoid logging sensitive data like passwords or API keys.
Example: Complete Workflow with Audit
workflow:
workflowId: "2e28201d-704e-40b1-8568-7a87d198e255"
name: "Process Order"
description: "Process incoming orders"
isActive: true
enableAudit: true
logLevel: "Info"
executionMode: "Async"
triggers:
- type: "Entity"
entityName: "Order"
eventType: "Added"
position: "After"
inputs:
- name: "orderId"
type: "Order"
outputs:
- name: "result"
mapping: "processOrder.result"
activities:
- name: "processOrder"
steps:
- task: "Order/Process@1"
name: "process"
inputs:
orderId: "{{ orderId }}"
outputs:
- name: "result"
With audit enabled, each execution of this workflow will:
- Create a record in the
WorkflowExecutionLogsdatabase table - Upload detailed logs to S3 (
.txtand.jsonfiles) - Be queryable via GraphQL API