← Back to PRs

#12635: Gateway: add inbound webhook dispatch framework

by jhs129 open 2026-02-09 12:13 View on GitHub →
gateway stale
Hey! I've been running OpenClaw as my daily gateway and kept finding myself wanting external services to push events directly into the agent. So I built a generic webhook framework that plugs into the existing hooks/gateway infrastructure. Figured this would be useful for the wider community too. ## Summary - Adds a generic webhook HTTP endpoint at `/webhooks/{token}/{source}` on the gateway - Receives POST payloads from external services and dispatches them to the agent via the cron isolated-agent system - Uses constant-time token validation (reuses the existing hooks token) - Pluggable transform architecture — easy to add new services ### Architecture ``` POST /webhooks/{token}/{source} → token validation (constant-time) → source lookup in enabled presets → transform function converts raw payload → structured message → dispatch to agent via isolated cron job → system event enqueued for visibility ``` **Files:** - `src/gateway/webhooks-http.ts` — Core HTTP handler - `src/gateway/server/webhooks.ts` — Gateway integration (agent dispatch) - `src/gateway/webhook-transforms/index.ts` — Transform registry - `src/gateway/webhook-transforms/*.ts` — Per-service transforms - Config wiring: `server-http.ts`, `server-runtime-config.ts`, `server-runtime-state.ts`, `server-reload-handlers.ts`, `server.impl.ts` - Schema: `types.hooks.ts`, `zod-schema.ts` ### Built-in transforms (4 services, 50 tests) | Service | Events | Notes | |---------|--------|-------| | **Shopify** | Orders, customers, products, refunds | Infers event type from payload shape | | **QuickBooks** | Entity CRUD | Supports both legacy and CloudEvents (May 2026 migration) | | **Read.ai** | Meeting notes | Triggers on `meeting_end` only | | **OwnerRez** | Bookings, contacts, properties | Entity insert/update/delete with embedded entity parsing | ### Configuration ```yaml hooks: enabled: true token: "your-secret-token" webhooks: enabled: true presets: - shopify - quickbooks - readai - ownerrez ``` ## Test plan - [x] All 50 webhook tests pass (`pnpm vitest run src/gateway/webhook-transforms/ src/gateway/webhooks-http.test.ts`) - [x] Build passes (`pnpm build`) - [x] Type-check and lint pass (`pnpm check` — only pre-existing `.vercel/project.json` format issue) - [ ] Integration test: deploy and POST sample payloads to `/webhooks/{token}/{source}` 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR adds an inbound webhook framework to the gateway: a new `/webhooks/{token}/{source}` endpoint validates the existing hooks token, selects an enabled source preset, transforms the received JSON payload into a structured agent message (Shopify / QuickBooks / Read.ai / OwnerRez), and dispatches it to the agent via an isolated cron job. Runtime/config wiring is added so the endpoint can be enabled/disabled and hot-reloaded alongside existing hooks. The main correctness gap is around payload shape support: the HTTP handler currently coerces any non-object JSON body (including top-level arrays) into `{}`, while the QuickBooks transform includes logic to handle top-level array CloudEvents. As written, direct-array CloudEvents batches will be silently dropped and never reach the transform. <h3>Confidence Score: 4/5</h3> - Generally safe to merge once array-payload handling is clarified/fixed. - Core wiring and token validation are straightforward and covered by tests, but there is a concrete behavioral mismatch: the HTTP layer normalizes non-object JSON bodies to `{}`, making the QuickBooks CloudEvents top-level array path unreachable and causing real payloads to be ignored depending on sender format. - src/gateway/webhooks-http.ts, src/gateway/webhook-transforms/quickbooks.ts <!-- greptile_other_comments_section --> <sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub> **Context used:** - Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8)) - Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13)) <!-- /greptile_comment -->

Most Similar PRs