> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Qlik Cloud

> Client-mode REST API connector for Qlik Cloud Analytics: reload apps when a shift or batch closes and watch reload status, upload CSV rollups as data files, schedule reload tasks, run Qlik Automate automations and register webhooks, authenticated with an API key or an OAuth machine-to-machine token

Technical Preview

## Overview

A client-mode connector for the [Qlik Cloud REST APIs](https://qlik.dev/apis/rest/). Use it to let plant events drive Qlik: reload an app when a shift, batch or ETL job closes, upload a CSV rollup (shift totals, OEE per line) as a data file the app loads, schedule or pause reload tasks, and run a Qlik Automate automation. It also puts Qlik state on MQTT: reload status and logs, data model metadata, items, spaces and audit events.

The connector `QlikCloudRestApi` in `QlikCloudRestApi.lot` has 44 events (7 POST, 34 GET, 2 PUT, 1 PATCH). Nine are writes: `ReloadCreate`, `ReloadCancel`, `ReloadTaskCreate`, `ReloadTaskUpdate`, `DataFileUpload`, `DataFileReplace`, `AutomationRunCreate`, `WebhookCreate` and `WebhookUpdate`. There are no DELETE events.

Set `BASE_ADDRESS` to your tenant URL with no trailing slash, for example `https://acme.eu.qlikcloud.com` (a tenant alias hostname also works). Every endpoint already starts with `/api/v1`, except `/oauth/token`. Topics use `analytics/qlik-cloud/<object>/<verb>`; replies land on `<topic>/response`.

See the base guide: [REST API Connectors](/latest/lot-language/routes/rest-api-routes).

> [!NOTE]
>
> **Auth:** every event except `OAuthToken` sends `Authorization: Bearer <access_token>`. The token is either an API key (a tenant admin enables API keys and gives the user the Developer role, then the user creates a key under **Profile settings > API keys**) or an OAuth machine-to-machine token: publish `{"body":{"client_id":"...","client_secret":"...","grant_type":"client_credentials"}}` to `analytics/qlik-cloud/auth/token` and use the returned `access_token` until `expires_in` runs out. The key or token acts as its user, so give it only the spaces and roles the broker needs. Keep `QLIK_API_KEY` (or `QLIK_CLIENT_ID` and `QLIK_CLIENT_SECRET`) in broker secrets, read them with `GET SECRET` in the action that builds the payload, never retain topics that carry them, and restrict `analytics/qlik-cloud/auth/#` with ACLs.

## Events

| Event | Request | Source topic | Notes |
| - | - | - | - |
| OAuthToken | `POST /oauth/token` | `analytics/qlik-cloud/auth/token` | Get an access token for an OAuth machine-to-machine client (lifetime in expires\_in). No Authorization header. |
| UsersMe | `GET /api/v1/users/me` | `analytics/qlik-cloud/users/me` | Smoke: the user behind the API key or token (id, name, tenantId, roles). |
| TenantsMe | `GET /api/v1/tenants/me` | `analytics/qlik-cloud/tenants/me` | The tenant behind the key (id, name, hostnames, datacenter). |
| UsersList | `GET /api/v1/users?{value.json.query}` | `analytics/qlik-cloud/users/list` | Users in the tenant. |
| ReloadsList | `GET /api/v1/reloads?{value.json.query}` | `analytics/qlik-cloud/reloads/list` | Reload history of an app. |
| ReloadCreate | `POST /api/v1/reloads` | `analytics/qlik-cloud/reloads/create` | Write, starts a reload. Reload an app when a shift, batch or ETL job closes (catalog event). |
| ReloadGet | `GET /api/v1/reloads/{value.json.reload_id}` | `analytics/qlik-cloud/reloads/get` | One reload: status QUEUED, RELOADING, SUCCEEDED, FAILED, CANCELED (catalog event), duration and log. |
| ReloadCancel | `POST /api/v1/reloads/{value.json.reload_id}/actions/cancel` | `analytics/qlik-cloud/reloads/cancel` | Write. Cancel a queued or running reload (no body). |
| ReloadTasksList | `GET /api/v1/reload-tasks?{value.json.query}` | `analytics/qlik-cloud/reload_tasks/list` | Scheduled reload tasks. |
| ReloadTaskGet | `GET /api/v1/reload-tasks/{value.json.task_id}` | `analytics/qlik-cloud/reload_tasks/get` | One reload task (recurrence, state, next run). |
| ReloadTaskCreate | `POST /api/v1/reload-tasks` | `analytics/qlik-cloud/reload_tasks/create` | Write. Schedule reloads. |
| ReloadTaskUpdate | `PUT /api/v1/reload-tasks/{value.json.task_id}` | `analytics/qlik-cloud/reload_tasks/update` | Write. Replace or pause a reload task (no delete event: set state Disabled). |
| AppGet | `GET /api/v1/apps/{value.json.app_id}` | `analytics/qlik-cloud/apps/get` | App attributes (name, owner, space, lastReloadTime). |
| AppDataMetadata | `GET /api/v1/apps/{value.json.app_id}/data/metadata` | `analytics/qlik-cloud/apps/data_metadata` | Data model metadata: tables, fields, row counts, static byte size, reload\_meta. |
| AppDataLineage | `GET /api/v1/apps/{value.json.app_id}/data/lineage` | `analytics/qlik-cloud/apps/data_lineage` | Data sources the app loads from (lineage). |
| AppReloadLogs | `GET /api/v1/apps/{value.json.app_id}/reloads/logs` | `analytics/qlik-cloud/apps/reload_logs` | Reload log list for an app. |
| AppReloadLog | `GET /api/v1/apps/{value.json.app_id}/reloads/logs/{value.json.reload_id}` | `analytics/qlik-cloud/apps/reload_log` | One reload log as plain text (the script output). The broker publishes the body as received. |
| AppReloadMetadata | `GET /api/v1/apps/{value.json.app_id}/reloads/metadata/{value.json.reload_id}?{value.json.query}` | `analytics/qlik-cloud/apps/reload_metadata` | Metadata of one reload (duration, peak memory, rows per table). |
| ItemsList | `GET /api/v1/items?{value.json.query}` | `analytics/qlik-cloud/items/list` | Search apps and other items. |
| ItemGet | `GET /api/v1/items/{value.json.item_id}` | `analytics/qlik-cloud/items/get` | One item (resourceId is the app id for apps). |
| SpacesList | `GET /api/v1/spaces?{value.json.query}` | `analytics/qlik-cloud/spaces/list` | Spaces. |
| SpaceGet | `GET /api/v1/spaces/{value.json.space_id}` | `analytics/qlik-cloud/spaces/get` | One space. |
| DataFilesList | `GET /api/v1/data-files?{value.json.query}` | `analytics/qlik-cloud/data_files/list` | Data files in the personal or a space data connection. |
| DataFileGet | `GET /api/v1/data-files/{value.json.data_file_id}` | `analytics/qlik-cloud/data_files/get` | One data file (name, size, modifiedDate). |
| DataFileUpload | `POST /api/v1/data-files` | `analytics/qlik-cloud/data_files/upload` | Write. Catalog 'POST data-files': upload a CSV rollup (shift totals, OEE per line) for apps to load. |
| DataFileReplace | `PUT /api/v1/data-files/{value.json.data_file_id}` | `analytics/qlik-cloud/data_files/replace` | Write. Replace the content of an existing data file (same multipart shape, Json part may carry the name). Then ReloadCreate. |
| DataFilesQuotas | `GET /api/v1/data-files/quotas` | `analytics/qlik-cloud/data_files/quotas` | Data file quotas (max file size, space used). |
| DataFilesConnections | `GET /api/v1/data-files/connections?{value.json.query}` | `analytics/qlik-cloud/data_files/connections` | Data-file connections per space (use connectionId in DataFileUpload). |
| DataConnectionsList | `GET /api/v1/data-connections` | `analytics/qlik-cloud/data_connections/list` | Data connections visible to the user (the spec documents no query parameters). |
| AutomationsList | `GET /api/v1/automations?{value.json.query}` | `analytics/qlik-cloud/automations/list` | Qlik Automate automations. |
| AutomationGet | `GET /api/v1/automations/{value.json.automation_id}` | `analytics/qlik-cloud/automations/get` | One automation. |
| AutomationRunCreate | `POST /api/v1/automations/{value.json.automation_id}/runs` | `analytics/qlik-cloud/automations/run` | Write, runs the automation. Run an automation from a plant event. |
| AutomationRunsList | `GET /api/v1/automations/{value.json.automation_id}/runs?{value.json.query}` | `analytics/qlik-cloud/automations/runs` | Runs of an automation. |
| AutomationRunGet | `GET /api/v1/automations/{value.json.automation_id}/runs/{value.json.run_id}` | `analytics/qlik-cloud/automations/run_get` | One automation run (status, title, timestamps). |
| WebhooksList | `GET /api/v1/webhooks?{value.json.query}` | `analytics/qlik-cloud/webhooks/list` | Webhooks. |
| WebhookGet | `GET /api/v1/webhooks/{value.json.webhook_id}` | `analytics/qlik-cloud/webhooks/get` | One webhook. |
| WebhookEventTypes | `GET /api/v1/webhooks/event-types` | `analytics/qlik-cloud/webhooks/event_types` | Event types a webhook can subscribe to (for example com.qlik.v1.reload.finished). |
| WebhookCreate | `POST /api/v1/webhooks` | `analytics/qlik-cloud/webhooks/create` | Write. Send Qlik events (reload finished, app published) to a broker ENABLE\_SERVER connector. |
| WebhookUpdate | `PATCH /api/v1/webhooks/{value.json.webhook_id}` | `analytics/qlik-cloud/webhooks/update` | Write. JSON Patch a webhook (no delete event: disable it). |
| WebhookDeliveries | `GET /api/v1/webhooks/{value.json.webhook_id}/deliveries?{value.json.query}` | `analytics/qlik-cloud/webhooks/deliveries` | Delivery attempts of a webhook (status, response code). |
| AuditsList | `GET /api/v1/audits?{value.json.query}` | `analytics/qlik-cloud/audits/list` | Audit events (tenant admin). |
| AuditGet | `GET /api/v1/audits/{value.json.audit_id}` | `analytics/qlik-cloud/audits/get` | One audit event. |
| LicensesOverview | `GET /api/v1/licenses/overview` | `analytics/qlik-cloud/licenses/overview` | License overview (tenant admin). |
| LicensesConsumption | `GET /api/v1/licenses/consumption?{value.json.query}` | `analytics/qlik-cloud/licenses/consumption` | License consumption records (tenant admin). |

Payload fields are JSON on the source topic: ids (`app_id`, `reload_id`, `task_id`, `item_id`, `space_id`, `data_file_id`, `automation_id`, `run_id`, `webhook_id`, `audit_id`), `query` (a URL query string without `?`, or `""`; `ReloadsList` needs `appId=...`), `body` (JSON for POST, PUT and PATCH events; a JSON Patch array for `WebhookUpdate`), `multipart` for data file uploads, and `access_token`.

## Basic Syntax

An excerpt of the template (the full file has all 44 events and comments):

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE QlikCloudRestApi WITH TYPE REST_API
    ADD REST_API_CONFIG
        WITH BASE_ADDRESS "https://YOUR_TENANT.YOUR_REGION.qlikcloud.com"
        WITH ENABLE_CLIENT "true"
        WITH USE_SSL "true"
        WITH REQUEST_TIMEOUT '120'
        WITH DEFAULT_CONTENT_TYPE "application/json"
    ADD EVENT UsersMe
        WITH SOURCE_TOPIC "analytics/qlik-cloud/users/me"
        WITH DESTINATION_TOPIC "analytics/qlik-cloud/users/me/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/v1/users/me"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
            ADD "Authorization" "Bearer {value.json.access_token}"
    ADD EVENT ReloadCreate
        WITH SOURCE_TOPIC "analytics/qlik-cloud/reloads/create"
        WITH DESTINATION_TOPIC "analytics/qlik-cloud/reloads/create/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/v1/reloads"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
            ADD "Authorization" "Bearer {value.json.access_token}"
            ADD "Content-Type" "application/json"
        WITH BODY "{value.json.body}"
    ADD EVENT ReloadGet
        WITH SOURCE_TOPIC "analytics/qlik-cloud/reloads/get"
        WITH DESTINATION_TOPIC "analytics/qlik-cloud/reloads/get/response"
        WITH METHOD "GET"
        WITH ENDPOINT "/api/v1/reloads/{value.json.reload_id}"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
            ADD "Authorization" "Bearer {value.json.access_token}"
    ADD EVENT DataFileUpload
        WITH SOURCE_TOPIC "analytics/qlik-cloud/data_files/upload"
        WITH DESTINATION_TOPIC "analytics/qlik-cloud/data_files/upload/response"
        WITH METHOD "POST"
        WITH ENDPOINT "/api/v1/data-files"
        WITH HEADERS
            ADD "Accept" "application/json"
            ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
            ADD "Authorization" "Bearer {value.json.access_token}"
            ADD "Content-Type" "multipart/form-data; boundary=CorefluxQlikBoundary"
        WITH BODY "{value.json.multipart}"
```

## Examples

### Reload an app when a shift closes

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION QlikReloadOnShiftClose
ON TOPIC "plant/line2/shift/closed" DO
    SET "key" WITH GET SECRET "QLIK_API_KEY"
    PUBLISH TOPIC "analytics/qlik-cloud/reloads/create" WITH { "access_token": {key}, "body": { "appId": "116dbfae-7fb9-4983-8e23-5ccd8c508722", "partial": false, "variables": { "vLine": "Line 2" } } }
```

The response on `analytics/qlik-cloud/reloads/create/response` carries the reload `id` and `status` `QUEUED`. Publish `{"access_token":"...","reload_id":"<id>"}` to `analytics/qlik-cloud/reloads/get` to follow it to `SUCCEEDED` or `FAILED`.

### Upload a CSV rollup as a data file

`DataFileUpload` sends `multipart` unchanged with `Content-Type: multipart/form-data; boundary=CorefluxQlikBoundary`. Build the body with CRLF line endings: a `Json` part with the file name, then a `File` part with the CSV.

```text theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
--CorefluxQlikBoundary
Content-Disposition: form-data; name="Json"
Content-Type: application/json

{"name":"plant/oee-2026-10-05.csv"}
--CorefluxQlikBoundary
Content-Disposition: form-data; name="File"; filename="oee-2026-10-05.csv"
Content-Type: text/csv

line,shift,good,scrap,oee
Line 1,A,4210,37,0.81
--CorefluxQlikBoundary--
```

A 201 response returns the file `id`. If the name already exists Qlik answers 409; publish to `analytics/qlik-cloud/data_files/replace` with `data_file_id` instead, then reload the app.

### Get Qlik events back on MQTT

`WebhookCreate` registers a webhook that posts Qlik events, such as `com.qlik.v1.reload.finished`, to a URL you choose. Point it at a separate REST API connector with `ENABLE_SERVER` on the broker. Pause it later with `WebhookUpdate` and `[{"op":"replace","path":"/enabled","value":false}]`.

## Not packaged

* DELETE operations (apps, data files, reload tasks, webhooks, spaces, users), on purpose. Pause reload tasks with `state` `Disabled` and webhooks with `enabled` false.
* App import, export and publish; space, user, API key and tenant administration.
* The Qlik associative engine (QIX), which uses WebSockets, not REST.
* The webhook receiver itself (use a separate `ENABLE_SERVER` connector).

## Testing status

Offline checks pass: static checks on the template, a check of every event against the Qlik Cloud OpenAPI specs, schema validation of every example body, and a local mock with round-trips (token, reload, data file upload, webhook update) plus negative controls. A live test needs a Qlik Cloud tenant and an API key; see `TEST.md` in the package.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.