# How to Configure Webhooks for Prompt Lifecycle Events in prompts.chat

> Configure prompts.chat webhooks for prompt lifecycle events like created, updated, and deleted. Get instant HTTP notifications with configurable templates and SSRF protection.

- Repository: [Fatih Kadir Akın/prompts.chat](https://github.com/f/prompts.chat)
- Tags: how-to-guide
- Published: 2026-04-02

---

**prompts.chat includes a built-in webhook system that sends asynchronous HTTP notifications to external endpoints whenever prompts are created, updated, or deleted, featuring configurable payload templates, placeholder interpolation, and SSRF protection.**

The **prompts.chat** repository provides a complete webhook infrastructure for monitoring prompt lifecycle changes. By configuring webhooks through the admin interface or REST API, you can integrate external services like Slack, Discord, or custom CI pipelines to react immediately when content changes. This guide explains the architecture, configuration options, and security mechanisms implemented in the source code.

## Webhook Architecture Overview

The system consists of three distinct layers working together to deliver reliable event notifications.

The **Admin API** layer, located in [`src/app/api/admin/webhooks/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/api/admin/webhooks/route.ts), handles CRUD operations for webhook configurations, input validation via `validateWebhook` (lines 20-76), and SSRF protection through the `isPrivateUrl` check. This layer ensures only valid, publicly reachable endpoints can be registered.

The **Webhook Engine** in [`src/lib/webhook.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/webhook.ts) manages the core execution logic. It queries the Prisma `webhookConfig` table for active webhooks matching specific events, interpolates placeholder tokens using `replacePlaceholders` (lines 23-56), and dispatches HTTP requests asynchronously in fire-and-forget mode.

The **React UI** layer in [`src/components/admin/webhooks-table.tsx`](https://github.com/f/prompts.chat/blob/main/src/components/admin/webhooks-table.tsx) provides the administrative interface for creating, editing, and testing webhooks, including a visual placeholder picker that reads from the `WEBHOOK_PLACEHOLDERS` definition.

## Configuring Webhook Endpoints

To create a webhook configuration, send a POST request to the admin endpoint or use the web interface. The system validates the payload structure and rejects private network addresses to prevent SSRF attacks.

The configuration object stored in the database via `db.webhookConfig.create` includes the following fields:

- **name**: Descriptive identifier for the webhook
- **url**: Target endpoint (must pass `isPrivateUrl` validation)
- **method**: HTTP verb (`GET`, `POST`, `PUT`, or `PATCH`)
- **headers**: Optional JSON object for custom request headers
- **payload**: Template string with placeholders for dynamic data
- **events**: Array specifying which lifecycle events trigger the webhook
- **isEnabled**: Boolean toggle to activate or deactivate the webhook

When a prompt changes, the prompt API routes call `triggerWebhooks(event, data)` with the appropriate event type and prompt data. For example, in [`src/app/api/prompts/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/api/prompts/route.ts) (lines 41-55), the creation handler executes:

```typescript
triggerWebhooks("PROMPT_CREATED", { /* PromptData */ });

```

Similar calls exist for update and delete operations in their respective route handlers.

## Supported Lifecycle Events

The webhook system supports three primary event types that correspond to CRUD operations on prompts:

- **PROMPT_CREATED**: Fires immediately after a new prompt is persisted to the database
- **PROMPT_UPDATED**: Fires when an existing prompt's metadata or content is modified
- **PROMPT_DELETED**: Fires when a prompt is removed from the system

You can configure a single webhook to listen to multiple events or create separate webhooks for different notification channels.

## Payload Templates and Placeholders

The payload template system uses string interpolation to inject runtime prompt data. Placeholders defined in `WEBHOOK_PLACEHOLDERS` (lines 24-41 of [`src/lib/webhook.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/webhook.ts)) include:

| Placeholder | Value Injected |
|-------------|----------------|
| `{{PROMPT_ID}}` | UUID of the prompt |
| `{{PROMPT_TITLE}}` | Escaped title string |
| `{{PROMPT_DESCRIPTION}}` | Description text or "No description" |
| `{{PROMPT_CONTENT}}` | Body text truncated to 2000 characters |
| `{{PROMPT_TYPE}}` | Prompt category enum value |
| `{{PROMPT_URL}}` | Public URL (`https://prompts.chat/prompts/<id>`) |
| `{{AUTHOR_USERNAME}}` | Creator's handle |
| `{{AUTHOR_NAME}}` | Display name or username fallback |
| `{{AUTHOR_AVATAR}}` | Avatar URL or default image |
| `{{CATEGORY_NAME}}` | Assigned category or "Uncategorized" |
| `{{TAGS}}` | Comma-separated tag list |
| `{{TIMESTAMP}}` | Human-readable creation time |
| `{{SITE_URL}}` | Base application URL from `NEXT_PUBLIC_APP_URL` |
| `{{CHATGPT_URL}}` | Direct link to execute in ChatGPT |

The admin UI automatically populates the placeholder picker by reading `Object.values(WEBHOOK_PLACEHOLDERS)` from the configuration.

## Security and Validation

The system implements multiple safeguards to prevent abuse and ensure reliable delivery.

**SSRF Protection**: The `isPrivateUrl` function validates that target URLs are not loopback addresses, private IP ranges, or internal network hosts. This check runs both during configuration creation and immediately before dispatch to prevent DNS rebinding attacks.

**Asynchronous Execution**: Webhooks fire in fire-and-forget mode using `fetch`. Delivery failures are logged to the console but do not block the user-facing prompt operations or return errors to the end user.

**Input Sanitization**: The `validateWebhook` function in the admin route (lines 20-76) enforces schema validation on incoming configuration requests, ensuring URLs are well-formed and event arrays contain only valid enum values.

## Testing Your Configuration

Before relying on webhooks in production, you can verify connectivity and payload formatting using the built-in test endpoint.

Send a POST request to `/api/admin/webhooks/[id]/test`, where `[id]` is the webhook configuration UUID. This endpoint, defined in `src/app/api/admin/webhooks/[id]/test/route.ts` (lines 28-53), constructs a synthetic payload using the same `replacePlaceholders` logic and transmits a single request to your endpoint.

The test returns a JSON response indicating success or detailed error information, allowing you to debug template syntax or connectivity issues without modifying live prompt data.

## Implementation Examples

### Creating a Slack Notification Webhook

```typescript
await fetch("/api/admin/webhooks", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    name: "Slack Prompt Alerts",
    url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
    method: "POST",
    headers: { "Content-Type": "application/json" },
    payload: JSON.stringify({
      text: "New prompt created: {{PROMPT_TITLE}}",
      blocks: [
        { 
          type: "section", 
          text: { 
            type: "mrkdwn", 
            text: "Author: {{AUTHOR_NAME}}\nContent: {{PROMPT_CONTENT}}" 
          } 
        }
      ]
    }),
    events: ["PROMPT_CREATED", "PROMPT_UPDATED"],
    isEnabled: true
  })
});

```

### Triggering a Manual Test

```typescript
const testWebhook = async (webhookId: string) => {
  const response = await fetch(`/api/admin/webhooks/${webhookId}/test`, {
    method: "POST"
  });
  return response.json(); // Returns { success: boolean, error?: string }
};

```

### Custom Placeholder Extension

To add custom tokens, modify [`src/lib/webhook.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/webhook.ts):

```typescript
// Add to WEBHOOK_PLACEHOLDERS (lines 24-41)
export const WEBHOOK_PLACEHOLDERS = {
  // ... existing placeholders
  "{{CUSTOM_FIELD}}": "customField"
};

// Update replacePlaceholders (lines 23-56) to handle the new mapping
const valueMap: Record<string, string> = {
  "{{PROMPT_ID}}": data.id,
  "{{CUSTOM_FIELD}}": data.customField,
  // ... other mappings
};

```

## Summary

- **prompts.chat** provides a three-layer webhook architecture separating admin configuration, engine execution, and UI management.
- Webhooks support three lifecycle events: **PROMPT_CREATED**, **PROMPT_UPDATED**, and **PROMPT_DELETED**.
- Configuration requires admin privileges and passes through `validateWebhook` and `isPrivateUrl` checks to prevent SSRF.
- Payload templates support 13+ placeholders including prompt metadata, author details, and direct ChatGPT links.
- The system executes webhooks asynchronously without blocking user operations, logging errors to the server console.
- Use the `/api/admin/webhooks/[id]/test` endpoint to verify configurations before production deployment.

## Frequently Asked Questions

### How do I prevent webhooks from firing on private network addresses?

The system automatically blocks private URLs through the `isPrivateUrl` validation function, which checks both during configuration creation in [`src/app/api/admin/webhooks/route.ts`](https://github.com/f/prompts.chat/blob/main/src/app/api/admin/webhooks/route.ts) and immediately before dispatch in [`src/lib/webhook.ts`](https://github.com/f/prompts.chat/blob/main/src/lib/webhook.ts). This prevents Server-Side Request Forgery (SSRF) attacks against internal services like `localhost`, `10.0.0.0/8`, or `192.168.0.0/16`.

### Can I modify the webhook payload structure for different services?

Yes, the **payload** field accepts any valid JSON string with embedded placeholders. The system uses `replacePlaceholders` (lines 23-56) to substitute tokens like `{{PROMPT_TITLE}}` before transmission. You can create custom templates for Slack, Discord, Microsoft Teams, or proprietary APIs by adjusting the JSON structure and headers accordingly.

### What happens if a webhook endpoint is down or returns an error?

Webhook delivery failures are caught and logged to the server console via `console.error`, but they never block the prompt creation, update, or deletion flow. The system operates in fire-and-forget mode, meaning it does not implement retry logic or dead-letter queues. For critical integrations, implement idempotent receiving endpoints and handle retries on the consumer side.

### Which database table stores the webhook configurations?

Configurations persist in the **webhookConfig** table managed by Prisma ORM. The schema stores `name`, `url`, `method`, `headers`, `payload` template, `events` array, and the `isEnabled` boolean flag. You can query this directly for administrative audits or backup purposes, though all CRUD operations should route through the `/api/admin/webhooks` endpoints to maintain validation and security checks.