Skip to main content

App Settings

Almost every app needs a few answers from the merchant: a phone number, an account id, a colour, a switch to turn a feature on. Let SitePack build that settings page for you. You describe the fields in app.json, and SitePack renders the form inside the merchant's admin, stores the answers per site and hands them to your app.

This is the recommended way for every app, storefront app or API integration:

  • Seamless for the merchant. The settings page looks and works like the rest of the SitePack admin, in the merchant's own language. They never leave SitePack or sign in somewhere else.
  • No work for you. No settings screen to build, host or secure. Dropdowns, colour pickers, a media picker and required fields come built in.
  • Safe by default. Values are typed and checked against your schema; anything outside the schema is dropped.
  • Available everywhere. The values reach your storefront templates as settings and your server through the public API.

Where the merchant finds it​

After installing your app, the merchant sees it in their admin under Apps, listed by its menu_label (or name). Manage apps shows every installed app with an Open button. Opening the app shows your settings form with a Save button.

Describing the form​

Set settings_type to "abstract" and describe the fields as a JSON Schema in settings_schema:

{
"admin": {
"settings_type": "abstract",
"settings_schema": {
"type": "object",
"properties": {
"api_key": { "type": "string", "title": "Your API Key" },
"enable_sync": { "type": "boolean", "default": true }
}
}
}
}

The form supports string (with an optional enum for a select, and placeholder), boolean, integer/number and array of object properties; title labels the field and description is shown as help text.

What a property can declare​

KeyApplies toWhat it does
typeallstring, integer, number, boolean, array or object
titleallThe label above the field
descriptionallThe help text under the field
defaultallThe value a merchant sees before saving anything
placeholderstringThe greyed-out example inside an empty field
enumstringTurns the field into a dropdown of fixed values
enum_titlesstringThe labels for those values, in the same order as enum
formatstringcolor renders a colour picker next to the hex field; textarea renders a multi-line field; image renders the media picker (see Image settings); password renders a masked input for a secret (see Password settings)
translatablestringtrue lets the merchant write the value once and translate it into the site's other languages. See Translating what the merchant writes
minimum / maximuminteger, numberThe range the field accepts

required at the schema root marks fields the merchant has to fill in:

{
"settings_schema": {
"type": "object",
"properties": {
"phone_number": {
"type": "string",
"title": "WhatsApp number",
"placeholder": "+44 7700 900123"
},
"position": {
"type": "string",
"title": "Where the button appears",
"enum": ["bottom_right", "bottom_left"],
"enum_titles": ["Bottom right", "Bottom left"],
"default": "bottom_right"
},
"button_color": { "type": "string", "format": "color", "default": "#25D366" }
},
"required": ["phone_number"]
}
}

The merchant's answers are stored per site, typed by your schema, and handed to your storefront blocks as settings and to your own backend through the public API. Keys outside your schema are dropped, so an app can never write outside what it declares.

Write the labels in the merchant's language

Every merchant-facing string here (title, description, placeholder, enum_titles) can be a translation key that SitePack resolves against your app's own dictionaries. See the Translations guide.

Text your visitors read

A popup title or a button text that the merchant types in is content, not a label. Mark it "translatable": true and SitePack translates it with the rest of the site, so a German visitor reads the German text. See Translating what the merchant writes.

Image settings​

Give a string property "format": "image" and the merchant picks the image instead of typing an address. The form shows the same media picker the rest of the admin uses: a preview, a Select image button that opens the site's media library (where the merchant can also upload a new image) and a Remove button.

{
"popup_image": {
"type": "string",
"title": "Image",
"description": "Shown next to the popup text.",
"format": "image",
"default": "assets/img/popup-default.svg"
}
}

An image setting accepts exactly two kinds of value:

  1. An image from the site's media library. It lives on the SitePack CDN and reaches you as its absolute URL (https://cdn.sitepack.eu/...). Older SitePack media addresses are rewritten to the current CDN URL.
  2. The default image your app ships. Set default to a path inside your app, relative to the app root, such as assets/img/popup-default.svg. SitePack serves it next to your scripts and hands you its full address on the app CDN, https://cdn-app.sitepack.eu/<app uuid>/<version>/assets/img/popup-default.svg (on a local development storefront: /sitepack-apps/<app uuid>/<version>/...). The merchant sees it as the preview with a note that it is your app's standard image, and Remove puts it back after they picked their own. Without a default, an empty field means no image.

Nothing else is accepted: an image on another website, a javascript: or data: URI or a path outside your app is refused when the merchant saves (the settings page names the field and asks them to pick from the media library), and a stored value that no longer qualifies reads as your default. What you receive in settings.popup_image in your block templates and in merchant_settings on the public API is therefore always one of those two URLs, or an empty string.

