From c678e52f449c192ac70a226b0173951dfd68830d Mon Sep 17 00:00:00 2001 From: Ondrej Drapalik Date: Wed, 8 Jul 2026 17:32:26 +0200 Subject: [PATCH 1/3] docs: add importable example workflows (levels 1-4) --- docs/examples/1-run-command-ephemeral.json | 98 ++++ docs/examples/2-web-app-with-preview.json | 197 +++++++ docs/examples/3-clone-build-download.json | 231 ++++++++ docs/examples/4-ai-agent-tool.json | 605 +++++++++++++++++++++ docs/examples/README.md | 26 + 5 files changed, 1157 insertions(+) create mode 100644 docs/examples/1-run-command-ephemeral.json create mode 100644 docs/examples/2-web-app-with-preview.json create mode 100644 docs/examples/3-clone-build-download.json create mode 100644 docs/examples/4-ai-agent-tool.json create mode 100644 docs/examples/README.md diff --git a/docs/examples/1-run-command-ephemeral.json b/docs/examples/1-run-command-ephemeral.json new file mode 100644 index 0000000..9b1854d --- /dev/null +++ b/docs/examples/1-run-command-ephemeral.json @@ -0,0 +1,98 @@ +{ + "name": "E2B · Level 1 — Run a command (ephemeral sandbox)", + "nodes": [ + { + "parameters": { + "content": "## E2B — Run one command, throwaway sandbox\n\nThe simplest E2B pattern: leave **Sandbox ID** empty and enable **Kill After Run** — a single node creates a sandbox, runs the command, and destroys it. No lifecycle to manage.\n\n### Setup\n1. Add an **E2B API** credential (key at e2b.dev/dashboard) to the E2B node.", + "height": 340, + "width": 460, + "color": "#CE2601" + }, + "id": "aaaa0000-0000-0000-0000-0000000000a1", + "name": "About & setup", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 120 + ] + }, + { + "parameters": { + "content": "### How to run\nClick **Test workflow** — the node output shows the command's `stdout` (a hello from Python inside the sandbox).\n\nNothing to clean up: the sandbox is killed automatically after the run.", + "height": 200, + "width": 460, + "color": 7 + }, + "id": "cccc0000-0000-0000-0000-0000000000c1", + "name": "How to run", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 480 + ] + }, + { + "parameters": {}, + "id": "11111111-1111-1111-1111-111111111111", + "name": "When clicking 'Test workflow'", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 240, + 320 + ] + }, + { + "parameters": { + "resource": "code", + "operation": "runCommand", + "sandboxId": "", + "command": "python3 -c \"import platform, sys; print(f'Python {sys.version_info.major}.{sys.version_info.minor} on {platform.system()}'); print('Hello from an E2B sandbox!')\"", + "allowInternetAccess": true, + "killAfterRun": true + }, + "id": "22222222-2222-2222-2222-222222222222", + "name": "Run Command (Ephemeral)", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 520, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + } + ], + "connections": { + "When clicking 'Test workflow'": { + "main": [ + [ + { + "node": "Run Command (Ephemeral)", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "tags": [ + { + "name": "e2b-example", + "id": "e2b-example" + } + ], + "meta": { + "templateCredsSetupCompleted": false + } +} diff --git a/docs/examples/2-web-app-with-preview.json b/docs/examples/2-web-app-with-preview.json new file mode 100644 index 0000000..74dc8e2 --- /dev/null +++ b/docs/examples/2-web-app-with-preview.json @@ -0,0 +1,197 @@ +{ + "name": "E2B · Level 2 — Run a web app and return a preview URL", + "nodes": [ + { + "parameters": { + "content": "## E2B — Web app with a public preview URL\n\nCreate a sandbox → **write** a tiny Python web server → start it on **port 3000** → return a public **preview URL**. Shows how to expose a running service from a sandbox to the internet.\n\n### Setup\n1. Add an **E2B API** credential (key at e2b.dev/dashboard) to every E2B node.", + "height": 360, + "width": 460, + "color": "#CE2601" + }, + "id": "aaaa0000-0000-0000-0000-0000000000a2", + "name": "About & setup", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 120 + ] + }, + { + "parameters": { + "content": "### How to run\nClick **Test workflow**, then open the `url` from the **Get Preview URL** output in your browser — the page is served live from the sandbox.\n\nThe sandbox (and the server) stays up until its timeout expires, then it is cleaned up automatically.", + "height": 220, + "width": 460, + "color": 7 + }, + "id": "cccc0000-0000-0000-0000-0000000000c2", + "name": "How to run", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 500 + ] + }, + { + "parameters": {}, + "id": "11111111-1111-1111-1111-111111111111", + "name": "When clicking 'Test workflow'", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 200, + 320 + ] + }, + { + "parameters": { + "resource": "sandbox", + "operation": "create", + "template": "", + "allowInternetAccess": true + }, + "id": "22222222-2222-2222-2222-222222222222", + "name": "Create Sandbox", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 440, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "resource": "file", + "operation": "write", + "sandboxId": "={{ $json.sandboxId }}", + "path": "/home/user/server.py", + "content": "from http.server import HTTPServer, BaseHTTPRequestHandler\n\nclass Handler(BaseHTTPRequestHandler):\n def do_GET(self):\n self.send_response(200)\n self.send_header('Content-Type', 'text/html')\n self.end_headers()\n self.wfile.write(b'

Hello from an E2B sandbox!

')\n\nHTTPServer(('0.0.0.0', 3000), Handler).serve_forever()\n" + }, + "id": "33333333-3333-3333-3333-333333333333", + "name": "Write Server File", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 680, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "resource": "code", + "operation": "runCommand", + "sandboxId": "={{ $('Create Sandbox').item.json.sandboxId }}", + "command": "nohup python3 /home/user/server.py > /tmp/server.log 2>&1 & sleep 1 && echo 'Server started on port 3000'", + "allowInternetAccess": true + }, + "id": "44444444-4444-4444-4444-444444444444", + "name": "Start Web Server", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 920, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "resource": "sandbox", + "operation": "getPreviewUrl", + "sandboxId": "={{ $('Create Sandbox').item.json.sandboxId }}", + "port": 3000 + }, + "id": "55555555-5555-5555-5555-555555555555", + "name": "Get Preview URL", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 1160, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + } + ], + "connections": { + "When clicking 'Test workflow'": { + "main": [ + [ + { + "node": "Create Sandbox", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Sandbox": { + "main": [ + [ + { + "node": "Write Server File", + "type": "main", + "index": 0 + } + ] + ] + }, + "Write Server File": { + "main": [ + [ + { + "node": "Start Web Server", + "type": "main", + "index": 0 + } + ] + ] + }, + "Start Web Server": { + "main": [ + [ + { + "node": "Get Preview URL", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "tags": [ + { + "name": "e2b-example", + "id": "e2b-example" + } + ], + "meta": { + "templateCredsSetupCompleted": false + } +} diff --git a/docs/examples/3-clone-build-download.json b/docs/examples/3-clone-build-download.json new file mode 100644 index 0000000..ecce778 --- /dev/null +++ b/docs/examples/3-clone-build-download.json @@ -0,0 +1,231 @@ +{ + "name": "E2B · Level 3 — Clone a repo, build, download an artifact", + "nodes": [ + { + "parameters": { + "content": "## E2B — Clone, build, download the artifact\n\nA CI-style template: create a sandbox → **git clone** a repo → run a build step → **download** the artifact into n8n as **binary data** → kill the sandbox.\n\n### Setup\n1. Add an **E2B API** credential (key at e2b.dev/dashboard) to every E2B node.", + "height": 340, + "width": 460, + "color": "#CE2601" + }, + "id": "aaaa0000-0000-0000-0000-0000000000a3", + "name": "About & setup", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 120 + ] + }, + { + "parameters": { + "content": "### How to run\nClick **Test workflow** — the **Download Artifact** node output carries the file as binary `data` (view or download it from the output panel).\n\nThe build step is a placeholder (`ls` into a file) — swap in your real build command and artifact path.", + "height": 220, + "width": 460, + "color": 7 + }, + "id": "cccc0000-0000-0000-0000-0000000000c3", + "name": "How to run", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 480 + ] + }, + { + "parameters": {}, + "id": "11111111-1111-1111-1111-111111111111", + "name": "When clicking 'Test workflow'", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 200, + 320 + ] + }, + { + "parameters": { + "resource": "sandbox", + "operation": "create", + "template": "", + "allowInternetAccess": true + }, + "id": "22222222-2222-2222-2222-222222222222", + "name": "Create Sandbox", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 440, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "resource": "git", + "operation": "clone", + "sandboxId": "={{ $json.sandboxId }}", + "repositoryUrl": "https://github.com/octocat/Hello-World.git", + "repositoryPath": "/home/user/repo", + "cloneOptions": {} + }, + "id": "33333333-3333-3333-3333-333333333333", + "name": "Clone Repo", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 680, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "resource": "code", + "operation": "runCommand", + "sandboxId": "={{ $('Create Sandbox').item.json.sandboxId }}", + "command": "ls -la /home/user/repo > /tmp/build-output.txt && echo done", + "allowInternetAccess": true + }, + "id": "44444444-4444-4444-4444-444444444444", + "name": "Run Build", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 920, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "resource": "file", + "operation": "download", + "sandboxId": "={{ $('Create Sandbox').item.json.sandboxId }}", + "remotePath": "/tmp/build-output.txt", + "binaryPropertyName": "data" + }, + "id": "55555555-5555-5555-5555-555555555555", + "name": "Download Artifact", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 1160, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "resource": "sandbox", + "operation": "kill", + "sandboxId": "={{ $('Create Sandbox').item.json.sandboxId }}" + }, + "id": "66666666-6666-6666-6666-666666666666", + "name": "Kill Sandbox", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 1400, + 320 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + } + ], + "connections": { + "When clicking 'Test workflow'": { + "main": [ + [ + { + "node": "Create Sandbox", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Sandbox": { + "main": [ + [ + { + "node": "Clone Repo", + "type": "main", + "index": 0 + } + ] + ] + }, + "Clone Repo": { + "main": [ + [ + { + "node": "Run Build", + "type": "main", + "index": 0 + } + ] + ] + }, + "Run Build": { + "main": [ + [ + { + "node": "Download Artifact", + "type": "main", + "index": 0 + } + ] + ] + }, + "Download Artifact": { + "main": [ + [ + { + "node": "Kill Sandbox", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "tags": [ + { + "name": "e2b-example", + "id": "e2b-example" + } + ], + "meta": { + "templateCredsSetupCompleted": false + } +} diff --git a/docs/examples/4-ai-agent-tool.json b/docs/examples/4-ai-agent-tool.json new file mode 100644 index 0000000..c890ad3 --- /dev/null +++ b/docs/examples/4-ai-agent-tool.json @@ -0,0 +1,605 @@ +{ + "name": "E2B · Level 4 — AI Agent runs code as a tool", + "nodes": [ + { + "parameters": { + "content": "## E2B — AI Agent runs code as a tool\n\nThe agent controls **one E2B sandbox** through four tools: `run_command`, `write_file`, `read_file`, `list_files`. State persists **across tool calls _and_ across chat runs** — the sandbox is tagged with metadata, found again on the next run, and *paused* (not killed) between runs, so files survive. The first message creates it; later messages resume the same one.\n\n### Setup\n1. Add an **E2B API** credential (key at e2b.dev/dashboard) to every E2B node.\n2. Add an **OpenAI** credential to the chat model.\n\n### Reset\nKill the tagged sandbox (or let it expire) to start fresh — a new one is created and tagged automatically.", + "height": 460, + "width": 460, + "color": "#CE2601" + }, + "id": "aaaa0000-0000-0000-0000-0000000000aa", + "name": "About & setup", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 20 + ] + }, + { + "parameters": { + "content": "### How to run\nClick **Chat** (bottom of the canvas), paste the prompt on the right, and send.\n\nThen send it **again** — the run counter goes up because the sandbox and its files persist between chats.", + "height": 200, + "width": 460, + "color": 7 + }, + "id": "cccc0000-0000-0000-0000-0000000000cc", + "name": "How to run", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 500 + ] + }, + { + "parameters": { + "content": "```\nThis sandbox persists across chats. Run this one command, then reply with ONLY its raw output inside a code block (no extra prose):\n\nrun_command:\nmkdir -p /home/user \\\n && echo \"run $(date -u +%FT%TZ)\" >> /home/user/runs.log \\\n && printf \"Run #%s\\n\\n\" \"$(wc -l < /home/user/runs.log)\" \\\n && cat -n /home/user/runs.log\n```", + "height": 360, + "width": 680, + "color": 7 + }, + "id": "bbbb0000-0000-0000-0000-0000000000bb", + "name": "Example prompt", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -380, + 720 + ] + }, + { + "parameters": { + "content": "### Persistence across chats\n`Find Sandbox` uses **Get → By Metadata** — E2B filters **server-side** for the sandbox tagged `purpose=n8n-agent-persist` (one API call, no client-side scan; paused sandboxes match too). If found it is reused (and resumed from pause). If none matches, the node errors — *On Error: Continue* turns that into an empty item, so `Has sandbox?` routes to `Create Sandbox`, which makes a new tagged one. After the agent runs, `Pause Sandbox` suspends it — the next chat finds and resumes the *same* sandbox, so files persist.", + "height": 220, + "width": 640, + "color": 7 + }, + "id": "dddd0000-0000-0000-0000-0000000000dd", + "name": "Persistence note", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 1180, + -40 + ] + }, + { + "parameters": {}, + "id": "11111111-1111-1111-1111-111111111111", + "name": "When chat message received", + "type": "@n8n/n8n-nodes-langchain.chatTrigger", + "typeVersion": 1.3, + "position": [ + 400, + 400 + ], + "webhookId": "e2b-agent-example-webhook" + }, + { + "parameters": { + "resource": "sandbox", + "operation": "get", + "getBy": "metadata", + "filterMetadataJson": "{ \"purpose\": \"n8n-agent-persist\" }" + }, + "id": "22220000-0000-0000-0000-000000000022", + "name": "Find Sandbox", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 700, + 400 + ], + "alwaysOutputData": true, + "onError": "continueRegularOutput", + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "loose", + "version": 2 + }, + "conditions": [ + { + "id": "c1", + "leftValue": "={{ $json.sandboxId ?? '' }}", + "rightValue": "", + "operator": { + "type": "string", + "operation": "empty", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "id": "44440000-0000-0000-0000-000000000044", + "name": "Has sandbox?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + 1060, + 400 + ] + }, + { + "parameters": { + "resource": "sandbox", + "operation": "create", + "template": "", + "metadataJson": "{ \"purpose\": \"n8n-agent-persist\" }", + "allowInternetAccess": true + }, + "id": "55550000-0000-0000-0000-000000000055", + "name": "Create Sandbox", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 1280, + 200 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "a1", + "name": "sandboxId", + "value": "={{ $json.sandboxId }}", + "type": "string" + } + ] + }, + "options": {} + }, + "id": "66660000-0000-0000-0000-000000000066", + "name": "Sandbox ready", + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + 1500, + 400 + ] + }, + { + "parameters": { + "promptType": "define", + "text": "={{ $('When chat message received').item.json.chatInput }}", + "options": { + "systemMessage": "You control ONE E2B cloud sandbox that PERSISTS across chats through these tools: run_command (bash), write_file, read_file, list_files. Files you create remain available in later messages.\n\nRules:\n- A tool may return an error (e.g. read_file returns 404 if the file does not exist yet). Do NOT stop — create the file first (run_command or write_file), then retry.\n- For append-style logs use run_command with >> (write_file OVERWRITES the whole file).\n- To run Python use run_command with python3 -c \"...\" or write a script and run it.\n- When the user asks for output, reply with the tool's raw stdout inside a fenced code block — no extra prose, no restating." + } + }, + "id": "77770000-0000-0000-0000-000000000077", + "name": "AI Agent", + "type": "@n8n/n8n-nodes-langchain.agent", + "typeVersion": 3.1, + "position": [ + 1720, + 400 + ] + }, + { + "parameters": { + "model": { + "__rl": true, + "mode": "list", + "value": "gpt-4o-mini" + }, + "options": {} + }, + "id": "88880000-0000-0000-0000-000000000088", + "name": "OpenAI Chat Model", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1.2, + "position": [ + 1360, + 640 + ], + "credentials": { + "openAiApi": { + "id": "REPLACE_WITH_YOUR_OPENAI_CREDENTIAL_ID", + "name": "OpenAi account" + } + } + }, + { + "parameters": { + "toolDescription": "Run a BASH shell command in the sandbox and return its stdout. This is a shell, not a Python REPL — to run Python use python3 -c \"\" or execute a script file.", + "resource": "code", + "operation": "runCommand", + "sandboxId": "={{ $('Sandbox ready').item.json.sandboxId }}", + "command": "={{ $fromAI('command', 'a single bash command to run in the sandbox, e.g. ls -la /home/user or python3 script.py') }}", + "allowInternetAccess": true + }, + "id": "99990000-0000-0000-0000-000000000099", + "name": "run_command", + "type": "@e2b/n8n-nodes-e2b.e2bTool", + "typeVersion": 1, + "position": [ + 1540, + 640 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "toolDescription": "Write a text file in the sandbox at an absolute path, creating it or overwriting any existing file.", + "resource": "file", + "operation": "write", + "sandboxId": "={{ $('Sandbox ready').item.json.sandboxId }}", + "path": "={{ $fromAI('path', 'absolute file path, e.g. /home/user/script.py') }}", + "content": "={{ $fromAI('content', 'the full text content to write to the file') }}" + }, + "id": "aaaa1111-0000-0000-0000-0000000000a1", + "name": "write_file", + "type": "@e2b/n8n-nodes-e2b.e2bTool", + "typeVersion": 1, + "position": [ + 1720, + 640 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "toolDescription": "Read a text file from the sandbox at an absolute path and return its contents.", + "resource": "file", + "operation": "read", + "sandboxId": "={{ $('Sandbox ready').item.json.sandboxId }}", + "path": "={{ $fromAI('path', 'absolute file path to read, e.g. /home/user/output.txt') }}" + }, + "id": "bbbb1111-0000-0000-0000-0000000000b1", + "name": "read_file", + "type": "@e2b/n8n-nodes-e2b.e2bTool", + "typeVersion": 1, + "position": [ + 1900, + 640 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "toolDescription": "List files in a sandbox directory at an absolute path.", + "resource": "file", + "operation": "list", + "sandboxId": "={{ $('Sandbox ready').item.json.sandboxId }}", + "path": "={{ $fromAI('path', 'absolute directory path, e.g. /home/user') }}", + "depth": 1 + }, + "id": "cccc1111-0000-0000-0000-0000000000c1", + "name": "list_files", + "type": "@e2b/n8n-nodes-e2b.e2bTool", + "typeVersion": 1, + "position": [ + 2080, + 640 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "resource": "sandbox", + "operation": "pause", + "sandboxId": "={{ $('Sandbox ready').item.json.sandboxId }}" + }, + "id": "dddd1111-0000-0000-0000-0000000000d1", + "name": "Pause Sandbox", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 1940, + 400 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "content": "### Reset — delete the persistent sandbox\nA separate flow to wipe the agent’s memory. It lists your sandboxes, keeps the ones tagged `purpose=n8n-agent-persist`, and **kills** them, so the next chat starts fresh. Not connected to the chat flow — run it manually with the ▶ button.", + "height": 200, + "width": 700, + "color": 3 + }, + "id": "eeee0000-0000-0000-0000-0000000000ee", + "name": "Reset flow", + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 400, + 780 + ] + }, + { + "parameters": {}, + "id": "r1110000-0000-0000-0000-0000000000r1", + "name": "Reset — delete persistent sandbox", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 400, + 980 + ] + }, + { + "parameters": { + "resource": "sandbox", + "operation": "getMany", + "limit": 100 + }, + "id": "r2220000-0000-0000-0000-0000000000r2", + "name": "List Sandboxes", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 620, + 980 + ], + "alwaysOutputData": true, + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + }, + { + "parameters": { + "jsCode": "return $input.all().filter(\n (i) => i.json && i.json.metadata && i.json.metadata.purpose === 'n8n-agent-persist'\n);" + }, + "id": "r3330000-0000-0000-0000-0000000000r3", + "name": "Keep tagged", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 840, + 980 + ] + }, + { + "parameters": { + "resource": "sandbox", + "operation": "kill", + "sandboxId": "={{ $json.sandboxId }}" + }, + "id": "r4440000-0000-0000-0000-0000000000r4", + "name": "Kill persistent sandbox", + "type": "@e2b/n8n-nodes-e2b.e2b", + "typeVersion": 1, + "position": [ + 1060, + 980 + ], + "credentials": { + "e2bApi": { + "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", + "name": "E2B API account" + } + } + } + ], + "connections": { + "When chat message received": { + "main": [ + [ + { + "node": "Find Sandbox", + "type": "main", + "index": 0 + } + ] + ] + }, + "Find Sandbox": { + "main": [ + [ + { + "node": "Has sandbox?", + "type": "main", + "index": 0 + } + ] + ] + }, + "Has sandbox?": { + "main": [ + [ + { + "node": "Create Sandbox", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Sandbox ready", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Sandbox": { + "main": [ + [ + { + "node": "Sandbox ready", + "type": "main", + "index": 0 + } + ] + ] + }, + "Sandbox ready": { + "main": [ + [ + { + "node": "AI Agent", + "type": "main", + "index": 0 + } + ] + ] + }, + "AI Agent": { + "main": [ + [ + { + "node": "Pause Sandbox", + "type": "main", + "index": 0 + } + ] + ] + }, + "OpenAI Chat Model": { + "ai_languageModel": [ + [ + { + "node": "AI Agent", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "run_command": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "write_file": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "read_file": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "list_files": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "Reset — delete persistent sandbox": { + "main": [ + [ + { + "node": "List Sandboxes", + "type": "main", + "index": 0 + } + ] + ] + }, + "List Sandboxes": { + "main": [ + [ + { + "node": "Keep tagged", + "type": "main", + "index": 0 + } + ] + ] + }, + "Keep tagged": { + "main": [ + [ + { + "node": "Kill persistent sandbox", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "tags": [ + { + "name": "e2b-example", + "id": "e2b-example" + } + ], + "meta": { + "templateCredsSetupCompleted": false + } +} diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 0000000..ea54cc6 --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,26 @@ +# Example workflows + +Importable n8n workflows that show the E2B node in action. Each is a `.json` you can drop straight into n8n. + +## Import + +In n8n: **Workflows → Import from File** and select the JSON. After import, open the E2B node(s) and set your **E2B API** credential (the examples ship with a placeholder credential ID). + +You'll need an E2B API key — create one at [e2b.dev/dashboard](https://e2b.dev/dashboard?tab=keys). + +## The examples + +Ordered from simplest to most advanced — start at Level 1 and work up. + +| Level | File | What it shows | +| --- | --- | --- | +| 1 | [`1-run-command-ephemeral.json`](./1-run-command-ephemeral.json) | The simplest possible flow — run a command in a one-shot ephemeral sandbox (empty `Sandbox ID` + **Kill After Run**), no explicit create/kill needed. Great starting point and AI-agent tool pattern. | +| 2 | [`2-web-app-with-preview.json`](./2-web-app-with-preview.json) | Create a sandbox → **write** a server file → start it on port 3000 → return a **preview URL**. Demonstrates exposing a running service for demos. | +| 3 | [`3-clone-build-download.json`](./3-clone-build-download.json) | Persistent sandbox: create → **Git clone** a repo → run a build command → **download** the artifact back into n8n as binary → kill. A CI-style template. | +| 4 | [`4-ai-agent-tool.json`](./4-ai-agent-tool.json) | An **AI Agent with full control of one persistent sandbox** — it's given four E2B tools (`run_command`, `write_file`, `read_file`, `list_files`) and decides which to call. The sandbox is reused across chat runs (tagged with metadata, found via `Get Sandbox → By Metadata` server-side filtering, and paused between runs), so files persist between messages — send the example prompt twice to watch a run counter grow. Needs a chat-model credential (OpenAI in the example) plus E2B. | + +## Notes + +- Sandbox operations chain by referencing the created sandbox: `={{ $('Create Sandbox').item.json.sandboxId }}`. +- `Run Command` can run standalone: leave **Sandbox ID** empty to spin up a throwaway sandbox for that step, and enable **Kill After Run** to clean it up automatically. +- All examples default to `allowInternetAccess: true` so `pip`/`git`/network calls work; disable it for locked-down runs. From c869f0969541149dfb34f4d48330483e0da37f0b Mon Sep 17 00:00:00 2001 From: Ondrej Drapalik Date: Wed, 8 Jul 2026 17:32:26 +0200 Subject: [PATCH 2/3] docs: reference example workflows in README --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 3088a73..4e315a6 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,19 @@ The E2B node uses n8n's resource and operation layout: - Snapshot: Create, Get Many, Delete - Volume: Create, Get, Get Many, Delete +## Examples + +Importable example workflows live in [`docs/examples/`](./docs/examples): + +Ordered simplest → most advanced (Level 1–4): + +- **Level 1** — [`1-run-command-ephemeral.json`](./docs/examples/1-run-command-ephemeral.json) — run a command in a one-shot ephemeral sandbox (the simplest flow). +- **Level 2** — [`2-web-app-with-preview.json`](./docs/examples/2-web-app-with-preview.json) — write and start a web server, return a preview URL. +- **Level 3** — [`3-clone-build-download.json`](./docs/examples/3-clone-build-download.json) — clone a repo, run a build, download the artifact back into n8n. +- **Level 4** — [`4-ai-agent-tool.json`](./docs/examples/4-ai-agent-tool.json) — an AI Agent that runs code in E2B as a tool. + +In n8n: **Workflows → Import from File**, select a JSON, then set your **E2B API** credential. See [`docs/examples/README.md`](./docs/examples/README.md) for details. + ## Development ```bash From 53d573d56d1808c0b4e2fe845b694e728c2dd49d Mon Sep 17 00:00:00 2001 From: Ondrej Drapalik Date: Wed, 8 Jul 2026 22:25:56 +0200 Subject: [PATCH 3/3] docs: rework level 4 reset flow to metadata lookup, refresh canvas layout --- docs/examples/4-ai-agent-tool.json | 138 ++++++++++++----------------- 1 file changed, 57 insertions(+), 81 deletions(-) diff --git a/docs/examples/4-ai-agent-tool.json b/docs/examples/4-ai-agent-tool.json index c890ad3..6b7caaf 100644 --- a/docs/examples/4-ai-agent-tool.json +++ b/docs/examples/4-ai-agent-tool.json @@ -4,7 +4,7 @@ { "parameters": { "content": "## E2B — AI Agent runs code as a tool\n\nThe agent controls **one E2B sandbox** through four tools: `run_command`, `write_file`, `read_file`, `list_files`. State persists **across tool calls _and_ across chat runs** — the sandbox is tagged with metadata, found again on the next run, and *paused* (not killed) between runs, so files survive. The first message creates it; later messages resume the same one.\n\n### Setup\n1. Add an **E2B API** credential (key at e2b.dev/dashboard) to every E2B node.\n2. Add an **OpenAI** credential to the chat model.\n\n### Reset\nKill the tagged sandbox (or let it expire) to start fresh — a new one is created and tagged automatically.", - "height": 460, + "height": 524, "width": 460, "color": "#CE2601" }, @@ -13,14 +13,14 @@ "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ - -380, - 20 + -384, + 32 ] }, { "parameters": { "content": "### How to run\nClick **Chat** (bottom of the canvas), paste the prompt on the right, and send.\n\nThen send it **again** — the run counter goes up because the sandbox and its files persist between chats.", - "height": 200, + "height": 232, "width": 460, "color": 7 }, @@ -29,14 +29,14 @@ "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ - -380, - 500 + -384, + 592 ] }, { "parameters": { "content": "```\nThis sandbox persists across chats. Run this one command, then reply with ONLY its raw output inside a code block (no extra prose):\n\nrun_command:\nmkdir -p /home/user \\\n && echo \"run $(date -u +%FT%TZ)\" >> /home/user/runs.log \\\n && printf \"Run #%s\\n\\n\" \"$(wc -l < /home/user/runs.log)\" \\\n && cat -n /home/user/runs.log\n```", - "height": 360, + "height": 248, "width": 680, "color": 7 }, @@ -45,14 +45,14 @@ "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ - -380, - 720 + -384, + 864 ] }, { "parameters": { "content": "### Persistence across chats\n`Find Sandbox` uses **Get → By Metadata** — E2B filters **server-side** for the sandbox tagged `purpose=n8n-agent-persist` (one API call, no client-side scan; paused sandboxes match too). If found it is reused (and resumed from pause). If none matches, the node errors — *On Error: Continue* turns that into an empty item, so `Has sandbox?` routes to `Create Sandbox`, which makes a new tagged one. After the agent runs, `Pause Sandbox` suspends it — the next chat finds and resumes the *same* sandbox, so files persist.", - "height": 220, + "height": 268, "width": 640, "color": 7 }, @@ -61,8 +61,8 @@ "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ - 1180, - -40 + 400, + -48 ] }, { @@ -72,8 +72,8 @@ "type": "@n8n/n8n-nodes-langchain.chatTrigger", "typeVersion": 1.3, "position": [ - 400, - 400 + 384, + 272 ], "webhookId": "e2b-agent-example-webhook" }, @@ -89,8 +89,8 @@ "type": "@e2b/n8n-nodes-e2b.e2b", "typeVersion": 1, "position": [ - 700, - 400 + 688, + 272 ], "alwaysOutputData": true, "onError": "continueRegularOutput", @@ -131,8 +131,8 @@ "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ - 1060, - 400 + 992, + 272 ] }, { @@ -148,8 +148,8 @@ "type": "@e2b/n8n-nodes-e2b.e2b", "typeVersion": 1, "position": [ - 1280, - 200 + 1232, + 96 ], "credentials": { "e2bApi": { @@ -177,8 +177,8 @@ "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ - 1500, - 400 + 1488, + 288 ] }, { @@ -194,8 +194,8 @@ "type": "@n8n/n8n-nodes-langchain.agent", "typeVersion": 3.1, "position": [ - 1720, - 400 + 1712, + 288 ] }, { @@ -212,8 +212,8 @@ "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", "typeVersion": 1.2, "position": [ - 1360, - 640 + 1344, + 528 ], "credentials": { "openAiApi": { @@ -236,8 +236,8 @@ "type": "@e2b/n8n-nodes-e2b.e2bTool", "typeVersion": 1, "position": [ - 1540, - 640 + 1536, + 528 ], "credentials": { "e2bApi": { @@ -261,8 +261,8 @@ "type": "@e2b/n8n-nodes-e2b.e2bTool", "typeVersion": 1, "position": [ - 1720, - 640 + 1712, + 528 ], "credentials": { "e2bApi": { @@ -285,8 +285,8 @@ "type": "@e2b/n8n-nodes-e2b.e2bTool", "typeVersion": 1, "position": [ - 1900, - 640 + 1888, + 528 ], "credentials": { "e2bApi": { @@ -310,8 +310,8 @@ "type": "@e2b/n8n-nodes-e2b.e2bTool", "typeVersion": 1, "position": [ - 2080, - 640 + 2064, + 528 ], "credentials": { "e2bApi": { @@ -332,8 +332,8 @@ "type": "@e2b/n8n-nodes-e2b.e2b", "typeVersion": 1, "position": [ - 1940, - 400 + 1936, + 288 ], "credentials": { "e2bApi": { @@ -344,18 +344,18 @@ }, { "parameters": { - "content": "### Reset — delete the persistent sandbox\nA separate flow to wipe the agent’s memory. It lists your sandboxes, keeps the ones tagged `purpose=n8n-agent-persist`, and **kills** them, so the next chat starts fresh. Not connected to the chat flow — run it manually with the ▶ button.", + "content": "### Reset — delete the persistent sandbox\nA separate flow to wipe the agent’s memory. `Find Sandbox (reset)` uses the same **Get → By Metadata** lookup as the chat flow to locate the sandbox tagged `purpose=n8n-agent-persist`, then **kills** it, so the next chat starts fresh. If nothing matches, the node errors with “No sandbox found” — nothing to reset. Not connected to the chat flow — run it manually with the ▶ button.", "height": 200, "width": 700, - "color": 3 + "color": 7 }, "id": "eeee0000-0000-0000-0000-0000000000ee", "name": "Reset flow", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ - 400, - 780 + 384, + 704 ] }, { @@ -365,25 +365,24 @@ "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ - 400, - 980 + 384, + 944 ] }, { "parameters": { "resource": "sandbox", - "operation": "getMany", - "limit": 100 + "operation": "kill", + "sandboxId": "={{ $json.sandboxId }}" }, - "id": "r2220000-0000-0000-0000-0000000000r2", - "name": "List Sandboxes", + "id": "r4440000-0000-0000-0000-0000000000r4", + "name": "Kill persistent sandbox", "type": "@e2b/n8n-nodes-e2b.e2b", "typeVersion": 1, "position": [ - 620, - 980 + 832, + 944 ], - "alwaysOutputData": true, "credentials": { "e2bApi": { "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID", @@ -391,32 +390,20 @@ } } }, - { - "parameters": { - "jsCode": "return $input.all().filter(\n (i) => i.json && i.json.metadata && i.json.metadata.purpose === 'n8n-agent-persist'\n);" - }, - "id": "r3330000-0000-0000-0000-0000000000r3", - "name": "Keep tagged", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 840, - 980 - ] - }, { "parameters": { "resource": "sandbox", - "operation": "kill", - "sandboxId": "={{ $json.sandboxId }}" + "operation": "get", + "getBy": "metadata", + "filterMetadataJson": "{ \"purpose\": \"n8n-agent-persist\" }" }, - "id": "r4440000-0000-0000-0000-0000000000r4", - "name": "Kill persistent sandbox", + "id": "r2220000-0000-0000-0000-0000000000r2", + "name": "Find Sandbox (reset)", "type": "@e2b/n8n-nodes-e2b.e2b", "typeVersion": 1, "position": [ - 1060, - 980 + 608, + 944 ], "credentials": { "e2bApi": { @@ -559,25 +546,14 @@ "main": [ [ { - "node": "List Sandboxes", - "type": "main", - "index": 0 - } - ] - ] - }, - "List Sandboxes": { - "main": [ - [ - { - "node": "Keep tagged", + "node": "Find Sandbox (reset)", "type": "main", "index": 0 } ] ] }, - "Keep tagged": { + "Find Sandbox (reset)": { "main": [ [ {