Agent skill

Cover Genius Webhooks Skill

Receive and verify Cover Genius (XCover) Offers webhooks. Use when setting up a Cover Genius webhook listener, checking the X-Api-Key header, debugging 401s on Offers webhooks, or handling embedded insurance events like BOOKING_CREATED, BOOKING_CANCELLED and RENEWAL_DUE.

Install this skill

npx skills add hookdeck/webhook-skills --skill cover-genius-webhooks


Cover Genius is an embedded insurance platform. Its Offers API sends an HTTP POST to your listener when a booking or policy renewal changes. The webhook carries no payload signature: Cover Genius sends your authentication key as plain text in the X-Api-Key header, and you verify it by comparing that header with your key.

This skill covers Offers webhooks only. XClaim (XClaim-Signature) and RentalCover (X-Signature plus a timestamp) use other schemes and aren't covered.

When to Use This Skill

  • How do I receive Cover Genius webhooks?
  • How do I verify the X-Api-Key header on a Cover Genius Offers webhook?
  • Does Cover Genius sign webhook payloads? (No: shared key in X-Api-Key)
  • How do I handle BOOKING_CREATED, BOOKING_CANCELLED or RENEWAL_DUE events?
  • Why does my Cover Genius webhook endpoint return 401?

Verification (core)

const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyCoverGeniusRequest(headers, apiKey = process.env.COVER_GENIUS_API_KEY) {
  if (!apiKey) return false;                       // never accept when unconfigured
  const received = headers['x-api-key'];           // plain text, no prefix
  return typeof received === 'string' && safeEqual(received, apiKey);
}

Return 401 when this fails, 400 for a body that isn't a JSON object, and 200 once handled (any other status makes Cover Genius retry up to 3 times). There is no body signature, so the raw body isn't needed for verification, but authenticate before parsing it. In Express, read the body with express.raw({ type: '*/*' }) so every content type is checked.

Cover Genius also sends Authorization: Signature ..., an HMAC-SHA256 over the Date header only. It adds nothing over the key check, so it's optional; see references/verification.md.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Event Types

Every body is { "event": "...", "payload": { ... } }.

EventSent When
BOOKING_CREATEDAn Offer is confirmed through an API request
BOOKING_UPDATEDA confirmed Offer is updated through an API request
BOOKING_CANCELLEDA confirmed Offer is cancelled through an API request
RENEWAL_CREATEDThe policy has been renewed
RENEWAL_NOTIFICATIONA renewal is approaching
RENEWAL_DUEThe renewal due date arrives (policy active until expiry_date)
RENEWAL_EXPIREDThe renewal expiry date arrives; no longer renewable

Environment Variables

COVER_GENIUS_API_KEY=your_cover_genius_api_key   # the authentication key you gave your CSE

Local Development

For local webhook testing, run the Hookdeck CLI (no account required):

npx hookdeck-cli listen 3000 cover-genius --path /webhooks/cover-genius

Give the printed URL to your Cover Genius Client Solution Engineer as the listener URL.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 3, 2026

View on GitHub →