Webhooks

Webhooks are the core of Notifs. They allow you to send notifications to multiple channels with a single API call. This guide covers everything you need to know about creating, configuring, and using webhooks.

What are Webhooks?

A webhook in Notifs is a notification endpoint that you can trigger via our API or SDK. When you send data to a webhook, Notifs automatically delivers it to all configured channels (Email, Slack, SMS, Discord, etc.).

Key Concepts

  • Webhook ID: Unique UUID identifier for your webhook
  • Webhook Secret: Secret key used to sign requests (HMAC-SHA256)
  • Services: Delivery destinations configured for each webhook (Email, Slack, SMS)
  • Payload: The data you send with each notification request (title, message, data)

Creating a Webhook

Via Dashboard

  1. Navigate to Webhooks in your dashboard
  2. Click Create Webhook
  3. Configure your webhook:
    • Name: Descriptive name (e.g., "Payment Notifications")
    • Project: Optional project to organize webhooks
  4. Save and copy the Webhook ID and Webhook Secret
  5. Add services (Email, Slack, SMS) to configure delivery destinations

Via SDK

import { Notifs } from '@notifs/sdk'

const notifs = new Notifs({
  apiKey: process.env.NOTIFS_API_KEY
})

const webhook = await notifs.webhooks.create({
  name: 'Payment Notifications',
  projectId: 'optional-project-uuid' // Optional
})

console.log(webhook.id)             // Webhook ID for sending notifications
console.log(webhook.webhook_secret) // Secret for signature verification

Note: Services (Email, Slack, SMS) are configured via the dashboard after creating the webhook.

Configuring Services

Each webhook can deliver to multiple services simultaneously. Services are configured via the dashboard.

Email

  1. Go to your webhook in the dashboard
  2. Click Add ServiceEmail
  3. Configure:
    • To: Recipient email address
    • Subject: Email subject line (optional)

Slack

  1. Go to your webhook in the dashboard
  2. Click Add ServiceSlack
  3. Click Connect Slack to authorize via OAuth
  4. Select up to 3 channels to post notifications

SMS

  1. Go to your webhook in the dashboard
  2. Click Add ServiceSMS
  3. Add phone numbers in E.164 format (e.g., +14155551234)

Sending Notifications

Basic Send

First get the webhook, then send notifications:

import { Notifs } from '@notifs/sdk'

const notifs = new Notifs({
  apiKey: process.env.NOTIFS_API_KEY
})

// Get webhook once, reuse for multiple sends
const webhook = await notifs.webhooks.get('your-webhook-id')

// Send a notification
await webhook.send({
  title: 'Payment Received',
  message: 'Your payment of $99.00 has been processed',
  data: {
    user: 'john@example.com',
    amount: '$99.00',
    orderId: 'order_123'
  }
})

Payload Structure

| Field | Type | Required | Description | |-------|------|----------|-------------| | title | string | Yes | Notification title | | message | string | No | Notification message body | | data | object | No | Custom data passed to services |

Response

const response = await webhook.send({
  title: 'Payment Received',
  data: { amount: '$99.00' }
})

console.log(response.notificationId)    // Unique notification ID
console.log(response.servicesExecuted)  // true if services ran
console.log(response.usageInfo)         // Current usage stats

Duplicate Prevention

Notifs automatically prevents duplicate notifications within a 5-minute window. If you send an identical payload, you'll receive a 409 Conflict response.

To send the same notification again, either:

  • Wait 5 minutes
  • Add a unique field like a timestamp to your payload

Payload Data

The data you send is passed directly to each configured service. Structure your payload with fields that make sense for your notification.

Recommended Fields

await webhook.send({
  title: 'Order Confirmed',           // Used as email subject, Slack header
  message: 'Your order is on the way', // Main notification body
  data: {
    // Custom fields for your services
    orderId: 'order_123',
    customerName: 'John Doe',
    total: '$99.00',
    trackingUrl: 'https://...'
  }
})

How Services Use Data

| Service | Title | Message | Data | |---------|-------|---------|------| | Email | Subject line | Email body | Included in body | | Slack | Bold header | Message text | Formatted fields | | SMS | Prefix | SMS content | Appended to message |

Example Payloads

Order Notification:

{
  title: 'Order #12345 Shipped',
  message: 'Your order is on the way!',
  data: {
    trackingNumber: 'TRACK123',
    estimatedDelivery: '2024-01-15',
    carrier: 'FedEx'
  }
}

Alert Notification:

{
  title: '[CRITICAL] Server Down',
  message: 'Production server is not responding',
  data: {
    server: 'prod-api-1',
    lastSeen: '2024-01-10T15:30:00Z',
    region: 'us-east-1'
  }
}

Service Executions

Each notification triggers service executions for all configured services (Email, Slack, SMS). You can view execution status and rerun failed executions.

Execution Status

  • success - Service delivered successfully
  • error - Service failed (can be rerun)
  • skipped - Skipped due to usage limits

Manual Rerun

Rerun failed service executions via the dashboard:

  1. Go to Webhooks → Select your webhook
  2. Click on a notification to see service executions
  3. Click Rerun on any failed execution

