Home About Who We Are Team Services Startups Businesses Enterprise Case Studies Blog Guides Contact Connect with Us
Back to Guides
Software & Platforms 14 min read

OpenClaw Webhook Integration: Real-Time Event-Driven Automation

OpenClaw Webhook Integration: Real-Time Event-Driven Automation

Most OpenClaw setups start reactive: you type a command, the agent responds. Webhooks flip that relationship. Once configured, external systems push events directly into your agent, and it acts on them without you lifting a finger. A Stripe charge succeeds, a GitHub PR opens, a Shopify order ships, and your agent knows about it in under a second.

This guide walks through configuring OpenClaw’s webhook endpoints, wiring up real events from GitHub, Stripe, and Shopify, securing those endpoints against spoofed requests, and processing payloads so your agent takes the right action every time.

If you have not installed OpenClaw yet, start with our setup guide. For Docker-based deployments, see the Docker deployment guide.

How OpenClaw Webhooks Work

OpenClaw distinguishes between two event mechanisms, and confusing them causes most first-time setup failures.

Hooks are internal lifecycle events. They fire when the agent starts, resets, or receives a command. You configure them in settings.json and they run inside the gateway process. Think of hooks as the agent reacting to its own state changes.

Webhooks are external HTTP endpoints. They accept POST requests from outside systems like GitHub, Stripe, or any service that supports webhook callbacks. The gateway validates the request, extracts the relevant data, and feeds it to your agent as a new task.

The gateway exposes two primary webhook endpoints once you enable them:

  • /hooks/wake enqueues a system event for the agent’s main session. Use this for simple notifications where you want the agent to know something happened.
  • /hooks/agent runs an isolated agent turn with full control over model, session, and delivery options. Use this when the incoming event requires the agent to do real work and optionally report back to a channel like Slack or Telegram.

Enabling the Webhook Gateway

Add the webhook configuration to your settings.json:

{
  "hooks": {
    "enabled": true,
    "token": "${OPENCLAW_HOOKS_TOKEN}",
    "path": "/hooks"
  }
}

Set the token as an environment variable. Never hardcode it:

export OPENCLAW_HOOKS_TOKEN="your-secret-token-here"

Tighten the configuration for production deployments:

{
  "hooks": {
    "enabled": true,
    "token": "${OPENCLAW_HOOKS_TOKEN}",
    "path": "/hooks",
    "defaultSessionKey": "hook:ingress",
    "allowRequestSessionKey": false,
    "allowedSessionKeyPrefixes": ["hook:"],
    "allowedAgentIds": ["hooks", "main"]
  }
}

The allowedAgentIds array restricts which agents external callers can target. Without it, any webhook caller with your token could route work to any agent in your system. The allowRequestSessionKey: false setting prevents external callers from specifying arbitrary session keys, which could pollute your agent’s memory with unwanted context.

Restart your gateway after saving. Verify the endpoint is live:

curl -s -o /dev/null -w "%{http_code}" \
  -X POST http://127.0.0.1:18789/hooks/wake \
  -H "Authorization: Bearer $OPENCLAW_HOOKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text":"Health check","mode":"now"}'

A 200 response means your webhook endpoint is accepting requests.

Receiving Events from GitHub

GitHub webhooks notify your agent when code-related events happen: pushes, pull requests, issues, deployments. Here is how to wire a GitHub repository to your OpenClaw instance.

Step 1: Register the webhook in GitHub.

Go to your repository’s Settings, then Webhooks, then Add webhook. Set the Payload URL to your OpenClaw endpoint:

https://your-domain.com/hooks/agent

Set the Content type to application/json. Add your OPENCLAW_HOOKS_TOKEN as the Secret. Select the events you want: Pull requests, Issues, and Push events cover most use cases.

Step 2: Create a custom transform for GitHub payloads.

GitHub sends detailed payloads with nested objects. A transform extracts the relevant fields so your agent receives clean, actionable context instead of raw JSON.

Create hooks/transforms/github.js in your OpenClaw project:

