Skip to main content
This guide creates an endpoint, verifies deliveries in your receiver, and sends a test event.

Create an endpoint

import Casedev from 'casedev'

const client = new Casedev({ apiKey: process.env.CASEDEV_API_KEY })

const endpoint = await client.webhooks.v1.endpoints.create({
  url: 'https://your-app.example.com/webhooks/casedev',
  description: 'Production event receiver',
  eventTypeFilters: ['vault.ingest.completed'],
})

console.log(endpoint.endpoint.id, endpoint.signingSecret)
The response includes endpoint.id and signingSecret. Store the signing secret in your secrets manager before closing the response.
Response
{
  "endpoint": {
    "id": "evsub_123",
    "url": "https://your-app.example.com/webhooks/casedev",
    "eventTypeFilters": ["vault.ingest.completed"],
    "status": "active"
  },
  "signingSecret": "whsec_..."
}

Add a receiver

Verify the signature with the raw request body. Parsing and re-stringifying JSON changes the bytes and invalidates the signature.
import express from 'express'
import crypto from 'node:crypto'

const app = express()
const secret = process.env.CASEDEV_WEBHOOK_SECRET!

app.post('/webhooks/casedev', express.raw({ type: 'application/json' }), (req, res) => {
  const rawBody = req.body.toString('utf8')

  if (!verifySignature(rawBody, req.headers, secret)) {
    return res.status(400).send('invalid signature')
  }

  const event = JSON.parse(rawBody)
  console.log(event.type, event.id)

  return res.sendStatus(204)
})

function verifySignature(body: string, headers: express.Request['headers'], secret: string) {
  const id = String(headers['case-webhook-id'] ?? '')
  const timestamp = String(headers['case-webhook-timestamp'] ?? '')
  const signature = String(headers['case-webhook-signature'] ?? '')
  const timestampSeconds = Number(timestamp)

  if (!id || !Number.isFinite(timestampSeconds)) return false
  if (Math.abs(Date.now() / 1000 - timestampSeconds) > 300) return false

  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64')
  const expected = crypto
    .createHmac('sha256', key)
    .update(`${id}.${timestamp}.${body}`)
    .digest('base64')

  return signature.split(' ').some((part) => {
    const [version, value] = part.split(',', 2)
    return version === 'v1' && safeEqual(value, expected)
  })
}

function safeEqual(a = '', b = '') {
  const left = Buffer.from(a)
  const right = Buffer.from(b)
  return left.length === right.length && crypto.timingSafeEqual(left, right)
}

Send a test event

await client.webhooks.v1.endpoints.test(endpointId, {
  eventType: 'vault.ingest.completed',
  payload: {
    vaultId: 'vault_123',
    objectId: 'obj_123',
    durationMs: 1284,
    chunkCount: 42,
  },
})
The test endpoint performs one synchronous delivery. Use it to confirm reachability and signature verification before broadening your filters.

Go live

Start with exact event names, then widen to patterns like vault.* after your receiver is stable. Use Event types for the generated event list and API reference for endpoint update, replay, and rotation APIs.