# Deploy tokens and the ext-host CLI

> Mint an exh_ deploy token and push an extension from your machine or CI. Why a deploy is a sync that removes files not in the upload.

Canonical: https://ext.tableauops.com/knowledge/deploy-token-and-cli
Updated: 2026-09-18
Author: Eric Summers


Uploading a zip in the web UI works, but the real deploy loop is one command from your machine or zero from CI. It runs on an **account deploy token**, so nothing needs a browser session. The same token also authenticates the MCP server, so one credential covers both the CLI and your AI agent.

## Mint a deploy token

Open any extension, go to **Deploy from Git / CI**, and click **generate**. The token looks like `exh_…`. There is one token per account, it can deploy any extension you own, and regenerating it revokes the old one. Keep it out of committed files: it lives in an environment variable or a CI secret, never in `exthost.json`.

## The deploy endpoint

Deploys hit `POST /api/deploy/:slug`, where `:slug` is the extension's slug. Authentication is the deploy token, sent either as an `Authorization: Bearer <token>` header or as a `?token=` query parameter. The route is mounted before the cookie-guarded API, so it never needs a session. Responses are precise about failure: a wrong or missing token is a 401, an unknown slug is a 404, and a slug that is in the trash tells you to restore it in the web UI first.

The zero-dependency CLI wraps this endpoint. Put an `exthost.json` next to your build and push:

```jsonc
// exthost.json
{ "host": "https://ext.tableauops.com", "slug": "my-extension", "root": "dist" }
```

```bash
curl -sSL https://ext.tableauops.com/exthost.mjs -o exthost.mjs   # once
EXTHOST_TOKEN=exh_… node exthost.mjs push .
```

For CI, drop the token into a repo secret named `EXTHOST_TOKEN` and let a GitHub Action push on every commit, so `git push` becomes deploy. Because the `.trex` never changes, there is nothing to re-sideload in Tableau after a deploy.

## A deploy is a sync

This is the part to internalise: the upload is a git-style **sync**, not an append. The set of files you push becomes the extension's exact file set. Renamed or deleted files disappear from the host too, so what you built is exactly what is live. That is the point, it is how a rename propagates cleanly.

It is also exactly what happens if you run the push from the wrong directory, or a build silently emitted nothing but `index.html`. So the host guards against a push that would wipe out a live extension: if a push has few files and would delete more than half of what is already published, it is refused with a 409 that tells you how many files it would have deleted. If you truly meant it, push again with `--force`. The old bytes are recoverable either way (the host keeps file versions), but the error beats a restore.

## One token, two clients

The `exh_` deploy token is not only for the CLI. The TableauOps MCP server accepts the same token as `Authorization: Bearer <token>`, so an AI agent (Claude Code, Cursor) can publish and manage extensions with the credential you already minted. The MCP server is stateless: the token carries the account on every request. See [the MCP connector](/knowledge/mcp-connector) for connecting it.

## FAQ

### What is the deploy endpoint and method?

`POST /api/deploy/:slug`. Authenticate with the account deploy token, either as `Authorization: Bearer <token>` or a `?token=` query parameter. There is no cookie session involved.

### Will a deploy delete files I did not include?

Yes. A deploy is a sync: the uploaded set becomes the exact hosted file set, so files not in the push are removed. To stop an accidental wipe, a push that would delete more than half of a live extension's files is refused with a 409 until you re-run it with `--force`.

### Where does the token go so it is not leaked?

In an environment variable (`EXTHOST_TOKEN`) or a CI secret. `exthost.json` holds only the host, slug, and root, none of which are secret, so it is safe to commit. Never put the token in the JSON.

### Can the same token drive my AI agent?

Yes. The TableauOps MCP server accepts the same `exh_` deploy token as a Bearer credential, so one token covers both the CLI and an agent publishing through MCP. Regenerating the token revokes it for both.


---
Try it live: Host an extension — https://ext.tableauops.com/login
