Files
swarm-zap/skills/n8n-webhook/references/openclaw-action.md

16 KiB

openclaw-action workflow

This skill ships an importable workflow at:

  • assets/openclaw-action.workflow.json

It implements a real local OpenClaw → n8n router.

What it does

  • accepts POST /webhook/openclaw-action
  • normalizes incoming JSON into an action contract
  • supports these actions in the shipped asset:
    • append_log
    • get_logs
    • notify
    • send_notification_draft
    • send_email_draft
    • list_email_drafts
    • delete_email_draft
    • send_gmail_draft (alias: send_approved_email)
    • create_calendar_event
    • list_upcoming_events
    • update_calendar_event
    • delete_calendar_event
    • approval_queue_add
    • approval_queue_list
    • approval_queue_resolve
    • approval_history_attach_execution
    • fetch_and_normalize_url
    • inbound_event_filter
  • returns normalized JSON responses
  • returns 400 for unknown actions
  • returns 400 when required args are missing

Current side effects

append_log

  • appends records into workflow static data under key:
    • actionLog
  • keeps the most recent 200 entries
  • persists in n8n's database when the workflow execution succeeds

Example stored record:

{"ts":"2026-03-12T07:00:00Z","source":"openclaw-action","request_id":"abc","text":"backup complete"}

send_notification_draft

  • queues an approval-gated notification proposal into workflow static data under key:
    • approvalQueue
  • when resolved with decision=approve, it executes the existing notify path and sends through Telegram + Discord
  • uses only the already-configured notification credentials in the live n8n instance

Gmail + Calendar approval-gated actions

Actions:

  • send_email_draft
  • list_email_drafts
  • delete_email_draft
  • send_gmail_draft (alias: send_approved_email)
  • create_calendar_event
  • list_upcoming_events
  • update_calendar_event
  • delete_calendar_event

Behavior:

  • queue proposals into workflow static data under key:
    • approvalQueue
  • keep the most recent 200 pending entries
  • return explicit approval policy metadata per action (approval.policy, approval.required, approval.mutation_level)
  • do not execute Gmail/Calendar side effects directly in the shipped starter workflow
  • are intended for host-side execution via the included gog bridge after explicit approval resolution

Approval policy defaults by action family:

  • notification family
    • send_notification_draft
    • approval.family = "notification"
    • approval.required = true
    • approval.mutation_level = "high"
    • approved items execute inline in n8n via the existing notify path
  • Gmail family
    • read-only: list_email_drafts
      • approval.family = "gmail"
      • approval.required = true
      • approval.mutation_level = "low"
      • still queued so operators must explicitly acknowledge host-side Gmail reads
    • mutating: send_email_draft, delete_email_draft, send_gmail_draft / send_approved_email
      • approval.family = "gmail"
      • approval.required = true
      • approval.mutation_level = "high"
  • Calendar family
    • read-only: list_upcoming_events
      • approval.family = "calendar"
      • approval.required = true
      • approval.mutation_level = "low"
    • mutating: create_calendar_event, update_calendar_event, delete_calendar_event
      • approval.family = "calendar"
      • approval.required = true
      • approval.mutation_level = "high"
  • manual/generic approvals
    • approval_queue_add
    • no automatic side effect is implied; the operator decides what the queued item means

Queue/history entries now also carry compact operator-facing fields for low-noise review:

  • approvalQueue[].payload_preview
  • approvalQueue[].operator.summary_line
  • approvalHistory[].operator.execution_state
  • approvalHistory[].operator.result_refs

approval_queue_resolve

  • removes one item from approvalQueue
  • appends the resolved entry into:
    • approvalHistory
  • supports optional notification on approval/rejection
  • executes notification drafts inline when the approved item kind is notification
  • returns both the full resolved item and a compact operator view at:
    • result.item_compact

