#I'm trying to have my agent respond to a google script webhook but I just can't get it to work.

1 messages · Page 1 of 1 (latest)

vapid kettle
#

Please help.

silent nimbusBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

cinder hareBOT
#

Happy to help — webhook setup has a few sharp edges. First: what does openclaw --version show?

For a basic Google Apps Script → OpenClaw trigger, the current docs pattern is:

{
  hooks: {
    enabled: true,
    token: "use-a-dedicated-secret-not-gateway-token",
    path: "/hooks",
    mappings: [
      {
        match: { path: "google-script" },
        action: "agent",
        agentId: "main",
        messageTemplate: "Google Script event:
#
{{text}}",
        deliver: true,
        channel: "discord",
        to: "channel:1502954900626342018"
      }
    ]
  }
}

Then Apps Script should POST to:

UrlFetchApp.fetch("https://YOUR-GATEWAY.example.com/hooks/google-script", {
  method: "post",
#
  contentType: "application/json",
  headers: {
    Authorization: "Bearer YOUR_HOOK_TOKEN"
  },
  payload: JSON.stringify({ text: "hello from Apps Script" }),
  muteHttpExceptions: true
});

Common gotchas:

  • Token must be in a header: Authorization: Bearer ... or x-openclaw-token. Query-string tokens are rejected.
  • hooks.token must not be the same as gateway.auth.token.
  • hooks.path cannot be /; use something like /hooks.
  • If your Gateway is local, Google Apps Script can’t reach 127.0.0.1; you need a public HTTPS URL, Tailscale Funnel/Serve, or a reverse proxy.
  • If you expect the HTTP response itself to contain the agent’s final answer, that’s probably the mismatch — hooks trigger a run; use deliver: true to send the result somewhere like Discord.
#

Good, thanks — your /hooks/agent payload shape is basically right. Two things jump out:

  1. to for Discord should be channel:<id>, not just the raw channel id. For this thread/channel it should look like:
to: "channel:1502954900626342018"
  1. If nothing appears to hit OpenClaw, separate “network reaches Gateway” from “agent delivery works”. On the machine running OpenClaw, try this first:
curl -i -X POST http://127.0.0.1:18789/hooks/agent \
  -H 'Authorization: Bearer YOUR_HOOK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"AlbumReview","deliver":true,"channel":"discord","to":"channel:1502954900626342018","message":"test from local curl"}'
#

Expected response if hooks are wired:

{"ok":true,"runId":"..."}

Then test the public URL from anywhere outside that box:

curl -i -X POST https://YOUR_PUBLIC_HOST/hooks/agent \
  -H 'Authorization: Bearer YOUR_HOOK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"AlbumReview","deliver":true,"channel":"discord","to":"channel:1502954900626342018","message":"test from public curl"}'

If local works but public doesn’t, the bug is your tunnel/reverse proxy/firewall/DNS path, not Apps Script or the hooks config. Make sure the proxy forwards /hooks/* to the Gateway port, usually 127.0.0.1:18789.

#

Also: Apps Script should log a response even if OpenClaw never sees it. The most useful missing bit is the actual Response code: / Response body: from Apps Script. If UrlFetchApp.fetch throws instead of logging, wrap it:

try {
  const response = UrlFetchApp.fetch(WEBHOOK_URL, options);
  Logger.log(response.getResponseCode());
  Logger.log(response.getContentText());
} catch (e) {
  Logger.log("Fetch failed: " + e);
}

Paste the local curl result + public curl result and we’ll know exactly which layer is broken. 🦞

vapid kettle
#

Remote test returns ```HTTP/2 200
server: nginx
date: Sun, 10 May 2026 09:23:49 GMT
content-type: application/json; charset=utf-8
x-content-type-options: nosniff
referrer-policy: no-referrer
permissions-policy: camera=(), microphone=(self), geolocation=()

{"ok":true,"runId":"7608cf80-9b55-4be0-ac39-d397eb6b45ea"}```

Local test returns ```HTTP/1.1 200 OK
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
Permissions-Policy: camera=(), microphone=(self), geolocation=()
Content-Type: application/json; charset=utf-8
Date: Sun, 10 May 2026 09:21:35 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 58

{"ok":true,"runId":"3dbcaeee-1b6d-40c7-94a4-7e5baf5e407c"}```

Hmmm... the appscript logs returns 9:25:54 Error TypeError: Cannot read properties of undefined (reading '0')

cinder hareBOT
#

Nice, that narrows it down a lot: OpenClaw is reachable and accepting the hook. The Apps Script is failing before it sends anything.

This line is the culprit:

const artist = data[0];

data is undefined, so sendToDiscord(data) is being called without an array/row.

Add a quick guard first:

function sendToDiscord(data) {
  if (!Array.isArray(data)) {
    throw new Error("sendToDiscord expected a row array, got: " + JSON.stringify(data));
#
  }

  // existing code...
}

Then make sure your trigger passes a row. For example, if this is from an onEdit(e) spreadsheet trigger:

function onEdit(e) {
  const sheet = e.range.getSheet();
  const row = e.range.getRow();

  // optional: skip header row
  if (row === 1) return;
#

  const data = sheet.getRange(row, 1, 1, 11).getValues()[0];
  sendToDiscord(data);
}

And also update your Discord target to include the prefix:

to: "channel:1502954900626342018"

So the webhook/network side is good 🦞 — now it’s just Apps Script plumbing: pass the sheet row into sendToDiscord() instead of calling it empty.

vapid kettle
#

Hmmm... I think I know where the appscript is going wrong. I'll get back to you.