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 };
}