Portal Community

start_workflow Tool Schema

{
  "name": "start_workflow",
  "description": "Start a Flow Studio workflow and optionally wait for its result. " +
                 "Use when the user's request requires a structured business process " +
                 "(approval, multi-step operation, ERP update, notification). " +
                 "Always confirm the action with the user before calling.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "workflow_id": {
        "type":        "string",
        "description": "The workflow identifier to trigger (e.g. 'leave-approval-workflow')"
      },
      "inputs": {
        "type":        "object",
        "description": "Key-value pairs passed to the workflow as input data"
      },
      "mode": {
        "type":        "string",
        "enum":        ["sync", "async"],
        "default":     "async",
        "description": "sync: wait for completion (up to SyncTimeoutSeconds). async: fire and return reference."
      },
      "correlation_id": {
        "type":        "string",
        "description": "Optional caller-provided ID to track this execution externally"
      }
    },
    "required": ["workflow_id", "inputs"]
  }
}

Sync vs Async Mode

ModeAgent waits?ReturnsUse When
syncYes (up to SyncTimeoutSeconds)Workflow final outputShort workflows (< 2 min) where the result matters to the user
asyncNoExecution ID + status URLLong-running approvals, multi-step processes, background tasks

IWorkflowTriggerService Implementation

public class WorkflowTriggerService : IWorkflowTriggerService
{
    private readonly IProcessServerClient _processServer;
    private readonly ProcessPluginConfig  _config;
    private readonly ITenantContext       _tenant;

    public async Task<string> HandleStartWorkflowAsync(
        JsonElement input, ConversationContext ctx, CancellationToken ct)
    {
        var workflowId     = input.GetProperty("workflow_id").GetString()!;
        var inputs         = input.GetProperty("inputs");
        var mode           = input.TryGetProperty("mode", out var m)
            ? m.GetString()! : "async";
        var correlationId  = input.TryGetProperty("correlation_id", out var c)
            ? c.GetString() : null;

        // Check workflow access policy
        if (!_config.WorkflowAccessPolicy.IsAllowed(workflowId))
            return JsonSerializer.Serialize(new
            {
                error  = "Workflow not permitted",
                reason = $"Workflow '{workflowId}' is not in the allowed workflow list."
            });

        // Start the workflow on ProcessServer
        var startRequest = new StartWorkflowRequest
        {
            WorkflowId     = workflowId,
            TenantId       = _tenant.TenantId,
            InitiatedBy    = ctx.UserId,
            Inputs         = inputs.Deserialize<Dictionary<string, object>>()!,
            CorrelationId  = correlationId
        };

        var execution = await _processServer.StartWorkflowAsync(startRequest, ct);

        if (mode == "sync")
        {
            // Poll for completion
            var result = await _processServer.WaitForCompletionAsync(
                execution.ExecutionId,
                TimeSpan.FromSeconds(_config.SyncTimeoutSeconds),
                ct);

            return JsonSerializer.Serialize(new
            {
                execution_id = execution.ExecutionId,
                status       = result.Status,
                outputs      = result.Outputs
            });
        }

        // Async — return reference immediately
        return JsonSerializer.Serialize(new
        {
            execution_id  = execution.ExecutionId,
            status        = "running",
            tracking_url  = $"/workflows/executions/{execution.ExecutionId}"
        });
    }
}

Successful Sync Response

{
  "execution_id": "exec-abc123",
  "status":       "Completed",
  "outputs": {
    "request_id":   "LR-8821",
    "status":       "PendingApproval",
    "approver":     "manager@company.com",
    "submitted_at": "2024-06-15T10:00:00Z"
  }
}