AI Engineer Europe 2026

Building Your Own Secure AI Workflows: Human-in-the-Loop Automation with n8n

Read the talk

Building Your Own Secure AI Workflows: Human-in-the-Loop Automation with n8n

Start with a chat-driven Gmail and Calendar assistant, constrain its tool arguments, and add human approval that intercepts sensitive actions before execution.

From a talk by Liam McGarrigle

Before you start: To follow the build, have an n8n instance, Gmail and Google Calendar accounts you can connect, and credentials for a supported model provider; basic JavaScript helps with expressions.

An assistant for your inbox and calendar

Can an assistant manage your Gmail and Google Calendar while leaving you in control of consequential actions? That is the starting project in Liam McGarrigle’s workshop: build a first workflow, turn it into an agent, and then add a human review step. The useful result is an assistant whose proposed actions you can inspect before allowing them to proceed.

Welcome slide with the workshop title, n8n logo, and a glowing lightning bolt on black.Open full source frame
Building an n8n agent with a human in the loop.

n8n supplies a visual canvas, but its fields also accept small JavaScript expressions. A transformation such as .toUpperCase() does not require a separate Code node or another file. This combination matters once an agent starts operating across applications: you need to see what it can do, inspect what it actually did, and change the part that went wrong. The companion Notion guide supplies follow-along instructions and extension exercises.

Participants can use an existing Cloud or self-hosted instance with credentials configured. For new accounts, McGarrigle describes a 14-day Cloud trial and offers attendees one year of Cloud Pro, valued at $600, through temporary workshop codes. These were workshop offers, not ongoing signup terms. His setup advice is to use the latest stable release; troubleshooting a bespoke self-hosting configuration falls outside the session.

The audience already has practical automation in mind. One attendee describes a daily AI-news curator that combines multiple sources, useful enough to pay for; McGarrigle points participants toward colleagues who can help with workplace adoption. For anyone falling behind during the build, the guide includes a reference screenshot and completed workflow JSON. Copying that JSON and pasting it directly onto the canvas restores the reference workflow, which can then be compared with the live version. Keeping versions reasonably current avoids another source of workshop friction.

1:001:17
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

1:00 · section reference included

From integration workflows to a chat entry point

Before adding AI, establish where the workflow and its credentials live. In the interface shown, McGarrigle distinguishes personal space on a basic self-hosted setup from projects available on Cloud and Enterprise. Projects separate credentials and collaborator access, reducing the chance that a workflow uses an unrelated account.

n8n began in 2019 as a workflow integration tool. Its basic structure is still useful for understanding the agent you are about to build: a trigger starts an execution, actions call applications, and branches choose what happens next. The first example is a native lead form that accepts a name. A contact can then be routed to Google Contacts if personal, or Salesforce if business.

PrimitiveExampleResponsibility
TriggerForm, schedule, webhookStart the workflow
ActionCreate a contact, send emailPerform an application operation
BranchPersonal or business contactChoose an execution path

For a developer, the canvas is an abstraction over API calls and control flow. An agent will change how actions are selected, while retaining these integration building blocks.

Start the assistant with a Chat Trigger. Slack message events are another possible entry point, but the built-in chat keeps the first test local to the editor. Adding the trigger opens a chat box on the canvas; a message can start a workflow even before any AI is connected.

To use the separate internal chat interface, enable Make available in Chat Hub and publish the workflow. Open Chat Hub from the sidebar and send a test message. McGarrigle sends a greeting while the trigger is still unattached to an agent, then finds that same input in the workflow’s Executions tab. This verifies the entry point independently of model behavior. Connecting an external messaging platform comes later.

9:049:11
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

9:04 · section reference included

Attach a model, then give the conversation memory

Add an AI Agent after the trigger. Its lower connectors accept a chat model, memory, and tools; the model is the only required attachment at this initial stage. n8n offers several provider nodes. McGarrigle also notes that the OpenAI model node can often reach another compatible provider or an enterprise proxy by changing its base URL.

The demonstration uses OpenRouter and selects Sonnet 4.6 for tool use. Participants receive a temporary OpenRouter key through the workshop guide, scheduled for removal after the session day. The credential must match the provider node: an OpenRouter token does not become a credential for another provider simply because that provider serves a similar model.

