HeadlessOps
Examples

Webhook → Process → Store

Receive a webhook, process the payload, and persist results to a data store

Webhook → Process → Store

This example receives a webhook from an external system, processes the payload, and stores the result in a Data Store.

integration.yaml

name: webhook-process-store
description: Receive webhook, process, and store result

trigger:
  type: webhook
  method: POST

steps:
  - id: validate
    name: Validate Payload
    description: Validates the incoming webhook payload
    file: validate.ts
    input:
      record_id: "${{ trigger.body.record_id }}"
      action: "${{ trigger.body.action }}"
    on_success: process
    on_failure: handle_error

  - id: process
    name: Process Record
    description: Enriches the record with external API data
    file: process.ts
    credentials:
      - CRM_API_KEY
    input:
      record_id: "${{ steps.validate.output.record_id }}"
    on_success: store_result
    on_failure: handle_error

  - id: store_result
    name: Store Result
    description: Persists the processed record to the data store
    file: store_result.ts
    input:
      record_id: "${{ steps.validate.output.record_id }}"
      enriched_data: "${{ steps.process.output.data }}"

  - id: handle_error
    name: Handle Error
    description: Logs the error and sends an alert
    file: handle_error.ts
    input:
      error_message: "${{ error.message }}"
      failed_step: "${{ error.step }}"

validate.ts

export async function run(input: Record<string, any>, ctx: StepContext) {
  ctx.log("validate: start", { record_id: input.record_id });

  if (!input.record_id) {
    throw new Error("Missing required field: record_id");
  }

  ctx.log("validate: ok", { record_id: input.record_id });
  return { record_id: input.record_id, action: input.action || "upsert" };
}

process.ts

export async function run(input: Record<string, any>, ctx: StepContext) {
  ctx.log("process: start", { record_id: input.record_id });

  const response = await ctx.http.get(
    `https://api.crm.example.com/records/${input.record_id}`,
    {
      headers: { Authorization: `Bearer ${ctx.credentials.CRM_API_KEY}` },
      timeout: 25000,
    }
  );
  if (!response.ok) {
    throw new Error(`CRM API returned ${response.status}`);
  }
  const data = await response.json();

  ctx.log("process: done", { name: data.name });
  return { data };
}

store_result.ts

export async function run(input: Record<string, any>, ctx: StepContext) {
  ctx.log("store_result: start", { record_id: input.record_id });

  // Idempotency check
  const existing = await ctx.stores.processed_records.find_one({ record_id: input.record_id });
  if (existing) {
    ctx.log("store_result: already stored, updating", {});
    await ctx.stores.processed_records.update(
      { record_id: input.record_id },
      { data: JSON.stringify(input.enriched_data), updated_at: "2026-01-15T10:00:00Z" }
    );
    return { status: "updated" };
  }

  const row = await ctx.stores.processed_records.insert({
    record_id: input.record_id,
    data: JSON.stringify(input.enriched_data),
  });
  ctx.log("store_result: inserted", { id: row.id });
  return { status: "inserted", id: row.id };
}