Webhooks

Receive real-time events from GrenLogger when things happen in your organization. Configure webhooks in the dashboard and verify payloads using the Standard Webhooks signature scheme.

Real-time notifications

When you activate remote logging for a user, create or delete a project, or exceed an error threshold, GrenLogger sends a signed webhook to your configured URL. Respond with 2xx within 15 seconds to acknowledge receipt.

Event types

You can subscribe to these events when creating a webhook:

  • remote_activation.added — Remote log activation added for a user
  • remote_activation.removed — Remote log activation removed
  • project.created — New project created
  • project.updated — Project updated
  • project.deleted — Project deleted
  • member.invited — Team member invited
  • member.removed — Team member removed
  • log.error.threshold_exceeded — Error count in last 24h exceeded tier threshold

Payload format

Webhooks follow the Standard Webhooks spec. Each request includes:

  • Headers: webhook-id, webhook-timestamp, webhook-signature
  • Body: JSON with type, timestamp (ISO 8601), and data
{
  "type": "remote_activation.added",
  "timestamp": "2026-01-15T14:30:00.000Z",
  "data": {
    "projectId": "proj_abc123",
    "userId": "user_xyz789",
    "expiresAt": "2026-01-16T14:30:00.000Z",
    "activatedBy": "uid_owner"
  }
}

Verifying signatures

Use HMAC-SHA256 to verify the payload. The signed content is:

webhook-id.webhook-timestamp.raw_body

The signature header format is v1,base64_signature. Use your webhook secret (shown once when creating the webhook) to compute the HMAC and compare with constant-time comparison.

Example: Node.js verification

const crypto = require('crypto');

function verifyWebhook(payload, headers, secret) {
  const webhookId = headers['webhook-id'];
  const timestamp = headers['webhook-timestamp'];
  const signature = headers['webhook-signature'];
  if (!webhookId || !timestamp || !signature) return false;

  const signedContent = webhookId + '.' + timestamp + '.' + payload;
  const rawSecret = Buffer.from(
    secret.replace(/^whsec_/, '').replace(/-/g, '+').replace(/_/g, '/'),
    'base64'
  );
  const expected = 'v1,' + crypto.createHmac('sha256', rawSecret)
    .update(signedContent)
    .digest('base64');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Best practices

  • Respond with 2xx within 10–15 seconds to avoid retries
  • Use the webhook-id as an idempotency key to avoid processing duplicates
  • Verify webhook-timestamp is within a few minutes to prevent replay attacks
  • Process webhooks asynchronously (queue and respond quickly)

Setup

  1. Go to your project in the dashboard
  2. Open the Webhooks section
  3. Add a webhook URL (HTTPS required)
  4. Select the events you want to receive
  5. Save the secret shown once — you will need it to verify signatures

Configure webhooks

Webhooks are available on Expert and higher plans.

Open dashboard

Related