Skip to main content

App Manifest

manifest.json is the package contract Shuuka reads at install time and runtime.

Full Example

{
"name": "My App",
"slug": "my-app",
"version": "1.0.0",
"runtime_kind": "smart",
"entry": "index.html",
"supportedFrameworks": ["react"],
"supported_languages": ["en", "de"],
"permissions": ["profile:read", "settings:read", "settings:write"],
"documentation_link": "https://docs.example.com/my-app",
"support_link": "https://example.com/support",
"author_link": "https://example.com",
"api_url": [
"https://api.example.com"
],
"card_settings": {
"card_style": true,
"box_shadow": true,
"full_width": false,
"disable_scroll_overlay": true,
"size": {
"width": "100%",
"height": "auto"
}
},
"localization": {
"default_locale": "en",
"supported_locales": ["en", "de"],
"translation_namespace": "apps.my_app",
"files": {
"en": "lang/en.json",
"de": "lang/de.json"
}
},
"groups": [
{
"id": "content",
"title": "Content"
},
{
"id": "design",
"title": "Design",
"sections": [
{ "id": "layout", "title": "Layout" },
{ "id": "colors", "title": "Colors" }
]
}
],
"placeholders": [
{
"key": "title",
"type": "text",
"required": true,
"default": "Hello Shuuka",
"group": "content"
},
{
"key": "layout",
"type": "select",
"default": "compact",
"options": [
{ "value": "compact", "label": "Compact" },
{ "value": "square", "label": "Square" }
],
"group": "design",
"section": "layout"
}
],
"admin_pages": [
{
"key": "overview",
"title": "Overview",
"entry": "admin/overview.html",
"icon": "chart-bar"
}
],
"admin_detail": {
"instance_selector": {
"enabled": true,
"label_field": "title",
"subtitle_field": "description"
},
"include_data_page": false
},
"bottom_sheet_contract": "Describe how the app opens, renders, and closes inside the Shuuka bottom sheet.",
"privacy": {
"collects_pii": true,
"collects_visitor_data": true,
"gdpr_lawful_basis": "consent",
"data_controller": "profile_owner",
"data_processor": "shuuka",
"data_fields": ["email", "ip_address"],
"data_categories": [
{
"name": "Contact information",
"purpose": "Lead capture",
"retention_days": null
}
],
"requires_visitor_consent": true,
"visitor_consent_text": {
"en": "This app shares visitor data with Example Service.",
"de": "Diese App teilt Besucherdaten mit Example Service."
},
"third_party_transfers": [
{
"url": "https://api.example.com",
"purpose": "Form delivery and submissions",
"country": "US",
"transfer_mechanism": "DPF-certified",
"privacy_policy_url": "https://example.com/privacy",
"data_sent": ["email", "ip_address"]
}
],
"third_party_embeds": [
{
"name": "Example Service",
"url": "https://embed.example.com",
"privacy_policy_url": "https://example.com/privacy"
}
],
"retention_hint": "Controlled by the service provider and the profile owner.",
"retention_days": null
}
}

Minimum Required Fields

FieldRequiredRule
nameYesMarketplace-facing app name
versionYesSemver string
entryYesMust point to a real file inside the uploaded bundle

The installer also requires icon.svg, thumbnail.jpg, and header.jpg. Read App package checklist for the file rules.

Top-Level Fields

FieldRequiredUse
slugRecommendedStable app identifier
runtime_kindRecommendedUsually simple or smart
supportedFrameworksNoBuilder metadata such as vanilla or react
supported_languagesNoLanguages the package supports
permissionsNoDeclared app capabilities
documentation_linkNoPublic docs URL
support_linkNoSupport URL
author_linkNoAuthor URL
api_urlNoExternal API domains used by the app
localizationNoTranslation file map for simple and smart apps
groupsNoVisual grouping for App Instance Settings
placeholdersNoPer-instance sidebar fields for simple and smart apps
card_settingsNoApp-owned card behavior and layout constraints
admin_pagesNoCustom owner tabs
admin_detailNoExtra admin-detail behavior
bottom_sheet_contractNoRequired when the app supports bottom-sheet behavior
privacyStrongly recommendedConsent, data-processing, and CSP declaration
screenshotsNoOptional marketplace screenshots list

