> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leaf7.fun/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Send Discord webhooks from your scripts without exposing webhook URLs to clients.

# L7\_SEND\_WEBHOOK

The `L7_SEND_WEBHOOK` macro lets you fire Discord webhooks directly from your Lua scripts — without ever exposing your webhook URL to the client. The URL and payload template are stored securely on the server; only the evaluated variables are sent by the client.

<Warning>
  Webhook URLs **never** leave the server. The client only sends variable values — the server assembles and fires the real webhook.
</Warning>

## How it works

```mermaid theme={null}
sequenceDiagram
    participant Script as Your Lua Script
    participant Client as Roblox Client
    participant Bodyguard as Leaf7 Bodyguard (Relay)
    participant Server as Leaf7 Main Server
    participant Discord as Discord Webhook

    Script->>Client: L7_SEND_WEBHOOK("url", template)
    Client->>Bodyguard: POST /api/v1/webhook-relay {slot, sid, vars}
    Bodyguard->>Server: Proxy request
    Server->>Server: Validate project/key, load encrypted template
    Server->>Server: Substitute variables, apply sanitization
    Server->>Discord: POST webhook with assembled payload
    Discord-->>Server: 200 OK
    Server-->>Bodyguard: 200 OK
    Bodyguard-->>Client: {ok: true, delivered: true}
```

## Defining a webhook in your source

The macro takes exactly **two arguments**:

1. The **webhook URL** (must be a string literal).
2. The **Discord JSON payload** (as a Lua table).

You can use standard Lua string concatenation (`..`) or direct variable references inside the table. Leaf7's preprocessor automatically detects these variables, stores the template with placeholders on the server, and sets up the client to only send the variable values.

```lua theme={null}
local player = game.Players.LocalPlayer
local kills = 15

L7_SEND_WEBHOOK(
    "https://discord.com/api/webhooks/1234567890/abcdefg",
    {
        content = "Webhook triggered!",
        embeds = {
            {
                title = "Player Activity",
                description = "Player " .. player.Name .. " just loaded the script.",
                color = 0x22C55E,
                fields = {
                    { name = "Kills", value = tostring(kills), inline = true },
                    { name = "JobId", value = game.JobId, inline = false }
                }
            }
        }
    }
)
```

<Info>
  Behind the scenes, Leaf7 strips the URL and the table structure out of your script entirely. It replaces the block with a secure stub that only transmits `{ "player.Name": "...", "kills": "15", "game.JobId": "..." }` to the server.
</Info>

## Server-Side Variables

You can inject trusted data directly on the server by using `%VARIABLE%` strings in your template. The client cannot spoof these values:

| Variable       | Description                            |
| -------------- | -------------------------------------- |
| `%CLIENT_IP%`  | The client's IP address                |
| `%USER_KEY%`   | The script key used for authentication |
| `%DISCORD_ID%` | The Discord ID linked to the key       |
| `%USER_NOTE%`  | The note set on the buyer key          |

**Example:**

```lua theme={null}
L7_SEND_WEBHOOK(
    "https://discord.com/api/webhooks/...",
    {
        content = "Script executed by Discord ID: %DISCORD_ID%"
    }
)
```

## Input Sanitization (`L7_SANITIZE`)

To prevent users from injecting malicious content (like `@everyone` pings or malicious links) into your webhooks via variable manipulation, use the inline `L7_SANITIZE(expression, "regex")` macro.

If a variable fails to match the provided regex, the server blocks the webhook entirely.

```lua theme={null}
L7_SEND_WEBHOOK(
    "https://discord.com/api/webhooks/...",
    {
        embeds = {
            {
                title = "Item Dropped",
                -- Only allow alphanumeric characters and underscores in the player name
                description = "Dropped by: " .. L7_SANITIZE(player.Name, "^[%w_]+$"),
                
                -- Only allow digits for the score
                footer = { text = "Score: " .. L7_SANITIZE(tostring(score), "^%d+$") }
            }
        }
    }
)
```

## Multiple webhooks

Each project supports up to **6 webhook slots** (0-5). You can just call `L7_SEND_WEBHOOK` multiple times in your script; Leaf7 automatically assigns a slot ID to each call.

```lua theme={null}
-- Slot 0: Execution notification
L7_SEND_WEBHOOK("https://discord.com/api/webhooks/channel1/...", {
    embeds = {{ title = "Script Loaded", color = 0x22C55E }}
})

-- Slot 1: Kill feed
L7_SEND_WEBHOOK("https://discord.com/api/webhooks/channel2/...", {
    embeds = {{ title = "Player Killed: " .. target.Name, color = 0xFF0000 }}
})
```

## Rate limits

The webhook relay enforces a rate limit (typically **30 requests per minute per IP**). Exceeding this returns `429 Too Many Requests`.

<Tip>
  Debounce your webhook calls in Lua if they're triggered by frequent events (like kills or item pickups) to avoid hitting the rate limit.
</Tip>
