Skip to main content
Webhooks are available on Business and Enterprise plans.

1. Create an endpoint

In the dashboard, go to Settings > Webhooks and click Create Endpoint. Enter your HTTPS endpoint URL and select which events to listen for:
EventDescription
device.newFirst attestation from a new device
device.high_riskDevice exceeds risk threshold
device.attestation_failedAttestation validation failed
attestation.anomalyUnusual attestation pattern
Grantiva generates a signing secret (whsec_...) — save this for signature verification.

2. Handle webhook events

// Node.js / Express example
const crypto = require('crypto');

app.post('/webhooks/grantiva', (req, res) => {
  // Verify signature
  const signature = req.headers['x-grantiva-signature'];
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (signature !== expected) {
    return res.status(401).send('Invalid signature');
  }

  // Process event
  const { event, data } = req.body;

  switch (event) {
    case 'device.high_risk':
      console.log(`High risk device: ${data.device_id}, score: ${data.risk_score}`);
      blockDevice(data.device_id);
      break;
    case 'device.new':
      console.log(`New device: ${data.device_id}`);
      break;
  }

  res.sendStatus(200);
});
// Vapor example
func handleWebhook(_ req: Request) throws -> HTTPStatus {
    let signature = req.headers.first(name: "X-Grantiva-Signature") ?? ""
    let body = req.body.data ?? ByteBuffer()

    let key = SymmetricKey(data: Data(Environment.get("WEBHOOK_SECRET")!.utf8))
    let mac = HMAC<SHA256>.authenticationCode(for: Data(buffer: body), using: key)
    let expected = "sha256=" + mac.map { String(format: "%02x", $0) }.joined()

    guard expected == signature else {
        throw Abort(.unauthorized)
    }

    let payload = try req.content.decode(WebhookPayload.self)

    switch payload.event {
    case "device.high_risk":
        // Handle high risk device
        break
    default:
        break
    }

    return .ok
}

3. Test your endpoint

From the webhook detail page in the dashboard, click Send Test Event. This fires a webhook.test event to verify your endpoint is reachable.

4. Verify it works

Check the delivery log in the dashboard. Each delivery shows:
  • HTTP status code
  • Response body
  • Delivery time
  • Retry count

Retries

Failed deliveries (non-2xx response or timeout) are retried up to 3 times with exponential backoff:
AttemptDelay
1st retry~1 minute
2nd retry~5 minutes
3rd retry~30 minutes

Next steps