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_logget_logsnotifysend_notification_draftsend_email_draftlist_email_draftsdelete_email_draftsend_gmail_draft(alias:send_approved_email)create_calendar_eventlist_upcoming_eventsupdate_calendar_eventdelete_calendar_eventapproval_queue_addapproval_queue_listapproval_queue_resolveapproval_history_attach_executionfetch_and_normalize_urlinbound_event_filter
- returns normalized JSON responses
- returns
400for unknown actions - returns
400when required args are missing
Current side effects
append_log
- appends records into workflow static data under key:
actionLog
- keeps the most recent
200entries - 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 existingnotifypath 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_draftlist_email_draftsdelete_email_draftsend_gmail_draft(alias:send_approved_email)create_calendar_eventlist_upcoming_eventsupdate_calendar_eventdelete_calendar_event
Behavior:
- queue proposals into workflow static data under key:
approvalQueue
- keep the most recent
200pending 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
gogbridge after explicit approval resolution
Approval policy defaults by action family:
- notification family
send_notification_draftapproval.family = "notification"approval.required = trueapproval.mutation_level = "high"- approved items execute inline in n8n via the existing
notifypath
- Gmail family
- read-only:
list_email_draftsapproval.family = "gmail"approval.required = trueapproval.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_emailapproval.family = "gmail"approval.required = trueapproval.mutation_level = "high"
- read-only:
- Calendar family
- read-only:
list_upcoming_eventsapproval.family = "calendar"approval.required = trueapproval.mutation_level = "low"
- mutating:
create_calendar_event,update_calendar_event,delete_calendar_eventapproval.family = "calendar"approval.required = trueapproval.mutation_level = "high"
- read-only:
- 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_previewapprovalQueue[].operator.summary_lineapprovalHistory[].operator.execution_stateapprovalHistory[].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.pybridge to attach Gmail/Calendar execution results - the updated history entry now includes low-noise operator metadata such as:
operator.summary_lineoperator.execution_stateoperator.result_refsexecution.summary
- returns both the full item and
result.item_compact
fetch_and_normalize_url
- fetches a remote
httporhttpsURL 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: truefor runtimes with incomplete CA trust
inbound_event_filter
- classifies inbound events as
urgent,important,watch, ordeduped - 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 → Authentication →
Header Auth - bind a credential with:
- header name:
x-openclaw-secret - header value: your generated shared secret
- header name:
Import steps
- In n8n, create or open a workflow.
- Import
assets/openclaw-action.workflow.json. - Open the Webhook node.
- Set Authentication to
Header Auth. - Bind your local credential.
- Save.
- Use Listen for test event and call the test URL first.
- 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.jsonassets/test-notify.jsonassets/test-send-notification-draft.jsonassets/test-send-email-draft.jsonassets/test-list-email-drafts.jsonassets/test-delete-email-draft.jsonassets/test-send-gmail-draft.jsonassets/test-send-approved-email.jsonassets/test-create-calendar-event.jsonassets/test-list-upcoming-events.jsonassets/test-update-calendar-event.jsonassets/test-delete-calendar-event.jsonassets/test-verify-email-draft-cycle.jsonassets/test-verify-calendar-event-cycle.jsonassets/test-fetch-and-normalize-url.jsonassets/test-approval-queue-list.jsonassets/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[]andresult.history_compact[]summary_linefor a one-line operator digestexecution_stateforpending,awaiting_host_execution,dry_run,executed, orfailedresult_refsfor durable IDs such asdraft_id,message_id, orevent_id
Canned recurring verification flows
Gmail draft queue → approve → verify → cleanup
- Queue the canned payload:
scripts/call-action.sh --args-file assets/test-verify-email-draft-cycle.json --pretty
- Find the new approval id from
pending_compact. - Approve with the host bridge:
python3 scripts/resolve-approval-with-gog.py --id <approval-id> --decision approve
- Re-run
approval_queue_listand confirm the matching history item shows:execution_state = "executed"result_refs.draft_idpopulated
- Cleanup by queueing
assets/test-delete-email-draft.jsonwith the returned draft id and approving that item.
Calendar event queue → approve → verify → cleanup
- Queue the canned payload:
scripts/call-action.sh --args-file assets/test-verify-calendar-event-cycle.json --pretty
- Approve with the host bridge:
python3 scripts/resolve-approval-with-gog.py --id <approval-id> --decision approve
- Re-run
approval_queue_listand confirm the matching history item shows:execution_state = "executed"result_refs.event_idpopulated
- Cleanup by queueing
assets/test-delete-calendar-event.jsonwith the returnedevent_idand 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_draft→gog gmail drafts createemail_list_drafts→gog gmail drafts listemail_draft_delete→gog gmail drafts deleteemail_draft_send→gog gmail drafts sendcalendar_event→gog calendar createcalendar_list_events→gog calendar eventscalendar_event_update→gog calendar updatecalendar_event_delete→gog calendar delete
- writes execution metadata back via
approval_history_attach_execution
Important automation note:
- real unattended execution needs
GOG_KEYRING_PASSWORDavailable to the executor - the included bridge auto-loads
/home/openclaw/.openclaw/credentials/gog.envwhen present - keep that file mode
600if you use it forGOG_ACCOUNT/GOG_KEYRING_PASSWORD - without the password, non-TTY
gogcalls will fail when the file keyring tries to prompt --dry-runworks 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