With the model connected, the agent can answer a greeting. Ask what the first message was, however, and it cannot recall it. The application has not supplied conversation history. Attach Simple Memory to store that history within n8n. Its context-window setting controls how much prior conversation is supplied; McGarrigle increases the suggested setting from the displayed 5 toward 50 for longer conversations, while warning that past tokens also cost money. He describes the setting in terms of messages, but the workshop does not establish its exact message-versus-interaction counting semantics.

Memory choiceReason to choose it
Simple MemoryLet n8n handle chat-history storage for the initial workflow
PostgresStore messages in a table another application can query
Redis or another backendIntegrate memory with an existing system

The Postgres example is concrete: an external chat UI or dashboard can query the messages through its ORM and display them. Simple Memory hides that storage work. In this workflow, the Chat Trigger provides the session ID that associates messages with the right conversation.

n8n workflow with a chat trigger, AI Agent, OpenRouter Chat Model, and Simple Memory; the Memory panel lists Simple Memory, MongoDB, Postgres, Redis, and Xata.Open full source frame
Memory options alongside the chat agent workflow.
14:2414:41
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

14:24 · section reference included

Map inputs and connect Google tools

A field can contain a fixed value or an expression. In expression mode, the content inside {{ ... }} is evaluated, while surrounding text remains literal. The editor displays these expressions in green and offers helpers such as $now, alongside JavaScript functions such as Math.random(). Dragging a field from the trigger into another field creates a reference to that input; references and literal text can share the same destination.

For example, an n8n expression field can combine the trigger’s chat input with a short label:

javascript

Request: {{ $json.chatInput }}
Current time: {{ $now }}

This is the same mapping mechanism used to pass session identifiers and other upstream values through the workflow.

The assistant now chats, but it cannot yet manage email or events. Attach integration actions as tools beneath the agent, rather than placing them only in a fixed sequence after it. The agent can choose when to invoke these tools. Gmail’s Send a Message action is the first example, and its per-field buttons let the model supply selected arguments.

Authenticate Gmail with Google, then configure Google Calendar separately. McGarrigle describes the Google setup as the straightforward path for this workshop and Microsoft setup as less convenient. A useful credential distinction appears during the live setup: opening the existing credential edits that connection; selecting Add New Credential creates another one. Gmail and Calendar each need their own credential sign-in in the demonstrated configuration.

20:1620:30
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

20:16 · section reference included

Choose exactly which arguments the model controls

The email tool set will read, search, archive, and send messages. Each operation exposes individual fields, and only explicitly delegated fields are filled by the model. McGarrigle motivates this with a contrast to giving an agent broad Calendar API access: here, the workflow author can narrow the interface argument by argument. His concrete example fixes the email subject and message while allowing the model to choose only To. The restriction comes from the configured tool interface, rather than a request in the prompt to leave the other fields alone.

Gmail Send parameters show an expression in To and literal text in Subject and Message, with no output data.Open full source frame
A generated recipient field alongside fixed subject and message text.

This control has a setup cost: every model-supplied field must be configured. The automatic-fill button inserts a helper expression; writing $fromAI directly provides the same mechanism and allows more elaborate templates. For a recipient field, the expression can be:

javascript

{{ $fromAI('to') }}

An undefined preview can simply mean that the agent has not supplied a value yet. The value arrives when the tool call is proposed at runtime.

Direct expressions are especially useful when generated content should occupy only part of a field. McGarrigle combines an AI-response disclosure, the original chat input, and model-generated text. An email Message field can follow that pattern:

javascript

This is an AI responding to this request:
{{ $('When chat message received').item.json.chatInput }}

{{ $fromAI('message') }}

Use the actual trigger node name when inserting the reference, or drag the input field into the template. The fixed disclosure remains part of the configured field while the model supplies the message content. For the workshop’s general email assistant, McGarrigle ultimately delegates all the necessary sending fields.

25:4125:53
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

25:41 · section reference included

Put instructions at the level where they apply

Tool names and descriptions are part of the model’s interface. Rename an automatically named Gmail action to something direct, such as Send an Email, and supply a manual description. The node name becomes the tool name, and the description explains its purpose and use to the model. McGarrigle prefers keeping email-specific instructions, including writing-style rules, inside that tool description. Copying the tool into another workflow then carries its instructions with it.

