Appearance
Configuration (app.toml)
app.toml is the config file for your app. The CLI reads it when you run app push, app validate, or any command that needs the app slug. Edit it locally and push; do not edit it through a panel.
What is not in
app.toml: the app icon, store listing (description, gallery, pricing, and category), and media assets for public apps. Those are managed in the Account-panel Dashboard. For icon specs and how to change it, see App icon.
Complete example
toml
name = "Smart Lock Manager"
type = "remote" # "remote" | "payment" | "channel" | "injection"
distribution = "public" # "private" | "public"
slug = "smart-lock-manager" # public: set before first push; private: server-generated
scopes = ["read_bookings", "read_contacts", "write_conversations"]
capabilities = []
webhooks = ["booking.confirmed", "booking.cancelled", "booking.checked_in"]
webhook_url = "https://app.smartlock.example/webhooks/stayblox"
[oauth]
redirect_uris = ["https://smartlock.example/auth/stayblox/callback"]
[app_page]
url = "https://app.smartlock.example/stayblox"
[[settings_schema]]
key = "api_key"
type = "string"
label = "Lock API key"
required = true
[[settings_schema]]
key = "default_code_length"
type = "number"
label = "Code length"
default = 6
[[metafields]]
owner = "booking"
key = "access_code"
type = "string"
visibility = "guest"
[[metafields]]
owner = "booking"
key = "lock_battery_pct"
type = "number"
visibility = "host"
[[injections]]
slot = "head"
template = '<script src="https://cdn.smartlock.example/widget.js?key={{ api_key }}"></script>'Keys
name
| Required | Type |
|---|---|
| Yes | string |
The display name shown to hosts in the marketplace and panel.
type
| Required | Type | Default |
|---|---|---|
| Yes | string | remote |
The app type. One of remote, payment, channel, or injection. Private apps support remote only.
distribution
| Required | Type | Default |
|---|---|---|
| Yes | string | public |
private or public. Cannot be changed after the first push. See App lifecycle.
slug
| Required | Type |
|---|---|
| Required for public | string |
A globally unique, URL-safe identifier for the app.
- Public apps: choose a slug and set it before the first push.
- Private apps: omit
slug; the server generates one on the first push and the CLI writes it back toapp.toml.
Format: ^[a-z0-9][a-z0-9-]{1,39}$ (lowercase letters, digits, and hyphens; must start with a letter or digit).
scopes
| Required | Type |
|---|---|
| No | array of strings |
Access scopes the host consents to when installing. Stayblox checks the install's granted_scopes on every API call and webhook delivery. See Scopes for the full catalog.
toml
scopes = ["read_bookings", "read_contacts", "write_conversations"]Validation: every entry must be a known scope value. A webhook topic also requires its scope to be listed here.
capabilities
| Required | Type |
|---|---|
| No | array of strings |
Privileged behaviors the app requests, separate from data-access scopes. The host grants these at install time.
| Value | What it enables |
|---|---|
act_as_assignment_provider | Receive task assignment requests and write back the chosen assignee. |
register_task_types | Add custom task types with their own fields and checklists. |
See Assignment provider apps and Tasks.
webhooks
| Required | Type |
|---|---|
| No | array of strings |
Topics delivered to webhook_url for each install. See Webhooks for the generated topic list.
toml
webhooks = ["booking.confirmed", "booking.cancelled"]Validation: every topic must be a known value, and its required scope must appear in scopes.
webhook_url
| Required | Type |
|---|---|
| No | string |
The HTTPS URL Stayblox delivers webhook events to. Copied onto each install at install time. Stayblox signs every delivery with a per-install webhook_secret issued at app install. See Signing & security.
toml
webhook_url = "https://app.example.com/webhooks/stayblox"Validation: must be a valid https:// URL.
[oauth]
| Required | Type |
|---|---|
| No | table |
Registers redirect URIs for the OAuth 2.0 authorization-code install flow. See OAuth install for the complete flow.
toml
[oauth]
redirect_uris = ["https://app.example.com/auth/stayblox/callback"]| Property | Required | Description |
|---|---|---|
redirect_uris | Yes | Array of https:// URIs. The redirect_uri in an authorize request must exactly match one; no wildcards, no partial matches. |
Validation: every URI must be a valid https:// URL.
Private apps:
[oauth]is not supported for private apps.
[app_page]
| Required | Type |
|---|---|
| No | table |
Declares an embedded UI page rendered inside the host panel as a sandboxed iframe. See Embedded pages for the JWT session protocol.
toml
[app_page]
url = "https://app.example.com/stayblox"| Property | Required | Description |
|---|---|---|
url | Yes | The https:// URL of your app page. The iframe loads {url}?session=<JWT>. |
Validation: url must be a valid https:// URL.
Private apps:
[app_page]is not supported for private apps.
[[settings_schema]]
| Required | Type |
|---|---|
| No | array of tables |
Defines the per-install settings form shown to the host at install time and on the app settings page. Each entry is a field definition.
| Property | Required | Type | Description |
|---|---|---|---|
key | Yes | string | Identifier for the setting. |
type | Yes | string | string, number, boolean, or select. |
label | Yes | string | Human-readable label shown in the form. |
required | No | boolean | Defaults to false. |
default | No | any | Default value shown pre-filled. |
options | For select | array | Array of { value, label } objects. |
Setting values are available in [[injections]] templates as and in protocol payloads under settings.key.
Private apps:
[[settings_schema]]is not supported for private apps.
[[injections]]
| Required | Type |
|---|---|
| No | array of tables |
HTML snippets rendered into named slots in the storefront. See Injections for slot names and template examples.
toml
[[injections]]
slot = "head"
template = '<script src="https://cdn.example.com/widget.js?key={{ api_key }}"></script>'| Property | Required | Description |
|---|---|---|
slot | Yes | Named storefront slot (e.g. head, body_end). |
template | Yes | Raw HTML. May use placeholders interpolated from the install's settings. |
Sanitization: injection templates are reviewed as part of the app review. Scripts must load from a fixed https:// origin; inline scripts are rejected.
Private apps:
[[injections]]is not supported for private apps.
[[metafields]]
| Required | Type |
|---|---|
| No | array of tables |
Declares app-owned data fields stored on bookings, properties, or contacts. See Metafields for how to write and read them at runtime.
| Property | Required | Values | Description |
|---|---|---|---|
owner | Yes | booking | property | contact | The model this field attaches to. |
key | Yes | ^[a-z0-9_]{1,64}$ | Unique identifier for this field (snake_case, max 64 chars). |
type | Yes | string | number | boolean | date | Value data type. |
visibility | Yes | private | host | guest | Who can see the value. |
Visibility:
| Value | Who sees it |
|---|---|
private | Your app only, via the API. Never shown in the host panel or guest emails. |
host | Shown in the host panel on booking, property, or contact detail pages. |
guest | Like host, plus exposed in guest email templates and in app_data(booking) in themes. |
Private apps:
[[metafields]]is not supported for private apps.
Distribution-aware validation
The CLI sends your distribution value with the manifest. The server validates accordingly.
| Check | Private | Public |
|---|---|---|
type: remote only | Required | Any type allowed |
[oauth], [app_page], [[settings_schema]], [[injections]], [[metafields]] | Not supported | Supported |
| Slug | Server-generated (written back on first push) | Required; ^[a-z0-9][a-z0-9-]{1,39}$ |
| Scope and topic validation | Same as public | Same as private |
channels
| Required | Type |
|---|---|
| No | array of objects |
Declares the messaging channels this app provides. Requires the provide_inbox_channel scope in scopes and a valid endpoints.message_send URL. See Inbox channel-provider apps for the full protocol reference.
jsonc
"channels": [
{
"key": "whatsapp",
"name": "WhatsApp",
"caps": {
"richText": false,
"quickReplies": false,
"outboundWindowHours": 24,
"templateRequiredOutsideWindow": true
}
}
]| Property | Required | Description |
|---|---|---|
key | Yes | Unique lowercase slug, ^[a-z0-9_]{2,40}$. Values email, web_form, and web are reserved. |
name | No | Display name shown to hosts in the inbox UI. 2 to 60 characters. When omitted, the key is prettified ("sample_chat" renders as "Sample chat"). |
caps.outboundWindowHours | Yes | Hours after the last inbound message during which free-form replies are allowed. |
caps.richText | No | Channel supports bold, links, etc. Defaults to false. |
caps.quickReplies | No | Channel supports quick-reply buttons. Defaults to false. |
caps.templateRequiredOutsideWindow | No | An approved template is required outside the outbound window. Defaults to false. |
endpoints
| Required | Type |
|---|---|
| No | object |
HTTPS endpoint URLs Stayblox calls on your server for protocol-based interactions.
| Property | Required when | Description |
|---|---|---|
message_send | channels is declared | Stayblox POSTs outbound host messages here, signed with HMAC. See Inbox channel-provider apps. |
ari_push | provide_channel is in scopes | Stayblox POSTs rate/availability/restriction pushes here, signed with HMAC. See Channel apps. |
toml
scopes = ["provide_channel", "read_properties", "read_rates"]
[endpoints]
ari_push = "https://app.example.com/stayblox/ari-push"Validation: the provide_channel scope requires endpoints.ari_push to be a valid https:// URL.
type = "channel"
A channel app implements the ARI push and reservation-ingestion protocol described in Channel apps. It needs provide_channel in scopes and endpoints.ari_push set; most channel apps also request read_properties and read_rates to discover inventory and back-fill rates, and subscribe to property.updated to re-sync listing content. A channel app may also provide guest messaging on the same install by additionally declaring provide_inbox_channel, channels, and endpoints.message_send.
Validation rules summary
| Rule | Detail |
|---|---|
| Scope strings | Must be known scope values. |
| Webhook topics | Must be known topic values; required scope must be listed. |
webhook_url | Must be a valid https:// URL. |
| All URLs | Must be valid https:// URLs (no http://). |
oauth.redirect_uris | Exact-match strings; no wildcards, no trailing slashes unless registered with one. |
| Metafield owners | Must be booking, property, or contact. |
| Metafield keys | Must match ^[a-z0-9_]{1,64}$. |
| Metafield types | Must be string, number, boolean, or date. |
| Injection slots | Must be known storefront slot names. |
| Injection templates | Sanitized at review; only trusted https:// script origins allowed. |
endpoints.ari_push | Required and must be a valid https:// URL when provide_channel is in scopes. |
| Settings schema | Each entry must declare a string key and a string type. |
Validation runs on every app validate and app push. Errors are shown inline; pushing with errors is blocked.
See also: Scopes reference · Webhook topics · GraphQL reference