19. HTTP Connectors (External Integrations)
The HTTP Connector Hub allows BPMN workflows to call external REST APIs (Slack, Jira, GitHub, etc.) from any Service Task — without writing Java code.
How It Works
- An admin configures a Connector in Admin → Connectors (name, URL, HTTP method, headers, body template, timeout).
- In a BPMN Service Task, you reference the connector by name using the
httpConnectordelegate. - At runtime, the delegate resolves any
${processVariable}placeholders in the URL, headers, and body, then makes the HTTP call. - Result variables are written back to the process instance automatically.
Result Variables
After a connector fires, three variables are available under the pattern {connectorName}_{suffix}:
| Variable | Example | Description |
|---|---|---|
{name}_status |
Slack_PostMessage_status |
SUCCESS (HTTP 2xx) or FAILED |
{name}_httpCode |
Slack_PostMessage_httpCode |
HTTP status code (e.g. 200), or 0 on network error |
{name}_responseBody |
Slack_PostMessage_responseBody |
Raw response body string |
Soft-fail design: The delegate never throws an exception. If the call fails,
_statusisFAILEDand the process continues. Use a gateway to branch on_statusif you need error handling.
Basic Usage
In any Service Task, set delegateExpression to ${httpConnector} and provide the connector name as a field:
<serviceTask id="notifySlack" name="Notify Slack" flowable:delegateExpression="${httpConnector}">
<extensionElements>
<flowable:field name="connectorName" stringValue="Slack_PostMessage"/>
</extensionElements>
</serviceTask>
That's it. The connector named Slack_PostMessage is looked up from the database, placeholders are resolved, and the HTTP call is made.
Placeholder Resolution
Any ${variableName} placeholder in the connector's URL, Headers, or Body Template is resolved from process variables at runtime.
Example: A connector with body template:
{"channel": "${slackChannel}", "text": "${message}"}
If at runtime slackChannel = "#alerts" and message = "Approval needed", the delegate sends:
{"channel": "#alerts", "text": "Approval needed"}
Example 1: Send a Slack Notification
A form collects the message, a service task calls Slack, then a gateway checks success.
<!-- Step 1: User enters a message -->
<userTask id="composeMsg" name="Compose Message" flowable:assignee="${initiator}">
<extensionElements>
<activiti:formProperty id="slackChannel" name="Slack Channel" type="string"
required="true" default="#general"/>
<activiti:formProperty id="message" name="Message" type="string" required="true">
<activiti:value id="type" name="textarea"/>
</activiti:formProperty>
</extensionElements>
</userTask>
<sequenceFlow sourceRef="composeMsg" targetRef="callSlack"/>
<!-- Step 2: Call the Slack connector -->
<serviceTask id="callSlack" name="Send Slack Message"
flowable:delegateExpression="${httpConnector}">
<extensionElements>
<flowable:field name="connectorName" stringValue="Slack_PostMessage"/>
</extensionElements>
</serviceTask>
<sequenceFlow sourceRef="callSlack" targetRef="checkResult"/>
<!-- Step 3: Branch on result -->
<exclusiveGateway id="checkResult"/>
<sequenceFlow sourceRef="checkResult" targetRef="notifySuccess">
<conditionExpression>${Slack_PostMessage_status == 'SUCCESS'}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="checkResult" targetRef="handleFailure">
<conditionExpression>${Slack_PostMessage_status == 'FAILED'}</conditionExpression>
</sequenceFlow>
Example 2: Create a Jira Issue on Process Start
Automatically open a Jira ticket when a workflow is started.
<startEvent id="start" flowable:initiator="initiator">
<extensionElements>
<activiti:formProperty id="projectKey" name="Jira Project" type="string"
required="true" default="WORK"/>
<activiti:formProperty id="summary" name="Issue Summary" type="string" required="true"/>
</extensionElements>
</startEvent>
<sequenceFlow sourceRef="start" targetRef="createJiraIssue"/>
<serviceTask id="createJiraIssue" name="Create Jira Issue"
flowable:delegateExpression="${httpConnector}">
<extensionElements>
<flowable:field name="connectorName" stringValue="Jira_CreateIssue"/>
</extensionElements>
</serviceTask>
<sequenceFlow sourceRef="createJiraIssue" targetRef="managerApproval"/>
Prerequisite: The
Jira_CreateIssueconnector must have its Authorization header configured with a real Base64-encodeduser:api_tokencredential in Admin → Connectors.
Example 3: Chaining Connectors
Call two external systems in sequence. Here, a GitHub issue is created and then Slack is notified.
<!-- Call GitHub -->
<serviceTask id="createGhIssue" name="Create GitHub Issue"
flowable:delegateExpression="${httpConnector}">
<extensionElements>
<flowable:field name="connectorName" stringValue="GitHub_CreateIssue"/>
</extensionElements>
</serviceTask>
<sequenceFlow sourceRef="createGhIssue" targetRef="notifySlack"/>
<!-- Call Slack (uses variables from the same process) -->
<serviceTask id="notifySlack" name="Notify Team"
flowable:delegateExpression="${httpConnector}">
<extensionElements>
<flowable:field name="connectorName" stringValue="Slack_PostMessage"/>
</extensionElements>
</serviceTask>
Example 4: Generic Webhook
For any REST endpoint not covered by a preset, use the Generic_Webhook connector. Override its URL and body by storing the target URL in a process variable... Actually, since placeholders are in the connector config itself, the simpler approach is to duplicate Generic_Webhook and configure it with a specific static URL via Admin → Connectors.
Alternatively, use the Generic_Webhook preset and put dynamic data in the body via placeholders:
<!-- Store the payload in a script task first -->
<scriptTask id="buildPayload" name="Build Payload" scriptFormat="super-js">
<script>
var amount = execution.getVariable('amount');
var requester = execution.getVariable('initiator');
execution.setVariable('webhookPayload',
'{"amount": ' + amount + ', "requester": "' + requester + '"}');
</script>
</scriptTask>
<!-- Then fire the connector (body template uses ${webhookPayload}) -->
<serviceTask id="fireHook" flowable:delegateExpression="${httpConnector}">
<extensionElements>
<flowable:field name="connectorName" stringValue="Generic_Webhook"/>
</extensionElements>
</serviceTask>
Tip: Configure the
Generic_Webhookbody template in Admin → Connectors as${webhookPayload}(a single variable) to inject a fully-formed JSON string built in a Script Task.
Admin → Connectors Reference
| Field | Description |
|---|---|
| Name | Unique identifier — must match stringValue in BPMN connectorName field. Cannot be changed after creation. |
| Description | Human-readable label for the admin UI. |
| Target URL | Full URL. Supports ${variable} placeholders. |
| HTTP Method | GET, POST, PUT, DELETE |
| Headers (JSON) | Flat JSON object, e.g. {"Authorization": "Bearer TOKEN"}. Values support ${variable} placeholders. |
| Body Template (JSON) | JSON body sent with the request. Supports ${variable} placeholders inside string values. |
| Timeout (s) | Connection + read timeout in seconds (default: 10). |
| Active | Inactive connectors are skipped silently (_status = FAILED). Toggle on the list page. |
Preset Connectors
The system seeds the following connectors on first startup (all active, credentials must be configured):
| Connector Name | Service | Method |
|---|---|---|
Generic_Webhook |
Any REST endpoint | POST |
Slack_PostMessage |
Slack Web API | POST |
Discord_Webhook |
Discord | POST |
Teams_Webhook |
Microsoft Teams | POST |
Telegram_SendMessage |
Telegram Bot API | POST |
Jira_CreateIssue |
Atlassian Jira | POST |
Linear_CreateIssue |
Linear | POST |
Trello_CreateCard |
Trello | POST |
GitHub_CreateIssue |
GitHub | POST |
GitLab_CreateIssue |
GitLab | POST |
HubSpot_CreateContact |
HubSpot CRM | POST |
PagerDuty_CreateIncident |
PagerDuty | POST |