Portal Community

When to Use

Configuration

FieldRequiredDefaultDescription
event_name Required Logical name of the event to wait for. Case-sensitive — must exactly match the event_name in the published event. Example: "payment.completed". Supports expressions for dynamic event names.
correlation_id Required The correlation value that must be present in the incoming event payload. The matching key is constructed as {event_name}:{correlation_value} — only events where both the name and this exact value match will resume this workflow instance. Typically an order ID, invoice ID, or workflow execution ID.
correlation_field Optional "correlation_id" The field name within the incoming event payload that contains the correlation value to match against. Override when the publishing system uses a different field name — for example "invoiceId" or "jobRef".
timeout_seconds Optional 0 Maximum wait time in seconds before the node times out and routes to the timed_out port. Set to 0 for indefinite waiting with no timeout. Always set a timeout in production to prevent workflows from accumulating indefinitely in the waiting state.
event_source Optional "any" Filters which event source the wait applies to. Supported values: "any" (matches all sources), "kafka", "webhook", "internal". Using a specific source prevents false matches when multiple systems publish events with the same name.
kafka_topic Conditional Required when event_source is "kafka". The Kafka topic name to subscribe to for this event. The event bus will only match events arriving on this specific topic, preventing cross-topic false matches.
filter_expression Optional An optional CEL (Common Expression Language) expression applied to the event payload after name and correlation matching. The event resumes the workflow only if this expression evaluates to true. Example: event.amount > 0 && event.currency == 'USD'.
 Composite correlation key: EventWait matches events using the combined key {event_name}:{correlation_value}. Two workflow instances waiting for the same event name but different correlation values will not interfere — each only resumes when its specific correlation value is present in the payload field named by correlation_field.

Sample Configuration

Wait for a payment webhook correlated on invoice ID

{
  "event_name": "payment.completed",
  "correlation_id": "{{ $output.createInvoice.invoiceId }}",
  "correlation_field": "invoiceId",
  "timeout_seconds": 86400,
  "event_source": "webhook"
}

Wait for a Kafka event on a specific topic

{
  "event_name": "order.shipped",
  "correlation_id": "{{ $json.orderId }}",
  "timeout_seconds": 259200,
  "event_source": "kafka",
  "kafka_topic": "logistics.shipment.events"
}

Cross-workflow approval signal (indefinite wait)

{
  "event_name": "approval.decision",
  "correlation_id": "{{ $var.approval_request_id }}",
  "timeout_seconds": 0,
  "event_source": "internal"
}

IoT sensor event with payload filter

{
  "event_name": "sensor.threshold.reached",
  "correlation_id": "{{ $json.deviceId }}",
  "correlation_field": "deviceId",
  "timeout_seconds": 3600,
  "filter_expression": "event.temperature > 80 && event.unit == 'celsius'"
}

Validation Errors

Error MessageCause
event_name is required for the event-wait nodeevent_name is missing, null, or an empty string. Every EventWait must target a specific named event.
correlation_id is required for the event-wait nodecorrelation_id is missing, null, or an empty string. Without a correlation value, the node cannot construct a unique matching key and would resume on any event matching the name.
kafka_topic is required when event_source is 'kafka'event_source is set to "kafka" but kafka_topic is not provided.
filter_expression '{expr}' is not valid CEL syntaxThe provided filter expression has a syntax error. CEL uses && and || operators, not and/or. Check brackets and field references.
Failed to persist EventCorrelationRegistration: {reason}The database write for the registration record failed at runtime — routes to the error port. The workflow cannot safely suspend without persisting the registration, so the error port fires immediately.

Output / Execution Ports

PortFires When
waiting Always — fires immediately after the EventCorrelationRegistration record is persisted to the database. The workflow thread is freed at this point. Use this port to log the registration ID or notify a monitoring system that the workflow is now suspended.
success A matching event was received before the timeout expired. Fires asynchronously when the event bus resolves the correlation key. The full event payload is available on this port.
timed_out The configured timeout_seconds elapsed without a matching event. Only fires if timeout_seconds is greater than 0. Always connect this port to an escalation, retry, or clean-exit path.
error Configuration validation failed at runtime (missing required fields, invalid CEL expression) or the registration persistence to the database failed. The workflow does not enter the waiting state.

waiting Port — Output Fields (fires immediately on suspension)

