Publish with Run
You need a built browser app, either one HTML file or a folder with index.html such as dist, and Run turned on for your organisation. The terminal commands need Node.js 22.15 or later. Run describes who can open an app, the limits and where data is stored.
Create the app
- In the hub, open Run and choose New app.
- Type the app's name. Run suggests an address from the name; change it if you want another, or empty the field to get a random address.
- Choose Create app. The app opens on its own page, ready for the first version.
A new app is Organisation only: members of your organisation sign in to komplai to open it.
Build for Run
- Deliver one self-contained HTML file, or a folder or ZIP file with
index.htmlat the top. In a ZIP file, everything may also sit inside one folder. - Build React, Vite or another framework on your own computer and publish the built output, never the source project.
- Use relative URLs, or URLs that start with
/on the app's own address. Client-side routes without a file extension get the app's HTML; a missing JavaScript or CSS file stays a 404. - The packer leaves out hidden files,
node_modulesand source maps, and refuses symbolic links and project files such aspackage.json. Keep.envfiles and keys out of the build: a browser app cannot keep a secret.
Try it on your own computer
dev serves your build at http://localhost:5180 the way Run does, with the same SDK, routes and Content-Security-Policy, and a local stand-in for komplai:
npx --package=https://hub.komplai.dk/komplai-publish/komplai-publish-0.2.1.tgz komplai-publish dev ./dist- Every use the app declares in
komplai.jsoncounts as approved. An undeclared use is refused withcapability_not_approved, as in Run. - You are
Udvikler, an administrator.--as NAMEchanges the name and--membermakes you a member. Athttp://localhost:5180/_komplai/deveach browser signs in as its own person, so a private window can be a second person, and you can end the session to trysession_expired. - Saved data is kept in memory until you stop the server.
- Redact finds patterns only. AI is refused with
ai_not_available_in_devunlessKOMPLAI_API_KEYholds a personal API key withchat:complete; calls then go to komplai's API in the EU cloud or through the connection the app names, and EU cloud usage counts towards your organisation's monthly AI spending cap. - Events, errors, counters and every refusal are printed in the terminal.
--port chooses another port.
Create a publishing key
- On the app's page, under Publish the first version (or Publish a new version), choose Terminal.
- Choose Create a publishing key. The key is shown only once.
- Copy the command Run shows. It contains the address of the command-line tool and the app's upload address, but no key.
A publishing key can only publish new versions of that one app. It cannot create apps, change an app's name, address or who can open it, open apps that are Organisation only, or use komplai's AI. It expires after 7 days, and it stops working when it is revoked, when the person who created it leaves the organisation or can no longer change the app, when the app is deleted or when Run is switched off for the organisation. An app can have 10 active keys. Revoke a key under Publishing keys on the app's page.
Keep the key out of code and chats
Never put the publishing key in code, Git, a command or a chat with an AI coding assistant. Paste it when the terminal asks, or set KOMPLAI_PUBLISH_TOKEN privately in your terminal or your tool's environment.
Publish a version
See what would be published first. This lists every file, checks the manifest as Run does, and uploads nothing:
npx --package=https://hub.komplai.dk/komplai-publish/komplai-publish-0.2.1.tgz komplai-publish ./dist --dry-runThen run the command from Run in your project folder. Replace ./dist if your built files are in another folder:
npx --package=https://hub.komplai.dk/komplai-publish/komplai-publish-0.2.1.tgz komplai-publish ./dist --endpoint https://hub.komplai.dk/api/publish/APP_UUID/releasesThe command shows the target app, asks you to confirm and asks for the key at a hidden prompt. The new version is live at once at the app's address. Open the address and check the app: an app that is Organisation only asks you to sign in to komplai, because the publishing key gives no access to view the app.
If the connection fails or the outcome is uncertain, the upload may still have gone through. Check in Run which version is live before you try again. The command never retries by itself.
To avoid typing the upload address each time, save it once. init writes .komplai-publish.json with the upload address and the build folder, never the key:
npx --package=https://hub.komplai.dk/komplai-publish/komplai-publish-0.2.1.tgz komplai-publish init --endpoint https://hub.komplai.dk/api/publish/APP_UUID/releases
npx --package=https://hub.komplai.dk/komplai-publish/komplai-publish-0.2.1.tgz komplai-publish ./distTo upload the files yourself with Upload file on the app's page, pack them into a ZIP file without sending anything:
npx --package=https://hub.komplai.dk/komplai-publish/komplai-publish-0.2.1.tgz komplai-publish pack ./dist --out app.zipInstall the command-line tool only from the package address in the command Run gives you.
Publish from a pipeline or a script
The upload API takes the file itself as the request body. Send a ZIP file as application/zip or a single HTML file as text/html, with the publishing key as a Bearer token:
curl https://hub.komplai.dk/api/publish/APP_UUID/releases \
-H "Authorization: Bearer $KOMPLAI_PUBLISH_TOKEN" \
-H "Content-Type: application/zip" \
--data-binary @app.zipThe command-line tool works in a pipeline too. It reads the key from KOMPLAI_PUBLISH_TOKEN, and --yes skips the confirmation:
npx --package=https://hub.komplai.dk/komplai-publish/komplai-publish-0.2.1.tgz komplai-publish ./dist --endpoint https://hub.komplai.dk/api/publish/APP_UUID/releases --yesThe endpoint is for terminals and build tools. It refuses any request with an Origin header, so JavaScript in a browser cannot call it. Because a publishing key expires after 7 days, a pipeline needs a new key from Run after that.
A successful upload answers HTTP 200 with the app's details: id, activeRelease (the version that is now live), visibility and the saved versions in releases, each with its id, when it was created, its unpacked size in bytes, its number of files and the SHA-256 checksum of the uploaded file. Ignore fields you do not recognise. If the archive or a capacity check fails, the version that was live stays live.
| Status | What happened |
|---|---|
| 400 | The archive or format is invalid, or the manifest cannot be used (invalid_manifest) |
| 401 | The key is invalid, expired or revoked |
| 403 | Missing permission, an Origin header, or Run is switched off for the organisation |
| 409 | The app is public and the version declares uses that need sign-in (public_app_capabilities) |
| 413 | The upload is over the limit |
| 429 | The traffic or concurrency limit is reached |
| 503 | Run is unavailable, or the outcome is uncertain |
| 507 | No room for the version in storage |
The body is JSON with an error code and no credential details. After a 503, check in Run which version is live before you send the file again.
Publish with your AI coding assistant
- On the app's page, under Publish a new version, choose AI coding assistant and Download the guide (ZIP file). The same file is at hub.komplai.dk/komplai-publish/komplai-publish.zip.
- Unzip it and put the
komplai-publishfolder in.claude/skillsin your home folder for Claude Code, or in.codex/skillsfor Codex. In a project, Claude Code also reads.claude/skillsand Codex reads.agents/skills. Other assistants can readSKILL.mdandAPI.mdin the folder. - Create a publishing key and give the assistant the command from Run, never the key.
- Start a new conversation and ask the assistant to build or publish the app. In Claude Code you can also type
/komplai-publish.
The assistant tries the app with dev, checks it with --dry-run and runs the command; you paste the key when the terminal asks. Once you have approved the publication and the target app, it may add --yes to skip the confirmation. If the outcome is uncertain, it does not try again: check in Run which version is live first.
Use komplai from the app
Load the SDK from the app's own address. /_komplai/run/v1.js defines komplai on the page, and /_komplai/run/v1.mjs exports it as a module. The SDK sends every call to the app's own address with the person's app session, and Run checks the session, the person's access and the administrator's approval on every call. Never call the hub or komplai's API from the app, and never put a key, token or password in it.
Declare what the app uses in komplai.json next to index.html. With Vite, put the file in public/ so the build copies it into dist. A single HTML file can carry the manifest in <script type="application/komplai+json"> in its <head>. The manifest is served with the app, so it must hold nothing secret, and it can be at most 16 KB.
{
"sdk": 1,
"uses": {
"identity": { "fields": ["name"], "reason": "Shows who booked the room." },
"data": {
"bookings": {
"scope": "app",
"write": "members",
"index": ["room", "day"],
"retainDays": 365,
"reason": "Bookings everyone in the app can see."
}
},
"log": { "events": ["booking_created"], "reason": "Records bookings in the Audit log." }
}
}<script src="/_komplai/run/v1.js"></script>
<ul id="bookings"></ul>
<script type="module">
const list = document.getElementById("bookings");
try {
const me = await komplai.me();
const bookings = komplai.data.collection("bookings");
const booking = await bookings.add({ room: "A", day: "2026-11-02", name: me.name });
await komplai.log.event("booking_created", { ref: booking.id });
const { items } = await bookings.list({ where: { room: "A" } });
for (const doc of items) {
const item = document.createElement("li");
item.textContent = `${doc.data.day}: ${doc.data.name}`;
list.append(item);
}
} catch (error) {
list.textContent = error.message;
}
</script>| Call | Needs in the manifest | Answers |
|---|---|---|
komplai.app(), komplai.theme.current, komplai.locale | nothing | The app's facts, light or dark, and da or en |
komplai.session | nothing; a public app has no session | expiresAt(), onExpired(fn), renew() and signOut() |
komplai.me() | identity | memberId and the approved fields |
komplai.ai.chat({ messages }) | ai.chat | { text, model, processing, usage } |
komplai.redact.findings(text), .pseudonymise(text), .restore(text, placeholders) | redact.text | Findings with offsets and type, or text with labels such as [PERSON-1] |
komplai.data.collection(name) | data | add, get, list, update, remove, changes, watch and exportMine |
komplai.log.event(name, { ref }), .error(error), .count(name) | log.events, log.errors, log.counters | Writes the event, report or count |
In a collection with scope set to person, each person sees only their own documents. With app, everyone who can open the app reads them, and write decides whether all members (members) or only the app's creator and your organisation's administrators (managers) write. With organisation, several apps share the collection. A member changes and removes only the documents they created; the app's creator and the administrators can change any. Pass { ifVersion: doc.version } to update when it must not overwrite someone else's change.
When a call is refused, the app gets a KomplaiError with a code, a status and a message in the person's language. Show the message, and write data from komplai into the page with textContent, never innerHTML. The codes you meet first:
capability_not_approved: an administrator has not approved this use for the app yet.session_expired: the person is no longer signed in to the app.komplai.session.renew()signs in again and returns to the page.app_not_assigned: the person has no access to the tool the app uses, such as Chat or Redact.version_conflict: someone changed the document meanwhile. Fetch it and try again.
API.md in the guide lists every call, option, limit and error code.
Approve what the app uses
After you publish a version that declares uses, an administrator opens the app's page in Run and goes to What the app may use. Each use shows the app's reason and Approve or Decline; an approved use can be revoked later.
- AI: the administrator chooses where the app may use AI, komplai's EU cloud within your organisation's monthly AI spending cap or one of your organisation's own connections, and sets the app's own monthly limit. When the limit is reached, the app's AI stops until the next month.
- Redact: the app uses patterns and NER on komplai's servers in Germany. The administrator can also allow Redact's language model in komplai's EU cloud.
- Send data to …: approve where the app may send data before the rest.
Try every feature in Run after approval: the local stand-in approves everything.
Manage the app
- Versions: Use this version makes an earlier version live again.
- Take the app offline: nobody can open the app until you publish or choose a version again. Make the newest version live again brings it back.
- Errors in the app: errors the app sent from the browser in the last 30 days. Only the person who created the app and your organisation's administrators see them.
- Download the app's data: all the app's documents, also those each person saved for themselves only. Only your organisation's administrators can download it.
- Delete app and all versions: type the app's name to confirm. Its publishing keys stop working, links to it stop working, and its address becomes available to others at once. This cannot be undone.