HeadlessOps
Scripting Guide

integration.yaml Schema

Complete reference for the integration.yaml manifest format used in HeadlessOps automations. Covers trigger types, step routing, npm package dependencies, webhook authentication, HTML responses, and synchronous response modes.

integration.yaml Full Schema

name: my-automation            # Required. Unique slug for this automation. kebab-case.
description: "What it does"    # Required. One-line human-readable description.

packages:                      # Optional. npm packages installed before every run.
  - zod
  - dayjs@2.0
  - lodash@^4.17.21

trigger:
  type: webhook                # Required. One of: webhook | schedule | manual

  # --- Webhook-only fields ---
  method: POST                 # Optional. HTTP method filter: ANY | POST | GET | PUT (default: ANY)
  response_mode: immediate     # Optional. immediate (default) | synchronous
  auth:                        # Optional. Protect the webhook with a secret header.
    header: x-api-key          # Required if auth present. HTTP header name to check.
    credential: MY_WEBHOOK_KEY # Required if auth present. Credential name whose value must match.

  # --- Schedule-only fields ---
  cron: "0 9 * * 1-5"         # Required when type is schedule.

steps:
  - id: step_one               # Required. Unique snake_case identifier. Must match .ts filename.
    name: "Fetch Contact"      # Required. Human-readable label.
    description: "Fetches contact details from CRM" # Required. One-line description.
    file: step_one.ts          # Required. Must equal {id}.ts.
    icon: database             # Optional. UI icon slug (database, mail, globe, zap, code, search).
    credentials:               # Optional. List of credential names used (for UI display).
      - MY_API_KEY
    input:                     # Optional. Key-value map passed as `input` object to the step.
      email: "${{ trigger.body.email }}"
      api_url: "${{ config.API_BASE_URL }}"
    output:                    # Optional. Declares the return value shape (for UI/docs).
      contact_id: string
      contact_name: string
    on_success: step_two       # Optional. Step id to route to on success.
    on_failure: handle_error   # Optional. Step id to route to on error.
    continue_on_error: false   # Optional. If true, on_success runs even when step errors.
    routes:                    # Optional. Conditional routing (turns step into a condition node).
      - when: "${{ steps.step_one.output.status }} == 'active'"
        next: step_two
      - default: handle_inactive

Top-Level Field Reference

FieldRequiredTypeDescription
namestringUnique kebab-case slug for this automation.
descriptionstringOne-line human-readable description.
packagesstring[]npm package specifiers installed into the integration's isolated environment before every run. Synced automatically to the integration's requirements on each push.
triggerobjectDefines how the automation is triggered.
stepsarrayOrdered list of step definitions.

The packages Field — Dependency Management via YAML

The packages field lets you declare all npm dependencies directly in integration.yaml. Every time you push files to the platform, the packages array is automatically parsed and synced to the integration's requirements — no separate API call needed.

name: data-pipeline
description: "Pulls records from an API, processes with lodash, stores results"

packages:
  - zod@^3.23
  - dayjs@2.0
  - lodash@^4.17.21

This is the recommended way to manage dependencies for most integrations. The packages field supports standard npm specifier formats:

FormatExampleBehaviour
LatestlodashInstalls the newest available version
Pinneddayjs@2.0.0Exact version lock — recommended for production
Rangezod@^3.23Semver-compatible version range
Taglodash@latestSpecific dist-tag

Note: Git URLs, local paths, and tarball URLs are blocked for security. Attempting to use them returns a 400 Bad Request error.

How Sync Works

When you push integration.yaml (via integration_files_push, integration_files_push_run, or integration_files_deploy), the platform:

  1. Parses the packages array from the YAML.
  2. Updates the integration's requirements field in the database to match.
  3. Installs the resolved packages into an isolated environment before the next run.

This means your YAML file is the single source of truth for both the automation logic and its dependencies. No separate integrations_update call is required to keep packages in sync.

Trigger Field Reference

FieldApplies toValuesDefaultDescription
typeallwebhook | schedule | manualRequired.
cronschedulecron stringRequired for schedule.
methodwebhookANY | POST | GET | PUTANYHTTP method filter.
response_modewebhookimmediate | synchronousimmediatesynchronous waits for run and returns ctx.output().
auth.headerwebhookstringHeader name to check for the secret.
auth.credentialwebhookcredential nameCredential whose value must match the header.

Step Field Reference

FieldRequiredTypeDescription
idstringUnique snake_case identifier. Must match the .ts filename.
namestringHuman-readable label shown in logs and UI.
descriptionstringOne-line description of what this step does.
filestringTypeScript filename (e.g. step_one.ts). Must equal {id}.ts.
iconstringIcon slug: database, mail, globe, zap, code, search.
credentialsstring[]Credential names used by this step (informational, for UI).
inputmapKey-value pairs passed as the input object. Supports ${{ }} expressions.
outputmapDeclares the return value shape (e.g. field_name: string).
on_successstep idStep to route to on success. Overrides linear order.
on_failurestep idStep to route to on error.
continue_on_errorbooleanIf true, on_success runs even when the step errors.
routesarrayConditional routing rules.

Warning: A step missing any of the four required fields (id, name, description, file) will be silently skipped at runtime with no error message.

Conditional Routing

steps:
  - id: check_status
    name: Check Status
    description: Routes based on contact status
    file: check_status.ts
    routes:
      - when: "${{ steps.check_status.output.status }} == 'active'"
        next: send_welcome
      - when: "${{ steps.check_status.output.status }} == 'churned'"
        next: send_winback
      - default: log_unknown

Synchronous Webhook Response

trigger:
  type: webhook
  response_mode: synchronous

Default (JSON):

export async function run(input: Record<string, any>, ctx: StepContext) {
  const result = { status: "ok", id: input.id };
  ctx.output(result);   // returned to the webhook caller as JSON
  return result;        // available to downstream steps
}

HTML Response:

To return HTML from a synchronous webhook, include a __html key in the value passed to ctx.output(). The platform automatically sets Content-Type: text/html on the response:

export async function run(input: Record<string, any>, ctx: StepContext) {
  const html = `<!DOCTYPE html>
<html>
<head><title>Result</title></head>
<body>
  <h1>Record Processed</h1>
  <p>ID: ${input.record_id}</p>
  <p>Status: OK</p>
</body>
</html>`;
  ctx.output({ __html: html });
  return { status: "ok" };
}

Note: When __html is present, no other top-level keys are included in the response body — only the HTML string is returned.

pinned-trigger-payload.json

Pin a static trigger payload for manual and test runs. The file must mirror the exact runtime structure:

Trigger typePinned payload shape
webhook{ "query": {...}, "body": {...}, "headers": {...} }
manualFlat object matching expected trigger.body shape