Skip to content
CLI
Webhooks, Schedules & Tasks

Webhooks, Schedules & Tasks

The Hutly CLI manages three event-driven automation surfaces:

  • Webhooks — standalone inbound URLs that fire handlers (a workflow or an agent) when called.
  • Schedules — cron triggers that fire handlers on a recurring UTC schedule.
  • Manual tasks — the human-in-the-loop approval tasks emitted by workflow approval nodes.

Every command resolves the target organization from your CLI config and key, and acts within that org’s membership scope. Most read commands emit JSON by default; pass --pretty to indent it. The --json flag is accepted everywhere as a no-op for compatibility.

To capture a live webhook or schedule into a versionable YAML file, see track-webhook and track-schedule.

Webhooks

A webhook is an addressable inbound URL. Each webhook owns a list of handlers (what runs when it is hit) and an ordered list of response rules (synchronous replies for handshakes such as the Slack URL-verification challenge). The webhook’s public url is derived at the API edge and returned by get — it is not stored, so always read it back rather than constructing it.

list

List webhooks owned by the current organization.

hutly webhooks list
hutly webhooks list --limit 50 --offset 50 --pretty
Option Description
-l, --limit <n> Max results (default 100)
-o, --offset <n> Result offset (default 0)

get

Fetch a single webhook by id, including its derived public url and any response rules.

hutly webhooks get <webhookId> --pretty

create

Create a new webhook. Returns the created record, including its public URL.

hutly webhooks create --name "Slack events" --description "Inbound Slack event subscription"
Option Description
--name <name> Webhook display name
--description <description> Webhook description

update

Replace a webhook’s name and/or description. This is a full replacement — fields you omit are cleared.

hutly webhooks update <webhookId> --name "Slack events (prod)"

delete

Delete a webhook and all of its handlers. Prompts for confirmation unless -y is passed.

hutly webhooks delete <webhookId> -y
Option Description
-y, --yes Skip confirmation prompts

execute

Manually fire a webhook. The payload is delivered as the request body to all of the webhook’s handlers — useful for testing a handler chain without an external caller. Pass at most one of --payload or --payload-file.

hutly webhooks execute <webhookId> --payload '{"event":"test"}'
hutly webhooks execute <webhookId> --payload-file ./sample.json
Option Description
--payload <json> Inline JSON object payload
--payload-file <path> Read payload JSON from a file

handlers

Manage the handlers attached to a webhook. A handler invokes either a workflow or an agent when the webhook fires.

hutly webhooks handlers list <webhookId>
hutly webhooks handlers get <webhookId> <handlerId>
hutly webhooks handlers create <webhookId> --workflow <workflowId> \
  --input-mapping '{"address":"$.body.property.address"}'
hutly webhooks handlers create <webhookId> --agent <agentId> \
  --instructions "Triage the inbound payload and reply in the thread."
hutly webhooks handlers update <webhookId> <handlerId> --instructions "..."
hutly webhooks handlers delete <webhookId> <handlerId> -y
Option Description
--workflow <workflowId> Workflow id to invoke
--agent <agentId> Agent id to invoke
--instructions <text> Instructions passed to the handler — required for agent handlers, optional for workflow handlers
--input-mapping <json> JSON object mapping target input fields to JSONPath / Handlebars expressions over the request body

create attaches a handler bound to exactly one of a workflow or an agent. update is a full replacement of the handler’s instructions and input mapping. list supports -l, --limit and -o, --offset; delete supports -y, --yes.

The --input-mapping value maps each target input field to a JSONPath or Handlebars expression evaluated against the inbound WebhookExecutionContext (body, headers, method, path, queryParams, pathParams). Use $. JSONPath for whole-value references and {{ … }} Handlebars for string interpolation.

rules

Manage response rules — synchronous replies evaluated in order before (or instead of) handler dispatch. Rules are matched first-match-wins by index, so order matters. The canonical use is the Slack URL-verification handshake: match on the verification event and echo back the challenge without firing handlers.

hutly webhooks rules list <webhookId>
hutly webhooks rules add <webhookId> \
  --name "Slack URL verification" \
  --condition "body.type == 'url_verification'" \
  --response-body '{"challenge":"{{body.challenge}}"}' \
  --skip-handlers
hutly webhooks rules update <webhookId> 0 --name "Slack URL verification"
hutly webhooks rules delete <webhookId> 0 -y
Option Description
--name <name> Human-readable rule name
--condition <jexl> JEXL expression evaluated against the request (e.g. body.type == 'url_verification'). Omit for a catch-all rule.
--response-body <json> JSON object of string → Handlebars template returned on match
--skip-handlers On match, return the response body without dispatching the webhook’s handlers (recommended for handshakes; default false)

