InstallPayload ({ previousVersion?: string; newVersion: string } — previousVersion is undefined on a fresh install); the uninstall hook receives an UninstallPayload ({ version?: string } — the version being removed).
Each app may define at most one of each hook (pre-install, post-install, uninstall). The manifest build errors if more than one of any kind is detected.
At a glance
Rule of thumb: default to post-install. Only reach for pre-install when the migration itself is destructive and you need to intercept the previous state before it is gone.
Behavior shared by both hooks
- The config is a
defineLogicFunctionconfig minus the trigger settings, plusshouldRunOnVersionUpgrade. - When it runs: fresh installs only, by default. Set
shouldRunOnVersionUpgrade: trueto also run on upgrades. UsepreviousVersion/newVersionto branch on the upgrade path. - Idempotency matters: async post-install may be retried, and either hook re-runs on upgrades when
shouldRunOnVersionUpgradeis on. - The usual logic-function environment (
APPLICATION_ID,APP_ACCESS_TOKEN,API_URL) is injected, so you can call the Twenty API with your app’s token. - The hook is attached to the application manifest automatically at build time (
preInstallLogicFunction/postInstallLogicFunction) — nothing to reference indefineApplication(). - The default
timeoutSecondsis 300 to allow longer setup tasks like data seeding. - Not executed in dev mode:
yarn twenty devskips the install flow and syncs files directly, so hooks never run there. Trigger them manually instead:
definePostInstallLogicFunction
Runs after the workspace metadata migration is applied
definePostInstallLogicFunction
Runs after the workspace metadata migration is applied
Runs once your app has finished installing: metadata synchronized, SDK client generated, new schema queryable. Example — seed a default record on fresh installs:The
src/logic-functions/post-install.ts
shouldRunSynchronously flag controls the execution model:false(default) — enqueued on the message queue (retryLimit: 3) and run by a worker. The install response returns as soon as the job is enqueued. Use for long-running work — seeding large datasets, slow third-party APIs.true— executed inline during the install flow. The install request blocks until the handler finishes; a thrown error surfaces asPOST_INSTALL_ERRORto the caller (no retries). Use for fast, must-complete-before-response work. The migration has already been applied at this point, so a failure does not roll back schema changes — it only surfaces the error.
definePreInstallLogicFunction
Runs before the workspace metadata migration is applied
definePreInstallLogicFunction
Runs before the workspace metadata migration is applied
Runs before the metadata migration, against the previous schema — the right place to back up data a migration would lose, or to refuse a risky upgrade. Before executing, the server runs a purely additive “pared-down sync” that registers only the new version’s pre-install function; everything else — the previous version’s objects, fields, and data — is untouched when your handler runs.Pre-install is always synchronous and blocks the install. If the handler throws, the install is aborted before any schema change — the workspace stays on the previous version in a consistent state. This is intentional: pre-install is your last chance to refuse a risky upgrade.Example — copy a legacy field’s values before the migration drops it:
src/logic-functions/pre-install.ts
Uninstall hook
defineUninstallLogicFunction declares a hook that runs when a user uninstalls your app. It executes before the app’s metadata, data, and code are removed — once the deletion migration runs there is nothing left to execute — so your handler can still query the app’s objects and records. Use it for cleanup of external resources: deprovision API resources, delete remaining bots, revoke webhooks.
Notes:
- The hook is best-effort: it runs synchronously, but a failure is logged and never blocks the uninstall — cleanup must not make an app impossible to remove.
- It receives
UninstallPayload({ version?: string }— the version being removed). - It does not run when a failed fresh install is rolled back — the app never finished installing.
- The hook cannot run after the app is gone, so external cleanup that depends on app data (e.g. bot IDs stored in records) belongs here, not in an external scheduled job.
- Like the install hooks, it is not executed in dev mode — trigger it manually instead:
src/logic-functions/uninstall.ts