Developers
Start building
Apps/Poltura CLI

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 commandForPage
poltura appCreate, build, validate and publish apps.This page
poltura checkThe verdict of review for an app or a theme, before you publish.Check before you publish
poltura themeStart, check, sync, preview and publish themes.Theme commands
poltura storeRead records, run actions and ask analytics questions in a store.Store commands
poltura mcp connectAdd the Poltura MCP server to Claude, Cursor, VS Code or Codex.Store commands
poltura tokenCreate, roll and revoke API tokens.API tokens
poltura login, logout, whoamiSign in and out, and see who you are.Sign in
poltura completion zsh|bash|fishPrint 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:

CommandsCredential
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 publishA 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 tokenYour 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 logout

poltura 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.

SettingFlagVariableDefault
API address--api <url>POLTURA_APIhttps://api.poltura.com, or the address of your last sign-in when no token is given
API token--token <token>POLTURA_TOKENnone
Store--store <id>POLTURA_STOREThe only store the credential reaches; required when it reaches several
Developer token (publish)--token <token>POLTURA_APP_TOKENnone

Exit codes and retries

CodeMeaning
0Done.
1The command failed, or a check found errors.
2Wrong usage, such as a missing flag.
3An 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-app

poltura 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.md

Its scripts run the same steps you run in your own app:

pnpm test:unit
pnpm build
pnpm validate
built recommendations@1.0.0  sha256-qwcsxz+/Sn1wjxUbCKy7Ah/ROvTpZrZMf/8OTBIGg9Q=  5 workers
built recommendations@1.0.0  sha256-qwcsxz+/Sn1wjxUbCKy7Ah/ROvTpZrZMf/8OTBIGg9Q=  5 workers
ok recommendations@1.0.0
  manifest.json  7765 bytes

Publish

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:

CheckForWhat it looks at
manifestappspoltura.app.json against the manifest schema, with the JSON pointer of every problem.
permissionsappsEvery 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.
buildappsThe same build and validation as poltura app validate, into a temp directory. Skipped while the manifest has errors.
agentapps with src/agentThe 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.
themethemesThe same theme check as poltura theme check: layout, sections, settings. Types are checked when a type pack is given (--types or POLTURA_THEME_TYPES).
lintbothThe 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.

OptionMeaning
--jsonPrint the report as one JSON line, for agents and CI.
--fixApply the safe automatic fixes of the code rules (var to const, for example). Nothing else is rewritten.
--strictExit 1 on warnings too.
--kind app|themeSkip 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

Last updated