approval_history_attach_execution

  • patches an existing resolved history item in approvalHistory
  • designed for host-side executors that run outside n8n itself
  • used by the included scripts/resolve-approval-with-gog.py bridge to attach Gmail/Calendar execution results
  • the updated history entry now includes low-noise operator metadata such as:
    • operator.summary_line
    • operator.execution_state
    • operator.result_refs
    • execution.summary
  • returns both the full item and result.item_compact

fetch_and_normalize_url

  • fetches a remote http or https URL from inside n8n
  • normalizes HTML/text/JSON into a single response shape
  • returns title/excerpt/body text suitable for downstream summarization or logging
  • uses n8n's runtime HTTP helper inside the Code node rather than relying on global fetch
  • supports optional arg skip_ssl_certificate_validation: true for runtimes with incomplete CA trust

inbound_event_filter

  • classifies inbound events as urgent, important, watch, or deduped
  • stores recent events in:
    • inboundEvents
  • stores recent dedupe keys in:
    • eventDedup
  • can fan out a notification for urgent/important non-duplicate events

notify

  • sends a Telegram message using credential:
    • Telegram Bot (OpenClaw)
  • sends a Discord message using credential:
    • Discord Bot Auth
  • current targets mirror the already-working reminder workflow

Why workflow static data first

Why this first:

  • built-in, no extra credentials
  • persists without guessing writable filesystem paths
  • good fit for queues, recent breadcrumbs, and small operational state
  • lets us implement safe approval-gated patterns immediately

When to add provider-backed steps later:

  • email draft creation in Gmail/Outlook
  • calendar writes in Google Calendar
  • Airtable/Sheets append pipelines
  • long-retention logs or external archival

Intentional security choice

The exported workflow leaves the Webhook node auth unset in the JSON file.

Why:

  • n8n credentials are instance-local
  • secrets should not be embedded in a shareable skill asset

After import, set this manually in n8n:

  • Webhook node → AuthenticationHeader Auth
  • bind a credential with:
    • header name: x-openclaw-secret
    • header value: your generated shared secret

Import steps

  1. In n8n, create or open a workflow.
  2. Import assets/openclaw-action.workflow.json.
  3. Open the Webhook node.
  4. Set Authentication to Header Auth.
  5. Bind your local credential.
  6. Save.
  7. Use Listen for test event and call the test URL first.
  8. Once successful, activate the workflow for production URL use.

Expected URLs

  • test: http://192.168.153.113:18808/webhook-test/openclaw-action
  • prod: http://192.168.153.113:18808/webhook/openclaw-action

Test payloads included

  • assets/test-append-log.json
  • assets/test-notify.json
  • assets/test-send-notification-draft.json
  • assets/test-send-email-draft.json
  • assets/test-list-email-drafts.json
  • assets/test-delete-email-draft.json
  • assets/test-send-gmail-draft.json
  • assets/test-send-approved-email.json
  • assets/test-create-calendar-event.json
  • assets/test-list-upcoming-events.json
  • assets/test-update-calendar-event.json
  • assets/test-delete-calendar-event.json
  • assets/test-verify-email-draft-cycle.json
  • assets/test-verify-calendar-event-cycle.json
  • assets/test-fetch-and-normalize-url.json
  • assets/test-approval-queue-list.json
  • assets/test-inbound-event-filter.json

Example tests

