Author an integration pack
Create a custom integration pack to add pre-built nodes for any HTTP API.
If DRAGOPS does not ship a pack for the API you need, you can create one. A pack is a single JSON file (.dragops-pack.json) that defines the service's authentication, one or more action nodes, and how input pins map to HTTP request parameters and response fields map back to output pins.
Before you begin
- Familiarity with the target API — endpoints, authentication method, request and response formats
- Understanding of integration packs
Step 1: Create the manifest skeleton
Every pack starts with the same structure:
{
"packVersion": "1.0",
"metadata": {},
"connection": {},
"nodes": []
}The packVersion is always "1.0". The following steps fill in each section.
Step 2: Define metadata
The metadata section identifies the pack in the node library and on the Integrations page.
{
"metadata": {
"slug": "acme-api",
"name": "Acme API",
"description": "Interact with the Acme platform API.",
"version": "1.0.0",
"category": "utility",
"tags": ["acme", "crm"]
}
}| Field | Description |
|---|---|
slug | Unique identifier. Lowercase letters, numbers, and hyphens only. |
name | Display name shown in the node library and Integrations page. |
description | Short summary of what this pack does. |
version | Semantic version (for example, 1.0.0). |
category | Grouping category (for example, enrichment, ticketing, event-logging, utility). |
tags | Search tags. Users can find the pack by searching for any of these terms. |
You can optionally include iconUrl with a path to the pack's icon.
Step 3: Define the connection
The connection section tells DRAGOPS what credentials the service requires and how to authenticate.
API key authentication
For services that use a single API key:
{
"connection": {
"provider": "acme-api",
"authType": "api_key",
"credentials": [
{
"name": "api-key",
"label": "API Key",
"description": "Your Acme API key from the developer dashboard",
"placeholder": "Enter your API key...",
"type": "secret",
"required": true
}
],
"baseUrl": "https://api.acme.com/v2",
"testRequest": {
"method": "GET",
"pathTemplate": "/me",
"expectedStatus": 200
}
}
}Basic authentication with dynamic base URL
For services that require multiple credentials (like Jira, which needs an email, API token, and instance URL):
{
"connection": {
"provider": "jira",
"authType": "basic",
"credentials": [
{
"name": "user-email",
"label": "User Email",
"type": "text",
"required": true
},
{
"name": "api-token",
"label": "API Token",
"type": "secret",
"required": true
},
{
"name": "base-url",
"label": "Instance URL",
"description": "Your Jira Cloud URL (e.g., https://mycompany.atlassian.net)",
"type": "url",
"required": true
}
],
"baseUrl": ""
}
}When baseUrl is empty, the base URL is resolved from a credential field at runtime. The node's execution specification indicates which credential provides the URL (see Step 4).
Connection fields
| Field | Description |
|---|---|
provider | Identifier linking to the DRAGOPS connections system. Must be unique. |
authType | One of api_key, bearer, basic, or oauth2_client_credentials. |
credentials | Array of credential fields the user must provide. |
baseUrl | Base URL for all API requests. Set to "" if the URL comes from a credential field. |
testRequest | Optional. A lightweight request to validate credentials work (method, path, expected status). |
Credential field options
| Field | Description |
|---|---|
name | Machine name used to reference this field in node execution specifications. |
label | Display label shown in the connection form. |
description | Help text shown below the input field. Optional. |
placeholder | Placeholder text in the input field. Optional. |
type | secret (masked input), text (visible input), or url (URL validation). |
required | Whether this field is required to save the connection. |
Step 4: Define nodes
Each node represents a single API operation. A node has input pins, output pins, and an execution specification that maps between them.
Node structure
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"nodeTypeId": "Acme-ListItems",
"title": "List Items",
"category": "Acme API",
"nodeCategory": "action",
"isPure": false,
"isBuiltin": true,
"inputPins": [
{ "name": "exec", "type": "exec" },
{ "name": "limit", "type": "int", "displayName": "Limit" }
],
"outputPins": [
{ "name": "exec", "type": "exec" },
{ "name": "items", "type": "object", "displayName": "Items" },
{ "name": "status", "type": "int", "displayName": "Status" }
],
"properties": [],
"executionSpec": {}
}| Field | Description |
|---|---|
id | Unique UUID for this node definition. |
nodeTypeId | Unique identifier. Convention: {Prefix}-{Action} (for example, VT-GetIPReport). |
title | Display name in the node library and on the canvas. |
category | Category name in the node library (typically the service name). |
nodeCategory | Always "action" for pack nodes. |
isPure | Always false — pack nodes make HTTP calls (side effects). |
isBuiltin | Always true. |
inputPins | Input pin definitions. Always include { "name": "exec", "type": "exec" } first. |
outputPins | Output pin definitions. Always include { "name": "exec", "type": "exec" } first. |
properties | Always [] for pack nodes. |
Execution specification
The execution specification tells the execution engine how to construct the HTTP request and extract the response.
{
"executionSpec": {
"method": "GET",
"pathTemplate": "/items/{{itemId}}",
"parameters": [
{ "pin": "itemId", "in": "path" }
],
"auth": {
"type": "header",
"credentials": ["api-key"],
"headerName": "x-api-key"
},
"responseMapping": [
{ "pin": "item", "path": "$body.data" },
{ "pin": "status", "path": "$status" }
]
}
}Method and path
| Field | Description |
|---|---|
method | HTTP method: GET, POST, PUT, PATCH, or DELETE. |
pathTemplate | URL path with {{pinName}} placeholders for path parameters. |
Parameters
Each parameter maps an input pin to a location in the HTTP request:
in value | Description | Example |
|---|---|---|
path | Replaces {{pinName}} in the path template | /issues/{{issueKey}} → /issues/OPS-42 |
query | Added as a URL query parameter | ?jql=project%3DOPS |
header | Added as a request header | X-Custom: value |
body_field | Added as a field in the request body | {"fields": {"summary": "..."}} |
The optional key field overrides the parameter name. For example, { "pin": "projectKey", "in": "body_field", "key": "fields.project.key" } maps the projectKey pin to a nested body path.
Authentication
The auth object defines how credentials are injected:
type | Behavior | Required fields |
|---|---|---|
header | Sends a credential as a custom header | headerName — the header name (for example, x-apikey) |
bearer | Sends Authorization: Bearer {token} | First credential is the token |
basic | Sends Authorization: Basic base64(user:pass) | At least two credentials (username + password) |
query | Sends a credential as a query parameter | queryParam — the parameter name |
oauth2_client_credentials | Obtains and caches a bearer token | tokenUrl, at least two credentials (client ID + secret) |
For services with a dynamic base URL (where the URL comes from a credential field), add baseUrlCredential with the credential field name:
{
"auth": {
"type": "basic",
"credentials": ["user-email", "api-token", "base-url"],
"baseUrlCredential": "base-url"
}
}Response mapping
Each response mapping extracts a value from the HTTP response and delivers it through an output pin:
| Path prefix | Source |
|---|---|
$body | The parsed JSON response body ($body for the whole body, $body.data.id for a nested field) |
$status | The HTTP status code (integer) |
$headers | Response headers ($headers.x-rate-limit-remaining for a specific header) |
Request body
For POST, PUT, and PATCH requests, add a body object:
{
"body": {
"contentType": "application/json",
"mode": "template",
"template": "{\"fields\":{\"project\":{\"key\":\"{{projectKey}}\"}}}"
}
}| Field | Description |
|---|---|
contentType | application/json or application/x-www-form-urlencoded. |
mode | pins (collect body_field parameters into an object), template (interpolate {{pinName}}), or raw (use a single pin as the body). |
template | Template string for template mode. Use {{pinName}} for interpolation. |
Step 5: Add defaults (optional)
Pack-level defaults apply to all nodes:
{
"defaults": {
"headers": {
"Accept": "application/json"
},
"timeout": 30
}
}| Field | Description |
|---|---|
headers | Headers sent with every request from this pack. |
timeout | Request timeout in seconds (1 to 300). Default is 30. |
Step 6: Validate and test
- Save the file with a
.dragops-pack.jsonextension. - Create a connection for the pack's provider on the Connections page.
- Add pack nodes to a pattern and wire test data to the inputs.
- Run the pattern and verify the nodes execute successfully.
Check the execution log for HTTP request and response details. If a node returns an error, verify the path template, parameter mappings, and authentication configuration in the manifest.
Complete example
This minimal pack has two nodes for a fictional "Acme API" — one to list items and one to get a single item:
{
"packVersion": "1.0",
"metadata": {
"slug": "acme-api",
"name": "Acme API",
"description": "Interact with the Acme platform API.",
"version": "1.0.0",
"category": "utility",
"tags": ["acme"]
},
"connection": {
"provider": "acme-api",
"authType": "api_key",
"credentials": [
{
"name": "api-key",
"label": "API Key",
"type": "secret",
"required": true
}
],
"baseUrl": "https://api.acme.com/v2",
"testRequest": {
"method": "GET",
"pathTemplate": "/me",
"expectedStatus": 200
}
},
"defaults": {
"headers": { "Accept": "application/json" },
"timeout": 30
},
"nodes": [
{
"id": "10000001-0000-4000-8000-000000000001",
"nodeTypeId": "Acme-ListItems",
"title": "List Items",
"category": "Acme API",
"nodeCategory": "action",
"isPure": false,
"isBuiltin": true,
"inputPins": [
{ "name": "exec", "type": "exec" },
{ "name": "limit", "type": "int", "displayName": "Limit" }
],
"outputPins": [
{ "name": "exec", "type": "exec" },
{ "name": "items", "type": "object", "displayName": "Items" },
{ "name": "total", "type": "int", "displayName": "Total" },
{ "name": "status", "type": "int", "displayName": "Status" }
],
"properties": [],
"executionSpec": {
"method": "GET",
"pathTemplate": "/items",
"parameters": [
{ "pin": "limit", "in": "query" }
],
"auth": {
"type": "header",
"credentials": ["api-key"],
"headerName": "x-api-key"
},
"responseMapping": [
{ "pin": "items", "path": "$body.data" },
{ "pin": "total", "path": "$body.total" },
{ "pin": "status", "path": "$status" }
]
}
},
{
"id": "10000001-0000-4000-8000-000000000002",
"nodeTypeId": "Acme-GetItem",
"title": "Get Item",
"category": "Acme API",
"nodeCategory": "action",
"isPure": false,
"isBuiltin": true,
"inputPins": [
{ "name": "exec", "type": "exec" },
{ "name": "itemId", "type": "string", "displayName": "Item ID", "required": true }
],
"outputPins": [
{ "name": "exec", "type": "exec" },
{ "name": "item", "type": "object", "displayName": "Item" },
{ "name": "status", "type": "int", "displayName": "Status" }
],
"properties": [],
"executionSpec": {
"method": "GET",
"pathTemplate": "/items/{{itemId}}",
"parameters": [
{ "pin": "itemId", "in": "path" }
],
"auth": {
"type": "header",
"credentials": ["api-key"],
"headerName": "x-api-key"
},
"responseMapping": [
{ "pin": "item", "path": "$body.data" },
{ "pin": "status", "path": "$status" }
]
}
}
]
}For real-world examples, see the VirusTotal and Jira pack reference pages.