FieldTypeDescription
event_wait.idintThe EventCorrelationRegistration database record ID. Log or store this value to enable manual resumption, monitoring dashboards, or administrative cancellation.
event_wait.event_namestringThe registered event name — confirms what was actually persisted for matching.
event_wait.correlation_keystringThe full composite key used for matching: {event_name}:{correlation_value}. Include this in monitoring logs to cross-reference with the event bus audit trail.
event_wait.timeout_atstring (ISO-8601)UTC datetime when the timeout will fire, or "none" if timeout_seconds is 0 (indefinite wait).

success Port — Output Fields (fires when event received)

FieldTypeDescription
event_payloadobjectThe complete data payload published with the event. Access individual fields directly via dot notation: {{ $output.waitPayment.event_payload.amount }}.
event_namestringThe name of the event that was received — confirms which event triggered the resume, useful when diagnosing workflows with multiple EventWait nodes.
received_atstring (ISO-8601)UTC datetime when the event was received and processed by the event bus. Use for audit logging and latency measurement against the workflow start time.

Sample Output

waiting port output (fires immediately)

{
  "event_wait": {
    "id": 44192,
    "event_name": "payment.completed",
    "correlation_key": "payment.completed:INV-20260526-0041",
    "timeout_at": "2026-05-27T14:22:00Z"
  }
}

success port output (fires when event arrives)

{
  "event_payload": {
    "invoiceId": "INV-20260526-0041",
    "amount": 1250.00,
    "currency": "USD",
    "transactionId": "TXN-88441922",
    "paidAt": "2026-05-26T15:08:44Z"
  },
  "event_name": "payment.completed",
  "received_at": "2026-05-26T15:08:45Z"
}

timed_out port output

{
  "event_wait": {
    "id": 44192,
    "event_name": "payment.completed",
    "correlation_key": "payment.completed:INV-20260526-0041",
    "timed_out_at": "2026-05-27T14:22:00Z"
  }
}

Expression Reference

ExpressionResult
{{ $output.waitPayment.event_payload }}The complete event payload object received when the event fired.
{{ $output.waitPayment.event_payload.amount }}A specific field from within the event payload.
{{ $output.waitPayment.received_at }}UTC timestamp when the event was received — use for SLA and audit logging.
{{ $output.waitPayment.event_wait.id }}Registration record ID (from the waiting port) — store for monitoring and manual resume.
{{ $output.waitPayment.event_wait.correlation_key }}Full composite correlation key used for matching — include in log messages for traceability.
{{ $output.waitPayment.event_wait.timeout_at }}Scheduled timeout UTC datetime (from the waiting port) — useful for display in ops dashboards.
 Always set a timeout in production: EventWait with timeout_seconds: 0 waits indefinitely. Workflows in the waiting state accumulate in the execution store over time — hundreds of indefinitely waiting workflows degrade query performance and complicate operational monitoring. Set a timeout appropriate to your SLA and connect the timed_out port to an escalation or cancellation path.

Node Policies & GuardRails

Policy AreaRecommendation
Always set a timeout Set timeout_seconds to a value appropriate for the expected event latency plus a safety margin. If payment typically arrives within 1 hour, a 24-hour or 48-hour timeout provides a safe window without letting stale workflows accumulate forever.
Specific event sources Set event_source to a specific value when possible. Using "any" matches events from all sources, which can cause false resumes if different systems publish events with the same name and matching correlation values.
Unique correlation IDs Correlation IDs must be unique per workflow instance. Use entity IDs (order ID, invoice ID) or workflow execution IDs. Reusing the same correlation ID across concurrent workflow instances will cause the event to resume the wrong workflow or multiple workflows simultaneously.
Handle the timed_out port Always connect the timed_out port to an escalation, retry, or clean-exit path. An unconnected timed_out port causes the workflow branch to terminate silently when the timeout fires — the failure will not appear as an error in execution history.
Log the registration ID Connect the waiting port to a logging node and store event_wait.id. This is the only handle for manual resumption, administrative cancellation, or debugging if the publisher fails to send the expected event.
Delay vs EventWait Use EventWait for any wait driven by an external event. Use Delay only for fixed time-period pauses with no external signal. The timeout_seconds field on EventWait covers the "wait up to X for an event, else escalate" pattern — there is no need to chain a separate Delay node for this.

Examples

Example 1 — Payment Confirmation Gate

An order workflow creates an invoice and emails it to the customer, then suspends waiting for the payment.completed event correlated on the invoice ID. When payment is confirmed via webhook, the event bus resumes the workflow and shipping is triggered. If payment is not received within 24 hours, an overdue reminder is sent.

