Publish from Claude in Slack
Let Claude Tag publish bundles from its cloud sandbox: allow the Flitz hosts, install the CLI, inject the organization API key at Claude's proxy, and ask in a thread.
Claude Tag puts Claude in your Slack channels, where it works in a cloud sandbox with your repositories. With the setup on this page, anyone in a channel can ask
@Claude publish a flitz bundle from branch mainand Claude checks out the branch, runs flitz publish, and posts the landing
page and bundle links back in the thread. A publish, including the SDK download
and the build, takes about a minute.
The organization API key never enters the sandbox or the Slack history: Claude's network proxy adds it to requests on their way to the Flitz service.
Prerequisites
- Claude Tag is set up for your Slack workspace, and Claude can reach the app's repository through the Claude GitHub App.
- The project pins an SDK in a committed
flitz.yaml(flitz sdk use). - Your Flitz organization has an API key, from the Settings page of the
portal or
flitz apikey create. Keep the plaintext at hand for the connection below. - An Owner of your Claude organization, or a Claude Tag admin, to add the connection and network settings. Shared cloud environments are managed by Owners only.
Setup steps
- Allow network access in the sandbox.
- Install the Flitz CLI.
- Provide the API key in the environment.
- Add the Flitz skill.
- Publish the bundle.
Allow network access
The sandbox reaches only the hosts you allow. Flitz needs three:
| Host | Used for |
|---|---|
*.flitz.dev | The CLI installer and update feed (download.flitz.dev), the Flitz service (api.flitz.dev), and the links (d.flitz.dev). The bare flitz.dev is not needed. |
storage.googleapis.com | The SDK download and the bundle upload, through short-lived signed URLs. |
pub.dev | Your app's Dart and Flutter package dependencies, fetched during the build. |
All use HTTPS on port 443. Add *.flitz.dev to the Custom allowed-domains
list of the cloud environment the channel runs in, with the default
package-manager list included; storage.googleapis.com and pub.dev are
already part of that default list. With
customer bundle storage (Enterprise), the bundle
upload goes to your own bucket instead. Amazon S3 and Google Cloud Storage
hosts are on the default list; for Azure Blob Storage, also allow your storage
account's host, <account>.blob.core.windows.net. See
network access
and which environment a channel uses.
Install the CLI
Claude installs the CLI in the thread before it publishes:
curl -fsSL https://download.flitz.dev/install.sh | shTo have the CLI ready in every thread, add the same command to the setup script of the cloud environment, where you also set the placeholder key.
See Install for what the script checks. You do not install the
SDK: flitz publish --yes downloads the
pinned SDK on its first run.
It is the Flitz SDK the project pins; a stock Flutter SDK cannot build bundles.
Provide the API key
Store the key as a Claude Tag connection, not as an environment variable. In the Claude Tag admin settings, open the Access bundle that covers the channel and add a custom connection on its Credentials tab (labels in the Claude UI may differ slightly):
- Credential type: Bearer.
- Allowed websites:
api.flitz.dev. - Value: the plaintext organization API key.
When someone else holds the key, send them a setup link instead; the connection is active only after an Owner or admin approves it. See Add a connection.
Claude's proxy now replaces the Authorization header of every request to
api.flitz.dev with the real key. The sandbox, the model, and the thread
never see it.
Never paste the key into Slack
A key in a message stays in the channel history. A key in an environment variable is readable by every Claude session that uses the environment, in every channel. The connection is the only place it belongs. When you rotate the key, update the connection too.
Set a placeholder key in the sandbox
The CLI still needs FLITZ_APIKEY to hold a value. If the variable is not set,
the CLI fails its local login check with [fail] you are not logged in to Flitz
(exit 3) and sends no request at all. Give it a placeholder for the proxy to
replace:
- Open the settings of the cloud environment the channel runs in (see which environment a channel uses).
- Add
FLITZ_APIKEY=fake-keyto its environment variables. Any non-empty value works. Never put the real key there. - Save the environment, then start a new thread: one already running may not pick up the change.
To skip the environment change, prefix the publish command with
FLITZ_APIKEY=fake-key instead, as in Publish.
Add the Flitz skill
Claude learns how to publish from the publish-flitz-bundle skill in the
lean-flitz plugin, which LeanCode maintains in
leancodepl/ai-plugins. The skill
tells Claude to find the project's flitz.yaml, install the CLI when it is
missing, run flitz publish --yes --json, post the landing page in the thread,
and explain a failure by its exit code. Without it, Claude has only the request
to go on.
An Owner or Claude Tag admin adds the plugin once: fork
leancodepl/ai-plugins as a private
repository.
To skip the fork, zip the plugins/lean-flitz folder and upload it as a
plugin instead. Keep the fork in sync to pick up skill
updates. Either way, the plugin applies
to new threads only, so start a fresh thread after adding it. See Attach plugins
and Set up a skills repository.
Publish
Name the branch in the request:
@Claude publish a flitz bundle from branch xyzWith the Flitz skill on, Claude checks out that branch and publishes it. Outside a CI system, nothing fills in the publish labels, so pass them explicitly. From the repository root:
branch=xyz # the branch named in the request
git checkout "$branch"
sha=$(git rev-parse HEAD)
FLITZ_APIKEY=fake-key flitz publish --yes --json \
--name "$branch" \
--version-name "$branch-${sha:0:7}" \
--comment "$branch @ $sha"FLITZ_APIKEY=fake-key: any non-empty value. The CLI refuses to send a request without a credential, so it needs a placeholder for the proxy to replace. Never put the real key here.--yesletspublishdownload the pinned SDK unattended, and create the app whenflitz.yamlnames one your organization does not have yet.--jsonprints{ "id", "page_url", "deeplink", "bundle_url", "app", "track", "track_url" }on stdout; Claude postspage_urlandbundle_urlin the thread. See Output and --json.- The track is the checked-out branch, so every publish from
xyzlands on the same track, and itstrack_urlalways opens that branch's newest build. See App, track, and scheme. --name,--version-name(at most 64 characters), and--commentlabel the publish in the publish history, where it is recorded as published by the organization API key.
Troubleshooting
| You see | What it means | Remedy |
|---|---|---|
[fail] you are not logged in to Flitz (exit 3) | FLITZ_APIKEY was not set, so the CLI sent nothing. | Prefix the command with FLITZ_APIKEY=fake-key. |
[fail] the Flitz credential was rejected (exit 3) | The placeholder reached the service unchanged: no connection injected the key. | Check that a Bearer connection with allowed website api.flitz.dev is active in an Access bundle on the channel's scope. A bundle attached after the thread started may not reach it; start a new thread. If the key was rotated, update the connection. |
403 on CONNECT, or Claude reports a host blocked by the network egress proxy | That host is not allowed. | Allow the hosts listed above. |
flitz: command not found | The CLI is not installed in this sandbox. | Run the install command. If the installer asks you to add a directory to your PATH, do so. |
Everything else fails as it would on your own machine; see CLI troubleshooting and Exit codes.
Publish from GitHub Actions
Publish a bundle for every pull request with the Flitz GitHub Actions: install the CLI and the pinned SDK on a runner, publish, and link the build from a pull-request comment.
Portal
Your organization's publishes, apps and tracks, API key, single sign-on, members, and billing at app.flitz.dev.