Reruns count toward your usage limit and are tracked with an incrementing rerun_number.

Webhook Logs

View request logs for debugging in the dashboard:

  1. Go to Webhooks → Select your webhook
  2. Click Logs tab
  3. View all incoming requests with:
    • Request payload
    • Response status
    • Signature validation status
    • Processing duration

Filtering Logs

Filter logs by status:

  • Success - HTTP 2xx responses
  • Error - HTTP 4xx/5xx responses

Log Data

Each log entry includes:

  • request_body - The payload sent
  • response_status - HTTP status code
  • signature_valid - Whether signature verification passed
  • duration_ms - Processing time in milliseconds

Rate Limits

Notification sending is subject to rate limits:

| Tier | Rate Limit | |------|------------| | Per IP | Configurable | | Per Webhook | Configurable | | Per User | Configurable |

If you exceed rate limits, you'll receive a 429 Too Many Requests response with a Retry-After header.

Usage Limits

Service executions are tracked against your plan's monthly limit:

| Plan | Service Executions | |------|-------------------| | Free | Limited | | Pro | Higher limit | | Enterprise | Custom |

When you exceed your limit, services are marked as skipped. A grace period of 5 additional executions is allowed before hard blocking.

Webhook Security

Signature Verification

Every request to the notification endpoint must include an HMAC-SHA256 signature:

X-Notifs-Signature: t=<timestamp>,v1=<hash>

The SDK handles signature generation automatically. For manual implementation, see Security.

Secret Regeneration

Regenerate webhook secrets via the dashboard:

  1. Go to Webhooks → Select your webhook
  2. Click SettingsRegenerate Secret
  3. Update your code with the new secret

Important: The old secret is invalidated immediately.

Testing Webhooks

Send Test Notification

Test your webhook configuration via the dashboard:

  1. Go to Webhooks → Select your webhook
  2. Click Test Webhook
  3. A test notification is sent through all configured services

Test Payload

The test notification includes:

{
  "title": "Test Notification",
  "message": "This is a test notification from your webhook",
  "timestamp": "2024-01-01T00:00:00.000Z",
  "test": true
}

Common Patterns

User Notifications

import { Notifs } from '@notifs/sdk'

const notifs = new Notifs({ apiKey: process.env.NOTIFS_API_KEY })
const userWebhook = await notifs.webhooks.get('your_user_webhook_id')

async function notifyUser(userId: string, event: string, data: any) {
  await userWebhook.send({
    title: `User Event: ${event}`,
    message: data.message,
    data: {
      userId,
      event,
      ...data,
      timestamp: new Date().toISOString()
    }
  })
}

// Usage
await notifyUser('user_123', 'password_changed', {
  message: 'Your password was changed',
  email: 'user@example.com'
})

System Alerts

const alertWebhook = await notifs.webhooks.get('your_alert_webhook_id')

async function sendAlert(severity: string, message: string) {
  await alertWebhook.send({
    title: `[${severity.toUpperCase()}] Alert`,
    message,
    data: {
      severity,
      timestamp: new Date().toISOString(),
      hostname: process.env.HOSTNAME
    }
  })
}

// Usage
await sendAlert('critical', 'Database connection pool exhausted')

Batch Notifications

const batchWebhook = await notifs.webhooks.get('your_batch_webhook_id')

async function sendBatchNotifications(users: User[], event: string) {
  const promises = users.map(user =>
    batchWebhook.send({
      title: `New ${event}`,
      data: {
        userId: user.id,
        email: user.email,
        event,
        timestamp: new Date().toISOString()
      }
    })
  )

  await Promise.allSettled(promises)
}

Best Practices

  1. Reuse Webhook Instances - Get the webhook once and reuse it for multiple sends
  2. Include Meaningful Titles - The title field is required and shown prominently
  3. Structure Data Consistently - Use consistent field names in your data object
  4. Handle Errors Gracefully - Wrap sends in try-catch and handle failures
  5. Monitor Usage - Check your usage in the dashboard to avoid hitting limits
  6. Use Unique Payloads - Add timestamps to avoid duplicate detection if resending
  7. Regenerate Secrets Periodically - Rotate webhook secrets for security

Troubleshooting

Notification Not Delivered

  1. Check service executions in the dashboard for error messages
  2. Verify service configuration (email addresses, Slack channels)
  3. Ensure you haven't exceeded usage limits
  4. Check if the service shows as skipped or error

Invalid Signature Error

  1. Verify you're using the correct webhook secret
  2. Check that the signature timestamp is within 5 minutes
  3. Ensure the payload hasn't been modified after signing
  4. If using the SDK, ensure your API key is valid

Duplicate Request Rejected

  1. Identical payloads within 5 minutes are blocked
  2. Add a timestamp or unique ID to your data object
  3. Wait 5 minutes before resending the same payload

Rate Limited

  1. Check response headers for X-RateLimit-* values
  2. Implement backoff based on Retry-After header
  3. Spread requests over time for batch operations

Next Steps