Settings
Fields a merchant fills in for your app, the form Storeware draws for them, how your app reads them, and pictures for your components.
Most apps need a few answers from each shop: an API key for another service, a colour, how many items to show. Declare them in [[settings]] and Storeware draws the form for the merchant, keeps the answers, encrypts the secrets and hands them to your app. You build no settings page of your own.
Early access
Declaring settings
Each [[settings]] entry is one field. Up to 30.
[[settings]]
key = "greeting"
label = "Greeting"
help = "Shown above the reviews on every product page."
type = "single_line_text"
default = "What people say"
[[settings]]
key = "reviews_shown"
label = "Reviews to show"
type = "integer"
required = true
default = 3
[[settings]]
key = "layout"
label = "Layout"
type = "select"
options = [
{ value = "list", label = "A list" },
{ value = "grid", label = "A grid" },
]
[[settings]]
key = "api_key"
label = "Your Reviews.io API key"
type = "secret"
required = true| Field | What it is |
|---|---|
key | How your app names it. lower_snake_case, never shown to the merchant. |
label | What the merchant reads beside the box. Up to 60 characters. |
help | A sentence under the box. Optional, up to 200 characters. |
type | One of the types below. |
required | Whether a save may leave it empty. false unless you say so. |
default | What your app reads until the merchant saves something. Not allowed on a secret. |
options | For a select only: 2 to 30 choices, each a value your app reads and a label shown. |
| Type | What the merchant sees | What your app reads |
|---|---|---|
single_line_text | A text box | A string of one line, at most 500 |
multi_line_text | A larger text box | A string, at most 2,000 characters |
integer | A number box | A whole number |
decimal | A number box | A number |
boolean | A tick box | true or false |
url | A web address box | A string starting https:// |
color | A colour picker and its code | A string like #1a6bd4 |
select | A list to choose from | The chosen option's value |
secret | A hidden box, and Set or Not set | The string, in the clear, to you alone |
storeware validate checks every default against its type, with the same rule the form and the API use, so a default that validates always saves.
What the merchant sees
On your app's page in their settings (Settings, Apps, then your app), the merchant finds a Settings card with one control per field, in the order you declared them. It appears only when your app declares settings.
A secret is never shown again once saved. The merchant sees Set or Not set beside it. Leaving its box empty keeps the saved one, typing in it replaces it, and Remove it clears it. A field that does not fit its type, or a required field left empty, is refused with the reason beside that box, and nothing is saved.
Fields come from the release the shop has approved. A field a newer release adds appears once the merchant approves that release. Only the merchant, or a key or person with permission to manage apps, can change them; your app cannot.
Reading them
GET /api/v1/app/settings, with your app's token. It needs no permission.
const { values, updatedAt } = await client.settings();
// { values: { greeting: 'What people say', reviews_shown: 3, layout: null, api_key: 'rk_...' }, updatedAt: null }GET /api/v1/app/settings
Authorization: Bearer swt_...{
"data": {
"values": {
"greeting": "What people say",
"reviews_shown": 3,
"layout": null,
"api_key": "rk_live_..."
},
"updatedAt": "2027-02-07T09:12:40.000Z"
}
}Every field your installed release declares is there, by key: what the merchant saved, else its default, else null. A secret comes back in the clear, and only to your app. A key that an older release declared and yours no longer does is left out, but it is kept, so a release that brings it back finds it again. updatedAt is null until the merchant first saves.
Read them when you need them rather than copying them into your own database, or keep your copy fresh with the event below.
When they change
When the merchant saves and something changed, your webhook URL is sent app.settings_updated.v1. You need not subscribe to it, and only your app hears it.
{
"event": "app.settings_updated.v1",
"data": {
"installationId": "9b1c...",
"app": "reviews",
"changed": ["greeting", "api_key"]
}
}changed lists the keys whose value your app would now read differently, and never the values: a delivery is logged and may be retried, and a secret has no place in either. Call GET /api/v1/app/settings for the new values. A save that changes nothing sends nothing.
With the SDK's router, it arrives at onLifecycle, or at a handler of its own:
router.on('app.settings_updated.v1', async (event) => {
// `client` is the StorewareClient for the shop the event is about.
const { values } = await client.settings();
await saveForShop(event.storeId, values);
});Pictures for your components
An image in your app's components may only come from cdn.mystoreware.com, so a shop page never makes a shopper's browser fetch from your servers. To put a picture there, send its bytes to POST /api/v1/app/assets with your app's token. It needs no permission: you do not need products:write to show a picture.
const { url } = await client.uploadAsset({
data: await readFile('badge.png'),
contentType: 'image/png',
});
// https://cdn.mystoreware.com/apps/<your app>/<shop>/<sha256>.pngPOST /api/v1/app/assets
Authorization: Bearer swt_...
Content-Type: application/json
{ "data": "<base64>", "contentType": "image/png" }- PNG, JPEG, WebP, GIF or AVIF, at most 5 MB. SVG is refused, because it can carry script.
- The bytes are checked. A file that is not a picture, or is a different type from the one you sent, is refused with a 400 saying why.
- A picture is kept under your app and the shop your token is for, named by the hash of its bytes. Sending the same picture again gives the same address, so you can upload on every deploy without filling the shop with copies.