export N8N_WEBHOOK_SECRET='YOUR_SECRET_HERE'
scripts/call-action.sh append_log --args '{"text":"backup complete"}' --pretty
scripts/call-action.sh get_logs --args '{"limit":5}' --pretty
scripts/call-action.sh notify --args '{"title":"Workflow finished","message":"n8n router test"}' --pretty
scripts/call-action.sh send_notification_draft --args-file assets/test-send-notification-draft.json --pretty
scripts/call-action.sh send_email_draft --args-file assets/test-send-email-draft.json --pretty
scripts/call-action.sh list_email_drafts --args-file assets/test-list-email-drafts.json --pretty
scripts/call-action.sh delete_email_draft --args-file assets/test-delete-email-draft.json --pretty
scripts/call-action.sh send_gmail_draft --args-file assets/test-send-gmail-draft.json --pretty
scripts/call-action.sh send_approved_email --args-file assets/test-send-approved-email.json --pretty
scripts/call-action.sh create_calendar_event --args-file assets/test-create-calendar-event.json --pretty
scripts/call-action.sh list_upcoming_events --args-file assets/test-list-upcoming-events.json --pretty
scripts/call-action.sh update_calendar_event --args-file assets/test-update-calendar-event.json --pretty
scripts/call-action.sh delete_calendar_event --args-file assets/test-delete-calendar-event.json --pretty
scripts/call-action.sh --args-file assets/test-verify-email-draft-cycle.json --pretty
scripts/call-action.sh --args-file assets/test-verify-calendar-event-cycle.json --pretty
scripts/call-action.sh fetch_and_normalize_url --args '{"url":"http://192.168.153.113:18808/healthz"}' --pretty
scripts/call-action.sh fetch_and_normalize_url --args '{"url":"https://example.com","skip_ssl_certificate_validation":true}' --pretty
scripts/call-action.sh approval_queue_list --args '{"limit":10,"include_history":true}' --pretty
scripts/call-action.sh inbound_event_filter --args-file assets/test-inbound-event-filter.json --pretty
python3 scripts/resolve-approval-with-gog.py --id <approval-id> --decision approve --dry-run

Operator command reference

Common approval flows:

# 1) inspect the queue with both full and compact views
scripts/call-action.sh approval_queue_list --args '{"limit":10,"include_history":true}' --pretty

# 2) reject an item without host-side execution
scripts/call-action.sh approval_queue_resolve \
  --args '{"id":"approval-abc123","decision":"reject","note":"not safe to run"}' \
  --pretty

# 3) approve through the host bridge, but keep it side-effect free
python3 scripts/resolve-approval-with-gog.py --id approval-abc123 --decision approve --dry-run

# 4) approve for real through the host bridge
python3 scripts/resolve-approval-with-gog.py --id approval-abc123 --decision approve

# 5) re-check recent history in compact form
scripts/call-action.sh approval_queue_list --args '{"limit":5,"include_history":true}' --pretty

What to look for in low-noise history output:

  • result.pending_compact[] and result.history_compact[]
  • summary_line for a one-line operator digest
  • execution_state for pending, awaiting_host_execution, dry_run, executed, or failed
  • result_refs for durable IDs such as draft_id, message_id, or event_id

Canned recurring verification flows

Gmail draft queue → approve → verify → cleanup

  1. Queue the canned payload:
scripts/call-action.sh --args-file assets/test-verify-email-draft-cycle.json --pretty
  1. Find the new approval id from pending_compact.
  2. Approve with the host bridge:
python3 scripts/resolve-approval-with-gog.py --id <approval-id> --decision approve
  1. Re-run approval_queue_list and confirm the matching history item shows:
    • execution_state = "executed"
    • result_refs.draft_id populated
  2. Cleanup by queueing assets/test-delete-email-draft.json with the returned draft id and approving that item.

Calendar event queue → approve → verify → cleanup

  1. Queue the canned payload:
scripts/call-action.sh --args-file assets/test-verify-calendar-event-cycle.json --pretty
  1. Approve with the host bridge:
python3 scripts/resolve-approval-with-gog.py --id <approval-id> --decision approve
  1. Re-run approval_queue_list and confirm the matching history item shows:
    • execution_state = "executed"
    • result_refs.event_id populated
  2. Cleanup by queueing assets/test-delete-calendar-event.json with the returned event_id and approving that item.

Expected success examples

send_notification_draft

{
  "ok": true,
  "request_id": "test-notify-draft-001",
  "result": {
    "action": "send_notification_draft",
    "status": "queued_for_approval",
    "pending_id": "approval-abc123",
    "approval_status": "pending"
  }
}

send_email_draft

{
  "ok": true,
  "request_id": "test-email-draft-001",
  "result": {
    "action": "send_email_draft",
    "status": "queued_for_approval",
    "pending_id": "approval-abc123",
    "approval_status": "pending"
  }
}

