#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: scripts/call-webhook.sh [--data '{"k":"v"}'] [--data-file payload.json] [--test] [--pretty] [--dry-run] Environment: N8N_BASE_URL Base n8n URL (default: http://192.168.153.113:18808) N8N_WEBHOOK_SECRET Shared secret value for header auth (required unless --dry-run) N8N_SECRET_HEADER Header name for shared secret (default: x-openclaw-secret) Examples: scripts/call-webhook.sh openclaw-ping --data '{"message":"hello"}' scripts/call-webhook.sh openclaw-ping --test --data-file payload.json echo '{"message":"hello"}' | scripts/call-webhook.sh openclaw-ping --pretty EOF } BASE_URL="${N8N_BASE_URL:-http://192.168.153.113:18808}" SECRET_HEADER="${N8N_SECRET_HEADER:-x-openclaw-secret}" SECRET_VALUE="${N8N_WEBHOOK_SECRET:-}" MODE="prod" PRETTY=0 DRY_RUN=0 PATH_OR_URL="" DATA="" DATA_FILE="" while [[ $# -gt 0 ]]; do case "$1" in --help|-h) usage exit 0 ;; --test) MODE="test" shift ;; --prod) MODE="prod" shift ;; --pretty) PRETTY=1 shift ;; --dry-run) DRY_RUN=1 shift ;; --data) DATA="${2:?missing value for --data}" shift 2 ;; --data-file) DATA_FILE="${2:?missing value for --data-file}" shift 2 ;; --*) echo "Unknown option: $1" >&2 usage >&2 exit 2 ;; *) if [[ -z "$PATH_OR_URL" ]]; then PATH_OR_URL="$1" else echo "Unexpected extra argument: $1" >&2 usage >&2 exit 2 fi shift ;; esac done if [[ -z "$PATH_OR_URL" ]]; then usage >&2 exit 2 fi if [[ -n "$DATA" && -n "$DATA_FILE" ]]; then echo "Use either --data or --data-file, not both." >&2 exit 2 fi if [[ -n "$DATA_FILE" ]]; then DATA="$(cat "$DATA_FILE")" elif [[ -n "$DATA" ]]; then : elif ! [[ -t 0 ]]; then DATA="$(cat)" else DATA='{}' fi python3 - <<'PY' "$DATA" import json, sys json.loads(sys.argv[1]) PY if [[ "$PATH_OR_URL" =~ ^https?:// ]]; then URL="$PATH_OR_URL" else PREFIX="webhook" [[ "$MODE" == "test" ]] && PREFIX="webhook-test" URL="${BASE_URL%/}/${PREFIX}/${PATH_OR_URL#/}" fi if [[ "$DRY_RUN" -eq 1 ]]; then printf 'POST %s\n' "$URL" printf 'Header: Content-Type: application/json\n' if [[ -n "$SECRET_VALUE" ]]; then printf 'Header: %s: [set]\n' "$SECRET_HEADER" else printf 'Header: %s: [missing]\n' "$SECRET_HEADER" fi printf 'Body: %s\n' "$DATA" exit 0 fi if [[ -z "$SECRET_VALUE" ]]; then echo "N8N_WEBHOOK_SECRET is required for non-dry-run calls." >&2 exit 2 fi BODY_FILE="$(mktemp)" trap 'rm -f "$BODY_FILE"' EXIT HTTP_CODE="$({ curl -sS \ -o "$BODY_FILE" \ -w '%{http_code}' \ -X POST "$URL" \ -H 'Content-Type: application/json' \ -H "$SECRET_HEADER: $SECRET_VALUE" \ --data "$DATA" } || true)" if [[ -z "$HTTP_CODE" ]]; then echo "Request failed before receiving an HTTP status." >&2 cat "$BODY_FILE" >&2 || true exit 1 fi if [[ "$HTTP_CODE" =~ ^2[0-9][0-9]$ ]]; then if [[ "$PRETTY" -eq 1 ]]; then python3 -m json.tool "$BODY_FILE" 2>/dev/null || cat "$BODY_FILE" else cat "$BODY_FILE" fi exit 0 fi echo "n8n webhook call failed with HTTP $HTTP_CODE" >&2 cat "$BODY_FILE" >&2 || true exit 1