The system message supplies the agent’s identity, purpose, and general behavior. Expand the field to full screen when editing it, start with a small prompt, and add instructions in response to observed failures. Two practical rules in the demonstration are to ask for missing information rather than invent it, and never send unresolved placeholders such as a prospective client’s missing name.

Requests involving today’s email require another piece of context: the current time. A model may otherwise search around an irrelevant date. Add $now to the system message and ensure the field is in expression mode:

javascript

The current date and time is {{ $now }}.

When adding more tools, configure one authenticated node first, then copy it. This retains the credential selection and avoids repeating the same setup for each action.

Field descriptions resolve ambiguity that neither the system prompt nor the tool name can handle precisely. A Gmail thread ID is different from a message ID; if the model substitutes one for the other, describe the required identifier on that argument. Calendar offers an even clearer example: the Google Calendar Events resource calls the event title summary. Add a description saying that this is the event title, then change the model-facing generated key from summary to title. The underlying Calendar field remains Summary.

29:0329:17
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

29:03 · section reference included

Inspect the execution before trusting the answer

The first test exposes a credential-access error. A credential can appear selected while the workflow’s project lacks access to it. The demonstrated repair is to open Home, go to Credentials, share the credential with the project, and save. Selection in a node and permission to use the credential are separate concerns.

Once the workflow runs, inspect both the individual node output and the detailed execution log. Here, the visible result is an email summary, and the execution includes Gmail’s Get Many Messages. McGarrigle narrates a Calendar question while inspecting that Gmail result, so this moment establishes the email-tool execution rather than a successful Calendar lookup. He examines its date-bounded search input and then the returned messages summarized in chat. The log makes the actual operation visible even when the surrounding narration is inconsistent.

n8n chat displays an email summary beside an execution tree including Get many messages in Gmail and an output panel.Open full source frame
An email summary alongside the successful execution log.
38:0338:06
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

38:03 · section reference included

Put human approval on the tool connection

Testing against a real account makes the next requirement immediate: an accidental email or reply would leave the system and reach another person. McGarrigle chooses email sending, replies, and Calendar event creation for human review. He leaves archiving unguarded as a personal risk decision. Review coverage therefore follows the consequence of each operation, rather than applying one undifferentiated rule to all tools.

To guard an action:

  1. Select the connection between the agent and its tool.
  2. Click the connection’s plus button to add human review.
  3. Choose the review channel; the workshop uses native n8n chat.
  4. Rename the approval button to Send and enable a decline option.
  5. Leave Block User Input off if the reviewer should be able to respond with changes.

The review step sits in the connected tool’s execution path. In this configuration, a typed response denies the pending call, allowing the user to correct the proposal instead of approving it.

The first guarded test fails with a useful error: the Chat Trigger must use response nodes. Under the trigger’s Options, change Response Mode from Streaming to Using Response Nodes. Streaming sends incremental model output directly to chat. Response-node mode gives workflow nodes control over what messages the user receives, including the review request.

That change also means the agent’s ordinary final answer needs an explicit delivery node. A Chat tool can send messages during tool use, but the final response in this workflow comes from a Send a Message chat node after the agent. McGarrigle initially guesses the wrong output field. He disables the broken response node, obtains test data, and discovers that the agent returns output, not message. Map the actual field:

javascript

{{ $json.output }}

The corrected response arrives as one chunk. Because delivery now passes through workflow nodes, an If branch could also choose different response paths.

40:5341:06
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

40:53 · section reference included

Show the proposed email before asking for approval

The next test reaches the review step, but its default message reveals only that the agent wants to call the email tool. That is insufficient to approve a send: the reviewer still needs to know the recipient and content. McGarrigle declines the request and opens the review node to change its message.

Inside the review message, $tool.name identifies the proposed tool and $tool.parameters exposes its arguments. Rendering the object directly may produce [object Object]. For inspection, convert it to JSON:

javascript

{{ $tool.parameters.toJSONString() }}

Once the parameter names are clear, a labeled message is easier to review than a raw object:

javascript

The agent wants to send an email.

To: {{ $tool.parameters.to }}
Subject: {{ $tool.parameters.subject }}

Message:
{{ $tool.parameters.message }}

These fields describe the pending call. Showing them does not execute it.

