Skip to main content

What are Webhooks?

Webhooks are HTTP endpoints that allow external services to send events to your agent. When something happens in an external system (like a payment completing or an order shipping), that system can notify your agent in real-time.

Think of it as:

A phone number for your agent - external services can “call” it when events happen
No Conversational Context: Webhooks execute outside of user conversations. You MUST provide a userId when calling User.get(userId) to notify specific users. Store user IDs in your payment/order metadata.
Built-in webhook support for seamless external integrations.

Why Webhooks?

Real-Time Events

Get notified instantly when events happen in external systems

Automated Actions

Automatically respond to external events without user interaction

Seamless Integration

Connect with Stripe, Shopify, GitHub, and any webhook-enabled service

Event-Driven

Build reactive agents that respond to real-world events

How Webhooks Work

1

External Event Occurs

Something happens: payment completes, order ships, PR merges, etc.
2

Service Sends HTTP Request

The external service (Stripe, Shopify) sends a POST request to your webhook URL
3

Your Webhook Receives Event

Your LuaWebhook’s execute function is called with the event data
4

Your Code Takes Action

Process the event: update orders, notify users, trigger jobs, etc.
5

Return Response

Return acknowledgment to the external service

Simple Example

Critical: Webhooks execute outside conversational context. User.get() REQUIRES a userId parameter. Always store the user ID in your payment/order metadata when creating transactions.

Common Use Cases

Stripe, PayPal, SquareHandle payment events:
  • Payment succeeded
  • Payment failed
  • Refund processed
  • Subscription updated
Actions:
  • Update order status
  • Notify customer
  • Trigger fulfillment

Event Subscriptions

Webhooks can also subscribe to platform events — real-time notifications from your messaging channels. When you send a template message via WhatsApp, for example, you can track whether it was delivered, read, or failed.
Webhook event subscriptions for message delivery tracking. Currently supports WhatsApp status events; additional channels may be added in the future.

Available Event Types

How It Works

1

Create a webhook

Define a LuaWebhook that handles delivery status events
2

Subscribe to events

Use the CLI to subscribe your webhook to the event types you care about
3

Receive events

Your webhook’s execute function is called with the event payload whenever a status update arrives

Delivery Semantics

Subscribed platform events are delivered at least once. Each event is durably queued and processed on isolated infrastructure, so a subscribed handler may run more than once for a single logical event:
  • Delivery is retried automatically — up to 3 attempts total, with a fixed 60-second wait between attempts, and only when an attempt fails.
  • Because retries (and occasional redeliveries) can repeat an event, your handler’s side effects should be idempotent — safe to run twice.
Each invocation receives execution metadata on event.execution:
event.execution is only present for durably-delivered platform events. It is undefined during local lua test / lua dev runs, which execute in-process. Very large event payloads (over 50,000 characters, which is rare) are delivered without the at-least-once retry guarantee.
Direct requests are unchanged. Requests sent straight to your webhook URL (Stripe, Shopify, GitHub, etc.) are still handled synchronously with no automatic retries — the caller receives your handler’s return value and applies its own retry policy. At-least-once delivery and event.execution apply only to subscribed platform events.

Example: Delivery Tracking Webhook

Managing Subscriptions via CLI

Adding Webhooks to Your Agent

Webhooks are added to your LuaAgent configuration:

Webhook URLs

After deploying, you can call webhooks by ID or by name:
Notes:
  • agentId is your agent identifier (e.g., agent_abc123)
  • webhookId is the UUID shown when the webhook is created
  • webhook-name is the friendly name from your code
  • lua push webhook prints both links
Configure either URL in your external service’s webhook settings.

Verify Requests

A webhook URL is public: anyone who knows or guesses it can invoke your execute function. Add a secret to the webhook definition and Lua will only run it for requests that prove they know that secret.
With a secret set, every request must carry:
Requests with a missing, malformed, or incorrect signature get 401 and your code never runs. Webhooks with no secret behave exactly as before — this is opt-in.
The signature is computed over the exact bytes of the request body, not over a re-serialized copy. A sender that pretty-prints or reorders JSON after signing will fail verification.
Signing covers JSON request bodies. A sender that posts application/x-www-form-urlencoded or text/plain will always be rejected, so leave secret unset for those and verify inside your execute function instead.

Computing the signature

The sender computes the HMAC. In a shell:
In Node.js:

Rotating the secret

Change the value in your webhook definition and re-deploy — lua push webhook applies the current secret to the webhook on every push:
Update the sender at the same time; requests signed with the old secret start failing as soon as the push lands. To turn verification off again, set secret: '' and push.
The secret must be a literal string or a constant the CLI can read at build time — a value like process.env.WEBHOOK_SECRET cannot be resolved during compilation and fails the build rather than silently deploying an unprotected webhook. Treat the file holding it as a credential.
Don’t put a signing secret on a webhook you publish as a marketplace template. The secret is part of the compiled webhook code, and publishing a template freezes that code for every installer — they would all receive your key. Publish the webhook without a secret and let each installer set their own.
Vendors that sign with their own scheme (Stripe’s stripe-signature, GitHub’s x-hub-signature-256) won’t produce an x-lua-signature header. Leave secret unset for those and verify the vendor’s header inside your execute function.

Best Practices

Always include user ID in payment/order metadataWhen creating payments or orders, store the Lua user ID:
Then in your webhook, retrieve the specific user:
Don’t throw errors - return error status
Webhooks should respond within 5 secondsFor long-running work, queue a job:

Next Steps

Webhooks API Reference

Complete API documentation with examples

Agent Concept

Learn about LuaAgent configuration

Jobs Concept

Understand scheduled tasks

Skills Concept

Learn about skills and tools