App manifest
Configure a Queek app with queek.app.toml, scoped access, webhooks, extensions and dashboard UI.
The CLI reads queek.app.toml as the local app configuration and maps it to the manifest accepted by Queek. A manifest requires a slug, name, [access].scopes list (which may be empty), and [app].install_url and [app].uninstall_url. The CLI's manifest validator is the source of truth for accepted keys and limits. Do not put secrets in this file.
A complete example
This is the manifest the app starter ships. It requests one read scope, subscribes to one webhook topic, defines one merchant setting and registers an embedded page.
slug = "my-app"
name = "My App"
distribution = "public"
icon = "wave"
developer = "You"
[listing]
description = "What your app does, in one sentence."
tagline = "One line for the listing."
[access]
scopes = ["merchant-business_profile-read"]
[webhooks]
topics = ["orders/updated"]
url = "https://my-app.example.com/webhooks"
[app]
install_url = "https://my-app.example.com/install"
uninstall_url = "https://my-app.example.com/uninstall"
settings_url = "https://my-app.example.com/settings"
[[settings]]
key = "greeting"
label = "Greeting"
type = "string"
required = false
help = "Logged when an order update arrives."
[extensions]
merchant_page_url = "https://my-app.example.com/admin"
[dev]
command = "node ./server.js"
port = 3000Use the same https origin for install_url, uninstall_url, settings_url, the webhook url
and merchant_page_url. Run queek app deploy to validate the file: the CLI's validator is the
source of truth, and it names the key and the limit when something is wrong.
Top-level TOML keys
The CLI accepts slug (or its handle alias), name, version, distribution, icon, developer, category, [listing], [access], [webhooks], [app], [[settings]], [extensions], [dashboard], and the CLI-only [dev] table. The version value is not used for deployment; the server assigns versions, or queek app deploy --version names one.
[listing]describes the app listing: description, tagline, long description, highlights, logo, pricing, developer, privacy, support, demo, and video URLs.[app]containsinstall_url,uninstall_url, and optionalsettings_urlendpoints.[webhooks]containstopicsand an optional app webhook URL.[[settings]]defines merchant settings. A setting may usetype = "secret"; the secret value belongs in the app's runtime configuration, not the manifest.[dev]tellsqueek app devhow to start the local server and which port it uses.
Access scopes
[access]
scopes = ["merchant-orders-read"]
optional_scopes = ["merchant-products-read"]scopes are requested at install. optional_scopes are available for the app to request later; they are not granted during installation. The two lists must be disjoint. An app installation cannot be granted app-management, API-key, roles, users, employees, or point-of-sale scopes. Choose the smallest set that supports the app's task, then call only operations those grants allow. See the Merchant API scopes.
Extensions
[extensions] accepts these entries:
-
[extensions.proxy]routes a configured storefront subpath to the app's HTTPS URL. The proxy servesGET. It serves signedPOSTonly whenallow_post = trueis set and the merchant has accepted it (defaultfalse; installs made before the setting existed stay read-only until the merchant accepts the update).subpathmust be 2–40 lowercase letters, digits, or dashes;share_customer_idis optional. Verify proxy requests with the SDK's proxy verifier. See Receiving storefront POSTs. -
[[extensions.blocks]]declares storefrontapp_blockorapp_embedextensions. The CLI allows at most 10, targets areproduct, andavailable_ifcan usedeclared_products. Block settings use the supporteddate,time,selectorline_textfield types. A link URL or image can be included. Aline_textblock holds onlyline_textfields, needsavailable_if = "declared_products", and needs no proxy. A manifest declares at most 3line_textfields, each with an optionalmax_lengthfrom 1 to 500 (default 100). Aline_textfield key may not beclaim,reschedule_urlorcancel_url. See Shopper text on a product line.Values a shopper types into a
line_textfield are untrusted input: the order line property is forgeable by design, so validate it in your app. -
merchant_page_urlis the embedded merchant page URL opened from the dashboard. How the page runs in the frame, and the messages it must send, are in Embedded apps and the dashboard bridge. -
[[extensions.nav]]declares up to 10 navigation entries shown under the app in the dashboard sidebar. Each entry has a 1–40 characterlabeland a relativepathbeginning with/;extensions.merchant_page_urlis required when nav items are present.
Shopper text on a product line
A line_text field lets a shopper type a short text, such as an engraving or a gift note, that is stored on the order line. Three steps wire it end to end:
- Declare the field in the manifest. Add a
line_textfield with akey, alabeland, optionally,requiredandmax_lengthto a product block that setsavailable_if = "declared_products". A version that addsline_textgoes through review. - Declare which products take it. Call
PUT /api/v1/merchant/app/line-inputswith themerchant-app-requirements-writescope. The body is the whole sheet,{ "items": [{ "product_p_id": 1042, "fields": [{ "key": "engraving", "required": true, "max_length": 40 }] }] }. Each call replaces the previous sheet, so an emptyitemsremoves every declaration. Akeymust be aline_textfield of the manifest the merchant granted. The label always comes from the manifest, and amax_lengthcannot exceed the manifest's. A sheet holds at most 200 products and 3 fields per product. - Read the value on the order line. The text arrives in the line's
propertiesunderapp.<handle>.<key>, where<handle>is the app'sslugwith dashes written as underscores. At checkout, a required field that is empty, or a value that is too long or contains control characters, is refused before payment. Orders a merchant's staff place themselves, such as at the point of sale, are not checked.
Queek derives the property key; an app never supplies it. Treat every value as untrusted shopper input: a client can send any property key and value, so check what you receive before you act on it, and never render it as HTML.
Receiving storefront POSTs
Set allow_post = true under [extensions.proxy] to let a storefront page submit data to your app, for example a form or a booking request. The merchant sees the setting on the install or update screen and has to accept it. Until they do, the proxy stays read-only for that store.
Queek relays POST <your proxy URL>/<path>?<signed query> with Content-Type: application/json and the shopper's body byte for byte. Cookies, the Authorization header, client keys, the Origin and the shopper's IP are never forwarded. The shopper's identity is forwarded only the way it is for reads, under share_customer_id.
Verify every POST before you act on it:
- Check
sigexactly as you do for a read. - Require the signed
methodparameter to equalPOST. - Compare the signed
body_sha256with the SHA-256 hex of the raw request body. - Enforce the
tswindow and accept eachjtionce. - Dedupe on the signed
idempotency_keywhen it is present. A request that times out may still have been processed, so make your handler idempotent.
Answer with 200, 201, 202 or 204 and a JSON body, or with 400, 409 or 422 and a JSON body when the submission has errors the shopper can fix. Any other status, a body that is not JSON, or a timeout reaches the shopper as app_proxy_failed (424). The shop parameter is the store's opaque id.
Dashboard blocks, actions and print
The optional [dashboard] table supports [[dashboard.blocks]], [[dashboard.actions]], and [[dashboard.print]] entries. Each list is bounded by the CLI validator:
| Entry | Maximum | Supported target / fields |
|---|---|---|
blocks | 10 | order-details or product-details; schema field types date, time, select, text, or slot-list. |
actions | 10 | order-details; an action declares a granted scope, a notification URL, and the order_appointment effect. |
print | 5 | order-print; up to 20 print fields per entry. |
Dashboard blocks and actions can include visibility conditions supported by the validator: order.has_appointment, order.is_paid, or product.has_files. Declare a scope required by an action in either scopes or optional_scopes.
The CLI rejects unknown keys and values outside these supported forms. Run queek app deploy --help and validate the manifest with queek app dev before submitting it.
Limits at a glance
| Item | Limit |
|---|---|
name | 120 characters |
Setting label and each options entry | 120 characters |
[[extensions.blocks]] | 10 entries, each descriptor at most 4,096 bytes |
line_text fields | 3 per manifest; max_length 1–500 (default 100) |
[[extensions.nav]] | 10 entries, label 1–40 characters |
extensions.proxy.subpath | 2–40 lowercase letters, digits or dashes |
extensions.proxy.allow_post | boolean, default false; JSON POST bodies up to 65,536 bytes; 20 submissions per minute per shopper IP, 600 per installation |
[[dashboard.blocks]], [[dashboard.actions]] | 10 entries each |
[[dashboard.print]] | 5 entries, up to 20 fields each |
| Extension URLs | https, at most 2,048 characters |