# Guide to Qlik Cloud Webhooks: Features and Best Practices

Qlik Cloud is Qlik's SaaS platform for analytics apps, data integration and automations. Tenant webhooks report what happens across the tenant, such as an app reload finishing, an automation run failing, an API key failing validation or a user being deleted, so you can trigger downstream jobs, alert on failures or keep an inventory in step.

This guide explains how to create a Qlik Cloud webhook, the supported events and the CloudEvents format, the switch away from legacy fields on December 10, 2026, and how to verify `Qlik-Signature`.

## What are Qlik Cloud webhooks?

A Qlik Cloud webhook is a tenant-level subscription to system events. Tenant admins create them in the Administration activity center, and they can also be created through the Webhooks API or from Qlik Automate. Webhooks made in Qlik Automate show in the activity center but can only be managed from Automate. Each user can own up to 150 webhooks.

Qlik is partway through a format change. Since November 3, 2025, events that weren't already CloudEvents have been sent as hybrids carrying both the CloudEvents fields and the legacy ones. On December 10, 2026 the legacy fields go, and only the CloudEvents form remains.

## Qlik Cloud webhook features

| Feature | Details |
| --- | --- |
| Signature | hex HMAC-SHA256 |
| Signature header | `qlik-signature` |
| Body | JSON |
| Event routing | Body field `type` |

From Qlik's [webhook administration page](https://help.qlik.com/en-US/cloud-services/Subsystems/Hub/Content/Sense_Hub/Admin/mc-administer-webhooks.htm):

* The endpoint must be HTTPS and use a domain name, not an IP address.
* An optional secret of 32 to 64 characters adds a `Qlik-Signature` header. It's write-once and can't be viewed after saving.
* Custom headers can be added and encrypted at rest (not in transit). Encrypted headers can be replaced but not edited.
* **Check certificate revocation** verifies your endpoint's certificate hasn't been revoked.
* The **Cloud event** option switches the webhook to the new format and can't be undone once saved.
* Delivery history is kept for 7 days, and you can resend an event by clicking its row.
* The example request carries `User-Agent: Qlik Webhook` and `Content-Type: application/json`.

## Events and payloads

Route on the CloudEvents `type`.

| Event routing | Body field `type` |

Qlik's [supported events list](https://help.qlik.com/en-US/cloud-services/Subsystems/Hub/Content/Sense_Hub/Admin/mc-administer-webhooks-supported-events.htm) has eight more: `com.qlik.v1.automation.run.ended`, `com.qlik.v1.automation.run.failed`, the data-integration task events `com.qlik.v1.di-projects.di-tasks.started`, `.stopped`, `.completed` and `.failed`, `com.qlik.analytics.change-store.changes.saved` and `com.qlik.scheduling.task.updated`. qlik.dev documents other system events too, but the help page says its list is what webhooks currently deliver.

A CloudEvents body has `id`, `time`, `type`, `source`, `userid`, `tenantid`, `specversion`, `datacontenttype` and `data`, plus Qlik extensions that vary by event. This is the `com.qlik.analytics.change-store.changes.saved` example from qlik.dev:

```json
{
  "id": "id123",
  "time": "2018-10-30T07:06:22Z",
  "type": "com.qlik.analytics.change-store.changes.saved",
  "source": "com.qlik/write-table",
  "userid": "id123",
  "tenantid": "id123",
  "specversion": "1.0",
  "datacontenttype": "application/json",
  "data": {
    "ownerId": "storeOwner1234",
    "spaceId": "space123",
    "storeId": "store012",
    "tenantId": "tenant123",
    "createdBy": "user345",
    "committedAt": "2024-01-01T12:20:00Z"
  }
}

```

Hybrid events add `eventId`, `eventTime`, `eventType`, `eventTypeVersion`, `cloudEventsVersion`, `contentType` and `extensions`. Payloads from before November 2025 carried only `eventType`, with data under `data.attributes`. Only 2 of the 22 event types have complete published examples, so capture real deliveries for the ones you handle.

## Setting up Qlik Cloud webhooks

1. In the Administration activity center, open **Webhooks** and click **Create new**.
2. Enter a name and the HTTPS endpoint URL, for example `https://example.com/webhooks/qlik-cloud`.
3. Enter a secret of 32 to 64 characters and store it as `QLIK_WEBHOOK_SECRET` at the same time, because Qlik won't show it again.
4. Tick **Cloud event** if you want the new format now. Leave it until your handler reads `type` if you're unsure, since the choice is permanent.
5. Under **Events**, pick from the Automation, API, Application, Data-integration and User groups, then click **Create**.

To receive through Hookdeck, create a Qlik Cloud source with the same secret and use its URL as the endpoint. Set a secret on the Qlik Cloud webhook. Without one, deliveries are unsigned and fail verification. If you run a webhook without a secret, leave verification off on the source. For local testing:

```bash
npx hookdeck-cli listen 3000 qlik-cloud --path /webhooks/qlik-cloud

```

## Securing Qlik Cloud webhooks

With a secret set, Qlik sends `Qlik-Signature`: the hex HMAC-SHA256 of the raw request body, keyed with the secret's text. It's the bare digest, with no timestamp in the signed content. Qlik's [verification guide on qlik.dev](https://qlik.dev/apis/event/verify-webhook-signatures-hmac/) shows the same calculation. Don't use the example signature on the help page as a test value: it's 53 hex characters, while a SHA-256 digest is 64.

### Node.js verification

```javascript
const crypto = require('node:crypto');

function verifySignature(rawBody, headers, secret) {
  if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret) return false;
  const signature = headers['qlik-signature'];
  if (typeof signature !== 'string') return false;
  const key = Buffer.from(secret, 'utf8');
  const message = rawBody;
  const candidates = [signature];
  const expected = crypto.createHmac('sha256', key).update(message).digest();
  return candidates.some((candidate) => {
    if (!/^[0-9a-fA-F]{64}$/.test(candidate)) return false;
    const received = Buffer.from(candidate, 'hex');
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

```