module.exports = function transform(payload, headers) {
  const event = headers['x-github-event'];
  const action = payload.action || 'unknown';

  if (event === 'pull_request') {
    return {
      message: `GitHub PR ${action}: "${payload.pull_request.title}" by ${payload.pull_request.user.login} in ${payload.repository.full_name}. URL: ${payload.pull_request.html_url}`,
      agentId: "hooks"
    };
  }

  if (event === 'issues') {
    return {
      message: `GitHub issue ${action}: "${payload.issue.title}" by ${payload.issue.user.login}. Labels: ${(payload.issue.labels || []).map(l => l.name).join(', ')}. URL: ${payload.issue.html_url}`,
      agentId: "hooks"
    };
  }

  return {
    message: `GitHub event: ${event} (${action}) in ${payload.repository?.full_name || 'unknown repo'}`,
    agentId: "hooks"
  };
};

Step 3: Map the transform in your configuration.

{
  "hooks": {
    "transformsDir": "./hooks/transforms",
    "mappings": [
      {
        "name": "github",
        "transform": "github",
        "match": { "source": "github" }
      }
    ]
  }
}

Now when a developer opens a pull request, your agent receives a clean message like: GitHub PR opened: "Add retry logic to payment handler" by djveen in sfai/dashboard. The agent can then review the changes, post a summary to Slack, or trigger a CI check, depending on your skills configuration.

For deeper GitHub integration including issue tracking and code review workflows, see our GitHub-to-OpenClaw connection guide.

Receiving Events from Stripe

Stripe webhooks trigger on payment events: successful charges, failed payments, subscription changes, disputes. Connecting them to OpenClaw lets your agent handle payment-related workflows automatically.

Step 1: Register the endpoint in the Stripe Dashboard.

Navigate to Developers, then Webhooks, then Add endpoint. Enter your OpenClaw webhook URL:

https://your-domain.com/hooks/agent

Select events to listen for. Start with these high-value events:

  • payment_intent.succeeded
  • payment_intent.payment_failed
  • customer.subscription.deleted
  • charge.dispute.created

Stripe generates a webhook signing secret (whsec_...). Save it as an environment variable:

export STRIPE_WEBHOOK_SECRET="whsec_your_signing_secret"

Step 2: Create the Stripe transform.

Create hooks/transforms/stripe.js:

module.exports = function transform(payload, headers) {
  const event = payload.type;
  const data = payload.data?.object;

  if (event === 'payment_intent.succeeded') {
    const amount = (data.amount / 100).toFixed(2);
    const currency = data.currency.toUpperCase();
    return {
      message: `Stripe payment succeeded: ${currency} ${amount} from ${data.receipt_email || 'unknown customer'}. Payment ID: ${data.id}`,
      agentId: "hooks"
    };
  }

  if (event === 'payment_intent.payment_failed') {
    const amount = (data.amount / 100).toFixed(2);
    return {
      message: `Stripe payment FAILED: $${amount} from ${data.receipt_email || 'unknown'}. Error: ${data.last_payment_error?.message || 'unknown error'}. Requires attention.`,
      agentId: "hooks"
    };
  }

  if (event === 'charge.dispute.created') {
    const amount = (data.amount / 100).toFixed(2);
    return {
      message: `URGENT: Stripe dispute opened for $${amount}. Reason: ${data.reason}. Respond within ${data.evidence_details?.due_by ? new Date(data.evidence_details.due_by * 1000).toLocaleDateString() : 'unknown deadline'}. Dispute ID: ${data.id}`,
      agentId: "hooks",
      deliver: true,
      channel: "slack"
    };
  }

  return {
    message: `Stripe event: ${event}`,
    agentId: "hooks"
  };
};

Notice the dispute handler sets deliver: true and channel: "slack". Disputes have response deadlines, so the agent both processes the event and pushes an alert to your team’s Slack channel.

For the full Stripe integration including subscription management and invoice workflows, see our Stripe-to-OpenClaw guide.

Receiving Events from Shopify

Shopify webhooks fire on storefront events: new orders, fulfillments, inventory changes, customer signups. Connecting them lets your agent automate order processing and inventory alerts.

Register the webhook in Shopify Admin under Settings, then Notifications, then Webhooks. Add your endpoint URL and select the events you need. Shopify signs all webhook payloads with HMAC-SHA256 using your app’s shared secret.

Create hooks/transforms/shopify.js:

module.exports = function transform(payload, headers) {
  const topic = headers['x-shopify-topic'];

  if (topic === 'orders/create') {
    const items = payload.line_items?.map(i => `${i.quantity}x ${i.title}`).join(', ');
    return {
      message: `New Shopify order #${payload.order_number}: ${items}. Total: ${payload.total_price} ${payload.currency}. Customer: ${payload.customer?.email || 'guest checkout'}.`,
      agentId: "hooks"
    };
  }

  if (topic === 'orders/fulfilled') {
    return {
      message: `Shopify order #${payload.order_number} fulfilled. Tracking: ${payload.fulfillments?.[0]?.tracking_url || 'no tracking'}. Customer: ${payload.customer?.email}.`,
      agentId: "hooks"
    };
  }

  return {
    message: `Shopify event: ${topic}`,
    agentId: "hooks"
  };
};

A practical tip: start with orders/create and orders/fulfilled only. Teams that subscribe to every Shopify event on day one end up with an agent drowning in inventory update noise. Add events incrementally as you build skills to handle them.

For the complete Shopify integration including product sync and customer data workflows, see our Shopify-to-OpenClaw guide.

Webhook Security and Signature Verification

Accepting webhook requests without verification is the equivalent of leaving your API endpoint open to the internet with no authentication. Anyone who discovers your webhook URL can send fabricated events.

OpenClaw’s built-in authentication (the Bearer token) protects the gateway endpoint itself. But that only proves the caller knows your OpenClaw token. It does not prove the request genuinely came from GitHub, Stripe, or Shopify.

Provider-side signature verification closes that gap. Each provider signs its payloads using HMAC-SHA256 with a shared secret, and you verify that signature before processing.

Verifying GitHub signatures in your transform:

const crypto = require('crypto');

function verifyGitHubSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  const digest = 'sha256=' + hmac.update(JSON.stringify(payload)).digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(digest)
  );
}

module.exports = function transform(payload, headers) {
  const signature = headers['x-hub-signature-256'];
  if (!verifyGitHubSignature(payload, signature, process.env.GITHUB_WEBHOOK_SECRET)) {
    throw new Error('Invalid GitHub signature');
  }
  // ... rest of transform
};

Key security rules:

  1. Always use constant-time comparison. The crypto.timingSafeEqual function prevents timing attacks where an attacker measures response times to guess the signature byte by byte. Standard === comparison short-circuits on the first mismatch, leaking information about the correct signature.

  2. Verify against raw bytes, not parsed JSON. If your framework parses JSON before your verification code runs, the re-serialized output may have different whitespace or key ordering. The signature was computed against the original bytes. Most webhook verification failures trace back to this single mistake.

  3. Add timestamp validation for replay prevention. Stripe includes a timestamp in its signature header. Reject events older than 5 minutes to prevent replay attacks where an attacker captures a legitimate webhook and resends it later.

  4. Rotate secrets periodically. Keep your webhook signing secrets in environment variables and rotate them at least quarterly. When rotating, accept both old and new secrets during a transition window.

Processing Payloads and Acting on Events

Receiving a webhook is half the job. The other half is making your agent do something useful with the data. Here is how the full processing loop works.

The wake endpoint is for fire-and-forget notifications. The agent sees the event next time it processes its queue. Use this for low-priority alerts:

curl -X POST http://127.0.0.1:18789/hooks/wake \
  -H "Authorization: Bearer $OPENCLAW_HOOKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text":"Daily backup completed successfully","mode":"next-heartbeat"}'

The agent endpoint is for events requiring immediate action. The agent processes the message, runs relevant skills, and optionally delivers results to a messaging channel:

curl -X POST http://127.0.0.1:18789/hooks/agent \
  -H "Authorization: Bearer $OPENCLAW_HOOKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "New high-priority support ticket from enterprise customer. Subject: API rate limiting in production. Please draft a response.",
    "agentId": "hooks",
    "deliver": true,
    "channel": "slack",
    "model": "openai/gpt-5.4",
    "timeoutSeconds": 120
  }'

The model field lets you pick the right model for the task. Quick notifications can use a smaller model. Complex analysis tasks benefit from a more capable one.

Handling duplicates is critical for production reliability. Webhook providers occasionally send the same event twice. Your agent should check if it has already processed an event before acting on it. Include the event ID (Stripe’s evt_..., GitHub’s delivery GUID) in your transform output, and build idempotency checks into your processing skills.

Error Handling and Troubleshooting

OpenClaw returns specific HTTP status codes that tell you exactly what went wrong:

CodeMeaningFix
200SuccessNothing to fix
400Invalid payload or query-string token attemptCheck JSON formatting. Move token from URL query to Authorization header
401Authentication failureVerify your token matches the one in settings.json
413Payload too largeReduce payload size or increase gateway limits
429Rate limited (after repeated auth failures)Fix your authentication. The gateway rate-limits after multiple failed attempts

Common failure patterns:

  • Webhook registered but agent never processes events: Check that hooks.enabled is true and the gateway was restarted after configuration changes. The gateway reads settings at startup, not dynamically.
  • GitHub sends events but gets 401 responses: The GitHub webhook secret and your OPENCLAW_HOOKS_TOKEN are different values. GitHub uses its secret for signature verification. OpenClaw uses its token for endpoint authentication. Both are needed.
  • Shopify deletes your webhook subscription: Shopify removes webhooks after 19 consecutive delivery failures. If your endpoint is down for maintenance, re-register the webhook after recovery.

Frequently Asked Questions

How do I test OpenClaw webhooks during local development?

Use a tunneling tool like ngrok or the Hookdeck CLI to expose your local gateway to the internet. Run ngrok http 18789 to get a public URL, then register that URL with your webhook provider. For Stripe specifically, the Stripe CLI can forward live webhook events directly to your local server with stripe listen --forward-to localhost:18789/hooks/agent.

Can OpenClaw verify webhook signatures from providers like Stripe?

OpenClaw’s built-in authentication validates the gateway token, not provider signatures. For provider-specific signature verification (Stripe’s whsec_, GitHub’s HMAC), implement the check inside your custom transform function. The transform runs before the agent processes the payload, so rejected signatures never reach your agent.

What happens if my OpenClaw agent is offline when a webhook arrives?

The gateway must be running to accept webhooks. If it is down, the provider’s request fails and most providers (Stripe, GitHub, Shopify) retry with exponential backoff. Stripe retries for up to 3 days. GitHub retries for a shorter window. Ensure your gateway has high uptime in production or use a webhook proxy service to buffer events.

How do I route different webhook events to different agents?

Set the agentId field in your transform’s return object. Each transform can inspect the event type and route to the appropriate agent. Combine this with allowedAgentIds in your hooks configuration to restrict which agents are available for webhook routing.

Do I need a public URL for OpenClaw webhooks to work?

Yes, for external providers to reach your webhook endpoint, it must be accessible over the internet. In production, deploy behind a reverse proxy with HTTPS. For local development, use tunneling tools. For internal services on the same network, direct HTTP connections work without public exposure.

How do I prevent duplicate webhook processing?

Track processed event IDs. Each provider includes a unique identifier per event (Stripe uses evt_ prefixed IDs, GitHub includes an X-GitHub-Delivery header). Store processed IDs in a simple key-value store or database and skip events you have already handled.

Can I use OpenClaw webhooks with n8n or Make?

Yes. Both n8n and Make support outgoing webhook actions. Configure an HTTP Request node in n8n or an HTTP module in Make that sends POST requests to your /hooks/agent endpoint with the Bearer token in the Authorization header. This lets you trigger OpenClaw from any workflow automation platform.

What is the maximum payload size for OpenClaw webhooks?

The gateway rejects payloads exceeding its configured limit with a 413 status code. The default limit handles most webhook payloads from standard providers. If you receive 413 errors, check your gateway’s body size configuration. Oversized payloads (file uploads, large data dumps) should be handled by sending a reference URL in the webhook rather than the full data.

Key Takeaways

  • OpenClaw exposes /hooks/wake for simple notifications and /hooks/agent for events requiring agent processing and action
  • Always authenticate with Bearer tokens in the Authorization header, never in query strings
  • Write custom transforms in hooks/transforms/ to parse provider-specific payloads into clean agent messages
  • Verify provider signatures (HMAC-SHA256) inside your transforms using constant-time comparison to prevent spoofed events
  • Start with a small set of high-value events per provider and expand incrementally
  • Handle duplicates by tracking event IDs from each provider
  • For production, restrict allowedAgentIds and disable allowRequestSessionKey to limit the blast radius of a compromised token

Last Updated: Apr 23, 2026

SL

SFAI Labs

SFAI Labs helps companies build AI-powered products that work. We focus on practical solutions, not hype.

Get OpenClaw Running — Without the Headaches

  • End-to-end setup: hosting, integrations, and skills
  • Skip weeks of trial-and-error configuration
  • Ongoing support when you need it
Get OpenClaw Help →
From zero to production-ready in days, not weeks

Related articles