From ff03e935ef93f34ca1743d3da2744bd93e2f6a19 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Tue, 16 Jun 2026 17:49:12 +0200 Subject: [PATCH] feat(workflow): expand manual-trigger runtime payload with payload + _metadata (#21676) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Step 1 (**expand**) of restructuring the manual-trigger output so record fields live under a `payload` key and trigger-level metadata (the running workspace member) lives alongside it. This step is **additive and runtime-only** — no behavior changes for existing workflows, and nothing new is surfaced in the variable picker yet. The manual-trigger runtime payload now additively carries: - `payload`: a mirror of the incoming record fields, reachable at `{{trigger.payload.*}}` - `_metadata.workspaceMemberId`: the member who ran the workflow, reachable at `{{trigger._metadata.workspaceMemberId}}` Record fields are still served at the trigger root, so existing `{{trigger.id}}` references keep working unchanged. The output schema / variable picker is intentionally left untouched here. ### Why `_metadata` (underscore) During the transition, record fields still sit at the `trigger` root next to the injected keys. Field API names can't start with `_`, so `_metadata` is collision-proof against any record field; picking the name now avoids a later variable-path rename migration. ### Phasing - **Step 1 (this PR):** write `payload` + `_metadata` at runtime; keep using direct `trigger.*`; don't display the new paths. - **Step 2:** surface `payload` + `_metadata` in the variable picker. - **Step 3:** migrate existing variables to `trigger.payload.*` and contract the root record fields. ## Test plan - [x] `twenty-shared` builds, `twenty-server` typechecks, lint clean on changed files - [x] Manual: run a manually-triggered (SINGLE_RECORD) workflow and confirm the run's trigger payload contains `payload.*` mirroring the record and `_metadata.workspaceMemberId` - [x] Manual: confirm existing `{{trigger.id}}` references still resolve Review in cubic --- .../resolvers/workflow-trigger.resolver.ts | 14 +++++++++++++- .../constants/WorkflowTriggerMetadataKey.ts | 1 + .../WorkflowTriggerMetadataWorkspaceMemberIdKey.ts | 2 ++ .../constants/WorkflowTriggerPayloadKey.ts | 1 + packages/twenty-shared/src/workflow/index.ts | 3 +++ 5 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataKey.ts create mode 100644 packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataWorkspaceMemberIdKey.ts create mode 100644 packages/twenty-shared/src/workflow/constants/WorkflowTriggerPayloadKey.ts diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts index f45d143cda..a9c628d383 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts @@ -2,6 +2,11 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; +import { + WORKFLOW_TRIGGER_METADATA_KEY, + WORKFLOW_TRIGGER_METADATA_WORKSPACE_MEMBER_ID_KEY, + WORKFLOW_TRIGGER_PAYLOAD_KEY, +} from 'twenty-shared/workflow'; import { CoreResolver } from 'src/engine/api/graphql/graphql-config/decorators/core-resolver.decorator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; @@ -98,7 +103,14 @@ export class WorkflowTriggerResolver { return this.workflowTriggerWorkspaceService.runWorkflowVersion({ workflowVersionId, workflowRunId: workflowRunId ?? undefined, - payload: payload ?? {}, + payload: { + ...(payload ?? {}), + [WORKFLOW_TRIGGER_PAYLOAD_KEY]: { ...(payload ?? {}) }, + [WORKFLOW_TRIGGER_METADATA_KEY]: { + [WORKFLOW_TRIGGER_METADATA_WORKSPACE_MEMBER_ID_KEY]: + workspaceMember.id, + }, + }, createdBy: buildCreatedByFromFullNameMetadata({ fullNameMetadata: { firstName: workspaceMember.name.firstName, diff --git a/packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataKey.ts b/packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataKey.ts new file mode 100644 index 0000000000..d7f3593358 --- /dev/null +++ b/packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataKey.ts @@ -0,0 +1 @@ +export const WORKFLOW_TRIGGER_METADATA_KEY = 'metadata'; diff --git a/packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataWorkspaceMemberIdKey.ts b/packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataWorkspaceMemberIdKey.ts new file mode 100644 index 0000000000..8414159abe --- /dev/null +++ b/packages/twenty-shared/src/workflow/constants/WorkflowTriggerMetadataWorkspaceMemberIdKey.ts @@ -0,0 +1,2 @@ +export const WORKFLOW_TRIGGER_METADATA_WORKSPACE_MEMBER_ID_KEY = + 'workspaceMemberId'; diff --git a/packages/twenty-shared/src/workflow/constants/WorkflowTriggerPayloadKey.ts b/packages/twenty-shared/src/workflow/constants/WorkflowTriggerPayloadKey.ts new file mode 100644 index 0000000000..481566c1e9 --- /dev/null +++ b/packages/twenty-shared/src/workflow/constants/WorkflowTriggerPayloadKey.ts @@ -0,0 +1 @@ +export const WORKFLOW_TRIGGER_PAYLOAD_KEY = 'payload'; diff --git a/packages/twenty-shared/src/workflow/index.ts b/packages/twenty-shared/src/workflow/index.ts index 07484acba5..41e92d6af5 100644 --- a/packages/twenty-shared/src/workflow/index.ts +++ b/packages/twenty-shared/src/workflow/index.ts @@ -12,6 +12,9 @@ export { CONTENT_TYPE_VALUES_HTTP_REQUEST } from './constants/ContentTypeValuesH export { IF_ELSE_BRANCH_POSITION_OFFSETS } from './constants/IfElseBranchPositionOffsets'; export { OBJECTS_BLOCKED_FROM_AUTOMATION } from './constants/ObjectsBlockedFromAutomation'; export { TRIGGER_STEP_ID } from './constants/TriggerStepId'; +export { WORKFLOW_TRIGGER_METADATA_KEY } from './constants/WorkflowTriggerMetadataKey'; +export { WORKFLOW_TRIGGER_METADATA_WORKSPACE_MEMBER_ID_KEY } from './constants/WorkflowTriggerMetadataWorkspaceMemberIdKey'; +export { WORKFLOW_TRIGGER_PAYLOAD_KEY } from './constants/WorkflowTriggerPayloadKey'; export { workflowAiAgentActionSchema } from './schemas/ai-agent-action-schema'; export { workflowAiAgentActionSettingsSchema } from './schemas/ai-agent-action-settings-schema'; export { baseTriggerSchema } from './schemas/base-trigger-schema';