Portal Community

Node Configuration

{
  "nodeType": "WebhookTrigger",
  "config": {
    "credentialId": 1042,
    "responseMode": "immediate",
    "responseBody": { "status": "received" },
    "allowedEventTypes": ["order.created", "order.updated"],
    "payloadSizeLimitKb": 512
  }
}

URL Structure

Each workflow thread gets a dedicated webhook URL automatically assigned when the process is published:

POST https://api.bizfirstai.com/webhook/{tenantId}/{threadId}

Example:
POST https://api.bizfirstai.com/webhook/tenant-acme/proc-orders-main

Payload to $json Mapping

The full request body (JSON) becomes $json. HTTP headers are accessible via $json._headers. Query parameters are in $json._query:

// Inbound payload: { "event": "order.created", "orderId": "ord-001" }
$json.event      // → "order.created"
$json.orderId    // → "ord-001"

// HTTP headers
$json._headers["X-Source-System"]  // → "shopify"

// Query parameters (?ref=campaign1)
$json._query.ref   // → "campaign1"

Response Modes

ModeBehaviorUse When
immediateReturns 200 with static body right away; workflow runs asyncMost integrations — don't make the caller wait
onCompleteHTTP connection held open until workflow finishes; returns workflow outputSynchronous request/response webhooks (short workflows only)

Executor Pattern

// ExecutionNodes/Webhooks/WebhookTriggerExecutor.cs
[NodeCapability(CapabilityType.Webhook)]
public class WebhookTriggerExecutor : BaseNodeExecutor
{
    public override async Task<NodeExecutionResult> ExecuteAsync(NodeExecutionContext ctx)
    {
        // $json is already populated by the trigger middleware before this node runs
        // This executor just validates the payload and routes
        var eventType = ctx.Json?.GetString("event");
        var allowedTypes = ctx.GetConfig<string[]>("allowedEventTypes");

        if (allowedTypes?.Length > 0 && !allowedTypes.Contains(eventType))
            return NodeExecutionResult.Success(
                new { skipped = true, reason = "event type not in allowlist" },
                portKey: "filtered");

        return NodeExecutionResult.Success(new
        {
            event = eventType,
            receivedAt = DateTimeOffset.UtcNow,
            payloadSize = ctx.Json?.ToString().Length ?? 0
        });
    }
}
Tip: Use the allowedEventTypes filter to avoid starting workflow executions for event types the workflow doesn't handle. This keeps execution logs clean and prevents unnecessary resource consumption.