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.
{
"$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โ
| Field | Required | Description |
|---|---|---|
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โ
| Field | Required | Description |
|---|---|---|
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 |
schedule | scheduled | 5-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:
_diron components โ maps a component to its directory undercomponents/handlerandassetspaths โ 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โ
- The manifest wins. Deploy is a sync, not a merge โ what's not declared gets removed from the version.
- Don't hand-edit
app_idor componentids of deployed components; they are identity. description.deis always required โ the platform's primary market is German-speaking.- Validate before deploying:
npx @epilot/cli app validateruns the same checks as the platform.