Skip to main content

The App Manifest

manifest.json is the declarative source of truth for your entire app. It lives in the root of your app repository, is updated by the CLI, and every epilot app deploy syncs the platform to exactly what it declares โ€” components and functions not in the manifest are removed from the deployed version.

manifest.json
{
"$schema": "https://cdn.app.sls.epilot.io/v1/schema.json",
"manifest_version": 1,
"app_id": "b1b9โ€ฆ",
"name": "My Integration",
"description": { "de": "Beschreibung (Pflicht)", "en": "Optional English description" },
"category": "integration",
"author": { "company": "ACME GmbH", "name": "Jane Doe", "email": "dev@acme.example" },
"documentation_url": "https://docs.acme.example",
"support_email": "support@acme.example",
"pricing": { "pricing_type": "FREE" },
"notifications": { "email": "dev@acme.example", "events": ["app.installed", "app.uninstalled"] },
"permissions": [
{ "action": "entity:view", "resource": "opportunity" }
],
"options": [
{ "key": "api_base_url", "label": "API Base URL", "type": "text", "required": true },
{ "key": "api_key", "label": "API Key", "type": "secret", "required": true }
],
"assets": { "logo": "./assets/logo.png" },
"functions": [
{
"name": "sync-things",
"type": "scheduled",
"handler": "./functions/sync-things/dist/handler.js",
"schedule": "rate(30 minutes)"
}
],
"components": [
{
"id": "4f3cโ€ฆ",
"component_type": "CUSTOM_CAPABILITY",
"name": { "de": "Mein Tab" },
"configuration": { "type": "tab", "allowed_schemas": ["opportunity"] },
"surfaces": { "capability_config": { "app_url": "./components/my-tab/dist/index.html" } },
"assets": { "zip": "./components/my-tab/dist/" }
}
]
}

The $schema reference gives you validation and autocompletion in editors like VS Code.

Top-level fieldsโ€‹

FieldRequiredDescription
manifest_versionโœ“Always 1
app_idโ€”Set automatically after the first deploy โ€” never set it by hand
nameโœ“App name shown everywhere
descriptionโœ“de is required; en optional
categoryโ€”Marketplace category, e.g. integration
authorโ€”company required when set
pricingโ€”FREE, SUBSCRIPTION, USAGE_BASED, ONE_TIME, CUSTOM
notificationsโ€”Email + events (app.installed, app.uninstalled) you want to be notified about
permissionsโ€”The grants your app's server-side code needs โ€” shown to the installing org for consent, see Permissions
optionsโ€”Settings the installing org fills in โ€” shared by all components and functions, see App Options
blueprintโ€”manifest_id of a blueprint to install alongside the app
assets.logoโ€”Local path to the app logo, uploaded on deploy
functionsโ€”Server-side functions (workflow actions and cron), see below
componentsโœ“The app's components (may be empty)

The functions blockโ€‹

FieldRequiredDescription
nameโœ“Unique kebab-case identifier (max 64 chars) โ€” treat it as a public contract
typeโœ“workflow (flow builder action) or scheduled (cron per installation)
handlerโœ“Local path to the built JavaScript file โ€” inlined as code on deploy
labelโ€”Display name (TranslatedString), e.g. shown in the installed app's functions summary
descriptionโ€”TranslatedString
schedulescheduled5-field cron or rate(...) โ€” see Scheduled Functions
schedule_timezoneโ€”IANA timezone for cron evaluation (default Europe/Berlin)
secretsโ€”Deprecated and ignored โ€” functions now receive all app option values in input.app_options automatically

A workflow function is wired into the flow builder by a CUSTOM_FLOW_ACTION component whose configuration references it โ€” the component carries the org-facing name, options, config surface and wait_for_callback:

{
"component_type": "CUSTOM_FLOW_ACTION",
"name": { "de": "Meine Aktion" },
"configuration": { "type": "function", "function_name": "my-action" }
}

Limits: at most 10 functions per app, at most 5 of them scheduled, 300 KB code per function.

Local-only fieldsโ€‹

Some fields exist only for the CLI and are never sent to the platform:

  • _dir on components โ€” maps a component to its directory under components/
  • handler and assets paths โ€” resolved and uploaded/inlined at deploy time
  • Option values are never in the manifest โ€” the manifest declares the options; installing orgs enter the values per installation

Golden rulesโ€‹

  1. The manifest wins. Deploy is a sync, not a merge โ€” what's not declared gets removed from the version.
  2. Don't hand-edit app_id or component ids of deployed components; they are identity.
  3. description.de is always required โ€” the platform's primary market is German-speaking.
  4. Validate before deploying: npx @epilot/cli app validate runs the same checks as the platform.