---
title: Hooks de instalación
description: "Ejecuta lógica durante el ciclo de vida de instalación, actualización o desinstalación: siembra datos, realiza copias de seguridad de registros, valida la actualización, limpia recursos externos."
icon: wrench
---

Los hooks de instalación son funciones de lógica especiales que se ejecutan durante el ciclo de vida de la instalación, actualización o desinstalación. Comparten el mismo tiempo de ejecución del controlador que las [logic functions](/l/es/developers/extend/apps/logic/logic-functions) normales, pero se declaran con sus propias funciones de definición y están fuera del modelo de desencadenadores normal (HTTP, cron, eventos de base de datos). Los hooks de instalación reciben un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` es `undefined` en una instalación nueva); el hook de desinstalación recibe un `UninstallPayload` (`{ version?: string }` — la versión que se está eliminando).

Cada aplicación puede definir **como máximo un** hook de cada tipo (preinstalación, postinstalación, desinstalación). La compilación del manifiesto genera un error si se detecta más de uno de cualquier tipo.

```
┌─────────────────────────────────────────────────────────────┐
│ install flow                                                │
│                                                             │
│   upload package → [pre-install] → metadata migration →     │
│   generate SDK → [post-install]                             │
│                                                             │
│                  old schema visible    new schema visible   │
└─────────────────────────────────────────────────────────────┘
```

## De un vistazo

|                  | `definePreInstallLogicFunction`                                                                                                    | `definePostInstallLogicFunction`                                                                                                                           |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ejecuciones      | Antes de la migración de metadatos — el esquema y los datos **anteriores** siguen intactos                                         | Después de la migración y la generación del SDK — el esquema **nuevo** está en su lugar                                                                    |
| Ejecución        | Siempre síncrona; bloquea la instalación                                                                                           | Asíncrona de forma predeterminada (en cola, 3 reintentos); modo síncrono opcional mediante `shouldRunSynchronously: true`                                  |
| En caso de fallo | La instalación se **aborta** antes de cualquier cambio de esquema                                                                  | Asíncrono: se vuelve a intentar hasta 3 veces. Síncrono: quien realiza la llamada recibe `POST_INSTALL_ERROR` (los cambios de esquema **no** se revierten) |
| Uso típico       | Hacer copia de seguridad o corregir datos que una migración perdería; rechazar una actualización arriesgada lanzando una excepción | Sembrar datos predeterminados, configurar el espacio de trabajo, registrar recursos externos                                                               |

**Regla general:** usa post-install de forma predeterminada. Recurra a la pre-instalación solo cuando la propia migración sea destructiva y necesite interceptar el estado anterior antes de que desaparezca.

| Quiere...                                                                                               | Usar                                                                      |
| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Sembrar datos, configurar el espacio de trabajo, registrar recursos externos                            | `post-install`                                                            |
| Trabajo de larga duración que no debería bloquear la respuesta de instalación                           | `post-install` (modo asíncrono predeterminado, con reintentos del worker) |
| Configuración rápida de la que el cliente depende inmediatamente después de que finaliza la instalación | `post-install` con `shouldRunSynchronously: true`                         |
| Leer o hacer copia de seguridad de datos que la próxima migración perdería                              | `pre-install`                                                             |
| Rechazar una actualización que corrompería datos existentes                                             | `pre-install` (lanzar desde el controlador)                               |
| Reconciliación en cada actualización                                                                    | Cualquiera de los hooks con `shouldRunOnVersionUpgrade: true`             |

## Comportamiento compartido por ambos hooks

* La configuración es una configuración de `defineLogicFunction` menos los ajustes de disparador, más `shouldRunOnVersionUpgrade`.
* **Cuándo se ejecuta**: solo en instalaciones nuevas, de forma predeterminada. Configura `shouldRunOnVersionUpgrade: true` para que también se ejecute en las actualizaciones. Usa `previousVersion` / `newVersion` para ramificar según la ruta de actualización.
* **La idempotencia es importante**: el post-install asíncrono puede reintentarse y cualquiera de los hooks se vuelve a ejecutar en las actualizaciones cuando `shouldRunOnVersionUpgrade` está activado.
* El entorno habitual de las logic functions (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) se inyecta, por lo que puedes llamar a la API de Twenty con el token de tu app.
* El hook se adjunta automáticamente al manifiesto de la aplicación en tiempo de compilación (`preInstallLogicFunction` / `postInstallLogicFunction`) — no hay nada que referenciar en [`defineApplication()`](/l/es/developers/extend/apps/config/application).
* El `timeoutSeconds` predeterminado es 300 para permitir tareas de configuración más largas como la siembra de datos.
* **No se ejecuta en modo de desarrollo**: `yarn twenty dev` omite el flujo de instalación y sincroniza los archivos directamente, por lo que los hooks nunca se ejecutan ahí. En su lugar, dispáralos manualmente:

```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
```

<AccordionGroup>
<Accordion title="definePostInstallLogicFunction" description="Se ejecuta después de que se aplique la migración de metadatos del espacio de trabajo">

Se ejecuta una vez que tu app ha terminado de instalarse: metadatos sincronizados, cliente SDK generado, nuevo esquema disponible para consulta. Ejemplo — sembrar un registro predeterminado en instalaciones nuevas:

```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

