Security Best Practices

Security is a top priority at Notifs. This guide covers best practices for keeping your integration secure.

API Key Management

Store Keys Securely

Never commit API keys to version control. Always use environment variables:

# .env
NOTIFS_API_KEY=notifs_sk_...
// ✅ Good
const notifs = new Notifs({
  apiKey: process.env.NOTIFS_API_KEY
})

// ❌ Bad
const notifs = new Notifs({
  apiKey: 'notifs_sk_abc123...' // Never hardcode!
})

Rotate Keys Regularly

Rotate your API keys periodically, especially if:

  • A team member with access leaves
  • You suspect a key has been compromised
  • As part of regular security hygiene (every 90 days)

Use Restricted Keys

Create separate API keys for different environments and services:

# Development
NOTIFS_API_KEY=notifs_sk_dev_...

# Production
NOTIFS_API_KEY=notifs_sk_prod_...

Webhook Signature Verification

When you send notifications via the SDK, signatures are generated automatically. This section explains how signature verification works for receiving webhooks from external services.

How Notifs Signatures Work

When sending to POST /webhook/:webhookId:

  1. The SDK computes an HMAC-SHA256 signature using your webhook secret
  2. The signature is sent in the X-Notifs-Signature header
  3. Format: t=<timestamp>,v1=<hash>

Signature Format

X-Notifs-Signature: t=1234567890,v1=abc123def456...
  • t - Unix timestamp (seconds) when the signature was created
  • v1 - HMAC-SHA256 hex digest of "<timestamp>.<payload>"

Manual Signature Generation

If not using the SDK, generate signatures like this:

import crypto from 'crypto'

function generateSignature(payload: string, secret: string): string {
  const timestamp = Math.floor(Date.now() / 1000)
  const signaturePayload = `${timestamp}.${payload}`

  const hash = crypto
    .createHmac('sha256', secret)
    .update(signaturePayload)
    .digest('hex')

  return `t=${timestamp},v1=${hash}`
}

// Usage
const payload = JSON.stringify({ title: 'Test', data: {} })
const signature = generateSignature(payload, process.env.WEBHOOK_SECRET!)

fetch(`https://notifs.io/api/webhook/${webhookId}`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Notifs-Signature': signature
  },
  body: payload
})

Signature Validation Rules

  • Timestamps must be within 5 minutes of the server time (prevents replay attacks)
  • Signatures use constant-time comparison to prevent timing attacks
  • Invalid signatures return 401 Unauthorized

Rate Limiting

Notifs implements rate limiting to prevent abuse:

Default Limits

  • Free Tier: 10 requests/second, 1,000 requests/month
  • Pro Tier: 100 requests/second, 100,000 requests/month
  • Enterprise: Custom limits

Handle Rate Limits

Implement exponential backoff when you hit rate limits:

import { Notifs, NotifsError } from '@notifs/sdk'

const notifs = new Notifs({ apiKey: process.env.NOTIFS_API_KEY! })
const webhook = await notifs.webhooks.get('your-webhook-id')

async function sendWithRetry(
  payload: { title: string; message?: string; data?: any },
  maxRetries = 3
) {
  let retries = 0

  while (retries < maxRetries) {
    try {
      return await webhook.send(payload)
    } catch (error) {
      if (error instanceof NotifsError && error.status === 429) {
        const delay = Math.pow(2, retries) * 1000 // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay))
        retries++
      } else {
        throw error
      }
    }
  }

  throw new Error('Max retries exceeded')
}

Duplicate Prevention

Notifs automatically prevents duplicate notifications within a 5-minute window based on payload fingerprinting.

If you send an identical payload within 5 minutes, you'll receive a 409 Conflict response:

{
  "message": "Duplicate request detected",
  "code": "WEBHOOK_2005"
}

To send the same notification again, either:

  • Wait 5 minutes
  • Add a unique field like a timestamp to your payload
await webhook.send({
  title: 'Payment Received',
  data: {
    paymentId: 'pay_123',
    timestamp: new Date().toISOString() // Makes each request unique
  }
})

Input Validation

Always validate and sanitize data before sending:

import { z } from 'zod'
import { Notifs } from '@notifs/sdk'

const notifs = new Notifs({ apiKey: process.env.NOTIFS_API_KEY! })
const paymentWebhook = await notifs.webhooks.get('your-payment-webhook-id')

const PaymentSchema = z.object({
  userId: z.string(),
  amount: z.number().positive(),
  currency: z.enum(['USD', 'EUR', 'GBP']),
  email: z.string().email()
})

async function sendPaymentNotification(data: unknown) {
  // Validate input
  const validated = PaymentSchema.parse(data)

  // Send notification with validated data
  await paymentWebhook.send({
    title: 'Payment Received',
    message: `Payment of ${validated.amount} ${validated.currency} received`,
    data: validated
  })
}

HTTPS Only

Notifs only accepts requests over HTTPS. Make sure your webhook endpoints also use HTTPS in production.

// ✅ Good
const webhookUrl = 'https://yourapp.com/webhooks/notifs'

// ❌ Bad (will fail in production)
const webhookUrl = 'http://yourapp.com/webhooks/notifs'

Content Security Policy

Add appropriate CSP headers to your API routes:

export async function POST(request: NextRequest) {
  const response = NextResponse.json({ success: true })

  response.headers.set(
    'Content-Security-Policy',
    "default-src 'none'; frame-ancestors 'none'"
  )

  return response
}

Monitoring and Alerts

Set up monitoring for suspicious activity:

  • Failed authentication attempts
  • Unusual request patterns
  • High error rates
  • Rate limit hits

Dashboard Monitoring

Use the Notifs dashboard to track and investigate issues:

  1. Go to Webhooks → Select your webhook → Logs
  2. Filter by status (success or error)
  3. Review failed requests for error messages

Service Execution Monitoring

Check service execution status in the dashboard:

  1. Click on any notification to see service executions
  2. Review error or skipped statuses
  3. Use Rerun to retry failed executions

Data Privacy

PII Handling

Be mindful of personally identifiable information (PII):

  • Don't include sensitive data in notification payloads unless necessary
  • Use references/IDs instead of full user data when possible
  • Comply with GDPR, CCPA, and other privacy regulations
// ✅ Good - Use references
await webhook.send({
  title: 'Password Reset Requested',
  data: {
    userId: 'user_123',
    event: 'password_reset_requested'
  }
})

// ❌ Avoid sensitive data
await webhook.send({
  title: 'Account Update',
  data: {
    email: 'user@example.com',
    socialSecurityNumber: '123-45-6789', // Never!
    creditCard: '4111111111111111' // Never!
  }
})

Compliance

Notifs is compliant with:

  • SOC 2 Type II
  • GDPR
  • CCPA
  • HIPAA (Enterprise plan)

For enterprise compliance requirements, contact enterprise@notifs.io.

Security Incident Response

If you suspect a security incident:

  1. Rotate your API keys immediately
  2. Review recent logs for suspicious activity
  3. Contact our security team: security@notifs.io
  4. Document the incident and affected resources

Bug Bounty Program

We run a bug bounty program. If you discover a security vulnerability, please report it responsibly to security@notifs.io.

Do not:

  • Test against production without permission
  • Access or modify data that doesn't belong to you
  • Perform DoS attacks
  • Publicly disclose vulnerabilities before we've had time to patch

Next Steps