packages/twenty-codex-plugin/references/develop-app/logic.md
Use this reference for Twenty app logic functions, skills, agents, and connection providers.
A logic function file should contain trigger registration, input validation, the call out to the work, and the writes back — nothing else. Everything else lives next to it (see app-structure.md):
src/<service>-client/<name>.ts.src/utils/<name>.util.ts.src/types/<name>.ts.Kebab-case filenames, one export per file. Response/DTO types go in src/types/<name>.ts and parsing/mapping helpers in src/utils/<name>.util.ts — never export both a type and a util from the same file (a local, non-exported type may stay with the util), and never put multiple function exports in one file. See app-structure.md.
Other rules:
records: Array<{ id: string; ...fields }> unless the user explicitly says the function is only for one record.id inside a records array for the Twenty record ID. Do not add recordId, object-specific IDs such as companyId, or flat single-record payloads unless the user explicitly requests a single-record contract.process.env.Soft cap: a *.logic-function.ts or *.post-install.ts file over 200 lines is a refactor signal.
Bulk is the default logic-function contract for actions that may run from front component selection. The front component should gather selected records and invoke the function once. Do not make the front component loop over selected records and execute the same logic function repeatedly unless there is an explicit single-record requirement.
Preferred input:
type BulkInput<TRecord extends { id: string }> = {
records: TRecord[];
};
Preferred output:
type BulkResult = {
ok: boolean;
enrichedCount: number;
noMatchCount: number;
failedCount: number;
results: Array<{
id: string;
status: 'ENRICHED' | 'NO_MATCH' | 'FAILED';
pdlId?: string;
error?: string;
}>;
};
For existing single-record functions that are being upgraded to selected-record actions, replace the old flat input with records: Array<{ id: string; ...fields }> unless the user explicitly asks for backward compatibility.
Extract and test:
Skills and agents should describe when they apply, what context they need, and what output is expected.
When adding AI behavior:
For third-party connections:
Use definePostInstallLogicFunction for records that must exist on install — default workflows, views, roles, or seeded reference data. Do not implement this as runtime first-run code.
Post-install hook files live alongside other logic functions (typically src/logic-functions/<name>.post-install.ts). Kebab-case filename, one export per file.
Hooks must be idempotent: find by stable identifier before creating, update if it exists, never duplicate. Treat a not-found from a single-record query as "needs create."
Do not write fields Twenty computes elsewhere (workflow statuses is computed from version status — see workflows.md).
Dev sync skips install hooks. Invoke locally:
yarn twenty dev:function:exec
Run again after rebuilding to verify idempotency.