Publish the workflow and test the updated proposal in Chat Hub. The reviewer can now inspect the email and choose Send or Decline. McGarrigle instead types a request for an email containing Markdown features. That response denies the pending call and prompts another attempt. The retry still does not follow the Markdown request; he attributes this to the tool description’s influence. He then approves the test email and reports the send completing. The review loop worked even though the requested content revision did not.

The agent calls the original tool; n8n intercepts the call. The model does not have to choose a separate review tool correctly. Because it may not know that interception exists, a cautious model can still ask conversational permission before proposing a send. McGarrigle addresses that in the tool description by explaining that the call will be reviewed before execution. Such an instruction makes sense only after the gate is actually installed.

A single review node can guard multiple tools, but its message must accommodate their different arguments. A recipient/subject/message template cannot automatically explain a Calendar event. McGarrigle therefore uses the working email gate as an example for AI Builder, asking it to add equivalent review steps elsewhere and match each tool’s parameter names. He describes the builder’s availability at the time as Cloud and Enterprise, with MCP providing an alternative route for external agents. After inspecting the generated changes, he publishes and requests lunch at noon plus an email invitation. The email and event receive separate approval requests; the event preview’s awkward timestamps become the next repair.

46:3546:50
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

46:35 · section reference included

Repair the Calendar preview with recorded execution data

Open Executions to find the Chat Hub run; test executions are marked with a flask. The recorded path shows the human-review step followed by event creation. Copy to editor brings that execution’s data into the editing context, so the approval message can be improved against actual values instead of imagined output.

The date-formatting repair begins with a revealing mistake: McGarrigle attempts to convert lunch, the event title, into a date. After selecting the actual timestamp field, he applies toDateTime() followed by format(). Expression autocomplete offers the compatible methods, documentation, and examples. The formatting uses n8n’s Luxon-based date support; DDDD and TT produce a more readable date and time.

Apply that formatting in the review message, leaving the event payload intact. A person can then assess the proposal without decoding the raw timestamp. McGarrigle republishes and tests a request to put dinner on the calendar for seven; the revised preview displays the date more clearly. The iteration is small and observable: reuse execution data, change presentation, publish, and inspect another proposal.

55:1955:30
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

55:19 · section reference included

Use n8n from internal services and external agents

An attendee’s coupon problem briefly exposes another use of the platform. McGarrigle promises to check or replace the workshop code and leave the replacement available temporarily; redemption is not confirmed during the session. He then describes the internal coupon service behind it: one n8n REST API entry point unifies merchandise, Enterprise, giveaway, and Cloud coupon systems, with audit logs. McGarrigle reports building that internal coupon system in approximately six hours.

An audience question then turns to native instance-level MCP, compared with an unofficial server the attendee has been using with Claude Code. McGarrigle opens Settings → Instance level MCP, enables the interface, and grants access to the email-and-calendar workflow. The walkthrough discusses published workflows and supported entry points such as webhooks, forms, schedules, and chat. Access is explicitly enabled per workflow, either from the MCP page or the workflow’s Available in MCP setting.

Connection Details supplies the server URL. The demonstrated setup options are OAuth through the Claude connector or an access token placed in the client configuration. McGarrigle describes the native integration as working directly inside n8n, rather than being constrained to wrapping its existing API. He says it can create, read, and execute workflows, including sending a chat message into the email-and-calendar bot. His comparison with the unofficial server’s execution support is tentative; the workshop does not establish feature parity between implementations.

58:4659:00
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

58:46 · section reference included

Keep review visible as the assistant runs in the background

A company-wide approval process introduces a new question: where does work wait, and which departmental reviews take longest? McGarrigle distinguishes Enterprise audit logging from a ready-made approval-delay dashboard. The latter is not presented as built in. He demonstrates that an approval request places an execution in Waiting, with the execution later reaching Finished after resuming, and proposes retrieving execution data with an n8n node. Waiting start and end times, together with relevant Slack details, could be parsed and aggregated in another workflow. He also acknowledges that human-review audit reporting needs more polish.

To move the assistant into Slack, replace the chat entry point, final response, and review-delivery steps with Slack equivalents. McGarrigle’s personal assistant also posts a dancing GIF as a loading indicator and removes it after responding. This gives users visible feedback during the interval between submitting a request and receiving the result.

