Skip to main content
The Webhooks plugin adds authenticated HTTP routes so a trusted external system (Zapier, n8n, a CI job, an internal service) can create and drive managed OpenClaw TaskFlows over HTTP, without writing a custom plugin. The plugin runs inside the Gateway process. For a remote Gateway, install and configure it on that host, then restart the Gateway. It ships with no routes configured, so it is a no-op until you add at least one route.

Configure routes

Set config under plugins.entries.webhooks.config:
{
  plugins: {
    entries: {
      webhooks: {
        enabled: true,
        config: {
          routes: {
            zapier: {
              path: "/plugins/webhooks/zapier",
              sessionKey: "agent:main:main",
              secret: {
                source: "env",
                provider: "default",
                id: "OPENCLAW_WEBHOOK_SECRET",
              },
              controllerId: "webhooks/zapier",
              description: "Zapier TaskFlow bridge",
            },
          },
        },
      },
    },
  },
}
Route fields:
FieldRequiredDefaultNotes
enablednotrue
pathno/plugins/webhooks/<routeId>Must be unique across routes.
sessionKeyyes-Session that owns the bound TaskFlows.
secretyes-Plain string or a SecretRef (below).
controllerIdnowebhooks/<routeId>Used as the default create_flow controller.
descriptionno-Operator note only.
secret accepts a plain string or a SecretRef: { source: "env" | "file" | "exec", provider: "default", id: "..." }. Every configured route registers at startup regardless of whether its secret currently resolves. An unresolvable secret does not disable or skip the route - requests to it fail authentication (401) until the secret can be resolved. SecretRef values are re-resolved on every request, so rotating the underlying secret (env var, file, or exec output) takes effect without a Gateway restart.

Security model

Each route acts with the TaskFlow authority of its configured sessionKey: it can inspect and mutate any TaskFlow owned by that session. TaskFlow access always goes through api.runtime.tasks.managedFlows.bindSession(...), so a route can never act outside its bound session. To limit blast radius:
  • Use a strong, unique secret per route.
  • Prefer a SecretRef over an inline plaintext secret.
  • Bind routes to the narrowest session that fits the workflow.
  • Expose only the specific webhook path you need.
Request handling order for each path: HTTP method (POST only) and Content-Type: application/json checks, then fixed-window rate limiting (120 requests per 60-second window per path+client-IP key, up to 4,096 tracked keys), then in-flight request limiting (8 concurrent requests per key, up to 4,096 tracked keys), then shared-secret authentication, then a 256 KB / 15-second JSON body read. Requests that fail an earlier check never reach later ones.

Request format

Send POST requests with Content-Type: application/json and either Authorization: Bearer <secret> or x-openclaw-webhook-secret: <secret>:
curl -X POST https://gateway.example.com/plugins/webhooks/zapier \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_SHARED_SECRET' \
  -d '{"action":"create_flow","goal":"Review inbound queue"}'

Supported actions

ActionPurpose
create_flowCreate a managed TaskFlow for the route’s session.
get_flowFetch one TaskFlow by id.
list_flowsList TaskFlows for the route’s session.
find_latest_flowFetch the most recently updated TaskFlow.
resolve_flowResolve a TaskFlow by opaque token.
get_task_summaryFetch the task summary for a TaskFlow.
set_waitingMark a TaskFlow waiting, with optional state/wait data.
resume_flowResume a waiting/blocked TaskFlow.
finish_flowMark a TaskFlow finished.
fail_flowMark a TaskFlow failed.
request_cancelRequest cooperative cancellation.
cancel_flowCancel a TaskFlow (may return 202 if children are still active).
run_taskCreate a managed child task inside an existing TaskFlow.
Mutating actions (set_waiting, resume_flow, finish_flow, fail_flow, request_cancel) require flowId and expectedRevision for optimistic concurrency; a stale revision returns 409 revision_conflict.

create_flow

{
  "action": "create_flow",
  "goal": "Review inbound queue",
  "status": "queued",
  "notifyPolicy": "done_only"
}

run_task

Allowed runtime values: subagent, acp. startedAt, lastEventAt, and progressSummary are only valid when status is "running"; sending them with any other status returns 400 invalid_request.
{
  "action": "run_task",
  "flowId": "flow_123",
  "runtime": "acp",
  "childSessionKey": "agent:main:acp:worker",
  "task": "Inspect the next message batch"
}

Response shape

{
  "ok": true,
  "routeId": "zapier",
  "result": {}
}
{
  "ok": false,
  "routeId": "zapier",
  "code": "not_found",
  "error": "TaskFlow not found.",
  "result": {}
}
Flow and task views never include owner/session metadata, so responses cannot leak the route’s bound sessionKey. code values include not_found, not_managed, revision_conflict, persist_failed, cancel_requested, cancel_pending, terminal, invalid_request, request_rejected, and action-specific fallback codes (mutation_rejected, create_rejected, task_not_created, cancel_rejected) when a mutation is rejected for a reason not covered by the named codes above.