Getting started

Integration & migration

Image & video API

DAM user guide

API overview

Account

Webhooks

Learn how to configure and receive webhooks from ImageKit securely.


ImageKit uses webhooks to notify your application when an event occurs in your account. Webhooks are particularly useful for asynchronous events such as video encoding and extension processing during uploads.

Steps to receive webhooks

  1. Configure the webhook in your ImageKit dashboard.
  2. Create a webhook endpoint as an HTTP endpoint (URL) on your server and listen for incoming webhook requests.
  3. Handle requests from ImageKit by parsing each event object and returning 2xx response status codes. You can also verify the webhook signature to ensure the authenticity of the request. We recommend using the ImageKit SDK to handle webhook signature verification and prevent replay attacks.

Configure a Webhook

Go to the Developer options in the ImageKit dashboard. Under Webhooks, you will see the list of configured webhook endpoints.

Click the "Add new" button to create a new webhook endpoint.

Enter a valid HTTP(S) endpoint, select the events you want to receive, and click "Create."

You should now see the webhook endpoint in the list.

To update an existing endpoint, click .... You can change its current status (enabled or disabled) and update the list of active events.

Listen to Webhook

Use a tool like Ngrok to make your webhook endpoint publicly accessible for testing webhook implementation.

All webhook bodies are JSON-encoded. The body schema may differ based on the event type, but the following fields are standard:

FieldDataTypeDescription
typestringType of event.
idstringUnique identifier of the event.
created_atstringTimestamp of the event in ISO8601 format.
dataJSONActual event payload in JSON format.

Verify webhook signature

Webhook endpoints are publicly accessible, so filtering out malicious requests is essential. We recommend using the webhook signature to verify the authenticity of the webhook request and payload.

ImageKit follows the Standard Webhooks specification for secure webhook verification and sends webhook-id, webhook-timestamp, and webhook-signature HMAC-SHA256 signature headers.

You can use the webhook secret, which starts with the whsec_ prefix, to verify the webhook payload as explained below.

We will continue to send the legacy x-ik-signature header as well if you are using an old SDK or old verification method.

Verify signature with ImageKit SDK

We recommend using our official libraries to verify signatures. You can perform the verification by providing the raw event payload, request headers, and the webhook secret. If verification fails, you will get an error.

For development and testing purposes, SDKs also provide methods that skip signature verification entirely - however, these should never be used in production environments as they bypass critical security measures.

We are updating our SDKs, so if you don't see a snippet for your language, refer to the manual verification process.

Copy
Copy
import ImageKit from '@imagekit/nodejs';
import express from 'express';

const client = new ImageKit({
  privateKey: "your_private_key",
  webhookSecret: "whsec_..." // Copy from ImageKit dashboard
});

const app = express();

// For mixed APIs: Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
  if (req.originalUrl === '/webhook') {
    next(); // Skip JSON parsing for webhook
  } else {
    express.json()(req, res, next);
  }
});

app.post(
  '/webhook',
  // ImageKit requires the raw body to construct the event
  express.raw({ type: 'application/json' }),
  (req, res) => {
    try {
      // Verify signature and parse webhook payload securely
      const event = client.webhooks.unwrap(req.body, {
        headers: req.headers
        // Optional: Use a different webhook secret for this request
        // key: 'whsec_different_secret'
      });
      
      // Alternative: For development/testing only (NEVER use in production)
      // const event = client.webhooks.unsafeUnwrap(req.body);

      console.log('✅ Verified webhook event:', event.type);

      // Handle different event types with full type safety
      switch (event.type) {
        case 'video.transformation.accepted':
          console.log('Video transformation accepted:', event.data.asset.url);
          // Debugging: Track transformation requests
          break;
          
        case 'video.transformation.ready':
          console.log('Video transformation ready:', event.data.transformation.output?.url);
          // Update your database/CMS to show the transformed video
          // Example: updateVideoStatus(event.data.transformation.output.url, 'ready')
          break;
          
        case 'video.transformation.error':
          console.error('Video transformation error:', event.data.transformation.error?.reason);
          // Log error and check your origin/URL endpoint settings
          break;
          
        case 'upload.pre-transform.success':
          console.log('Pre-transform success:', event.data.fileId);
          // File uploaded and pre-transformation completed
          break;
          
        case 'upload.pre-transform.error':
          console.error('Pre-transform error:', event.data.transformation.error?.reason);
          break;
          
        case 'upload.post-transform.success':
          console.log('Post-transform success:', event.data.url);
          // Additional transformation completed
          break;
          
        case 'upload.post-transform.error':
          console.error('Post-transform error:', event.data.transformation.error?.reason);
          break;
          
        default:
          console.log(`Unhandled event type: ${event.type}`);
      }

      // Return a response to acknowledge receipt of the event
      res.json({ received: true });
      
    } catch (error) {
      // On error, log and return the error message
      console.log(`❌ Error message: ${error.message}`);
      res.status(400).send(`Webhook Error: ${error.message}`);
    }
  }
);

const server = app.listen(3000);
console.log(
  `🚀 Webhook endpoint available at http://localhost:3000/webhook`
);

Verify signature manually

ImageKit follows the Standard Webhooks specification for secure webhook verification. This approach provides better security and standardized implementation.

For implementation examples in Python, Node.js (use our SDK), PHP, Go, and other languages, refer to the Standard Webhooks GitHub repository. The repository contains complete examples and libraries for all supported languages.

Important: When using Standard Webhooks libraries directly, make sure to base64 encode your webhook secret before passing it to the library.

Preventing replay attacks

When an attacker intercepts a webhook request, they can replay it multiple times with a valid signature.

To mitigate this, the ImageKit webhook signature contains a timestamp. The timestamp is generated before the webhook request is sent to your server.

The verification method in the ImageKit SDK returns the timestamp and parsed event object.

If the timestamp is within the tolerance limit, the request can be considered valid, or you can reject it.

Optionally, a stronger approach is to use a nonce to prevent replay attacks. You can use the Event ID as a nonce, which is guaranteed to be unique across all events. You can find the event ID in the id field of the event object.

List of events

Below is the list of events for which ImageKit calls your configured webhook: