Build an app
From an empty folder to a submitted app. Requirements, capability check, develop on a dev store, host, deploy, submit.
By the end of this page you will have an app running on your own dev store, deployed as a version, and submitted for review.
Check your Node.js version first
Run node -v. The tools need 22.14 or later. Older 22.x releases install the packages and
then fail further along, so a version check now saves a confusing error later.
Requirements
| Requirement | Detail |
|---|---|
| Node.js | 22.14 or later |
| Queek account | Create one |
| Developer access | Dashboard → account menu → Developer → accept the Developer Terms |
| Dev store | One your developer account owns (Developers → Dev stores). queek app dev offers to create the first one. See Dev stores. |
| Queek CLI | 0.16 or later: npm install -g @usequeek/cli, or prefix each queek command with npx -y @usequeek/cli. The starter does not install it for you. |
| Hosting | Only to go live. See Host the app |
The first command that needs authorisation opens your browser to approve sign-in. Without a browser, pass --no-browser to sign in with a device code. Keep secrets out of queek.app.toml and source control; the CLI writes the registration secret to .queek/.env.local, which is gitignored.
Check the platform can do it
Find the capabilities your app needs in App capabilities before designing anything. The overview also lists example apps and the capabilities they use. The same data is available as capabilities.json and as plain text in llms.txt, which gives the verdict, limits and workaround for every capability in one document. Read it first when an AI agent builds the app. Each example use case in the overview, such as the translation app, lists the capabilities it is built from and the ones to check before you start.
| Status | Meaning |
|---|---|
| Available | Works as described. |
| Partly available | Read what is missing and the workaround first. |
| Not yet available | Not offered yet. Narrow the feature, use a listed workaround, or leave it out. |
| Not offered by design | Queek does not offer it on purpose. The entry says why and what to use instead. |
Do not infer support from an API name, and do not call undocumented endpoints.
Read next
- App manifest
- App SDK
- Embedded apps and the dashboard bridge
- Merchant API: overview, authentication, scopes, idempotency and rate limits, safe integrations, errors
- Webhooks
- merchant.json, the typed spec
- The page for your use case, for example Translate store content for a translation app, or Dev stores for test data
Build with an AI coding agent
Install the AI toolkit skills, then give the agent this prompt:
Build this Queek app: [what it does and who it is for].
Start at /docs/apps/build-an-app and follow it in order.
Check the capabilities document (capabilities/llms.txt) first. Build only
what it marks supported, or partial within its limits and workaround.
Learn only from the public docs. Never invent endpoints or fields.
Request the smallest Merchant API scopes that cover the task.
Send Idempotency-Key on writes. Verify webhook signatures.
Check `node -v` is 22.14 or later before installing anything.
Develop with `queek app dev`. If the app touches shopper-facing content,
use a separate development app: queek app dev -c development.
Read /docs/apps/embedded-apps before building the embedded page.
Record every docs gap in DOCS-GAPS.md.
Never ask the developer to paste secrets.1. Scaffold a project
npm create @usequeek/app my-app
cd my-appThis creates the project and queek.app.toml. queek app init my-app is equivalent.
2. Configure access
Edit queek.app.toml: set the name and slug, list only the scopes the app needs under [access], and set the install and uninstall URLs. Add webhook topics and extensions only if the app uses them. See the manifest reference.
3. Run on a dev store
queek app devThis is the whole development loop in one command, and it works on a brand-new slug:
- Registers the app. A slug Queek has not seen yet is registered first, the way
queek app deploy --no-releaseregisters it: a development version, nothing served. The signing secret goes to.queek/.env.localand is never printed. - Picks the dev store. See Dev stores. With none, it offers to create one with test data.
- Starts a tunnel so Queek can reach your machine, and waits for it. See The tunnel.
- Registers a development build with the tunnel's addresses and installs it on the dev store with every scope in
queek.app.tomlgranted automatically. There is no consent screen. - Starts your app with the command in the
[dev]table, withAPP_BASE_URL,PORT,NODE_ENV=development,QUEEK_API_BASEand the credentials from.queek/.env.localin its environment. - Prints the ready block: the tunnel address, the Preview URL and the address the dashboard embeds the app from.
Saving queek.app.toml registers the build again. Changes to the [dev] table need a restart. Ctrl+C stops the app and the tunnel.
Preview
There is no separate preview mode: the dev install is the preview. Open the Preview URL the command prints. It has the form <dashboard address>/open-store?store=<dev store id>&app=<app slug>: it switches the dashboard to your dev store and opens the app inside it, exactly as a merchant would see it. An embedded page that never loads there is covered in Embedded apps.
Dev stores
A dev store is your own sandbox store (see Dev stores). queek app dev only installs on one:
- It must be a dev store your developer account owns. The list is under Developers → Dev stores in the dashboard. Merchant stores and merchant test stores are refused, and so is a store that belongs to someone else. The error lists the dev stores you own.
--store <p_id|slug|name>names the store and always wins. Without it, the store you used last is reused (it is remembered in.queek/dev.json), otherwise your only dev store is used without asking. With several and no remembered choice, a terminal shows a menu that includes Create a new dev store; with no terminal (CI), the command stops, lists them and names--store.- An explicit
--storeis a one-off. It never changes the remembered store. - With no dev store at all, the command offers once to create one named after the app, with test data. Pass
--create-dev-storeto do it without a prompt. --create-dev-storealways creates a new store, even when you already own others, and cannot be combined with--store. Name it with--dev-store-name(default"<app name> dev") and choose its address with--dev-store-address; a taken address answers with a free suggestion. It stops with the server's reason when you are at the limit.- The limit is 5 dev stores per developer. Delete one in the dashboard to make room.
- The storefront password of a store the CLI creates is printed once, when it is created. Save it then; you can also read it later from Developers → Dev stores.
To create a dev store without the CLI, for example from a script, see Create a dev store from a script.
queek app dev and queek app dev clean need a signed-in developer. An App Automation Token cannot run them.
The tunnel
The tunnel gives Queek's servers an https address for your local app: install, uninstall and webhook calls travel through it. The command waits until the tunnel's hostname resolves and GET /health on it answers, printing Waiting for the tunnel (n/90s)… every 5 seconds.
- The wait is 90 seconds. Change it with
--tunnel-timeout <seconds>or theQUEEK_APP_TUNNEL_TIMEOUTenvironment variable. - A free quick tunnel can take about a minute to appear in DNS. If it never answers, the command stops before installing anything and says why. Nothing is installed against a tunnel that cannot be reached. Run the command again: a new tunnel usually resolves within a minute.
--url <https-url>uses a tunnel you already run instead of starting one. The same wait and the same hard stop apply.
Develop from http://localhost
queek app dev --use-localhostThe dashboard then embeds your app from http://localhost:<port> plus the path of [extensions] merchant_page_url (for example /admin), so hot reload works inside the dashboard. The tunnel still starts and still serves install, uninstall and webhook calls. The ready block prints the embed address, for example http://localhost:3000/admin.
- The app needs an embedded page. Without
merchant_page_urlthe flag says so and keeps the tunnel address. - Only development apps can use it. See Develop against a development app.
- It only works on the machine that runs the app.
localhostis your own computer, so open the dashboard in a browser on the same machine and signed in there. - Browser support. A dashboard page on
https://loadinghttp://localhostis allowed by Chrome and Edge. Safari and Firefox may block it; run without the flag there.
Remove the dev install
queek app dev cleanUninstalls the dev build from the dev store and prints what was removed; it does nothing when there is no install. --store picks the store the same way dev does, and --reset also forgets the remembered store, so the next queek app dev asks again. Data your app keeps in its own database is not touched, and the dev store stays.
The install ends on the platform at once. The uninstall notice is sent to your app afterwards, by a queued job, so it can arrive seconds after a new queek app dev has already installed again. Key your app's state by the installation, not by the store, and do not rely on an uninstall arriving before the next install.
Develop against a development app
An app's distribution is set when it is registered. An app registered with distribution = "public" is a public listing: a build that needs review, such as a first listing or one that adds a shopper-facing, customer-data or payment scope, is put in review or stops queek app dev with needs review before it can install, and --use-localhost is refused. Editing distribution in the toml afterwards does not turn an app that is already registered into a development app.
For any app that touches shopper-facing content, customer data or payments, develop against a separate development app from the start:
slug = "my-app-dev"
name = "My App (dev)"
distribution = "development"Copy the rest of queek.app.toml into it, then run everything with -c development:
queek app dev -c developmentA development app is not listed in the app store and is never reviewed. Keep the real app for deploy and submit.
.queek/.env.local holds the signing secret of one app. A new slug registered by queek app dev replaces the value in it, and queek app deploy keeps the existing value and does not save the new app's secret. Before you register a second app in the same project folder, copy .queek/.env.local aside. If a secret is lost, reveal it under Developers → your app → Credentials. The starter keeps its slug in the APP_SLUG constant in app/config.ts, so set it to the slug of the app you run.
4. Generate API types
queek app codegenReads the live merchant.json and writes types to types/merchant.ts. Run it again to refresh them.
5. Host the app
Queek calls your app over https, so a deployed app needs a public https URL. Any host that runs a container or Node.js 22.14 or later works. The starter's Dockerfile builds one image that runs as a non-root user.
- Listen on
PORT(3000 by default).GET /healthreturns 200 without authentication. - Set
APP_BASE_URLto the public https origin and use the same origin for the install, uninstall, settings and webhook URLs inqueek.app.toml.queek app deploysends them. - Store installations in a Postgres database of the app's own, through
DATABASE_URL. The SDK refuses the dev SQLite file in production. - Supply secrets as environment variables, never in the image or repository:
| Variable | Value |
|---|---|
QUEEK_APP_SECRET | The whsec_ signing secret, printed once by the first queek app deploy |
APP_ENCRYPTION_KEY | 32 random bytes, base64 |
APP_KEY_ID | Key id of the public key registered under the app's Keys in the Developer page |
APP_PRIVATE_KEY | The matching PEM private key |
The starter's .env.example shows how to generate each one.
6. Deploy a version
queek app deployCreates a version from the local manifest and releases it when review rules allow; otherwise it tells you to submit. The registration secret is shown once and saved locally.
7. Submit for review
queek app submit my-appChecks readiness, collects the submission details and sends the version for review. Failed required checks block submission; warnings need acknowledgement. Give test-store instructions only, never production credentials. Run queek app submit --help for non-interactive options.
The submission includes a link to a screencast of the app: install, set-up, every feature and uninstall. Reviewers will not approve a listing without it. App review and distribution covers what the screencast must show and what review looks at.
CLI reference
| Command | Purpose |
|---|---|
npm create @usequeek/app my-app | Create a project from the app starter |
queek app dev | Register the app if needed, then develop through a tunnel on a dev store |
queek app dev clean | Remove the dev install from the dev store |
queek app codegen | Generate Merchant API types from the live spec |
queek app deploy | Create a version, and release it when allowed |
queek app submit my-app | Check and submit a version for review |
Flags for any command: queek app <command> --help, or npx -y @usequeek/cli app <command> --help without a global install.
queek app dev flags
| Flag | Environment variable | What it does |
|---|---|---|
--store <p_id|slug|name> | QUEEK_APP_STORE | The dev store to use. Without it: the store used last, your only dev store, or a choice on a terminal. |
--create-dev-store | Create a new dev store with test data and run on it, even when you own others. Cannot be combined with --store. | |
--dev-store-name <name> | QUEEK_APP_DEV_STORE_NAME | Name of the created store. Default "<app name> dev". |
--dev-store-address <slug> | QUEEK_APP_DEV_STORE_SLUG | Address (slug) of the created store. A taken address answers with a free suggestion. |
--port <n> | Your local server port. Default: [dev].port, else 3000. | |
--url <https-url> | Use a tunnel you already run instead of starting one. | |
--tunnel-timeout <seconds> | QUEEK_APP_TUNNEL_TIMEOUT | How long to wait for the tunnel to resolve and answer /health. Default 90. |
--use-localhost | Embed the app from http://localhost:<port> plus the merchant_page_url path. Development apps only. | |
-c, --config <name> | QUEEK_APP_CONFIG | Read queek.app.<name>.toml instead of queek.app.toml. |
--path <dir> | QUEEK_APP_PATH | The app project, where queek.app.toml lives. Default .. |
--no-browser | Sign in with a device code instead of opening a browser. | |
--verbose | QUEEK_THEME_VERBOSE | Print debug lines to stderr. |
queek app dev clean takes --store, --reset, -c, --path, --no-browser and --verbose, plus --json for machine-readable output.
Every failed API call prints the server's request id on its own line (Request id: …). Quote it when you ask support for help.
When something fails
| Symptom | Cause and fix |
|---|---|
queek: command not found | The CLI is not on your PATH. Install it globally or prefix the command with npx -y @usequeek/cli. |
| The install handoff answers an error and the installation never activates | Answer the handoff with a 2xx first, then verify. An installation turns active only after your install URL answers success, and token minting is refused (409 app_installation_pending) until it does. |
403 insufficient_scope from a Merchant API call | The installation was not granted the scope. Add it under [access] in queek.app.toml and deploy, or request an optional scope. See the manifest. |
| Webhook signature fails | Pass the raw request body to the SDK. Parsing and re-serialising it changes the bytes. See Webhooks. |
Could not find dev store '<name>' | queek app dev only installs on a dev store your developer account owns. The message lists yours. See Dev stores. |
You own N dev stores — pass --store … | There are several dev stores and no terminal to ask. Pass --store, or --create-dev-store for a new one. |
The tunnel … did not become reachable within 90s | Nothing was installed. Run queek app dev again: a fresh tunnel usually resolves within a minute. Raise --tunnel-timeout on a slow network. See The tunnel. |
v… needs review before it can install | The app is registered as a public listing and this build needs review. Develop against a development app. |
Missing QUEEK_APP_SECRET and it could not be fetched | The app's signing secret is not in .queek/.env.local and could not be fetched for this app. Reveal it under Developers → your app → Credentials and put it in that file. |
| The app opens in the dashboard as "didn't respond" | The dashboard never received ready from your page. See Embedded apps. |
| The SDK refuses to start in production | It does not use the development SQLite file in production. Set DATABASE_URL to a Postgres database. |
429 too_many_requests | You hit a rate limit. Wait Retry-After seconds. See Idempotency & rate limits. |