Poltura CLI
Install the poltura command line, sign in, and use poltura app and poltura check; the theme, store, MCP and token commands have their own pages.
On this page
@poltura/cli installs the poltura binary: one tool for apps, themes, a store's data and AI tools. Not on npm yet
$ npm install -g @poltura/cli
It needs Node.js 22 or later, sends no telemetry and only calls the Poltura API origin you give it. poltura --help, poltura <topic> --help and poltura --version answer from a prebuilt table without loading the rest of the tool, so they are instant.
| Topic or command | For | Page |
|---|---|---|
poltura app | Create, build, validate and publish apps. | This page |
poltura check | The verdict of review for an app or a theme, before you publish. | Check before you publish |
poltura theme | Start, check, sync, preview and publish themes. | Theme commands |
poltura store | Read records, run actions and ask analytics questions in a store. | Store commands |
poltura mcp connect | Add the Poltura MCP server to Claude, Cursor, VS Code or Codex. | Store commands |
poltura token | Create, roll and revoke API tokens. | API tokens |
poltura login, logout, whoami | Sign in and out, and see who you are. | Sign in |
poltura completion zsh|bash|fish | Print the shell completion script; --help shows where to put it. |
Every command takes -q, --quiet (print only results and errors), --no-color and -h, --help. NO_COLOR, FORCE_COLOR, CI and output that is not a terminal are honoured. An error prints what went wrong, a fix: or hint line, and a link to the page of this site that explains it.
Sign in or use a token
Which credential a command needs depends on what it touches:
| Commands | Credential |
|---|---|
poltura store, poltura theme (all but init, check, package), poltura whoami | --token, then POLTURA_TOKEN (an API token), then your poltura login sign-in. |
poltura app publish | A developer token: --token or POLTURA_APP_TOKEN. A pltd_ token your developer organization owns (for CI), or a pltu_ personal token. It publishes releases to that organization's apps and nothing else. |
poltura token | Your Poltura ID session: --access-token and --id-token (POLTURA_ACCESS_TOKEN, POLTURA_ID_TOKEN). See API tokens. |
The CLI checks an API token's checksum before sending it, so a truncated paste fails at once. An organization token (plto_) is refused for publishing, since it cannot publish.
poltura login
poltura whoami
poltura logoutpoltura login opens Poltura ID in your browser (--no-browser prints the address instead, for SSH and other machines without one) and signs in as Poltura's own CLI client with OAuth and PKCE, answered on a loopback address of your machine. It asks for every read permission unless --permissions a,b names fewer, and you narrow or widen what is granted on the approval page. --timeout <seconds> (10 to 3600, default 300) sets how long the CLI waits. The session is kept in ~/.config/poltura/credentials.json (mode 0600; POLTURA_CONFIG_DIR moves it) and renewed before it expires. poltura whoami shows who you are, the stores you reach and your permissions there; poltura logout revokes the session and forgets it.
The sign-in is bound to this machine with DPoP (RFC 9449). poltura login makes a P-256 key pair and keeps the private key in its own file, dpop/<thumbprint>.pem next to the credentials (mode 0600). Every token exchange, every renewal and every API call carries a proof signed by that key, and API calls send the token as Authorization: DPoP <token>. A sign-in token copied off the machine without the key is refused with invalid_dpop_proof, and so is a copied refresh token: Poltura ID checks the key before it renews anything. If the key file is gone, the CLI asks you to run poltura login again. When your clock is off, the server answers with a nonce and the CLI sends the request once more with it. Node has no non-exportable key store it can reach without a native module, so protect the key file as you would the credentials. API tokens (POLTURA_TOKEN) are not bound to a key. Treat them as secrets.
If the API's certificate is signed by an authority your system trusts but Node does not (a company proxy, or the local development CA), the CLI trusts it too. It adds the system's trusted roots to Node's own before the first request. When a request gets no answer, the error says why: an untrusted certificate, a name that does not resolve, or a refused or broken connection.
| Setting | Flag | Variable | Default |
|---|---|---|---|
| API address | --api <url> | POLTURA_API | https://api.poltura.com, or the address of your last sign-in when no token is given |
| API token | --token <token> | POLTURA_TOKEN | none |
| Store | --store <id> | POLTURA_STORE | The only store the credential reaches; required when it reaches several |
| Developer token (publish) | --token <token> | POLTURA_APP_TOKEN | none |
Exit codes and retries
| Code | Meaning |
|---|---|
| 0 | Done. |
| 1 | The command failed, or a check found errors. |
| 2 | Wrong usage, such as a missing flag. |
| 3 | An action is waiting for a person to approve it. --wait on store run, theme publish and theme rollback waits for the decision instead. |
A call Poltura refused because the credential's points budget ran out never reached the store, so the poltura theme commands wait for as long as the refusal says (at most 30 seconds each time) and send it again, up to 8 times, before they give up. poltura store commands report the refusal; run them again after the wait. See rate limits.
poltura app create
Scaffold a new app project.
USAGE
$ poltura app create [FLAGS] <dir>
ARGUMENTS
dir Directory to create the project in (must be empty).
FLAGS
--template <template> Project template (choices: "hosted", "native", default:
"native").
-h, --help Show help.--template native writes UI workers only. --template hosted also writes src/server.ts: a /connect?installation=<installation id> route that starts OAuth for that installation, the /oauth/callback that finishes it and calls the App API, and a product webhook. Its README lists the environment variables the server reads. The scaffold's poltura.app.json has no appId, so poltura app publish needs --app until you add one.
created hosted app in /home/dev/my-apppoltura app
Create, build, validate and publish a Poltura app.
USAGE
$ poltura app [FLAGS] COMMAND
COMMANDS
create <dir> Scaffold a new app project.
build Bundle workers and styles and write the release manifest.
validate Check the app config.
dev Rebuild on every change to the app sources.
elements List the storefront elements the app defines.
publish Build the app and upload it as a release for review.
help [command] Show help for a command.
FLAGS
-h, --help Show help.
EXAMPLES
$ poltura app create my-app Scaffold a native app.
$ poltura app dev Rebuild on every save.
$ poltura app publish Upload a release for review.
Build the app and upload it as a release for review.
USAGE
$ poltura app publish [FLAGS]
FLAGS
--api <url> Gateway origin (POLTURA_API).
--token <token> Developer token, pltd_ or pltu_ (POLTURA_APP_TOKEN),
from Developer apps in admin.
--app <appId> App id; defaults to appId in poltura.app.json.
--admin-origin <url> Admin origin, to print the review link.
--channel <channel> Release channel (default: "production").
-h, --help Show help.poltura app build bundles each workers[].entry to IIFE JavaScript, compiles workers[].stylesheet with Tailwind (each stylesheet is at most 262144 bytes), writes digests, and parses the result as the app manifest. poltura app validate builds into a temp directory, then parses manifest.json and any JSON extensions[]. poltura app dev rebuilds on src/ and poltura.app.json changes and prints watch <digest> when the manifest digest changes. build and dev write to dist unless you pass --out <dir>.
An app with no UI, an integration that only calls the App API and receives webhooks, declares no workers: it builds and publishes as a release without a UI runtime.
Building workers needs react, react-dom, tailwindcss and @poltura/app-ui to resolve from the project, so install the project's dependencies first. The build stops with Install react in this project to build workers. when they are missing.
$ npm install
Build and validate
The complete example, Recommendations by Poltura Labs (see Examples), has this layout:
packages/app-sdk/examples/complete-app/
poltura.app.json
package.json
src/server.ts
src/elements.ts
src/workers/admin-page.tsx
src/workers/admin-product.tsx
src/workers/storefront-product.tsx
src/workers/storefront-footer.tsx
src/workers/app-page.tsx
src/styles.css
src/locales/en.json
src/locales/hu.json
README.mdIts scripts run the same steps you run in your own app:
pnpm test:unit
pnpm build
pnpm validatebuilt recommendations@1.0.0 sha256-qwcsxz+/Sn1wjxUbCKy7Ah/ROvTpZrZMf/8OTBIGg9Q= 5 workersbuilt recommendations@1.0.0 sha256-qwcsxz+/Sn1wjxUbCKy7Ah/ROvTpZrZMf/8OTBIGg9Q= 5 workers
ok recommendations@1.0.0
manifest.json 7765 bytesPublish
poltura app publish builds the app and uploads it as a release for review. It sends the release to the Poltura API in --api (or POLTURA_API) and prints published <releaseId>. With --admin-origin it also prints the review link.
poltura app publish --api "$POLTURA_API" --app <app id> --token "$POLTURA_APP_TOKEN"--api is the Poltura API origin. It defaults to https://api.poltura.com; pass it, or set POLTURA_API, only for another environment. --app is the app id from Developer apps. It can come from appId in poltura.app.json instead, but the scaffold does not write one. Pass --channel to pick a release channel other than production.
A release on the development channel cannot be installed by merchants (release_not_production). Review and install: Publish, review, install.
Check before you publish
poltura check [dir] is the one command to run before you publish. It gives you the verdict Poltura's review gives, in a few seconds and without a network. It works out whether the directory is an app (it holds poltura.app.json) or a theme (it holds theme.json), and runs what applies:
| Check | For | What it looks at |
|---|---|---|
manifest | apps | poltura.app.json against the manifest schema, with the JSON pointer of every problem. |
permissions | apps | Every permission id exists and an app may ask for it. A misspelled id comes with the closest real ones. A sensitive permission is a warning: a release that asks for it needs marketplace review. |
build | apps | The same build and validation as poltura app validate, into a temp directory. Skipped while the manifest has errors. |
agent | apps with src/agent | The agent entries build into a manifest: key naming, at most 20 tools per toolset, existing permissions, destructive actions with confirm: 'always', and examples that match their input. |
theme | themes | The same theme check as poltura theme check: layout, sections, settings. Types are checked when a type pack is given (--types or POLTURA_THEME_TYPES). |
lint | both | The Poltura code rules, run with Oxlint on src with no setup. |
The code rules keep what comes in from outside parsed and typed: no unknown parameters or return types, no dictionaries of unknown, any or object, a // SAFETY: comment on every type assertion, and no runtime typeof on unparsed values. Parse the value with a schema where it enters your code and pass the typed result on.
| Option | Meaning |
|---|---|
--json | Print the report as one JSON line, for agents and CI. |
--fix | Apply the safe automatic fixes of the code rules (var to const, for example). Nothing else is rewritten. |
--strict | Exit 1 on warnings too. |
--kind app|theme | Skip the detection. |
--types <packRoot> | Theme type pack, as for poltura theme check. |
The human output is grouped by file; every finding has the rule that raised it and a fix: line saying what to change.
poltura.app.json
✖ /permissions/required/2/id "orders.reed" is not a permission id. [permissions/unknown]
fix: Did you mean "orders.read" or "orders.refund"? The full list is at https://developers.poltura.com/apps/permissions.
src/orders.ts
✖ 2:7 A `typeof` check narrows a representation without establishing its contract. Parse input at its I/O boundary, then branch on the domain value. [anti-slop/no-runtime-typeof]
fix: Parse the value with a schema at the boundary, then branch on the parsed value.
skipped build: fix the manifest first
✖ failed: 2 errors, 0 warnings (app, 1.4s)With --json the report has this shape:
{
"v": 1,
"kind": "app",
"root": "/work/my-app",
"ok": false,
"totals": { "error": 2, "warning": 0, "info": 0 },
"findings": [
{
"severity": "error",
"rule": "permissions/unknown",
"message": "\"orders.reed\" is not a permission id.",
"hint": "Did you mean \"orders.read\" or \"orders.refund\"? ...",
"file": "poltura.app.json",
"pointer": "/permissions/required/2/id"
}
],
"steps": [{ "id": "manifest", "ran": true }, { "id": "agent", "ran": false, "note": "the app declares no src/agent entry" }],
"fixed": 0,
"durationMs": 1432
}severity is error, warning or info. A finding carries file (relative to the project) and, where it has them, line, column and pointer. rule is stable, so you can match on it: manifest/*, permissions/*, build/failed, agent/*, theme/*, or the name of a code rule such as anti-slop/no-runtime-typeof. Exit codes: 0 when there are no errors (and no warnings with --strict), 1 when there are, 2 when the directory is neither an app nor a theme.
poltura check runs the app's own code only to read its src/agent entry; it does not call the network. Run it in CI with poltura check --json --strict, and before every poltura app publish.
Themes
Theme commands, including poltura theme check, have their own page: Theme commands.
Next
- Ship to merchants: what happens after
publish. - Theme commands, Store commands and API tokens.
- Permissions and topics: the generated reference tables.