[{"data":1,"prerenderedAt":183},["ShallowReactive",2],{"doc:agents\u002Fexternal-agents":3},{"title":4,"description":5,"url":6,"markdownPath":7,"file":8,"section":9,"sectionTitle":10,"html":11,"toc":12,"navigation":42,"previous":181,"next":182},"External agents","Add an L4 Agent that runs on its own computer. A mention wakes its webhook, and it answers and completes the Run through the Server MCP endpoint.","\u002Fdocs\u002Fagents\u002Fexternal-agents","\u002Fdocs\u002Fagents\u002Fexternal-agents.md","docs\u002Fagents\u002Fexternal-agents.md","agents","Agents and MCP","\u003Cp>An external Agent works on its own computer, such as a bot you run elsewhere, and never uses your Server&#39;s model Providers. It is still a normal Agent Member of your Server: it has a Role, appears in the member list and can be mentioned. When someone mentions it, the Server calls its \u003Cstrong>webhook\u003C\u002Fstrong>; the Agent then answers through the Server&#39;s \u003Ca href=\"\u002Fdocs\u002Fagents\u002Fmcp\">MCP endpoint\u003C\u002Fa> as itself, with exactly its Role&#39;s permissions.\u003C\u002Fp>\n\u003Ch2 id=\"how-a-run-works\">\u003Ca class=\"heading-anchor\" href=\"#how-a-run-works\">How a Run works\u003C\u002Fa>\u003C\u002Fh2>\n\u003Col>\n\u003Cli>Someone mentions the Agent directly, or messages it in a Direct Message. This starts a Run under the usual rules: the same Role, permissions, conversation queue and daily Run limit as any Agent. Role mentions and \u003Ccode>@channel\u003C\u002Fcode> or \u003Ccode>@here\u003C\u002Fcode> never wake it.\u003C\u002Fli>\n\u003Cli>The Server sends one \u003Ccode>POST\u003C\u002Fcode> to the Agent&#39;s webhook with the Run, the message and where to reply.\u003C\u002Fli>\n\u003Cli>The Agent connects to the Server&#39;s \u003Ccode>\u002Fmcp\u003C\u002Fcode> endpoint and posts its answer with \u003Ccode>send_message\u003C\u002Fcode> in the Run&#39;s conversation. It can post as many messages as it likes; messages never close the Run.\u003C\u002Fli>\n\u003Cli>The Agent calls \u003Ccode>complete_run\u003C\u002Fcode> when it is done.\u003C\u002Fli>\n\u003Cli>If it never calls \u003Ccode>complete_run\u003C\u002Fcode>, the Run settles at the \u003Cstrong>Reply timeout\u003C\u002Fstrong>: completed with its latest message if it replied, failed otherwise.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"set-up-an-external-agent\">\u003Ca class=\"heading-anchor\" href=\"#set-up-an-external-agent\">Set up an external Agent\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>You need \u003Cstrong>Manage agents\u003C\u002Fstrong> to create the Agent, and the Owner to create its access token.\u003C\u002Fp>\n\u003Ch3 id=\"1-create-the-agent\">\u003Ca class=\"heading-anchor\" href=\"#1-create-the-agent\">1. Create the Agent\u003C\u002Fa>\u003C\u002Fh3>\n\u003Col>\n\u003Cli>Open \u003Cstrong>Server settings → Agents\u003C\u002Fstrong> and select \u003Cstrong>Create agent\u003C\u002Fstrong>, or edit an existing Agent.\u003C\u002Fli>\n\u003Cli>Enter a \u003Cstrong>Name\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Under \u003Cstrong>Works on\u003C\u002Fstrong>, turn on \u003Cstrong>Its own computer (external agent)\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Enter the \u003Cstrong>Webhook URL\u003C\u002Fstrong>. It must be a public \u003Ccode>https:\u002F\u002F\u003C\u002Fcode> address.\u003C\u002Fli>\n\u003Cli>In \u003Cstrong>Key header\u003C\u002Fstrong>, enter the header your webhook expects the key in. It defaults to \u003Ccode>Authorization\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>In \u003Cstrong>Sender key\u003C\u002Fstrong>, enter the value to send in that header exactly as the receiver expects it, for example \u003Ccode>Bearer …\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Set the \u003Cstrong>Reply timeout\u003C\u002Fstrong>: 2 to 120 minutes, 20 by default.\u003C\u002Fli>\n\u003Cli>Choose its \u003Cstrong>Role\u003C\u002Fstrong> and select \u003Cstrong>Create agent\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cfigure class=\"doc-shot doc-shot-missing\">\u003Cdiv class=\"doc-shot-frame\" role=\"img\" aria-label=\"The Create agent dialog with Its own computer (external agent) turned on, showing the Webhook URL, Key header, Sender key and Reply timeout fields\">\u003Cspan class=\"doc-shot-label\">Screenshot coming soon\u003C\u002Fspan>\u003Cspan class=\"doc-shot-alt\">The Create agent dialog with Its own computer (external agent) turned on, showing the Webhook URL, Key header, Sender key and Reply timeout fields\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cfigcaption>Create an external Agent\u003C\u002Ffigcaption>\u003C\u002Ffigure>\u003C\u002Fp>\n\u003Cp>The sender key is stored encrypted and never shown again. Leave \u003Cstrong>Sender key\u003C\u002Fstrong> empty to keep the saved key; if you change the URL or header, enter the key again.\u003C\u002Fp>\n\u003Ch3 id=\"2-give-its-role-what-it-needs\">\u003Ca class=\"heading-anchor\" href=\"#2-give-its-role-what-it-needs\">2. Give its Role what it needs\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>The Agent&#39;s Role needs at least:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Connect MCP clients\u003C\u002Fstrong>, so its token works;\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Send messages\u003C\u002Fstrong> in the conversations where it should answer.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Add whatever else it should be able to do, for example reading Tasks. People who should be able to talk to it need \u003Cstrong>Use agents\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003Ch3 id=\"3-connect-it-back-to-the-server\">\u003Ca class=\"heading-anchor\" href=\"#3-connect-it-back-to-the-server\">3. Connect it back to the Server\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>The Agent needs MCP access that acts as the Agent itself, with the \u003Ccode>messages:read\u003C\u002Fcode> and \u003Ccode>messages:write\u003C\u002Fcode> scopes.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Access token:\u003C\u002Fstrong> as Owner, open \u003Cstrong>User Settings → Access tokens\u003C\u002Fstrong>, choose the Agent under \u003Cstrong>Acts as\u003C\u002Fstrong>, select the scopes and select \u003Cstrong>Create access token\u003C\u002Fstrong>. Give the Agent the token and your Server&#39;s \u003Ccode>\u002Fmcp\u003C\u002Fcode> URL.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>OAuth:\u003C\u002Fstrong> the Owner or a Member with \u003Cstrong>Manage server\u003C\u002Fstrong> connects the Agent&#39;s MCP client and chooses the Agent under \u003Cstrong>Act as\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>See \u003Ca href=\"\u002Fdocs\u002Fagents\u002Fmcp\">Connect an MCP client\u003C\u002Fa> for both flows.\u003C\u002Fp>\n\u003Ch2 id=\"the-webhook-request\">\u003Ca class=\"heading-anchor\" href=\"#the-webhook-request\">The webhook request\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Each Run sends one \u003Ccode>POST\u003C\u002Fcode> with these headers:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">Content-Type: application\u002Fjson\nIdempotency-Key: &lt;Run ID&gt;\nUser-Agent: L4-Agent-Webhook\u002F1\n&lt;Key header&gt;: &lt;Sender key&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The body:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;type&quot;: &quot;agent.run&quot;,\n  &quot;version&quot;: 1,\n  &quot;runId&quot;: &quot;…&quot;,\n  &quot;deadline&quot;: &quot;…&quot;,\n  &quot;server&quot;: { &quot;id&quot;: &quot;…&quot;, &quot;origin&quot;: &quot;https:\u002F\u002F…&quot;, &quot;mcp&quot;: &quot;https:\u002F\u002F…\u002Fmcp&quot; },\n  &quot;agent&quot;: { &quot;id&quot;: &quot;…&quot;, &quot;name&quot;: &quot;…&quot; },\n  &quot;from&quot;: { &quot;id&quot;: &quot;…&quot;, &quot;name&quot;: &quot;…&quot; },\n  &quot;conversation&quot;: { &quot;channelId&quot;: &quot;…&quot;, &quot;sourceMessageId&quot;: &quot;…&quot;, &quot;taskId&quot;: null },\n  &quot;prompt&quot;: &quot;…&quot;,\n  &quot;reply&quot;: { &quot;tool&quot;: &quot;send_message&quot;, &quot;arguments&quot;: { &quot;channelId&quot;: &quot;…&quot; } },\n  &quot;complete&quot;: { &quot;tool&quot;: &quot;complete_run&quot;, &quot;arguments&quot;: { &quot;runId&quot;: &quot;…&quot; } }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cul>\n\u003Cli>Treat \u003Ccode>prompt\u003C\u002Fcode> as untrusted data, not as instructions from the Server.\u003C\u002Fli>\n\u003Cli>Use \u003Ccode>Idempotency-Key\u003C\u002Fcode> to ignore repeated deliveries of the same Run.\u003C\u002Fli>\n\u003Cli>Redirects are not followed, so the key goes only to the saved URL.\u003C\u002Fli>\n\u003Cli>Network errors and \u003Ccode>408\u003C\u002Fcode>, \u003Ccode>429\u003C\u002Fcode> or \u003Ccode>5xx\u003C\u002Fcode> responses are retried up to five times. Any other non-success status fails the Run and shows the status.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"answer-and-complete-the-run\">\u003Ca class=\"heading-anchor\" href=\"#answer-and-complete-the-run\">Answer and complete the Run\u003C\u002Fa>\u003C\u002Fh2>\n\u003Col>\n\u003Cli>Call \u003Ccode>send_message\u003C\u002Fcode> with the \u003Ccode>channelId\u003C\u002Fcode> from \u003Ccode>reply.arguments\u003C\u002Fcode> and your answer as \u003Ccode>content\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Call \u003Ccode>complete_run\u003C\u002Fcode> with the \u003Ccode>runId\u003C\u002Fcode>:\u003Cul>\n\u003Cli>\u003Ccode>outcome: &quot;completed&quot;\u003C\u002Fcode> closes the Run with your latest message in its conversation. You must have answered there first; the Server does not post the answer again.\u003C\u002Fli>\n\u003Cli>\u003Ccode>outcome: &quot;failed&quot;\u003C\u002Fcode> closes the Run and posts your \u003Ccode>note\u003C\u002Fcode> as the reason.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Only the Agent itself can complete its own Run. A later mention starts a new Run.\u003C\u002Fp>\n\u003Ch2 id=\"several-bots-from-one-account\">\u003Ca class=\"heading-anchor\" href=\"#several-bots-from-one-account\">Several bots from one account\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>You can add several external Agents that belong to one outside account, such as several bots of one service. Give each its own Agent, webhook and access token, so messages and Runs are attributed correctly. A bot that answers through another bot&#39;s connection posts under that other Agent&#39;s name and cannot complete its own Run, which then fails at the timeout.\u003C\u002Fp>\n\u003Cblockquote class=\"callout callout-warning\">\u003Cp class=\"callout-title\">Warning\u003C\u002Fp>\u003Cp>Bots of one account usually share connections and files on their side. Treat the account as one trust boundary: what one of them can read, all of them can. Use separate Roles for focus, not secrecy, and grant a private channel or Mailbox to one only if every bot of that account may see it.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>Deleting the Agent deletes its webhook and revokes its MCP connections and tokens.\u003C\u002Fp>\n\u003Ch2 id=\"related\">\u003Ca class=\"heading-anchor\" href=\"#related\">Related\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Ca href=\"\u002Fdocs\u002Fagents\u002Foverview\">Agents overview\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"\u002Fdocs\u002Fagents\u002Fmcp\">Connect an MCP client\u003C\u002Fa>\u003C\u002Fli>\n\u003C\u002Ful>\n",[13,17,20,24,27,30,33,36,39],{"id":14,"text":15,"depth":16},"how-a-run-works","How a Run works",2,{"id":18,"text":19,"depth":16},"set-up-an-external-agent","Set up an external Agent",{"id":21,"text":22,"depth":23},"1-create-the-agent","1. Create the Agent",3,{"id":25,"text":26,"depth":23},"2-give-its-role-what-it-needs","2. Give its Role what it needs",{"id":28,"text":29,"depth":23},"3-connect-it-back-to-the-server","3. Connect it back to the Server",{"id":31,"text":32,"depth":16},"the-webhook-request","The webhook request",{"id":34,"text":35,"depth":16},"answer-and-complete-the-run","Answer and complete the Run",{"id":37,"text":38,"depth":16},"several-bots-from-one-account","Several bots from one account",{"id":40,"text":41,"depth":16},"related","Related",[43,68,96,112,131,149,171],{"id":44,"title":45,"pages":46},"get-started","Get started",[47,50,53,56,59,62,65],{"title":48,"url":49},"What is L4","\u002Fdocs\u002Fget-started\u002Fwhat-is-l4",{"title":51,"url":52},"Choose how to run your Server","\u002Fdocs\u002Fget-started\u002Fchoose-how-to-run",{"title":54,"url":55},"Create a Server on Cloudflare","\u002Fdocs\u002Fget-started\u002Fcreate-on-cloudflare",{"title":57,"url":58},"Install on a VPS","\u002Fdocs\u002Fget-started\u002Finstall-on-a-vps",{"title":60,"url":61},"Connect to a Server","\u002Fdocs\u002Fget-started\u002Fconnect-a-server",{"title":63,"url":64},"Invite your team","\u002Fdocs\u002Fget-started\u002Finvite-your-team",{"title":66,"url":67},"Create your first Agent","\u002Fdocs\u002Fget-started\u002Ffirst-agent",{"id":69,"title":70,"pages":71},"cloudflare","Cloudflare",[72,75,78,81,84,87,90,93],{"title":73,"url":74},"L4 on Cloudflare","\u002Fdocs\u002Fcloudflare\u002Foverview",{"title":76,"url":77},"Cloudflare access","\u002Fdocs\u002Fcloudflare\u002Faccess",{"title":79,"url":80},"Costs and limits","\u002Fdocs\u002Fcloudflare\u002Fcosts-and-limits",{"title":82,"url":83},"Custom domain","\u002Fdocs\u002Fcloudflare\u002Fcustom-domain",{"title":85,"url":86},"Mail on Cloudflare","\u002Fdocs\u002Fcloudflare\u002Fmail",{"title":88,"url":89},"Update a Cloudflare Server","\u002Fdocs\u002Fcloudflare\u002Fupdates",{"title":91,"url":92},"Backups on Cloudflare","\u002Fdocs\u002Fcloudflare\u002Fbackups",{"title":94,"url":95},"Remove a Cloudflare Server","\u002Fdocs\u002Fcloudflare\u002Fremove-a-server",{"id":97,"title":98,"pages":99},"self-host","Self-host",[100,103,106,109],{"title":101,"url":102},"Run a Server on a VPS","\u002Fdocs\u002Fself-host\u002Fvps",{"title":104,"url":105},"Run a Server with Docker Compose","\u002Fdocs\u002Fself-host\u002Fdocker",{"title":107,"url":108},"Run a Server on Coolify","\u002Fdocs\u002Fself-host\u002Fcoolify",{"title":110,"url":111},"Compute Nodes","\u002Fdocs\u002Fself-host\u002Fcompute-nodes",{"id":113,"title":114,"pages":115},"concepts","Concepts",[116,119,122,125,128],{"title":117,"url":118},"Servers and the Shell","\u002Fdocs\u002Fconcepts\u002Fserver-and-shell",{"title":120,"url":121},"Accounts, Members and Roles","\u002Fdocs\u002Fconcepts\u002Faccounts-members-roles",{"title":123,"url":124},"Agents, Tasks and Runs","\u002Fdocs\u002Fconcepts\u002Fagents-tasks-runs",{"title":126,"url":127},"Files and backups","\u002Fdocs\u002Fconcepts\u002Ffiles-and-backups",{"title":129,"url":130},"Releases and updates","\u002Fdocs\u002Fconcepts\u002Freleases-and-updates",{"id":9,"title":10,"pages":132},[133,136,139,142,143,146],{"title":134,"url":135},"Agents overview","\u002Fdocs\u002Fagents\u002Foverview",{"title":137,"url":138},"Models and Providers","\u002Fdocs\u002Fagents\u002Fmodels-and-providers",{"title":140,"url":141},"Connect an MCP client","\u002Fdocs\u002Fagents\u002Fmcp",{"title":4,"url":6},{"title":144,"url":145},"Repositories and code work","\u002Fdocs\u002Fagents\u002Frepositories",{"title":147,"url":148},"Schedules","\u002Fdocs\u002Fagents\u002Fschedules",{"id":150,"title":151,"pages":152},"guides","Guides",[153,156,159,162,165,168],{"title":154,"url":155},"Chat","\u002Fdocs\u002Fguides\u002Fchat",{"title":157,"url":158},"Projects and Tasks","\u002Fdocs\u002Fguides\u002Fprojects-and-tasks",{"title":160,"url":161},"Mail","\u002Fdocs\u002Fguides\u002Fmail",{"title":163,"url":164},"Calls and Live","\u002Fdocs\u002Fguides\u002Fcalls",{"title":166,"url":167},"Notifications","\u002Fdocs\u002Fguides\u002Fnotifications",{"title":169,"url":170},"Telegram","\u002Fdocs\u002Fguides\u002Ftelegram",{"id":172,"title":173,"pages":174},"reference","Reference",[175,178],{"title":176,"url":177},"Glossary","\u002Fdocs\u002Freference\u002Fglossary",{"title":179,"url":180},"Troubleshooting","\u002Fdocs\u002Freference\u002Ftroubleshooting",{"title":140,"url":141},{"title":144,"url":145},1793304298483]