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

# Webhooks

> Configure webhooks to receive real-time notifications for onchain events and loyalty actions.

<head>
  <script type="application/ld+json">
    {JSON.stringify({
            "@context": "https://schema.org",
            "@graph": [
              {
                "@type": "TechArticle",
                "headline": "Webhooks",
                "description": "Configure webhooks to receive real-time notifications for onchain events and loyalty actions.",
                "author": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/"},
                "publisher": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/", "logo": {"@type": "ImageObject", "url": "https://assets.snagsolutions.io/public/docs/snag-logo-dark-no-bg.svg"}},
                "mainEntityOfPage": "https://docs.snagsolutions.io/stratus/webhooks"
              },
              {
                "@type": "BreadcrumbList",
                "itemListElement": [
                  {"@type": "ListItem", "position": 1, "name": "Home", "item": "https://docs.snagsolutions.io/welcome"},
                  {"@type": "ListItem", "position": 2, "name": "Stratus", "item": "https://docs.snagsolutions.io/stratus/stratus-overview"},
                  {"@type": "ListItem", "position": 3, "name": "Webhooks"}
                ]
              }
            ]
          })}
  </script>
</head>

## Overview

Stratus subscriptions deliver events via webhooks, enabling your systems to respond immediately to key events. This guide explains how to set up and validate webhook notifications.

Webhooks are triggered by a scheduled job that runs **every minute**. During each run, Stratus processes all recent changes recorded since the previous run. Once updates are batched and applied, webhook events are dispatched to all destinations subscribed to the affected resources. This ensures near real-time delivery without missing intermediate updates.

## Getting Started

<Steps>
  <Step title="Enable Webhooks">
    Toggle webhooks within your subscription settings
  </Step>

  <Step title="Configure Endpoint">
    Provide the URL where your system will accept POST requests
  </Step>

  <Step title="Store Signing Key">
    Save the `Signing Key` from your subscription settings for payload validation
  </Step>
</Steps>

## Implementation Guide

### Webhook Validation

<Note>
  Each webhook payload includes a signature header (`x-signature`) that should be validated to ensure authenticity.
</Note>

<CodeGroup>
  ```javascript Next.js Example theme={null}
  // https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries
  export const config = {
    api: {
      bodyParser: false,
    },
  }

  export default async function handler(req, res) {
    try {
      if (req.method !== 'POST') {
        res.setHeader('Allow', 'POST')
        return res.status(405).json({ error: 'Method Not Allowed' })
      }

      const rawBody = (await getRawBody(req)).toString('utf-8')

      const signature = req.headers['x-signature']
      if (!signature) {
        console.error('Missing signature')
        return res.status(400).json({ error: 'Missing signature' })
      }

      const secret = process.env.WEBHOOK_SECRET
      if (!secret) {
        console.error('Server misconfiguration: Missing WEBHOOK_SECRET')
        return res
          .status(500)
          .json({ error: 'Server misconfiguration: Missing WEBHOOK_SECRET' })
      }

      const computedSignature = crypto
        .createHmac('sha256', secret)
        .update(rawBody)
        .digest('hex')

      if (
        !crypto.timingSafeEqual(
          new Uint8Array(Buffer.from(computedSignature)),
          new Uint8Array(Buffer.from(signature))
        )
      ) {
        console.error('Invalid signature')
        return res.status(401).json({ error: 'Invalid signature' })
      }

      const payload = JSON.parse(rawBody)
      payload.forEach((event) => {
        console.log('Received event:', event)
      })

      res.status(200).json({ status: 'Webhook processed successfully' })
    } catch (error) {
      console.error('Webhook processing error:', error)
      res.status(500).json({ error: 'Internal Server Error' })
    }
  }
  ```
</CodeGroup>

## Important Considerations

<CardGroup cols={3}>
  <Card title="Batching" icon="layer-group">
    Multiple events may be batched into a single webhook payload
  </Card>

  <Card title="Retry Logic" icon="rotate">
    Failed webhooks are retried exponentially up to five times
  </Card>

  <Card title="Security" icon="shield-check">
    Always validate webhook signatures using your WEBHOOK\_SECRET
  </Card>
</CardGroup>

<Warning>
  Never expose your WEBHOOK\_SECRET in client-side code or public repositories. Always store it securely in environment variables.
</Warning>