card_settings

Use card_settings only when the app must lock a real runtime constraint. Themes should still own the default visual system.

KeyTypePossible valuesUse
card_stylebooleantrue, falsePreferred key for whether Shuuka should render card chrome around the app
stylebooleantrue, falseLegacy alias for card_style
backgroundstringCSS color, gradient, or transparentLocks the card background when the app needs a fixed surface. Omit to inherit the platform's theme-aware default (light/dark mode).
border_radiusstringCSS radius such as 0, 12px, 999pxLocks the card radius
box_shadowboolean or stringtrue, false, or CSS shadow valueLocks card shadow behavior. Omit to inherit the platform default.
full_widthbooleantrue, falseLets the app stretch edge-to-edge inside theme-controlled layouts
disable_scroll_overlaybooleantrue, falseHost compatibility flag for apps that manage their own interaction flow
sizeobjectglobal size objectDeclares embed sizing rules
size.widthnumber or stringpixel value like 360 or host-controlled value like "100%"Default width
size.heightnumber or stringpixel value like 520 or "auto"Default height

localization

Use localization when your app ships translation files for a simple or smart app.

KeyRequiredUse
default_localeYesDefault language
supported_localesYesAvailable locales
translation_namespaceRecommendedTranslation namespace for your strings
filesYesLocale-to-file map

Example:

{
"localization": {
"default_locale": "en",
"supported_locales": ["en", "de"],
"translation_namespace": "apps.my_app",
"files": {
"en": "lang/en.json",
"de": "lang/de.json"
}
}
}

groups and placeholders

These are mainly for simple and smart HTML apps. They define App Instance Settings, which Shuuka renders in the per-app sidebar for one installed app instance and injects into the app template.

groups

groups organize the App Instance Settings UI.

KeyRequiredUse
idYesGroup identifier
title or title_keyYesGroup label
sectionsNoOptional subsection list inside the group

placeholders

placeholders define App Instance Settings fields.

KeyRequiredUse
keyYesSetting identifier injected into the template
typeYesInput type such as text, textarea, select, color, number, image, url, switch
requiredNoValidation rule
defaultNoDefault value
label or label_keyRecommendedField label
help or help_keyNoHelp text
example or example_keyNoExample or placeholder text
optionsNoSelect, radio, or checkbox options
groupNoGroup assignment
sectionNoSection assignment
show_ifNoConditional visibility
min, max, stepNoNumeric constraints
allow_noneNoAllows empty color or image style values

Example:

{
"groups": [
{ "id": "content", "title": "Content" },
{
"id": "design",
"title": "Design",
"sections": [
{ "id": "layout", "title": "Layout" }
]
}
],
"placeholders": [
{
"key": "title",
"type": "text",
"required": true,
"default": "Hello Shuuka",
"group": "content"
},
{
"key": "layout",
"type": "select",
"default": "compact",
"options": [
{ "value": "compact", "label": "Compact" },
{ "value": "square", "label": "Square" }
],
"group": "design",
"section": "layout"
}
]
}

admin_pages

Each admin_pages entry becomes an owner-facing custom tab.

KeyRequiredMeaning
keyYesStable page identifier
title or title_keyYesTab label
entryYesHTML file inside the uploaded bundle
iconNoOptional dashboard icon

admin_detail

Use admin_detail to change the owner detail-page behavior.

KeyRequiredUse
instance_selector.enabledNoShow or hide the built-in installation selector
instance_selector.label_fieldNoWhich field to use as the installation label
instance_selector.subtitle_fieldNoWhich field to use as the installation subtitle
include_data_pageNoWhether Shuuka should include the built-in Data tab

Example:

{
"admin_detail": {
"instance_selector": {
"enabled": true,
"label_field": "title",
"subtitle_field": "description"
},
"include_data_page": false
}
}

bottom_sheet_contract

Add this when the app supports bottom-sheet mode. It should describe:

  • how the card opens the sheet
  • what query param or runtime state the sheet uses
  • how the inner experience closes

Example:

{
"bottom_sheet_contract": "The card button sends SHUUKA_APP_BOTTOM_SHEET {action:'open'} to the parent. The sheet reloads the app with a bottom-sheet query param and the app renders the form directly. Close actions send {action:'done'} to dismiss the sheet."
}

Read App SDK UI Helpers for the public bottom-sheet open and close patterns.

privacy

The privacy block is not just descriptive. It directly affects:

  1. visitor consent behavior
  2. the CSP allowlist for external requests and embeds

If a third-party domain is missing here, the browser can block the request even if the visitor accepted consent.

privacy Fields

FieldRequiredUse
collects_piiYestrue if personal data is collected or sent
collects_visitor_dataRecommendedtrue if device, IP, or similar data is processed
gdpr_lawful_basisRecommendedUsually consent, legitimate_interest, or contract
data_controllerRecommendedUsually profile_owner
data_processorRecommendedUsually shuuka
data_fieldsRecommendedFlat list of data identifiers
data_categoriesRecommendedStructured categories shown to the user
requires_visitor_consentRecommendedWhether consent must be collected before loading
visitor_consent_textRecommendedLocalized consent copy
third_party_transfersRequired if contacting external APIs or SDKsOne entry per external origin the app contacts
third_party_embedsRecommended for iframe or embed servicesThird-party framed or embedded services
retention_hintNoHuman-readable retention note
retention_daysNoRetention value if known

third_party_transfers Entry

KeyRequiredUse
urlYesExternal origin
purposeYesWhy the service is used
countryYesProcessing country
transfer_mechanismYesLegal transfer basis such as DPF-certified, same_country, or Standard Contractual Clauses
privacy_policy_urlYesPrivacy-policy link
data_sentYesData identifiers sent to that service
controller_typeNoOptional controller relationship

third_party_embeds Entry

KeyRequiredUse
nameYesService name
urlYesEmbed origin
privacy_policy_urlRecommendedPrivacy-policy link

Privacy Examples

No third-party transfer:

{
"privacy": {
"collects_pii": true,
"collects_visitor_data": true,
"gdpr_lawful_basis": "legitimate_interest",
"data_controller": "profile_owner",
"data_processor": "shuuka",
"data_fields": ["firstname", "lastname", "email", "message"],
"data_categories": [
{
"name": "Contact information",
"purpose": "Deliver contact messages to the profile owner",
"retention_days": null
}
],
"requires_visitor_consent": false,
"visitor_consent_text": {
"en": "This app delivers contact data to the profile owner through Shuuka."
},
"third_party_transfers": [],
"third_party_embeds": []
}
}

External API transfer:

{
"privacy": {
"collects_pii": true,
"collects_visitor_data": true,
"requires_visitor_consent": true,
"third_party_transfers": [
{
"url": "https://api.example.com",
"purpose": "Hosted form delivery and submissions",
"country": "US",
"transfer_mechanism": "DPF-certified",
"privacy_policy_url": "https://example.com/privacy",
"data_sent": ["email", "ip_address", "form_responses"]
}
],
"third_party_embeds": []
}
}

Iframe or embed service:

{
"privacy": {
"collects_pii": false,
"collects_visitor_data": true,
"gdpr_lawful_basis": "consent",
"third_party_transfers": [],
"third_party_embeds": [
{
"name": "Google Maps",
"url": "https://www.google.com",
"privacy_policy_url": "https://policies.google.com/privacy"
}
]
}
}

Builder Rule for Third Parties

  • list every external origin the app contacts
  • do not assume one SDK domain is enough if the service also calls an API subdomain
  • if the service is framed or embedded, declare the embed origin too
  • if a domain is missing, fix the manifest before debugging the app logic

screenshots

Use screenshots if you want to control the marketplace screenshot order.

Example:

{
"screenshots": [
"screenshots/hero.png",
"screenshots/admin-overview.jpg"
]
}
App styleMost relevant manifest parts
React bundle appname, version, entry, supportedFrameworks, permissions, privacy, card_settings, admin_pages
simple or smart HTML appall of the above plus localization, groups, placeholders, bottom_sheet_contract, and sometimes admin_detail

Next Step

Read App settings and forms, App admin pages, and Release checklist after drafting your manifest.