const handler = async ({ previousVersion }: InstallPayload): Promise<void> => {
  if (previousVersion) return; // fresh installs only

  const client = new CoreApiClient();
  await client.mutation({
    createPostCard: {
      __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
      id: true,
    },
  });
};

export default definePostInstallLogicFunction({
  universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
  name: 'post-install',
  description: 'Seeds a welcome post card after install.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: false,
  shouldRunSynchronously: false,
  handler,
});
```

El flag `shouldRunSynchronously` controla el modelo de ejecución:

* `false` *(predeterminado)* — encolado en la cola de mensajes (`retryLimit: 3`) y ejecutado por un worker. La respuesta de instalación se devuelve tan pronto como el trabajo se pone en la cola. **Usar para trabajo de larga duración** — siembra de grandes conjuntos de datos, APIs de terceros lentas.
* `true` — se ejecuta en línea durante el flujo de instalación. La solicitud de instalación se bloquea hasta que el handler finaliza; un error lanzado aparece como `POST_INSTALL_ERROR` para quien realiza la llamada (sin reintentos). **Usar para trabajo rápido que debe completarse antes de la respuesta.** La migración ya se ha aplicado en este punto, por lo que un fallo no revierte los cambios de esquema — solo expone el error.

</Accordion>
<Accordion title="definePreInstallLogicFunction" description="Se ejecuta antes de que se aplique la migración de metadatos del espacio de trabajo">

Se ejecuta antes de la migración de metadatos, contra el esquema **anterior** — el lugar adecuado para hacer una copia de seguridad de los datos que una migración perdería o para rechazar una actualización arriesgada. Antes de ejecutarse, el servidor realiza una "sincronización simplificada" puramente aditiva que registra solo la función de pre-instalación de la versión nueva; todo lo demás — los objetos, campos y datos de la versión anterior — permanece sin cambios cuando se ejecuta tu handler.

La pre-instalación siempre es **síncrona** y bloquea la instalación. Si el handler lanza una excepción, la instalación se aborta antes de cualquier cambio de esquema — el espacio de trabajo permanece en la versión anterior en un estado consistente. Esto es intencional: la pre-instalación es su última oportunidad para rechazar una actualización arriesgada.

Ejemplo — copiar los valores de un campo heredado antes de que la migración lo elimine:

```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise<void> => {
  // Only the 1.x → 2.x upgrade drops the legacy `notes` field.
  if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) {
    return;
  }

  const client = new CoreApiClient();
  const { postCards } = await client.query({
    postCards: {
      __args: { filter: { notes: { isNot: null } } },
      edges: { node: { id: true, notes: true } },
    },
  });

  // Copy legacy `notes` into `description` before the migration drops the
  // column. If this fails, the upgrade aborts and the workspace stays on v1.
  for (const { node } of postCards.edges) {
    await client.mutation({
      updatePostCard: {
        __args: { id: node.id, data: { description: node.notes } },
        id: true,
      },
    });
  }
};

export default definePreInstallLogicFunction({
  universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
  name: 'pre-install',
  description: 'Backs up legacy notes into description before the v2 migration.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: true,
  handler,
});
```

</Accordion>
</AccordionGroup>

## Hook de desinstalación

`defineUninstallLogicFunction` declara un hook que se ejecuta cuando un usuario desinstala tu aplicación. Se ejecuta **antes** de que se eliminen los metadatos, los datos y el código de la aplicación — una vez que se ejecuta la migración de eliminación ya no queda nada que ejecutar — por lo que tu controlador todavía puede consultar los objetos y registros de la aplicación. Úsalo para limpiar recursos externos: cancelar el aprovisionamiento de recursos de API, eliminar bots restantes, revocar webhooks.

Notas:

* El hook se ejecuta con el mejor esfuerzo posible: se ejecuta de forma sincrónica, pero si falla se registra el error y **nunca bloquea la desinstalación**; la limpieza no debe hacer que una aplicación sea imposible de eliminar.
* Recibe `UninstallPayload` (`{ version?: string }` — la versión que se está eliminando).
* **No** se ejecuta cuando se revierte una instalación nueva fallida: la aplicación nunca terminó de instalarse.
* El hook no puede ejecutarse después de que la aplicación haya desaparecido, por lo que la limpieza externa que depende de datos de la aplicación (por ejemplo, IDs de bots almacenados en registros) debe realizarse aquí, no en una tarea programada externa.
* Al igual que los hooks de instalación, **no se ejecuta en modo de desarrollo**; en su lugar, actívalo manualmente:

```bash filename="Terminal"
yarn twenty dev:function:exec --uninstall
```

```ts src/logic-functions/uninstall.ts
import { defineUninstallLogicFunction, type UninstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

const handler = async (_payload: UninstallPayload): Promise<void> => {
  const client = new CoreApiClient();
  const { meetingBots } = await client.query({
    meetingBots: { edges: { node: { id: true, externalBotId: true } } },
  });

  // Delete the provider-side bots so nothing keeps recording after uninstall.
  for (const { node } of meetingBots.edges) {
    await fetch(`https://api.recorder.example/bots/${node.externalBotId}`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${process.env.RECORDER_API_KEY}` },
    });
  }
};

export default defineUninstallLogicFunction({
  universalIdentifier: 'b2c3d4e5-6789-01bc-def0-234567890abc',
  name: 'uninstall',
  description: 'Deletes remaining recorder bots when the app is uninstalled.',
  timeoutSeconds: 300,
  handler,
});
```
