Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt

Use this file to discover all available pages before exploring further.

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.
New in v3.0.0: 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

import { LuaWebhook, User } from 'lua-cli';

const paymentWebhook = new LuaWebhook({
  name: 'payment-webhook',
  description: 'Handle Stripe payment events',
  
  execute: async (event) => {
    const { body } = event;
    if (body?.type === 'payment_intent.succeeded') {
      // ⚠️ Webhooks have NO conversational context
      // You MUST provide userId to User.get()
      
      // Get user ID from payment metadata
      const customerId = body.data?.object?.metadata?.customerId;
      
      if (!customerId) {
        console.error('No customerId in payment metadata');
        return { received: true, error: 'No customer ID' };
      }
      
      // Retrieve specific user by ID
      const user = await User.get(customerId);
      
      // Send payment confirmation to that user
      await user.send([{
        type: 'text',
        text: `✅ Payment confirmed! Amount: ${body.data.object.amount} ${body.data.object.currency.toUpperCase()}`
      }]);
    }
    
    return { received: true };
  }
});
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.
New in v3.6.0: Webhook event subscriptions for message delivery tracking. Currently supports WhatsApp status events; additional channels may be added in the future.

Available Event Types

EventDescriptionChannel
message.sentMessage was sent to the recipientWhatsApp
message.deliveredMessage was delivered to the recipient’s deviceWhatsApp
message.readRecipient read the messageWhatsApp
message.failedMessage failed to sendWhatsApp
message.playedRecipient played a voice/video messageWhatsApp

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
lua webhooks subscribe --webhook-name delivery-tracker --event message.delivered
lua webhooks subscribe --webhook-name delivery-tracker --event message.read
lua webhooks subscribe --webhook-name delivery-tracker --event message.failed
3

Receive events

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

Example: Delivery Tracking Webhook

import { LuaWebhook, Data } from 'lua-cli';

const deliveryTracker = new LuaWebhook({
  name: 'delivery-tracker',
  description: 'Track WhatsApp message delivery status',
  
  execute: async (event) => {
    const { body } = event;
    const { eventType, recipientId, status, messageWamid } = body;
    
    await Data.create('delivery-events', {
      messageId: messageWamid,
      recipient: recipientId,
      status: status,
      eventType: eventType,
      trackedAt: new Date().toISOString()
    }, `${status} - ${recipientId}`);
    
    if (status === 'failed') {
      console.error(`Message ${messageWamid} failed for ${recipientId}`);
    }
    
    return { received: true };
  }
});

export default deliveryTracker;

Managing Subscriptions via CLI

# List all available event types
lua webhooks list-events

# Subscribe a webhook to an event
lua webhooks subscribe --webhook-name delivery-tracker --event message.delivered

# Unsubscribe a webhook from an event
lua webhooks unsubscribe --webhook-name delivery-tracker --event message.delivered

Adding Webhooks to Your Agent

Webhooks are added to your LuaAgent configuration:
import { LuaAgent } from 'lua-cli';
import paymentWebhook from './webhooks/payment';
import orderWebhook from './webhooks/order';

export const agent = new LuaAgent({
  name: "my-agent",
  persona: "...",
  skills: [...],
  
  // Add webhooks
  webhooks: [
    paymentWebhook,
    orderWebhook
  ]
});

Webhook URLs

After deploying, you can call webhooks by ID or by name:
https://webhook.heylua.ai/{agentId}/{webhookId}
https://webhook.heylua.ai/{agentId}/{webhook-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.

Best Practices

Always include user ID in payment/order metadataWhen creating payments or orders, store the Lua user ID:
// When creating Stripe payment
const paymentIntent = await stripe.paymentIntents.create({
  amount: 2000,
  currency: 'usd',
  metadata: {
    customerId: user.id,  // ← Lua user ID
    orderId: order.id
  }
});
Then in your webhook, retrieve the specific user:
execute: async (event) => {
  const { body } = event;
  const customerId = body.data.object.metadata.customerId;
  const user = await User.get(customerId);
  await user.send([{ type: 'text', text: 'Payment confirmed!' }]);
}
Don’t throw errors - return error status
execute: async (event) => {
  const { body } = event;
  try {
    // Process webhook
    return { success: true };
  } catch (error) {
    console.error('Webhook error:', error);
    return { success: false, error: error.message };
  }
}
Webhooks should respond within 5 secondsFor long-running work, queue a job:
execute: async (event) => {
  const { body } = event;
  // Quick validation
  if (!isValid(body)) return { error: 'Invalid' };
  
  // Queue long-running work
  await Jobs.create({
    execute: async () => {
      // Process here
    }
  });
  
  return { received: true };
}

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