Skip to main content

Receive Task - Wait for External Signals

Receive Task pauses process execution until an external signal or message is received. It's essential for integrating with external systems, webhooks, and asynchronous operations.

Overview

A Receive Task allows processes to:

  • ✅ Wait for external events (webhooks, callbacks, messages)
  • ✅ Integrate with external systems asynchronously
  • ✅ Resume automatically when signal received (Phase 3)
  • ✅ Timeout with boundaries (Phase 4)
  • ✅ Correlate messages with correlation keys

Properties

Required Properties

PropertyTypeRequiredDescription
TaskTypestringYesMust be "ReceiveTask"
NamestringYesDisplay name
messagestringNoExpected message name

Optional Properties

PropertyTypeDefaultDescription
correlationKeystringinstanceIdKey to match incoming signal
resultVariablestringlastSignalDataVariable to store signal data
outputMappingJSONnullMap signal data to context
timeoutdurationnoneMaximum wait time (Phase 4)

Basic Receive Task

Simple Wait for Signal

<bpmn:receiveTask id="WaitForApproval" 
name="Wait for Manager Approval"
messageRef="ManagerApprovalMessage">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="ManagerApproval"/>
<custom:property name="resultVariable" value="approvalData"/>
<custom:property name="correlationKey" value="context.requestId"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Message definition -->
<bpmn:message id="ManagerApprovalMessage" name="ManagerApproval"/>

How it works:

  1. Process reaches Receive Task
  2. Process pauses and waits
  3. Status: Waiting, waiting for: ManagerApproval message
  4. External system sends signal via API
  5. Process resumes automatically
  6. Signal data available in context.approvalData

Signaling a Receive Task

From External System (API)

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

From Another Process

// In another process, signal this process
BankLingo.ExecuteCommand('SignalProcess', {
instanceId: 'PROCESS-12345',
taskId: 'WaitForApproval',
signalData: {
approved: true,
approver: 'system',
comments: 'Auto-approved'
}
});

Correlation Keys

Use correlation keys to match signals to the correct process instance:

Setting Correlation Key

<bpmn:receiveTask id="WaitForPayment" 
name="Wait for Payment Confirmation">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="PaymentConfirmation"/>
<custom:property name="correlationKey" value="context.transactionId"/>
<custom:property name="resultVariable" value="paymentResult"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

Sending Signal with Correlation Key

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

Use Cases for Correlation Keys:

  • Payment gateway webhooks (use transaction ID)
  • External order tracking (use order number)
  • Partner API callbacks (use reference ID)
  • Multiple instances of same process (unique correlation per instance)

Output Mapping

Extract specific fields from signal data:

<bpmn:receiveTask id="WaitForAPIResponse" 
name="Wait for API Response">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="APIResponse"/>
<custom:property name="outputMapping" value="{
&quot;customerId&quot;: &quot;signal.data.customer.id&quot;,
&quot;customerName&quot;: &quot;signal.data.customer.name&quot;,
&quot;accountStatus&quot;: &quot;signal.data.account.status&quot;,
&quot;creditLimit&quot;: &quot;signal.data.account.creditLimit&quot;
}"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

Signal data structure:

{
"data": {
"customer": {
"id": "CUST-123",
"name": "John Doe"
},
"account": {
"status": "ACTIVE",
"creditLimit": 10000
}
}
}

Result in context:

context.customerId      // "CUST-123"
context.customerName // "John Doe"
context.accountStatus // "ACTIVE"
context.creditLimit // 10000

Timeout Handling (Phase 4)

Add timeout boundary events to prevent indefinite waiting:

<bpmn:receiveTask id="WaitForPartnerResponse" 
name="Wait for Partner API Response">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="PartnerAPIResponse"/>
<custom:property name="correlationKey" value="context.apiRequestId"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Timeout after 30 minutes -->
<bpmn:boundaryEvent id="ResponseTimeout"
name="30 Min Timeout"
attachedToRef="WaitForPartnerResponse"
cancelActivity="true">
<bpmn:timerEventDefinition>
<bpmn:timeDuration>PT30M</bpmn:timeDuration>
</bpmn:timerEventDefinition>
</bpmn:boundaryEvent>

<!-- Handle timeout -->
<bpmn:scriptTask id="HandleTimeout" name="Handle Timeout">
<bpmn:script>
logger.error('Partner API response timeout after 30 minutes');

context.apiStatus = 'TIMEOUT';
context.errorMessage = 'Partner did not respond within 30 minutes';

// Retry logic or fallback
if (context.retryCount < 3) {
context.retryCount = (context.retryCount || 0) + 1;
// Flow back to retry
} else {
// Flow to error handling
throw new BpmnError('API_TIMEOUT', 'Max retries exceeded');
}
</bpmn:script>
</bpmn:scriptTask>

Common Patterns

Pattern 1: Payment Gateway Integration

<bpmn:process id="PaymentProcess" name="Payment Processing">

