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

  1. An admin configures a Connector in Admin → Connectors (name, URL, HTTP method, headers, body template, timeout).
  2. In a BPMN Service Task, you reference the connector by name using the httpConnector delegate.
  3. At runtime, the delegate resolves any ${processVariable} placeholders in the URL, headers, and body, then makes the HTTP call.
  4. 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, _status is FAILED and the process continues. Use a gateway to branch on _status if 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_CreateIssue connector must have its Authorization header configured with a real Base64-encoded user:api_token credential 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_Webhook body 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