### HTTP method

Qlik's docs say a webhook sends "an HTTP JSON request" but never name the method. Hookdeck's Qlik Cloud source pre-selects `POST` without managing the method, so add other methods on the source if you see them arrive. The receiver below uses `app.all` and verifies whatever method comes in, treating a missing body as empty bytes.

```javascript
const express = require('express');

const app = express();

app.all('/webhooks/qlik-cloud', express.raw({ type: '*/*' }), async (req, res) => {
  const secret = process.env.QLIK_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500);
  const body = Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0);
  if (!verifySignature(body, req.headers, secret)) return res.sendStatus(401);
  if (!body.length) return res.sendStatus(200);

  let event;
  try {
    event = JSON.parse(body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }
  if (!event || typeof event !== 'object' || Array.isArray(event)) return res.sendStatus(400);
  const type = event.type ?? event.eventType;
  if (typeof type !== 'string') return res.sendStatus(400);

  try {
    if (type === 'com.qlik.v1.reload.finished' || type === 'com.qlik.v1.app.reload.finished') {
      await queue.add('qlik-reload', { id: event.id ?? event.eventId, type, data: event.data });
    } else if (type.endsWith('.failed')) {
      await alerts.send({ type, tenant: event.tenantid, data: event.data });
    }
  } catch {
    return res.sendStatus(503);
  }
  res.sendStatus(200);
});

```

The fallback to `eventType` only matters for hybrid or older payloads. After December 10, 2026, `type` is the only one present.

Hookdeck's Qlik Cloud source checks the same HMAC. It returns `200` to Qlik and records any mismatch as failed verification instead of rejecting it. Qlik signs no timestamp, and Hookdeck applies no time window.

> **Make Qlik Cloud webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) keeps Qlik events well beyond Qlik's 7-day history, with retries and bulk replay when your service has been down.

## Qlik Cloud webhook limitations and pain points

### Legacy fields disappear on December 10, 2026

**The Problem:** Handlers that read `eventType`, `eventId` or `eventTime` will stop working when the legacy fields are removed.

**Why It Happens:** Qlik has been sending hybrid payloads since November 3, 2025, and its changelog moved the removal date from October 6 to December 10, 2026. After that, only CloudEvents fields such as `type`, `id` and `time` are sent.

**Workarounds:**

* Switch your code to `type`, `id` and `time` now, keeping legacy fields only as a fallback.
* Tick **Cloud event** on a test webhook first to see CloudEvents-only payloads before the cut-over.

**How Hookdeck Can Help:** Hookdeck's request log shows the exact payloads Qlik is sending, so you can compare hybrid and CloudEvents-only bodies side by side, and a transformation can map legacy field names while you migrate.

### Retries are undocumented

**The Problem:** You can't tell whether Qlik retries a failed delivery, or how long it waits for a response.

**Why It Happens:** Qlik documents a 7-day history with manual resend, but no timeout, retry policy or ordering guarantee.

**Workarounds:**

* Check the webhook history after any outage and resend failed deliveries by hand within 7 days.
* Reply quickly and process reloads and alerts in the background.

**How Hookdeck Can Help:** Hookdeck accepts each event, keeps it, and retries your service automatically, so recovery doesn't depend on someone clicking resend.

### Signatures are optional and secrets are write-once

**The Problem:** A webhook created without a secret sends unsigned requests, and a lost secret can't be recovered.

**Why It Happens:** The secret field is optional, and Qlik never shows a saved secret again.

**Workarounds:**

* Always set a secret, and save it in your secret store before clicking **Create**.
* To rotate, edit the webhook, enter a new secret and update your receiver at the same time.

**How Hookdeck Can Help:** Hookdeck records requests that fail verification, so a mismatch after a secret change shows up in the log instead of disappearing.

### Most event payloads aren't published

**The Problem:** You can't write a handler from documentation alone for most events.

**Why It Happens:** Complete examples exist for 2 of the 22 types. Others use `"string"` placeholders or the legacy format, and qlik.dev names several app events differently from the webhook list.

**Workarounds:**

* Subscribe a test webhook and capture one real delivery for each event you need.
* Validate fields before reading them, and log unknown types.

**How Hookdeck Can Help:** Hookdeck stores every request body, giving you real examples to build and test against.

## Best practices

### Turn on Cloud event deliberately

The switch is permanent per webhook. Update your handler first, then enable it, or create a new webhook with it on and retire the old one.

### Deduplicate on the event id

Use `id` (or `eventId` on older payloads) as the deduplication key, since a manual resend can deliver the same event again. More on [making webhook handlers idempotent](/webhooks/guides/implement-webhook-idempotency).

### Hand reloads to a queue

Reload and data-integration events usually start longer jobs. Acknowledge first and run them from a queue. The reasons are in our guide to [asynchronous webhook processing](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Prefer encrypted headers for extra credentials

If your endpoint needs a bearer token as well as the signature, add it as an encrypted custom header so it isn't stored in plain text in Qlik.

## Conclusion

Qlik Cloud webhooks deliver tenant events as CloudEvents, signed with a hex HMAC-SHA256 in `Qlik-Signature` when you set a secret. The pressing task is the December 10, 2026 removal of legacy fields. After that, the gaps to plan around are the undocumented retries and HTTP method and the 7-day history.

[Hookdeck Event Gateway](https://hookdeck.com) verifies Qlik's signature, holds events for as long as you need them and retries delivery to your services.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Qlik Cloud webhooks reliably in minutes.