Skip to main content
Install hooks are special logic functions that run during the install, upgrade, or uninstall lifecycle. They share the same handler runtime as regular logic functions, but they’re declared with their own define functions and live outside the normal trigger model (HTTP, cron, database events). Install hooks receive an 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 defineLogicFunction config minus the trigger settings, plus shouldRunOnVersionUpgrade.
  • When it runs: fresh installs only, by default. Set shouldRunOnVersionUpgrade: true to also run on upgrades. Use previousVersion / newVersion to branch on the upgrade path.
  • Idempotency matters: async post-install may be retried, and either hook re-runs on upgrades when shouldRunOnVersionUpgrade is 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 in defineApplication().
  • The default timeoutSeconds is 300 to allow longer setup tasks like data seeding.
  • Not executed in dev mode: yarn twenty dev skips the install flow and syncs files directly, so hooks never run there. Trigger them manually instead:
Runs once your app has finished installing: metadata synchronized, SDK client generated, new schema queryable. Example — seed a default record on fresh installs:
src/logic-functions/post-install.ts
The 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 as POST_INSTALL_ERROR to 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.
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