Agent skill

Leonardo.Ai Webhooks Skill

Receive and authenticate Leonardo.Ai webhook callbacks. Use when setting up a Leonardo.Ai webhook callback endpoint, verifying the Authorization Bearer header, debugging 401s, or handling image_generation.complete events.

Install this skill

npx skills add hookdeck/webhook-skills --skill leonardo-ai-webhooks


Leonardo.Ai (the generative image API) can POST a JSON webhook callback when an image generation finishes. Callbacks are not signed. If you set a webhook callback API key, Leonardo.Ai sends it in the Authorization: Bearer <key> header of every callback, and your endpoint authenticates the request by comparing it with the key you stored.

Hookdeck's source type is LEONARDO_AI (labelled "Leonardo.Ai"). Its setup note is: "Set a webhook callback API key on the production API key, then paste it here."

When to Use This Skill

  • How do I receive Leonardo.Ai webhook callbacks?
  • How do I verify the Authorization: Bearer header on a Leonardo.Ai webhook?
  • Does Leonardo.Ai sign webhook payloads? (No: use the callback API key)
  • How do I handle image_generation.complete events?
  • Why does my Leonardo.Ai webhook endpoint return 401?

Verification (core)

const crypto = require('crypto');

function verifyLeonardoRequest(headers, configured = process.env.LEONARDO_AI_WEBHOOK_SECRET) {
  if (!configured) return false;                          // never accept when unconfigured
  // Store the bare key; tolerate a pasted "Bearer <key>" like Hookdeck does.
  const secret = /^bearer /i.test(configured) ? configured.slice(7) : configured;
  if (!secret) return false; // a configured value of just "Bearer " must not match
  const header = headers['authorization'] || '';
  if (!/^bearer /i.test(header)) return false;           // scheme is case-insensitive (RFC 9110)
  const received = Buffer.from(header.slice(7));
  const expected = Buffer.from(secret);
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}
  • Header: Authorization: Bearer <webhook callback API key>. Store only the key in LEONARDO_AI_WEBHOOK_SECRET; don't trim or alter it.
  • Missing header, wrong scheme or mismatch: return 401. Invalid JSON: 400. Handled: 200.
  • Read the body raw (express.raw({ type: '*/*' })) and authenticate before parsing it.
  • A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS only, and rotate the key if it leaks.

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

Event Types

Dispatch on the type field. The payload is {type, object, timestamp (ms), api_version, data: {object}}.

EventSent When
image_generation.completeAn image generation made with the production API key finishes; data.object is the generation (id, status, images, ...)

Acknowledge (200) any other type so new event types don't cause retries or alerts.

Environment Variables

LEONARDO_AI_WEBHOOK_SECRET=your-webhook-callback-api-key   # the bare key, without "Bearer "

The callback URL and key are optional settings on a production API key. Changing them means creating a new production API key, so update the variable when you do.

Local Development

Leonardo.Ai can't reach localhost. Tunnel with the Hookdeck CLI (no account required):

npx hookdeck-cli listen 3000 leonardo-ai --path /webhooks/leonardo-ai

Use the printed URL as the webhook callback URL on the production API key.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 10, 2026

View on GitHub →