Webhooks

Get real-time notifications when malicious prompts are detected with LockLLM webhooks.

Link to section: What Are Webhooks?What Are Webhooks?

Webhooks let you receive real-time notifications when LockLLM detects malicious prompts. Instead of polling for results, LockLLM sends HTTP POST requests to your specified URL whenever a prompt injection is detected.

Use cases:

  • Security incident alerts
  • Real-time monitoring dashboards
  • Automated response systems
  • Logging and compliance
  • Integration with Slack, Discord, PagerDuty, etc.

Link to section: When Are Webhooks Triggered?When Are Webhooks Triggered?

Webhooks are triggered automatically when:

  • A malicious prompt is detected (safe: false)
  • In both direct API scans and proxy mode requests
  • After the scan completes successfully

Webhooks are not triggered for safe prompts to reduce noise.

Link to section: Setting Up WebhooksSetting Up Webhooks

Link to section: Step 1: Create a Webhook EndpointStep 1: Create a Webhook Endpoint

Create an HTTPS endpoint that accepts POST requests:

// Express.js example
app.post('/webhooks/lockllm', async (req, res) => {
  const { event, scan_result, input_preview, request_id } = req.body

  if (event === 'prompt_injection_detected') {
    console.log('Security Alert!')
    console.log('Confidence:', scan_result.confidence)
    console.log('Preview:', input_preview)
    console.log('Request ID:', request_id)

    // Handle the incident
    await notifySecurityTeam(req.body)
  }

  // Always return 200 OK
  res.status(200).send('OK')
})

Important: Your endpoint must:

  • Use HTTPS (HTTP not supported)
  • Be publicly accessible (no localhost or private IPs)
  • Return HTTP 200 status code
  • Respond within 1 second

Link to section: Step 2: Add Webhook in DashboardStep 2: Add Webhook in Dashboard

  1. Sign in to your LockLLM dashboard
  2. Navigate to Webhooks
  3. Click Add Webhook
  4. Enter your webhook URL (must be HTTPS)
  5. Select format: Raw JSON, Slack, or Discord
  6. Optionally add a secret for signature verification
  7. Optionally add a custom message
  8. Click Save

Link to section: Step 3: Test Your WebhookStep 3: Test Your Webhook

Test webhook delivery from the dashboard:

  1. Find your webhook in the list
  2. Click Test
  3. Check your endpoint receives the test payload
  4. Verify it returns 200 OK

Link to section: Webhook FormatsWebhook Formats

Link to section: Raw JSON FormatRaw JSON Format

Default format with complete scan data:

{
  "event": "prompt_injection_detected",
  "timestamp": 1705334400000,
  "request_id": "req_abc123",
  "user_id": "user_456",
  "scan_result": {
    "safe": false,
    "label": 1,
    "confidence": 0.95,
    "injection": 0.95,
    "sensitivity": "medium"
  },
  "input_preview": "Ignore all previous instructions and..."
}

Fields:

  • event: Event type (always "prompt_injection_detected" or "test" for test webhooks)
  • timestamp: Unix timestamp in milliseconds
  • request_id: Unique request identifier for tracking
  • user_id: Your LockLLM user ID
  • scan_result: Complete scan results
  • input_preview: First 200 characters of the detected prompt

Link to section: Slack FormatSlack Format

Formatted for Slack incoming webhooks:

{
  "text": "LockLLM flagged a prompt.",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*LockLLM flagged a prompt.*"
      }
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Status:*\nMalicious"
        },
        {
          "type": "mrkdwn",
          "text": "*Confidence:*\n95.00%"
        },
        {
          "type": "mrkdwn",
          "text": "*Injection Score:*\n0.9500"
        },
        {
          "type": "mrkdwn",
          "text": "*Request ID:*\n`req_abc123`"
        }
      ]
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Input Preview:*\n```Ignore all previous instructions and...```"
      }
    }
  ]
}

Note: The actual Slack delivery includes status indicators for visual clarity in your Slack workspace.

Setup for Slack:

  1. Create a Slack incoming webhook at https://api.slack.com/messaging/webhooks
  2. Copy the webhook URL
  3. Add it to LockLLM dashboard with Slack format
  4. Customize the message (optional)

Link to section: Discord FormatDiscord Format

Formatted for Discord webhooks:

{
  "content": "LockLLM flagged a prompt.",
  "embeds": [
    {
      "title": "Malicious Prompt Detected",
      "color": 15158332,
      "fields": [
        {
          "name": "Confidence",
          "value": "95.00%",
          "inline": true
        },
        {
          "name": "Injection Score",
          "value": "0.9500",
          "inline": true
        },
        {
          "name": "Request ID",
          "value": "`req_abc123`",
          "inline": false
        },
        {
          "name": "Input Preview",
          "value": "```Ignore all previous instructions and...```",
          "inline": false
        }
      ],
      "timestamp": "2024-01-15T12:00:00.000Z"
    }
  ]
}