The trigger does not have to be a user message. A schedule can run hourly and provide an instruction such as clearing the inbox; guarded actions can still send their approval requests to Slack. McGarrigle describes using the same pattern for GitHub issues and pull requests: inspect the code, prepare clarification messages, and show those messages to him before sending them to coworkers or clients. The guide’s extension work adds persistent memory across sessions and combines interactive Slack chat with scheduled runs. Automatic initiation can coexist with human authorization of sensitive actions.

Unanswered requests need an operational policy. An attendee asks whether ten waiting approvals could occupy all ten concurrent execution slots on a self-hosted instance. McGarrigle does not know whether those waits count toward that limit and offers to follow up. His immediate suggestion is Limit Wait Time: for example, automatically deny after ten minutes so pending requests do not accumulate indefinitely. That timeout bounds how long a proposal remains unresolved; it does not answer the concurrency-accounting question.

1:02:581:03:10
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

1:02:58 · section reference included

Separate action approval from workflow change approval

A question about team collaboration first produces a useful answer about routing approvals. The Slack trigger provides the requesting user’s ID, which an ordinary response can reuse. But a vacation request should go to a manager or decision-making channel for approval, rather than back to its requester. Change the review message’s destination accordingly. The attendee clarifies that the original question concerns approving edits to the workflow itself, a different responsibility.

For workflow changes, McGarrigle describes two approaches:

  • Manual change management: Copy the workflow, make changes in the copy, and have its responsible maintainer incorporate or replace the original with the reviewed version.
  • Git and environments: Use the Enterprise capabilities he describes for development, staging, and production environments, with designated people managing changes and approvals.

Feature-branch-style work otherwise requires explicit copying and reintegration. These are his descriptions of the available workflow-management options, rather than a demonstrated pull-request approval sequence.

Autosave has also made remote edits visible through background updates. McGarrigle explicitly distinguishes that behavior from full simultaneous multiplayer editing. Seeing another person’s changes appear does not itself supply a review or release process.

1:10:101:10:39
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

1:10:10 · section reference included

What changes when there is no approval button?

A phone-based appointment agent cannot show the caller Send and Decline buttons. Could it ask for spoken confirmation instead? This question exposes the review gate’s trust boundary. Telling the language model to accept a proposal does not itself authorize the guarded tool. If another model interpretation decides whether a spoken reply counts as approval, that interpretation becomes part of the authorization mechanism.

In the workshop, built-in review is described as supporting chat platforms. McGarrigle explains that their approval actions call endpoints carrying unique tokens, then expresses a wish for a custom option that exposes approval and rejection links directly. He presents that as a desired feature, not an available configuration demonstrated in the session.

His alternative is a custom sub-workflow:

  1. Expose an Execute Sub-workflow tool, for example named Confirm.
  2. Ask the user the confirmation question through the relevant interface.
  3. Obtain a result such as true or false.
  4. Use an If node to branch into the approved or rejected path.

That is workflow-defined confirmation logic. Its guarantees depend on how the response is collected and interpreted; it is not the built-in human-review gate shown earlier.

1:13:281:13:44
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

1:13:28 · section reference included

Expose the engine and expand through specialized agents

The final audience question asks whether an organization that readily deploys Microsoft applications can keep its own interface and call n8n as an engine. A Webhook Trigger provides that HTTP entry point. Configure a path and method, such as POST to a user-creation endpoint, and let the workflow implement the operation behind it. McGarrigle returns to the coupon service as an example: other workflows and external tools consume its REST API documentation, including Swagger-style documentation, while orchestration and human-review logic remain inside n8n.

As the assistant grows beyond Gmail and Calendar, avoid placing every domain’s tools and instructions on the main agent. Use a subagent tool so the main agent can call specialized agents. The assistant built here becomes the email-and-calendar specialist; GitHub issues and Jira management can become separate specialists. This reduces the context carried by the main agent and lets each specialist use a model suited to its task. The next layer can therefore reuse the working assistant, including its tool configuration and review behavior, rather than rebuilding it as part of one increasingly crowded agent.

1:16:141:16:23
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

1:16:14 · section reference included

Resources

Updates since the talk

Transcript download
Read the transcript and explore slides

Loading transcript and slides…