<!-- Initiate payment -->
<bpmn:scriptTask id="InitiatePayment" name="Initiate Payment">
<bpmn:script>
// Call payment gateway
var result = BankLingo.ExecuteCommand('InitiatePayment', {
amount: context.amount,
customerId: context.customerId,
callbackUrl: 'https://api.bank.com/payments/callback'
});

context.paymentId = result.paymentId;
context.paymentStatus = 'PENDING';

logger.info('Payment initiated: ' + context.paymentId);
</bpmn:script>
</bpmn:scriptTask>

<!-- Wait for payment gateway callback -->
<bpmn:receiveTask id="WaitForPaymentCallback"
name="Wait for Payment Confirmation">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="PaymentCallback"/>
<custom:property name="correlationKey" value="context.paymentId"/>
<custom:property name="outputMapping" value="{
&quot;paymentStatus&quot;: &quot;signal.status&quot;,
&quot;transactionId&quot;: &quot;signal.transactionId&quot;,
&quot;paidAmount&quot;: &quot;signal.amount&quot;
}"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Timeout after 5 minutes -->
<bpmn:boundaryEvent id="PaymentTimeout"
attachedToRef="WaitForPaymentCallback">
<bpmn:timerEventDefinition>
<bpmn:timeDuration>PT5M</bpmn:timeDuration>
</bpmn:timerEventDefinition>
</bpmn:boundaryEvent>

<!-- Check payment status -->
<bpmn:exclusiveGateway id="CheckPayment" name="Payment Status?"/>

<bpmn:scriptTask id="PaymentSuccess" name="Payment Success">
<bpmn:script>
logger.info('Payment successful: ' + context.transactionId);
context.finalStatus = 'COMPLETED';
</bpmn:script>
</bpmn:scriptTask>

<bpmn:scriptTask id="PaymentFailed" name="Payment Failed">
<bpmn:script>
logger.error('Payment failed: ' + context.paymentStatus);
context.finalStatus = 'FAILED';

throw new BpmnError('PAYMENT_FAILED', 'Payment was not successful');
</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>

External Webhook Handler:

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

Pattern 2: External Approval System

<bpmn:process id="ExternalApprovalProcess" name="External Approval">

<!-- Send approval request to external system -->
<bpmn:scriptTask id="SendApprovalRequest" name="Send to External System">
<bpmn:script>
var result = BankLingo.ExecuteCommand('SendExternalApprovalRequest', {
requestType: 'LOAN_APPROVAL',
requestData: {
applicantId: context.applicantId,
loanAmount: context.loanAmount,
requestId: context.requestId
},
callbackUrl: 'https://api.bank.com/approvals/callback'
});

context.externalRequestId = result.requestId;
logger.info('Approval request sent: ' + result.requestId);
</bpmn:script>
</bpmn:scriptTask>

<!-- Wait for external approval -->
<bpmn:receiveTask id="WaitForExternalApproval"
name="Wait for External Approval">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="ExternalApproval"/>
<custom:property name="correlationKey" value="context.externalRequestId"/>
<custom:property name="resultVariable" value="approvalResponse"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Timeout after 24 hours -->
<bpmn:boundaryEvent id="ApprovalTimeout"
attachedToRef="WaitForExternalApproval">
<bpmn:timerEventDefinition>
<bpmn:timeDuration>P1D</bpmn:timeDuration>
</bpmn:timerEventDefinition>
</bpmn:boundaryEvent>

<!-- Process approval response -->
<bpmn:scriptTask id="ProcessApproval" name="Process Approval">
<bpmn:script>
var response = context.approvalResponse;

context.approvalStatus = response.status;
context.approvedBy = response.approver;
context.approvalDate = response.timestamp;
context.comments = response.comments;

logger.info('Approval received: ' + response.status);
</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>

Pattern 3: Multi-Stage External Process

<!-- Wait for multiple external signals in sequence -->
<bpmn:process id="MultiStageExternal" name="Multi-Stage External">

<!-- Stage 1: Identity Verification -->
<bpmn:scriptTask id="RequestIdentity" name="Request Identity Verification">
<bpmn:script>
BankLingo.ExecuteCommand('RequestIdentityVerification', {
customerId: context.customerId,
verificationType: 'FULL'
});
context.identityRequestId = 'ID-' + Date.now();
</bpmn:script>
</bpmn:scriptTask>

<bpmn:receiveTask id="WaitForIdentity" name="Wait for Identity Result">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="IdentityVerified"/>
<custom:property name="correlationKey" value="context.identityRequestId"/>
<custom:property name="outputMapping" value="{
&quot;identityVerified&quot;: &quot;signal.verified&quot;,
&quot;identityScore&quot;: &quot;signal.score&quot;
}"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Stage 2: Credit Check -->
<bpmn:scriptTask id="RequestCreditCheck" name="Request Credit Check">
<bpmn:script>
BankLingo.ExecuteCommand('RequestCreditCheck', {
customerId: context.customerId
});
context.creditRequestId = 'CR-' + Date.now();
</bpmn:script>
</bpmn:scriptTask>

