HUB.KI DocsCustom apps
HUB.KI Docs
Custom apps

Manifest

The manifest.json reference — fields, scopes, REST API scopes, and versioning.

View as Markdown

Every app folder contains a manifest.json. The platform validates it when it syncs a source; a manifest that fails validation is skipped with an error.

{
  "appId": "ai.justadd.stickyboard",
  "slug": "stickyboard",
  "name": { "en": "Stickyboard", "de": "Stickyboard" },
  "version": "2.3.0",
  "sdkVersion": "1.0.0",
  "artifactPath": "app.html",
  "iconPath": "icon.svg",
  "events": ["board-updated"],
  "scopes": ["workspace:own"],
  "apiScopes": ["meetings:read"],
  "connectOrigins": ["https://api.example.com"]
}

Fields

FieldTypeRequiredDescription
appIdstringyesGlobally unique identifier. Reverse-DNS is recommended (com.example.app).
slugstringyesShort, URL-safe name used in the app's route.
nameRecord<locale, string>yesLocalized display names. Include at least en; it is the fallback.
versionstringyesApp version, numeric and dotted (1.4.0, optionally 1.4.0-beta.1). Bump it to ship an update.
sdkVersionstringyesBridge contract you target, as exact major.minor.patch. The host supports major 1.
artifactPathstringyesPath to the single HTML file, relative to the folder.
iconstring*A Lucide icon name, e.g. sticky-note.
iconPathstring*Path to an icon image instead (svg, png, jpg, webp, gif; ≤ 256 KB).
eventsstring[]noEvent names the app may publish or receive.
description{ [locale]: string }noShort description shown on the marketplace card, keyed by locale like name.
scopesstring[]noCapabilities the app requests (see below).
apiScopesstring[]noHUB.KI REST API scopes the app requests (see REST API scopes).
connectOriginsstring[]noBackend origins the app may call from the browser (see Network access).

* Provide either icon or iconPath.

Scopes

Scopes are the only capabilities an app has beyond rendering itself. Request the minimum you need.

ScopeGrantsApproval
workspace:ownThe app's own private, per-user workspace (documents and files).None — always available.
store:ownA per-user, per-version key/value store for the app.None — always available.
documents:readRead the user's shared organization workspace documents.Org admin must approve the scope.
user:readRead the signed-in user's name and email.Org admin must approve the scope.
api:accessHand the app's own backend short-lived tokens for the user (see Backend access).Org admin must approve the scope.
responses:runRun the HUB.KI AI (Responses API) at the organization's expense via an automatically issued API key.Org admin must approve the scope.
capability:wasmRun WebAssembly in the app sandbox (wasm-unsafe-eval).Org admin must approve the scope.

workspace:own and store:own are granted automatically. Any other scope must be approved by an organization admin from Manage availability before the app can use it — an un-approved call is rejected.

Web Workers are available to every app (worker-src blob:), so heavy work can leave the main thread without asking for anything. WebAssembly is different: it widens what may execute in the sandbox, so it is opt-in through capability:wasm and, like any other approval, the org admin sees it before turning it on. Libraries such as pdf.js that can run either way will fall back to their non-WASM path when it is not granted.

REST API scopes