list_email_drafts

{
  "ok": true,
  "request_id": "test-list-email-drafts-001",
  "result": {
    "action": "list_email_drafts",
    "status": "queued_for_approval",
    "pending_id": "approval-ghi789",
    "approval_status": "pending",
    "approval": {
      "policy": "approval_queue_resolve",
      "required": true,
      "mutation_level": "low"
    }
  }
}

delete_email_draft

{
  "ok": true,
  "request_id": "test-delete-email-draft-001",
  "result": {
    "action": "delete_email_draft",
    "status": "queued_for_approval",
    "pending_id": "approval-jkl012",
    "approval_status": "pending"
  }
}

send_gmail_draft / send_approved_email

{
  "ok": true,
  "request_id": "test-send-gmail-draft-001",
  "result": {
    "action": "send_gmail_draft",
    "requested_action": "send_gmail_draft",
    "status": "queued_for_approval",
    "pending_id": "approval-mno345",
    "approval_status": "pending"
  }
}

create_calendar_event

{
  "ok": true,
  "request_id": "test-calendar-event-001",
  "result": {
    "action": "create_calendar_event",
    "status": "queued_for_approval",
    "pending_id": "approval-def456",
    "approval_status": "pending"
  }
}

list_upcoming_events

{
  "ok": true,
  "request_id": "test-list-calendar-events-001",
  "result": {
    "action": "list_upcoming_events",
    "status": "queued_for_approval",
    "pending_id": "approval-pqr678",
    "approval_status": "pending",
    "approval": {
      "policy": "approval_queue_resolve",
      "required": true,
      "mutation_level": "low"
    }
  }
}

update_calendar_event

{
  "ok": true,
  "request_id": "test-update-calendar-event-001",
  "result": {
    "action": "update_calendar_event",
    "status": "queued_for_approval",
    "pending_id": "approval-stu901",
    "approval_status": "pending"
  }
}

delete_calendar_event

{
  "ok": true,
  "request_id": "test-delete-calendar-event-001",
  "result": {
    "action": "delete_calendar_event",
    "status": "queued_for_approval",
    "pending_id": "approval-vwx234",
    "approval_status": "pending"
  }
}

fetch_and_normalize_url

{
  "ok": true,
  "request_id": "test-fetch-001",
  "result": {
    "action": "fetch_and_normalize_url",
    "status": "ok",
    "url": "http://192.168.153.113:18808/healthz",
    "title": "",
    "content_type": "application/json; charset=utf-8"
  }
}

inbound_event_filter

{
  "ok": true,
  "request_id": "test-inbound-001",
  "result": {
    "action": "inbound_event_filter",
    "status": "stored",
    "classification": "urgent",
    "duplicate": false,
    "notified": true
  }
}

Host bridge notes

The included host bridge scripts/resolve-approval-with-gog.py is for the case where Gmail/Calendar auth exists on the OpenClaw host via gog, not inside n8n itself.

Behavior:

  • resolves an approval item through openclaw-action
  • executes supported kinds on the host:
    • email_draftgog gmail drafts create
    • email_list_draftsgog gmail drafts list
    • email_draft_deletegog gmail drafts delete
    • email_draft_sendgog gmail drafts send
    • calendar_eventgog calendar create
    • calendar_list_eventsgog calendar events
    • calendar_event_updategog calendar update
    • calendar_event_deletegog calendar delete
  • writes execution metadata back via approval_history_attach_execution

Important automation note:

  • real unattended execution needs GOG_KEYRING_PASSWORD available to the executor
  • the included bridge auto-loads /home/openclaw/.openclaw/credentials/gog.env when present
  • keep that file mode 600 if you use it for GOG_ACCOUNT / GOG_KEYRING_PASSWORD
  • without the password, non-TTY gog calls will fail when the file keyring tries to prompt
  • --dry-run works without touching Google state and is useful for plumbing verification

Validation

Run the local validator before import/package changes:

python3 scripts/validate-workflow.py assets/openclaw-action.workflow.json