<bpmn:receiveTask id="WaitForCredit" name="Wait for Credit Result">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="CreditCheckComplete"/>
<custom:property name="correlationKey" value="context.creditRequestId"/>
<custom:property name="outputMapping" value="{
&quot;creditScore&quot;: &quot;signal.score&quot;,
&quot;creditRating&quot;: &quot;signal.rating&quot;
}"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Stage 3: Background Check -->
<bpmn:scriptTask id="RequestBackground" name="Request Background Check">
<bpmn:script>
BankLingo.ExecuteCommand('RequestBackgroundCheck', {
customerId: context.customerId
});
context.backgroundRequestId = 'BG-' + Date.now();
</bpmn:script>
</bpmn:scriptTask>

<bpmn:receiveTask id="WaitForBackground" name="Wait for Background Result">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="BackgroundCheckComplete"/>
<custom:property name="correlationKey" value="context.backgroundRequestId"/>
<custom:property name="outputMapping" value="{
&quot;backgroundCleared&quot;: &quot;signal.cleared&quot;,
&quot;backgroundRemarks&quot;: &quot;signal.remarks&quot;
}"/>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Aggregate all results -->
<bpmn:scriptTask id="AggregateResults" name="Aggregate Results">
<bpmn:script>
var allPassed = context.identityVerified &&
context.creditScore >= 650 &&
context.backgroundCleared;

context.verificationStatus = allPassed ? 'APPROVED' : 'REJECTED';

logger.info('Verification complete: ' + context.verificationStatus);
</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>

Callback Mechanism (Phase 3)

Receive Tasks use the callback system for automatic resumption:

Database Entry

-- CallbackRegistry table
INSERT INTO CallbackRegistry (
ProcessInstanceId, -- "PROCESS-12345"
CallbackType, -- "External"
Message, -- "PaymentCallback"
CorrelationKey, -- "PAY-789"
ParentTaskId, -- "WaitForPaymentCallback"
Status -- "Pending"
)

Process State

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

Automatic Resumption

When signal received:

  1. Match by correlationKey or instanceId
  2. Find process in CallbackRegistry
  3. Update process state with signal data
  4. Resume execution from Receive Task
  5. Continue to next task

Error Handling (Phase 5)

Handle signal errors or missing data:

<bpmn:receiveTask id="WaitForData" name="Wait for External Data">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="message" value="ExternalData"/>
<custom:property name="validateScript"><![CDATA[
// Validate received signal data
var signal = context._lastSignal;

if (!signal.data) {
throw new BpmnError('INVALID_SIGNAL', 'Signal data is missing');
}

if (!signal.data.customerId) {
throw new BpmnError('INVALID_SIGNAL', 'Customer ID is required');
}

if (signal.data.amount <= 0) {
throw new BpmnError('INVALID_SIGNAL', 'Invalid amount: ' + signal.data.amount);
}

return true;
]]></custom:property>
</custom:properties>
</bpmn:extensionElements>
</bpmn:receiveTask>

<!-- Catch validation errors -->
<bpmn:boundaryEvent id="InvalidSignal"
attachedToRef="WaitForData"
cancelActivity="true">
<bpmn:errorEventDefinition errorRef="ValidationError" />
</bpmn:boundaryEvent>

<bpmn:scriptTask id="HandleInvalidSignal" name="Handle Invalid Signal">
<bpmn:script>
var error = context._lastError;

logger.error('Invalid signal received: ' + error.errorMessage);

// Request resend or alert
BankLingo.ExecuteCommand('AlertInvalidSignal', {
instanceId: context._instanceId,
errorMessage: error.errorMessage
});
</bpmn:script>
</bpmn:scriptTask>

Best Practices

✅ Do This

<!-- ✅ Use correlation keys for webhooks -->
<custom:property name="correlationKey" value="context.transactionId"/>

<!-- ✅ Add timeouts to prevent hanging -->
<bpmn:boundaryEvent id="Timeout" attachedToRef="WaitForSignal">
<bpmn:timerEventDefinition>
<bpmn:timeDuration>PT30M</bpmn:timeDuration>
</bpmn:timerEventDefinition>
</bpmn:boundaryEvent>

<!-- ✅ Use output mapping to extract specific fields -->
<custom:property name="outputMapping" value="{...}"/>

<!-- ✅ Validate signal data -->
<custom:property name="validateScript" value="..."/>

❌ Don't Do This

<!-- ❌ No timeout (process hangs indefinitely) -->
<bpmn:receiveTask id="Wait" name="Wait Forever">
<!-- Missing timeout boundary -->
</bpmn:receiveTask>

<!-- ❌ No correlation key for external systems -->
<bpmn:receiveTask id="WaitWebhook" name="Wait for Webhook">
<!-- Missing correlationKey - hard to match -->
</bpmn:receiveTask>

<!-- ❌ No validation (accepts any data) -->
<bpmn:receiveTask id="WaitData" name="Wait for Data">
<!-- Missing validateScript - unsafe -->
</bpmn:receiveTask>

Features Used:

  • Core: Receive Task
  • Phase 3: Callbacks
  • Phase 4: Timer Events (timeouts)
  • Phase 5: Error Handling

Status: ✅ Production Ready
Version: 2.0
Last Updated: January 2026