apiScopes lists the parts of the HUB.KI REST API (/v1/*) the app wants to call as the user who has it open — from the app through createAppApiClient / requestApi, or from its backend with the REST API token. Every REST API scope except responses:write is allowed; apps run AI through the responses:run scope and hub.responses instead.

"apiScopes": ["meetings:read", "documents:write"]
ScopeAllows
documents:readList, get and search workspace documents, and read their children and ancestors.
documents:writeCreate, update, delete, move and share documents.
meetings:readRead meetings, their summaries and transcripts, and meeting series.
meetings:writeRename and delete meetings, and create one from an upload.
files:readRead a file and get its download URL.
files:writeUpload and delete files.
agents:readList agents.
skills:readList skills.
skills:writeCreate personal skills as the user and update skills that user created.
connectors:readRead connectors, connections, their tools and tool-call policies.
connectors:writeChange a connector's tool-call policy.
connectors:invokeRun connector tools.
connectors:approveRun connector tools that need approval, asserting the approval on the user's behalf.
usage:readRead API usage (/v1/usage).
statistics:readRead usage statistics (/v1/statistics).
traces:readRead traces (/v1/traces).
tracing:readRead the organization's trace ledger (/v1/tracing); only for users who may view tracing.
tracing_content:readRead the conversations of chats shared for debugging (/v1/tracing/traces/{traceId}/content, with tracing:read); only for users who may view tracing.
chats:readRead chats (/v1/chats).
helpdesk:readRead helpdesk tickets and their messages, and list helpdesk agents.
helpdesk:writeCreate tickets and replies, and update tickets.

With any approved REST API scope the app can also call GET /v1/models and GET /v1/jobs.

Like scopes, declaring a REST API scope is a request. An organization admin approves each one in Manage availability; scopes that change data or act on the user's behalf — every *:write, connectors:invoke, connectors:approve, tracing:read and tracing_content:read — are marked Sensitive there. Requests act as the user who has the app open, so they never reach more than that user may see. You do not need api:access to call the REST API from the app; you need it only to hand the REST API token to your backend.

An entry that is not in the table above — including responses:write — skips the app on sync with the problem api_scopes_unknown.

Validation

A sync validates every manifest and its files. An app that fails any rule is skipped as a whole — and if it was published before, it goes offline (see Syncing). The admin interface only reports the number of skipped apps, so check these before uploading:

RuleTypical mistake
The file is valid JSON and every required field is present.Trailing comma; missing name or artifactPath.
appId: letters, digits, ., -, _; segments never empty; at most 255 characters.Spaces, a leading or trailing dot.
slug: lowercase letters and digits separated by single hyphens; at most 128 characters.Uppercase letters, underscores, spaces.
sdkVersion: exactly major.minor.patch with a supported major (1)."1", "1.0", "^1.0.0", "2.0.0".
icon or iconPath is set. An icon file is svg, png, jpg, webp or gif, ≤ 256 KB.Neither given; icon too large.
Every entry in scopes is a known scope (table above); at most 20.A typo such as storage:own.
Every entry in apiScopes is a REST API scope; at most 22.responses:write; a typo such as meeting:read.
Every entry in connectOrigins is an exact https:// or wss:// origin (see below); at most 8 are used.A path, a wildcard, plain http:// or ws://.
artifactPath points to an existing file of at most 10 MB.Manifest says app.html, folder holds index.html.
The artifact starts like an HTML document (<!doctype html or <html near the top).Uploading a JavaScript bundle instead of the page.
appId + version appears in only one folder of the source.Copying a folder without bumping version.
appId is not already published by another source.Reusing an example appId.

Further limits: name values up to 200 characters, description values up to 2,000, version up to 32, and up to 100 events of up to 100 characters each.

Network access

Apps run in a sandboxed iframe whose Content-Security-Policy blocks all network requests by default — fetch, XMLHttpRequest, WebSocket, EventSource, and sendBeacon to any origin, including your own backend. To let your app talk to a server, list its exact origins in connectOrigins:

"connectOrigins": ["https://api.example.com", "wss://realtime.example.com"]

Rules:

  • Each entry must be an exact origin — scheme + host + optional port, no path, no wildcards. Use https:// for HTTP requests and wss:// for WebSockets; http:// and ws:// are allowed only for localhost / 127.0.0.1 during development. Up to 8 origins.
  • The scheme is part of the origin. https://api.example.com does not allow wss://api.example.com, so a host you call with both fetch and WebSocket needs two entries. Count them against the limit of 8 when you plan your manifest.
  • Declaring an origin is a request, not a grant. An organization admin approves each origin in Manage availability; only approved origins are added to the app's connect-src. Unapproved origins stay blocked.
  • Requests and WebSocket handshakes from the app carry Origin: null, because the sandbox has no origin of its own. Your server must accept that origin.
  • connect-src only covers connections the page opens itself. WebRTC media traffic is not governed by it, but WebRTC SDKs usually open a wss:// signaling connection, and some also fetch configuration over https:// — declare both.

See Calling HUB.KI from your backend for the end-to-end flow and the CORS requirement.

Versioning

  • version identifies your release. Publish a new version to roll out changes; the platform re-reads the artifact on its next sync.
  • sdkVersion declares the bridge contract. If its major version is not supported, the host refuses to run the app and shows an upgrade notice.