Note: The embed color indicates severity (red for malicious, green for safe). The actual Discord delivery includes status indicators in the title.

Setup for Discord:

  1. Create a Discord webhook in your server settings
  2. Copy the webhook URL
  3. Add it to LockLLM dashboard with Discord format
  4. Customize the message (optional)

Link to section: Webhook SecurityWebhook Security

Link to section: HTTPS RequiredHTTPS Required

All webhook URLs must use HTTPS. HTTP is not supported for security reasons.

Link to section: Private URLs BlockedPrivate URLs Blocked

Webhooks to private or localhost URLs are blocked to prevent SSRF attacks:

  • localhost, 127.0.0.1
  • 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • 169.254.0.0/16 (link-local)

Use a publicly accessible HTTPS endpoint.

Link to section: Signature VerificationSignature Verification

Add a secret when creating your webhook to verify authenticity:

app.post('/webhooks/lockllm', (req, res) => {
  const signature = req.headers['x-lockllm-signature']
  const secret = process.env.WEBHOOK_SECRET

  // Verify signature matches your secret
  if (signature !== secret) {
    return res.status(401).send('Invalid signature')
  }

  // Process webhook
  const { scan_result } = req.body
  console.log('Verified webhook:', scan_result)

  res.status(200).send('OK')
})

When you configure a secret for your webhook, it is sent in the X-LockLLM-Signature header with each request. Compare this value to your stored secret to verify the webhook originated from LockLLM.

Link to section: Retry LogicRetry Logic

LockLLM automatically retries failed webhook deliveries:

  • Max retries: 3 attempts
  • Backoff: Exponential (100ms × 2^attempt)
  • Timeout: 1 second per attempt
  • Retry conditions:
    • Network errors
    • Timeouts
    • 5xx server errors
    • 429 rate limit errors

Not retried:

  • 4xx client errors (except 429)
  • Invalid URLs
  • HTTPS certificate errors

Link to section: Handling WebhooksHandling Webhooks

Link to section: IdempotencyIdempotency

Webhooks may be delivered more than once. Use the request_id to prevent duplicate processing:

const processedRequests = new Set()

app.post('/webhooks/lockllm', async (req, res) => {
  const { request_id } = req.body

  // Check if already processed
  if (processedRequests.has(request_id)) {
    return res.status(200).send('Already processed')
  }

  // Process webhook
  await handleSecurityIncident(req.body)

  // Mark as processed
  processedRequests.add(request_id)

  res.status(200).send('OK')
})

Link to section: Async ProcessingAsync Processing

Process webhooks asynchronously to respond quickly:

app.post('/webhooks/lockllm', async (req, res) => {
  // Respond immediately
  res.status(200).send('OK')

  // Process asynchronously
  setImmediate(async () => {
    try {
      await handleSecurityIncident(req.body)
    } catch (error) {
      console.error('Webhook processing failed:', error)
    }
  })
})

Link to section: Error HandlingError Handling

Always return 200 OK, even if processing fails:

app.post('/webhooks/lockllm', async (req, res) => {
  try {
    await processWebhook(req.body)
    res.status(200).send('OK')
  } catch (error) {
    console.error('Processing error:', error)
    // Still return 200 to prevent retries
    res.status(200).send('Error logged')
  }
})

Link to section: Managing WebhooksManaging Webhooks

Link to section: View All WebhooksView All Webhooks

See all configured webhooks in your dashboard:

  • Webhook URL
  • Format (Raw, Slack, Discord)
  • Enabled/disabled status
  • Created date

Link to section: Enable/Disable WebhooksEnable/Disable Webhooks

Temporarily disable webhooks without deleting:

  1. Go to Webhooks
  2. Find your webhook
  3. Toggle the enable/disable switch

Link to section: Update WebhooksUpdate Webhooks

Modify existing webhooks:

  1. Go to Webhooks
  2. Click Edit on your webhook
  3. Update URL, format, secret, or message
  4. Click Save

Link to section: Delete WebhooksDelete Webhooks

Permanently remove webhooks:

  1. Go to Webhooks
  2. Click Delete on your webhook
  3. Confirm deletion

Link to section: Example IntegrationsExample Integrations

Link to section: PagerDutyPagerDuty

Trigger PagerDuty incidents for high-confidence attacks:

app.post('/webhooks/lockllm', async (req, res) => {
  const { scan_result, input_preview, request_id } = req.body

  // High-confidence attacks trigger PagerDuty
  if (scan_result.confidence > 0.9) {
    await fetch('https://events.pagerduty.com/v2/enqueue', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Token token=${PAGERDUTY_TOKEN}`
      },
      body: JSON.stringify({
        routing_key: PAGERDUTY_ROUTING_KEY,
        event_action: 'trigger',
        payload: {
          summary: 'High-confidence prompt injection detected',
          severity: 'critical',
          source: 'LockLLM',
          custom_details: {
            confidence: scan_result.confidence,
            injection_score: scan_result.injection,
            request_id: request_id,
            preview: input_preview
          }
        }
      })
    })
  }

  res.status(200).send('OK')
})

Link to section: Email AlertsEmail Alerts

Send email notifications:

const nodemailer = require('nodemailer')

app.post('/webhooks/lockllm', async (req, res) => {
  const { scan_result, input_preview, request_id } = req.body

  // Send email for all detections
  await transporter.sendMail({
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Prompt Injection Detected',
    html: `
      <h2>Security Alert</h2>
      <p><strong>Confidence:</strong> ${(scan_result.confidence * 100).toFixed(2)}%</p>
      <p><strong>Injection Score:</strong> ${scan_result.injection.toFixed(4)}</p>
      <p><strong>Request ID:</strong> ${request_id}</p>
      <p><strong>Preview:</strong></p>
      <pre>${input_preview}</pre>
    `
  })

  res.status(200).send('OK')
})

Link to section: Custom LoggingCustom Logging

Log to your monitoring system:

app.post('/webhooks/lockllm', async (req, res) => {
  const { scan_result, request_id, timestamp } = req.body

  // Log to your system
  logger.warn('prompt_injection_detected', {
    request_id: request_id,
    timestamp: new Date(timestamp).toISOString(),
    confidence: scan_result.confidence,
    injection: scan_result.injection,
    sensitivity: scan_result.sensitivity
  })

  // Update metrics
  metrics.increment('security.prompt_injection.detected')

  res.status(200).send('OK')
})

Link to section: TroubleshootingTroubleshooting

Link to section: Webhook Not Receiving EventsWebhook Not Receiving Events

Problem: No webhooks are being delivered.

Solution:

  1. Verify webhook is enabled in dashboard
  2. Check your endpoint is publicly accessible
  3. Test the webhook from the dashboard
  4. Check your server logs for incoming requests
  5. Verify you're returning 200 OK

Link to section: "Invalid URL" Error"Invalid URL" Error

Problem: Cannot add webhook URL.

Solution:

  1. Ensure URL uses HTTPS (not HTTP)
  2. URL must be publicly accessible (no localhost)
  3. URL must have a valid hostname
  4. Check for typos in the URL

Link to section: Webhook Delivery FailuresWebhook Delivery Failures

Problem: Webhooks are failing after retries.

Solution:

  1. Check your endpoint responds within 1 second
  2. Verify endpoint returns 200 OK status
  3. Check server logs for errors
  4. Ensure HTTPS certificate is valid
  5. Verify no firewall blocks LockLLM IPs

Link to section: Duplicate WebhooksDuplicate Webhooks

Problem: Receiving duplicate webhook events.

Solution: Implement idempotency using request_id (see example above).

Link to section: FAQFAQ

Link to section: What are webhooks used for?What are webhooks used for?

Webhooks provide real-time notifications when malicious prompts are detected. Use them for security alerts, monitoring dashboards, incident response, logging, and integrations with Slack, Discord, or PagerDuty.

Link to section: When are webhooks triggered?When are webhooks triggered?

Webhooks are triggered automatically when a malicious prompt is detected (when safe: false). They work with both direct API scans and proxy mode requests. Safe prompts do not trigger webhooks.

Link to section: Can I use localhost for testing?Can I use localhost for testing?

No. Webhook URLs must be publicly accessible with HTTPS. For local testing, use tools like ngrok to create a public tunnel to your local server.

Link to section: What if my webhook endpoint is down?What if my webhook endpoint is down?

LockLLM automatically retries failed deliveries up to 3 times with exponential backoff. After 3 failures, the webhook delivery is abandoned. You can check webhook delivery status in your activity logs.

Link to section: How many retries are attempted?How many retries are attempted?

LockLLM retries failed webhook deliveries 3 times with exponential backoff (100ms, 200ms, 400ms). Retries are triggered by network errors, timeouts, or 5xx/429 errors.

Link to section: Can I have multiple webhooks?Can I have multiple webhooks?

Yes! You can add multiple webhook URLs for different purposes (e.g., one for Slack, one for PagerDuty, one for custom logging).

Link to section: How do I verify webhook authenticity?How do I verify webhook authenticity?

Add a secret when creating your webhook. LockLLM will include it in the X-LockLLM-Signature header. Verify this matches your secret before processing the webhook.

Link to section: What's the difference between webhook formats?What's the difference between webhook formats?

  • Raw JSON: Complete data for custom processing
  • Slack: Pre-formatted for Slack's block kit (incoming webhooks)
  • Discord: Pre-formatted for Discord embeds

Choose based on your integration needs.

Updated 2 days ago