Rules for the default, checked by the automated review (a violation blocks the review):

  • a relative path inside the app: no leading /, no .., no scheme, only letters, digits, -, _, . and /;
  • ending in .png, .jpg, .jpeg, .gif, .webp, .avif or .svg;
  • pointing at a file that exists in your app and is an image (an SVG has to be an <svg> document). Keep SVGs free of scripts and external references: they are served as they are.

Pick an image that works when it is cropped: a landscape picture with the subject in the centre survives both a portrait and a wide slot.

It is still good practice to check the value in your template before you print it, for example that it starts with the SitePack CDN (https://cdn.sitepack.eu/), the app CDN (https://cdn-app.sitepack.eu/) or /sitepack-apps/, so your app also behaves on an older SitePack, where the field is a plain text input and the default is not resolved.

An image setting is never translatable: the picture is the same in every language. A "translatable": true on it is ignored and the review checklist warns about it. Make the image's alt text a separate string setting and mark that one translatable instead.

Password settings​

Give a string property "format": "password" when your app needs a secret from the merchant, such as the password or API key of an external system your backend connects to:

{
"api_password": {
"type": "string",
"title": "API password",
"description": "The password of your account with the external system.",
"format": "password"
}
}

SitePack treats the value as a secret:

  • The admin renders a masked input and never sends the stored value back to the browser. Once a value is saved, the form tells the merchant a password is set; leaving the field empty and saving keeps the stored password, typing a new one replaces it.
  • The storefront never receives it. In your block templates and in your storefront scripts the setting is always an empty string.
  • Only your app's own backend can read it, as merchant_settings on GET /api/public/v1/app/settings (scope app:settings:read), with the token of your app's installation on that site.

A password setting is never translatable: a "translatable": true on it is ignored. Use it only for type: "string"; on any other type the format has no effect.

React to a changed password

Subscribe to the app.settings_updated webhook to hear when the merchant saves the settings page, then read the settings back through the public API to pick up the new credentials.

Help above the settings form​

An app can put a short explanation above its settings form: what the merchant needs before filling it in, and where to read more. Declare it as admin.help next to settings_schema:

"admin": {
"settings_type": "abstract",
"help": {
"title": "admin.help.title",
"body": "admin.help.body",
"link": {
"label": "admin.help.link_label",
"url": "admin.help.link_url"
}
},
"settings_schema": { "...": "..." }
}
// translations/en.json
{
"admin": {
"help": {
"title": "Connect your webshop",
"body": "Fill in the API details you received from the supplier.\n\nAfter you save, the sync starts within a few minutes.",
"link_label": "How the sync works",
"link_url": "https://help.example.com/docs/my-app/"
}
}
}
  • title, body, link.label and link.url are translation keys, resolved against your app's translations like the rest of the form (plain English works too). Because the URL is translated as well, every language can point at its own help page.
  • body is plain text. A blank line starts a new paragraph. Markup is shown as text, never rendered.
  • The link is optional and only shown for an https:// URL; anything else is dropped. It opens in a new tab.
  • Leave help out, or leave both title and body empty, and nothing is shown.

Reading settings from your server​

An API integration reads the merchant's answers with its access token and the app:settings:read scope:

GET https://api.sitepack.eu/api/public/v1/app/settings
X-SitePack-Access-Token: YOUR_ACCESS_TOKEN
{
"settings": { "last_sync_cursor": "2026-09-01T10:00:00Z" },
"merchant_settings": { "warehouse_code": "AMS-1", "sync_orders": true }
}
  • merchant_settings is what the merchant saved on your settings page, typed by your schema. It is read-only for your app: the merchant owns those values.
  • settings is a small key-value store for your app's own state on this site, such as a sync cursor. Replace it with PUT /api/public/v1/app/settings and the app:settings:write scope; keys you leave out are removed.

A storefront app does not need this: its templates receive the values as settings directly. See Storefront Blocks & Events.

Your own settings page in an iframe​

For an integration that needs more than a form, for example a mapping screen or a sign-in with your own service, set settings_type to "iframe". SitePack then shows your hosted page inside its admin instead of a form:

{
"admin": {
"settings_type": "iframe",
"settings_url": "https://your-app.com/sitepack/settings"
}
}

SitePack adds site_uuid, app_id, locale, timestamp, secure and signature to the URL, so your page knows which site and language it is shown for.

Prefer the SitePack form

An iframe page is yours to build, host, translate and secure, and it will never feel as native to the merchant as the SitePack form. Use it only when a form really cannot do the job. Do not let the URL parameters alone unlock anything sensitive: tie the page to the merchant's install through your own OAuth session.

Admin navigation and widgets​

Extra menu items (admin.navigation) and widgets on existing admin pages (admin.widgets) are not available yet. The keys are accepted in app.json but not shown in the admin. Put everything the merchant configures on your settings page for now.

Next steps​