add appends a rule. update <index> and delete <index> take the rule’s 0-based index from list (which prints rules in match order). update is a full replacement — omitted optional fields are cleared. delete supports -y, --yes.

Schedules

A schedule trigger fires its handlers on a recurring cron schedule. Like webhooks, a trigger owns a list of handlers, each invoking a workflow or an agent.

Cron expressions are AWS EventBridge 6-field Quartz in UTC: minute hour day-of-month month day-of-week year. Exactly one of day-of-month / day-of-week must be ?. Examples:

  • 0 9 ? * MON-FRI * — weekdays at 09:00 UTC
  • */15 * * * ? * — every 15 minutes

list

List schedule triggers owned by the current organization.

hutly schedules list --pretty

Supports -l, --limit <n> (default 100) and -o, --offset <n> (default 0).

get

Fetch a single schedule trigger by id.

hutly schedules get <triggerId> --pretty

create

Create a new schedule trigger.

hutly schedules create --name "Weekday digest" \
  --description "Morning summary" \
  --cron "0 9 ? * MON-FRI *"
Option Description
--name <name> Schedule display name
--description <description> Schedule description
--cron <expr> EventBridge 6-field Quartz cron (UTC)

update

Full replacement of a trigger’s name, description, and cron.

hutly schedules update <triggerId> --cron "0 8 ? * MON-FRI *"

delete

Delete a schedule trigger and all of its handlers. Prompts for confirmation unless -y is passed.

hutly schedules delete <triggerId> -y

handlers

Manage the handlers attached to a schedule trigger. Identical in shape to webhook handlers, except the input mapping is evaluated over the schedule context rather than a request body.

hutly schedules handlers list <triggerId>
hutly schedules handlers get <triggerId> <handlerId>
hutly schedules handlers create <triggerId> --workflow <workflowId>
hutly schedules handlers create <triggerId> --agent <agentId> \
  --instructions "Compile and send the weekday digest."
hutly schedules handlers update <triggerId> <handlerId> --instructions "..."
hutly schedules handlers delete <triggerId> <handlerId> -y
Option Description
--workflow <workflowId> Workflow id to invoke
--agent <agentId> Agent id to invoke
--instructions <text> Instructions passed to the handler — required for agent handlers, optional for workflow handlers
--input-mapping <json> JSON object mapping target input fields to JSONPath / Handlebars expressions over the schedule context

list supports -l, --limit / -o, --offset; delete supports -y, --yes.

Manual Tasks

A manual task is the human-in-the-loop approval prompt emitted when a workflow reaches an approval node. The workflow run is suspended until the task is approved (with data matching the task’s output schema) or denied; either response resumes the run.

get

Fetch a manual approval task, including its outputSchema and the prompt payload. Read the outputSchema first — it defines the exact shape respond --data must satisfy.

hutly manual-tasks get <taskId> --pretty

respond

Respond to a manual approval task. Pass exactly one of --approved or --denied.

hutly manual-tasks respond <taskId> --approved --data '{"decision":"accept","notes":"ok"}'
hutly manual-tasks respond <taskId> --approved --data-file ./response.json
hutly manual-tasks respond <taskId> --denied
Option Description
--approved Approve the task and submit data
--denied Deny the task
--data <json> Inline JSON object matching the task’s outputSchema — required for --approved, ignored for --denied
--data-file <path> Read the response data from a JSON file

Constraints enforced by the command: --approved and --denied are mutually exclusive (and one is required); --data and --data-file are mutually exclusive; and passing --data / --data-file with --denied is rejected (data is ignored on denial — omit the flag).

Tracking to YAML

The top-level track-webhook and track-schedule commands fetch a single resource by id and serialise it to a YAML file you can version-control and re-deploy. Both wrap the resource id in an !org_map structure so the file can be deployed across organizations.

track-webhook

hutly track-webhook <webhookId>
hutly track-webhook <webhookId> -o ./webhooks/slack-events.yaml
Option Description
-o, --output <file> Output path (defaults to ./webhooks/<webhook-name>.yaml)

track-schedule

hutly track-schedule <triggerId>
hutly track-schedule <triggerId> -o ./schedules/weekday-digest.yaml
Option Description
-o, --output <file> Output path (defaults to ./schedules/<schedule-name>.yaml)

YAML shape

The generated files strip system fields (createdAt, updatedAt, organizationId) and wrap the resource id in !org_map. A tracked webhook looks like:

webhookId: !org_map
  <organizationId>: <webhookId>
name: Slack events
description: Inbound Slack event subscription
responseRules:
  - name: Slack URL verification
    condition: "body.type == 'url_verification'"
    responseBody:
      challenge: "{{body.challenge}}"
    skipHandlers: true

A tracked schedule wraps triggerId the same way and carries name, description, and the cron expression.