WebhookTrigger  [key: "orderPlaced"]
  └─► HttpRequest  [key: "createInvoice"]
        POST /invoices  body: { orderId: {{ $json.orderId }}, amount: {{ $json.total }} }
        └─► EmailSmtp  [key: "sendInvoice"]
              To: {{ $json.customerEmail }}
              Subject: "Invoice {{ $output.createInvoice.invoiceId }} — Payment due"
              └─► EventWait  [key: "waitPayment"]
                    event_name: "payment.completed"
                    correlation_id: "{{ $output.createInvoice.invoiceId }}"
                    correlation_field: "invoiceId"
                    timeout_seconds: 86400        // 24-hour payment window
                    event_source: "webhook"
                    ├─► waiting  ──► Slack  (log: "Payment pending — invoice {{ $output.createInvoice.invoiceId }}, wait_id: {{ $output.waitPayment.event_wait.id }}")
                    ├─► success  ──► HttpRequest  (POST /orders/{{ $json.orderId }}/ship
                    │                              body: { transactionId: {{ $output.waitPayment.event_payload.transactionId }} })
                    └─► timed_out ──► EmailSmtp  (To: {{ $json.customerEmail }} — payment overdue notice)

Example 2 — Async External Job with Callback

A workflow triggers a long-running PDF report generation job on an external service, then suspends. When the service completes the job, it publishes a job.finished event via the internal message bus. The workflow resumes, downloads the report URL from the event payload, and emails it to the requester. A 1-hour timeout catches jobs that hang.

FormTrigger  [key: "reportRequested"]
  └─► HttpRequest  [key: "triggerJob"]
        POST /reports/generate  body: { type: {{ $json.reportType }}, userId: {{ $json.userId }} }
        └─► EventWait  [key: "waitJobComplete"]
              event_name: "job.finished"
              correlation_id: "{{ $output.triggerJob.jobId }}"
              correlation_field: "jobId"
              timeout_seconds: 3600
              event_source: "internal"
              ├─► waiting  ──► HttpRequest  (PATCH /jobs/{{ $output.triggerJob.jobId }}  body: { status: "awaiting_callback" })
              ├─► success  ──► EmailSmtp
              │                 To: {{ $json.userEmail }}
              │                 Subject: "Your report is ready"
              │                 Body: "Download: {{ $output.waitJobComplete.event_payload.reportUrl }}"
              └─► timed_out ──► EmailSmtp  (To: {{ $json.userEmail }} — report generation timed out, please retry)

Example 3 — Cross-Workflow Approval Signal

An expense submission workflow creates an approval request, emails the approver, then suspends waiting for an approval.decision event. The approver acts via the HR portal, which calls an internal API endpoint that publishes the event with the decision and reason. The waiting workflow resumes and branches on the decision outcome. A 7-day timeout escalates stale approvals.

WebhookTrigger  [key: "expenseSubmitted"]
  └─► MongoDB  [key: "createApproval"]
        (insert: { expenseId: {{ $json.expenseId }}, submittedBy: {{ $json.userId }}, amount: {{ $json.amount }} })
        └─► EmailSmtp  [key: "notifyApprover"]
              To: {{ $json.approverEmail }}
              Subject: "Action required: Expense {{ $json.expenseId }} needs your approval"
              Body: "Approve at {{ $var.portal_url }}/approvals/{{ $output.createApproval.insertedId }}"
              └─► EventWait  [key: "waitDecision"]
                    event_name: "approval.decision"
                    correlation_id: "{{ $output.createApproval.insertedId }}"
                    correlation_field: "approvalRequestId"
                    timeout_seconds: 604800       // 7-day approval window
                    event_source: "internal"
                    ├─► waiting  ──► Slack  (log: "Approval pending, expense: {{ $json.expenseId }}, wait_id: {{ $output.waitDecision.event_wait.id }}")
                    ├─► success  ──► IfCondition
                    │                 Condition: {{ $output.waitDecision.event_payload.decision == "approved" }}
                    │                 ├─► true  ──► HttpRequest  (POST /finance/disburse  body: { expenseId: {{ $json.expenseId }} })
                    │                 └─► false ──► EmailSmtp  (To: submitter — rejected: {{ $output.waitDecision.event_payload.reason }})
                    └─► timed_out ──► EmailSmtp  (To: {{ $json.approverEmail }} — approval request expired after 7 days)