[{"data":1,"prerenderedAt":1233},["ShallowReactive",2],{"content-query-iQJVn3NNnO":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"author":11,"tags":12,"categories":19,"date":22,"image":23,"faq":24,"excerpt":34,"body":99,"_type":900,"_id":1227,"_source":1228,"_file":1229,"_stem":1230,"_extension":1231,"sitemap":1232},"/blog/self-hosted-ai-agent-webhooks","blog",false,"","Trigger a Self-Hosted AI Agent with Webhooks (No Public IP)","Run a local AI agent built with Vercel's eve framework on a Raspberry Pi or Mac mini behind NAT, and trigger it with GitHub, Stripe or any webhooks via Webhook Relay — no port forwarding, no public IP.","post","Karolis Rusenas",[13,14,15,16,17,18],"ai agents","self-hosted","webhooks","eve","raspberry pi","docker",[20,21],"tutorials","automation","2026-07-02 12:00:00","/images/blog/eve-agent/cover.jpg",[25,28,31],{"q":26,"a":27},"Can I run an AI agent on a Raspberry Pi or Mac mini?","Yes. An agent built with a framework like Vercel's eve is a small Node.js server, and the example image is multi-arch — it runs fine on a Raspberry Pi (arm64), a Mac mini or any home server. The model itself is called over an API, so the hardware only orchestrates: receiving events, running tools, storing state.",{"q":29,"a":30},"How do I trigger a self-hosted AI agent with webhooks?","Give the agent an HTTP endpoint (in eve, a custom channel), then put Webhook Relay in front of it: providers POST to your public bucket URL and the webhookrelayd sidecar delivers each event to the agent over an outbound connection. Each webhook starts a fresh agent session with the payload as the message.",{"q":32,"a":33},"Do I need a public IP or port forwarding to receive webhooks at home?","No. The webhookrelayd agent makes an outbound connection to Webhook Relay and pulls events down through it, so the machine stays invisible to the internet — no public IP, no opened ports, no dynamic DNS. This works behind NAT, CGNAT and corporate firewalls.",{"type":35,"children":36},"root",[37,47,53,72],{"type":38,"tag":39,"props":40,"children":41},"element","p",{},[42],{"type":38,"tag":43,"props":44,"children":46},"img",{"alt":45,"src":23},"Self-hosted AI agent on a Raspberry Pi receiving webhooks",[],{"type":38,"tag":39,"props":48,"children":49},{},[50],{"type":51,"value":52},"text","Self-hosted AI agents are having a moment. A Mac mini or Raspberry Pi is plenty to run an always-on agent — the model is an API call away, and everything that matters (your tools, your data, your state) stays on hardware you own.",{"type":38,"tag":39,"props":54,"children":55},{},[56,58,64,66,70],{"type":51,"value":57},"But a ",{"type":38,"tag":59,"props":60,"children":61},"strong",{},[62],{"type":51,"value":63},"local AI agent",{"type":51,"value":65}," that can only be poked over the LAN misses the point of being always-on. The interesting work arrives as events from the outside world: a GitHub issue is opened, a Stripe payment fails, Grafana fires an alert. Those are ",{"type":38,"tag":59,"props":67,"children":68},{},[69],{"type":51,"value":15},{"type":51,"value":71}," — and GitHub can't POST to a box behind NAT with no public IP. Opening a port and pointing dynamic DNS at your home network is exactly the kind of thing you shouldn't do to the machine that runs an agent with shell access.",{"type":38,"tag":39,"props":73,"children":74},{},[75,77,89,91,97],{"type":51,"value":76},"We built a small, fully working example that solves this properly: ",{"type":38,"tag":78,"props":79,"children":83},"a",{"href":80,"rel":81},"https://github.com/webhookrelay/webhookrelay-eve-agent-example",[82],"nofollow",[84],{"type":38,"tag":59,"props":85,"children":86},{},[87],{"type":51,"value":88},"webhookrelay-eve-agent-example",{"type":51,"value":90}," — an agent built with ",{"type":38,"tag":78,"props":92,"children":95},{"href":93,"rel":94},"https://vercel.com/eve",[82],[96],{"type":51,"value":16},{"type":51,"value":98},", Vercel's filesystem-first framework for durable agents, triggered by any webhook from anywhere, with zero inbound ports.",{"type":35,"children":100,"toc":1221},[101,107,111,125,144,151,159,195,201,209,223,423,443,768,780,922,928,933,1016,1029,1082,1087,1093,1121,1126,1158,1202,1215],{"type":38,"tag":39,"props":102,"children":103},{},[104],{"type":38,"tag":43,"props":105,"children":106},{"alt":45,"src":23},[],{"type":38,"tag":39,"props":108,"children":109},{},[110],{"type":51,"value":52},{"type":38,"tag":39,"props":112,"children":113},{},[114,115,119,120,124],{"type":51,"value":57},{"type":38,"tag":59,"props":116,"children":117},{},[118],{"type":51,"value":63},{"type":51,"value":65},{"type":38,"tag":59,"props":121,"children":122},{},[123],{"type":51,"value":15},{"type":51,"value":71},{"type":38,"tag":39,"props":126,"children":127},{},[128,129,137,138,143],{"type":51,"value":76},{"type":38,"tag":78,"props":130,"children":132},{"href":80,"rel":131},[82],[133],{"type":38,"tag":59,"props":134,"children":135},{},[136],{"type":51,"value":88},{"type":51,"value":90},{"type":38,"tag":78,"props":139,"children":141},{"href":93,"rel":140},[82],[142],{"type":51,"value":16},{"type":51,"value":98},{"type":38,"tag":145,"props":146,"children":148},"h2",{"id":147},"the-architecture",[149],{"type":51,"value":150},"The architecture",{"type":38,"tag":39,"props":152,"children":153},{},[154],{"type":38,"tag":43,"props":155,"children":158},{"alt":156,"src":157},"Self-hosted AI agent architecture: webhooks cross the NAT/firewall network boundary from Webhook Relay Cloud to a webhookrelayd sidecar and eve agent running on a home network or office LAN","/images/blog/eve-agent/architecture.svg",[],{"type":38,"tag":39,"props":160,"children":161},{},[162,168,170,177,179,185,187,193],{"type":38,"tag":78,"props":163,"children":165},{"href":164},"/",[166],{"type":51,"value":167},"Webhook Relay",{"type":51,"value":169}," provides the stable public HTTPS endpoint. The ",{"type":38,"tag":171,"props":172,"children":174},"code",{"className":173},[],[175],{"type":51,"value":176},"webhookrelayd",{"type":51,"value":178}," sidecar runs next to the agent in docker-compose and keeps an ",{"type":38,"tag":180,"props":181,"children":182},"em",{},[183],{"type":51,"value":184},"outbound",{"type":51,"value":186}," connection to it, so every webhook is pulled down to the agent over the internal compose network. The box stays invisible to the internet — this is the same ",{"type":38,"tag":78,"props":188,"children":190},{"href":189},"/features/webhook-to-internal-server",[191],{"type":51,"value":192},"webhook forwarding",{"type":51,"value":194}," that powers local webhook development, used as permanent infrastructure.",{"type":38,"tag":145,"props":196,"children":198},{"id":197},"the-agent-three-small-files",[199],{"type":51,"value":200},"The agent: three small files",{"type":38,"tag":39,"props":202,"children":203},{},[204],{"type":38,"tag":43,"props":205,"children":208},{"alt":206,"src":207},"eve agent project structure — instructions.md, agent.ts and a tools directory — powered by Webhook Relay","/images/blog/eve-agent/eve-webhookrelay.png",[],{"type":38,"tag":39,"props":210,"children":211},{},[212,214,221],{"type":51,"value":213},"eve agents are just files. The model config points at any AI SDK provider — the example calls Claude through ",{"type":38,"tag":78,"props":215,"children":218},{"href":216,"rel":217},"https://lightning.ai",[82],[219],{"type":51,"value":220},"Lightning AI",{"type":51,"value":222},"'s Anthropic-compatible API:",{"type":38,"tag":224,"props":225,"children":229},"pre",{"className":226,"code":227,"language":228,"meta":7,"style":7},"language-ts shiki shiki-themes github-dark","// agent/agent.ts\nconst lightning = createAnthropic({\n  baseURL: process.env.LIGHTNING_BASE_URL ?? \"https://lightning.ai/v1\",\n  apiKey: process.env.LIGHTNING_API_KEY ?? \"\",\n});\n\nexport default defineAgent({\n  model: lightning(process.env.LIGHTNING_MODEL ?? \"claude-sonnet-4-6\"),\n});\n","ts",[230],{"type":38,"tag":171,"props":231,"children":232},{"__ignoreMap":7},[233,245,278,308,335,344,354,377,415],{"type":38,"tag":234,"props":235,"children":238},"span",{"class":236,"line":237},"line",1,[239],{"type":38,"tag":234,"props":240,"children":242},{"style":241},"--shiki-default:#6A737D",[243],{"type":51,"value":244},"// agent/agent.ts\n",{"type":38,"tag":234,"props":246,"children":248},{"class":236,"line":247},2,[249,255,261,266,272],{"type":38,"tag":234,"props":250,"children":252},{"style":251},"--shiki-default:#F97583",[253],{"type":51,"value":254},"const",{"type":38,"tag":234,"props":256,"children":258},{"style":257},"--shiki-default:#79B8FF",[259],{"type":51,"value":260}," lightning",{"type":38,"tag":234,"props":262,"children":263},{"style":251},[264],{"type":51,"value":265}," =",{"type":38,"tag":234,"props":267,"children":269},{"style":268},"--shiki-default:#B392F0",[270],{"type":51,"value":271}," createAnthropic",{"type":38,"tag":234,"props":273,"children":275},{"style":274},"--shiki-default:#E1E4E8",[276],{"type":51,"value":277},"({\n",{"type":38,"tag":234,"props":279,"children":281},{"class":236,"line":280},3,[282,287,292,297,303],{"type":38,"tag":234,"props":283,"children":284},{"style":274},[285],{"type":51,"value":286},"  baseURL: process.env.",{"type":38,"tag":234,"props":288,"children":289},{"style":257},[290],{"type":51,"value":291},"LIGHTNING_BASE_URL",{"type":38,"tag":234,"props":293,"children":294},{"style":251},[295],{"type":51,"value":296}," ??",{"type":38,"tag":234,"props":298,"children":300},{"style":299},"--shiki-default:#9ECBFF",[301],{"type":51,"value":302}," \"https://lightning.ai/v1\"",{"type":38,"tag":234,"props":304,"children":305},{"style":274},[306],{"type":51,"value":307},",\n",{"type":38,"tag":234,"props":309,"children":311},{"class":236,"line":310},4,[312,317,322,326,331],{"type":38,"tag":234,"props":313,"children":314},{"style":274},[315],{"type":51,"value":316},"  apiKey: process.env.",{"type":38,"tag":234,"props":318,"children":319},{"style":257},[320],{"type":51,"value":321},"LIGHTNING_API_KEY",{"type":38,"tag":234,"props":323,"children":324},{"style":251},[325],{"type":51,"value":296},{"type":38,"tag":234,"props":327,"children":328},{"style":299},[329],{"type":51,"value":330}," \"\"",{"type":38,"tag":234,"props":332,"children":333},{"style":274},[334],{"type":51,"value":307},{"type":38,"tag":234,"props":336,"children":338},{"class":236,"line":337},5,[339],{"type":38,"tag":234,"props":340,"children":341},{"style":274},[342],{"type":51,"value":343},"});\n",{"type":38,"tag":234,"props":345,"children":347},{"class":236,"line":346},6,[348],{"type":38,"tag":234,"props":349,"children":351},{"emptyLinePlaceholder":350},true,[352],{"type":51,"value":353},"\n",{"type":38,"tag":234,"props":355,"children":357},{"class":236,"line":356},7,[358,363,368,373],{"type":38,"tag":234,"props":359,"children":360},{"style":251},[361],{"type":51,"value":362},"export",{"type":38,"tag":234,"props":364,"children":365},{"style":251},[366],{"type":51,"value":367}," default",{"type":38,"tag":234,"props":369,"children":370},{"style":268},[371],{"type":51,"value":372}," defineAgent",{"type":38,"tag":234,"props":374,"children":375},{"style":274},[376],{"type":51,"value":277},{"type":38,"tag":234,"props":378,"children":380},{"class":236,"line":379},8,[381,386,391,396,401,405,410],{"type":38,"tag":234,"props":382,"children":383},{"style":274},[384],{"type":51,"value":385},"  model: ",{"type":38,"tag":234,"props":387,"children":388},{"style":268},[389],{"type":51,"value":390},"lightning",{"type":38,"tag":234,"props":392,"children":393},{"style":274},[394],{"type":51,"value":395},"(process.env.",{"type":38,"tag":234,"props":397,"children":398},{"style":257},[399],{"type":51,"value":400},"LIGHTNING_MODEL",{"type":38,"tag":234,"props":402,"children":403},{"style":251},[404],{"type":51,"value":296},{"type":38,"tag":234,"props":406,"children":407},{"style":299},[408],{"type":51,"value":409}," \"claude-sonnet-4-6\"",{"type":38,"tag":234,"props":411,"children":412},{"style":274},[413],{"type":51,"value":414},"),\n",{"type":38,"tag":234,"props":416,"children":418},{"class":236,"line":417},9,[419],{"type":38,"tag":234,"props":420,"children":421},{"style":274},[422],{"type":51,"value":343},{"type":38,"tag":39,"props":424,"children":425},{},[426,428,433,435,441],{"type":51,"value":427},"A custom ",{"type":38,"tag":59,"props":429,"children":430},{},[431],{"type":51,"value":432},"channel",{"type":51,"value":434}," gives the agent its webhook endpoint. Each delivery starts a fresh, durable agent session with the payload (and the provider headers Webhook Relay preserves, like ",{"type":38,"tag":171,"props":436,"children":438},{"className":437},[],[439],{"type":51,"value":440},"x-github-event",{"type":51,"value":442},") as the message:",{"type":38,"tag":224,"props":444,"children":446},{"className":226,"code":445,"language":228,"meta":7,"style":7},"// agent/channels/webhook.ts\nexport default defineChannel({\n  routes: [\n    POST(\"/eve/v1/webhook\", async (req, { send }) => {\n      const body = await req.text();\n      const session = await send(\n        `A webhook just arrived. Triage it and save a report.\\n\\n${body}`,\n        { auth: null, continuationToken: `webhook-${crypto.randomUUID()}` },\n      );\n      return Response.json({ ok: true, sessionId: session.id });\n    }),\n  ],\n});\n",[447],{"type":38,"tag":171,"props":448,"children":449},{"__ignoreMap":7},[450,458,478,486,550,586,616,648,700,708,742,751,760],{"type":38,"tag":234,"props":451,"children":452},{"class":236,"line":237},[453],{"type":38,"tag":234,"props":454,"children":455},{"style":241},[456],{"type":51,"value":457},"// agent/channels/webhook.ts\n",{"type":38,"tag":234,"props":459,"children":460},{"class":236,"line":247},[461,465,469,474],{"type":38,"tag":234,"props":462,"children":463},{"style":251},[464],{"type":51,"value":362},{"type":38,"tag":234,"props":466,"children":467},{"style":251},[468],{"type":51,"value":367},{"type":38,"tag":234,"props":470,"children":471},{"style":268},[472],{"type":51,"value":473}," defineChannel",{"type":38,"tag":234,"props":475,"children":476},{"style":274},[477],{"type":51,"value":277},{"type":38,"tag":234,"props":479,"children":480},{"class":236,"line":280},[481],{"type":38,"tag":234,"props":482,"children":483},{"style":274},[484],{"type":51,"value":485},"  routes: [\n",{"type":38,"tag":234,"props":487,"children":488},{"class":236,"line":310},[489,494,499,504,509,514,519,525,530,535,540,545],{"type":38,"tag":234,"props":490,"children":491},{"style":268},[492],{"type":51,"value":493},"    POST",{"type":38,"tag":234,"props":495,"children":496},{"style":274},[497],{"type":51,"value":498},"(",{"type":38,"tag":234,"props":500,"children":501},{"style":299},[502],{"type":51,"value":503},"\"/eve/v1/webhook\"",{"type":38,"tag":234,"props":505,"children":506},{"style":274},[507],{"type":51,"value":508},", ",{"type":38,"tag":234,"props":510,"children":511},{"style":251},[512],{"type":51,"value":513},"async",{"type":38,"tag":234,"props":515,"children":516},{"style":274},[517],{"type":51,"value":518}," (",{"type":38,"tag":234,"props":520,"children":522},{"style":521},"--shiki-default:#FFAB70",[523],{"type":51,"value":524},"req",{"type":38,"tag":234,"props":526,"children":527},{"style":274},[528],{"type":51,"value":529},", { ",{"type":38,"tag":234,"props":531,"children":532},{"style":521},[533],{"type":51,"value":534},"send",{"type":38,"tag":234,"props":536,"children":537},{"style":274},[538],{"type":51,"value":539}," }) ",{"type":38,"tag":234,"props":541,"children":542},{"style":251},[543],{"type":51,"value":544},"=>",{"type":38,"tag":234,"props":546,"children":547},{"style":274},[548],{"type":51,"value":549}," {\n",{"type":38,"tag":234,"props":551,"children":552},{"class":236,"line":337},[553,558,563,567,572,577,581],{"type":38,"tag":234,"props":554,"children":555},{"style":251},[556],{"type":51,"value":557},"      const",{"type":38,"tag":234,"props":559,"children":560},{"style":257},[561],{"type":51,"value":562}," body",{"type":38,"tag":234,"props":564,"children":565},{"style":251},[566],{"type":51,"value":265},{"type":38,"tag":234,"props":568,"children":569},{"style":251},[570],{"type":51,"value":571}," await",{"type":38,"tag":234,"props":573,"children":574},{"style":274},[575],{"type":51,"value":576}," req.",{"type":38,"tag":234,"props":578,"children":579},{"style":268},[580],{"type":51,"value":51},{"type":38,"tag":234,"props":582,"children":583},{"style":274},[584],{"type":51,"value":585},"();\n",{"type":38,"tag":234,"props":587,"children":588},{"class":236,"line":346},[589,593,598,602,606,611],{"type":38,"tag":234,"props":590,"children":591},{"style":251},[592],{"type":51,"value":557},{"type":38,"tag":234,"props":594,"children":595},{"style":257},[596],{"type":51,"value":597}," session",{"type":38,"tag":234,"props":599,"children":600},{"style":251},[601],{"type":51,"value":265},{"type":38,"tag":234,"props":603,"children":604},{"style":251},[605],{"type":51,"value":571},{"type":38,"tag":234,"props":607,"children":608},{"style":268},[609],{"type":51,"value":610}," send",{"type":38,"tag":234,"props":612,"children":613},{"style":274},[614],{"type":51,"value":615},"(\n",{"type":38,"tag":234,"props":617,"children":618},{"class":236,"line":356},[619,624,629,634,639,644],{"type":38,"tag":234,"props":620,"children":621},{"style":299},[622],{"type":51,"value":623},"        `A webhook just arrived. Triage it and save a report.",{"type":38,"tag":234,"props":625,"children":626},{"style":257},[627],{"type":51,"value":628},"\\n\\n",{"type":38,"tag":234,"props":630,"children":631},{"style":299},[632],{"type":51,"value":633},"${",{"type":38,"tag":234,"props":635,"children":636},{"style":274},[637],{"type":51,"value":638},"body",{"type":38,"tag":234,"props":640,"children":641},{"style":299},[642],{"type":51,"value":643},"}`",{"type":38,"tag":234,"props":645,"children":646},{"style":274},[647],{"type":51,"value":307},{"type":38,"tag":234,"props":649,"children":650},{"class":236,"line":379},[651,656,661,666,671,676,681,686,691,695],{"type":38,"tag":234,"props":652,"children":653},{"style":274},[654],{"type":51,"value":655},"        { auth: ",{"type":38,"tag":234,"props":657,"children":658},{"style":257},[659],{"type":51,"value":660},"null",{"type":38,"tag":234,"props":662,"children":663},{"style":274},[664],{"type":51,"value":665},", continuationToken: ",{"type":38,"tag":234,"props":667,"children":668},{"style":299},[669],{"type":51,"value":670},"`webhook-${",{"type":38,"tag":234,"props":672,"children":673},{"style":274},[674],{"type":51,"value":675},"crypto",{"type":38,"tag":234,"props":677,"children":678},{"style":299},[679],{"type":51,"value":680},".",{"type":38,"tag":234,"props":682,"children":683},{"style":268},[684],{"type":51,"value":685},"randomUUID",{"type":38,"tag":234,"props":687,"children":688},{"style":299},[689],{"type":51,"value":690},"()",{"type":38,"tag":234,"props":692,"children":693},{"style":299},[694],{"type":51,"value":643},{"type":38,"tag":234,"props":696,"children":697},{"style":274},[698],{"type":51,"value":699}," },\n",{"type":38,"tag":234,"props":701,"children":702},{"class":236,"line":417},[703],{"type":38,"tag":234,"props":704,"children":705},{"style":274},[706],{"type":51,"value":707},"      );\n",{"type":38,"tag":234,"props":709,"children":711},{"class":236,"line":710},10,[712,717,722,727,732,737],{"type":38,"tag":234,"props":713,"children":714},{"style":251},[715],{"type":51,"value":716},"      return",{"type":38,"tag":234,"props":718,"children":719},{"style":274},[720],{"type":51,"value":721}," Response.",{"type":38,"tag":234,"props":723,"children":724},{"style":268},[725],{"type":51,"value":726},"json",{"type":38,"tag":234,"props":728,"children":729},{"style":274},[730],{"type":51,"value":731},"({ ok: ",{"type":38,"tag":234,"props":733,"children":734},{"style":257},[735],{"type":51,"value":736},"true",{"type":38,"tag":234,"props":738,"children":739},{"style":274},[740],{"type":51,"value":741},", sessionId: session.id });\n",{"type":38,"tag":234,"props":743,"children":745},{"class":236,"line":744},11,[746],{"type":38,"tag":234,"props":747,"children":748},{"style":274},[749],{"type":51,"value":750},"    }),\n",{"type":38,"tag":234,"props":752,"children":754},{"class":236,"line":753},12,[755],{"type":38,"tag":234,"props":756,"children":757},{"style":274},[758],{"type":51,"value":759},"  ],\n",{"type":38,"tag":234,"props":761,"children":763},{"class":236,"line":762},13,[764],{"type":38,"tag":234,"props":765,"children":766},{"style":274},[767],{"type":51,"value":343},{"type":38,"tag":39,"props":769,"children":770},{},[771,773,778],{"type":51,"value":772},"And a typed ",{"type":38,"tag":59,"props":774,"children":775},{},[776],{"type":51,"value":777},"tool",{"type":51,"value":779}," is what the model does about it — here, writing a triage report to disk. This is the part you'd swap for a Slack message, a database insert or a home-automation call:",{"type":38,"tag":224,"props":781,"children":783},{"className":226,"code":782,"language":228,"meta":7,"style":7},"// agent/tools/save_report.ts\nexport default defineTool({\n  description: \"Save a triage report for a webhook event as a markdown file.\",\n  inputSchema: z.object({ slug: z.string(), markdown: z.string() }),\n  async execute({ slug, markdown }) { /* write reports/\u003Cdate>-\u003Cslug>.md */ },\n});\n",[784],{"type":38,"tag":171,"props":785,"children":786},{"__ignoreMap":7},[787,795,815,832,869,915],{"type":38,"tag":234,"props":788,"children":789},{"class":236,"line":237},[790],{"type":38,"tag":234,"props":791,"children":792},{"style":241},[793],{"type":51,"value":794},"// agent/tools/save_report.ts\n",{"type":38,"tag":234,"props":796,"children":797},{"class":236,"line":247},[798,802,806,811],{"type":38,"tag":234,"props":799,"children":800},{"style":251},[801],{"type":51,"value":362},{"type":38,"tag":234,"props":803,"children":804},{"style":251},[805],{"type":51,"value":367},{"type":38,"tag":234,"props":807,"children":808},{"style":268},[809],{"type":51,"value":810}," defineTool",{"type":38,"tag":234,"props":812,"children":813},{"style":274},[814],{"type":51,"value":277},{"type":38,"tag":234,"props":816,"children":817},{"class":236,"line":280},[818,823,828],{"type":38,"tag":234,"props":819,"children":820},{"style":274},[821],{"type":51,"value":822},"  description: ",{"type":38,"tag":234,"props":824,"children":825},{"style":299},[826],{"type":51,"value":827},"\"Save a triage report for a webhook event as a markdown file.\"",{"type":38,"tag":234,"props":829,"children":830},{"style":274},[831],{"type":51,"value":307},{"type":38,"tag":234,"props":833,"children":834},{"class":236,"line":310},[835,840,845,850,855,860,864],{"type":38,"tag":234,"props":836,"children":837},{"style":274},[838],{"type":51,"value":839},"  inputSchema: z.",{"type":38,"tag":234,"props":841,"children":842},{"style":268},[843],{"type":51,"value":844},"object",{"type":38,"tag":234,"props":846,"children":847},{"style":274},[848],{"type":51,"value":849},"({ slug: z.",{"type":38,"tag":234,"props":851,"children":852},{"style":268},[853],{"type":51,"value":854},"string",{"type":38,"tag":234,"props":856,"children":857},{"style":274},[858],{"type":51,"value":859},"(), markdown: z.",{"type":38,"tag":234,"props":861,"children":862},{"style":268},[863],{"type":51,"value":854},{"type":38,"tag":234,"props":865,"children":866},{"style":274},[867],{"type":51,"value":868},"() }),\n",{"type":38,"tag":234,"props":870,"children":871},{"class":236,"line":337},[872,877,882,887,892,896,901,906,911],{"type":38,"tag":234,"props":873,"children":874},{"style":251},[875],{"type":51,"value":876},"  async",{"type":38,"tag":234,"props":878,"children":879},{"style":268},[880],{"type":51,"value":881}," execute",{"type":38,"tag":234,"props":883,"children":884},{"style":274},[885],{"type":51,"value":886},"({ ",{"type":38,"tag":234,"props":888,"children":889},{"style":521},[890],{"type":51,"value":891},"slug",{"type":38,"tag":234,"props":893,"children":894},{"style":274},[895],{"type":51,"value":508},{"type":38,"tag":234,"props":897,"children":898},{"style":521},[899],{"type":51,"value":900},"markdown",{"type":38,"tag":234,"props":902,"children":903},{"style":274},[904],{"type":51,"value":905}," }) { ",{"type":38,"tag":234,"props":907,"children":908},{"style":241},[909],{"type":51,"value":910},"/* write reports/\u003Cdate>-\u003Cslug>.md */",{"type":38,"tag":234,"props":912,"children":913},{"style":274},[914],{"type":51,"value":699},{"type":38,"tag":234,"props":916,"children":917},{"class":236,"line":346},[918],{"type":38,"tag":234,"props":919,"children":920},{"style":274},[921],{"type":51,"value":343},{"type":38,"tag":145,"props":923,"children":925},{"id":924},"what-it-does",[926],{"type":51,"value":927},"What it does",{"type":38,"tag":39,"props":929,"children":930},{},[931],{"type":51,"value":932},"Send a test event to your bucket's public URL:",{"type":38,"tag":224,"props":934,"children":938},{"className":935,"code":936,"language":937,"meta":7,"style":7},"language-bash shiki shiki-themes github-dark","curl -X POST https://xxxxx.hooks.webhookrelay.com/ \\\n  -H 'content-type: application/json' \\\n  -H 'x-github-event: issues' \\\n  -d '{\"action\":\"opened\",\"issue\":{\"number\":42,\"title\":\"Payments webhook drops events during deploys\", ...}}'\n","bash",[939],{"type":38,"tag":171,"props":940,"children":941},{"__ignoreMap":7},[942,970,987,1003],{"type":38,"tag":234,"props":943,"children":944},{"class":236,"line":237},[945,950,955,960,965],{"type":38,"tag":234,"props":946,"children":947},{"style":268},[948],{"type":51,"value":949},"curl",{"type":38,"tag":234,"props":951,"children":952},{"style":257},[953],{"type":51,"value":954}," -X",{"type":38,"tag":234,"props":956,"children":957},{"style":299},[958],{"type":51,"value":959}," POST",{"type":38,"tag":234,"props":961,"children":962},{"style":299},[963],{"type":51,"value":964}," https://xxxxx.hooks.webhookrelay.com/",{"type":38,"tag":234,"props":966,"children":967},{"style":257},[968],{"type":51,"value":969}," \\\n",{"type":38,"tag":234,"props":971,"children":972},{"class":236,"line":247},[973,978,983],{"type":38,"tag":234,"props":974,"children":975},{"style":257},[976],{"type":51,"value":977},"  -H",{"type":38,"tag":234,"props":979,"children":980},{"style":299},[981],{"type":51,"value":982}," 'content-type: application/json'",{"type":38,"tag":234,"props":984,"children":985},{"style":257},[986],{"type":51,"value":969},{"type":38,"tag":234,"props":988,"children":989},{"class":236,"line":280},[990,994,999],{"type":38,"tag":234,"props":991,"children":992},{"style":257},[993],{"type":51,"value":977},{"type":38,"tag":234,"props":995,"children":996},{"style":299},[997],{"type":51,"value":998}," 'x-github-event: issues'",{"type":38,"tag":234,"props":1000,"children":1001},{"style":257},[1002],{"type":51,"value":969},{"type":38,"tag":234,"props":1004,"children":1005},{"class":236,"line":310},[1006,1011],{"type":38,"tag":234,"props":1007,"children":1008},{"style":257},[1009],{"type":51,"value":1010},"  -d",{"type":38,"tag":234,"props":1012,"children":1013},{"style":299},[1014],{"type":51,"value":1015}," '{\"action\":\"opened\",\"issue\":{\"number\":42,\"title\":\"Payments webhook drops events during deploys\", ...}}'\n",{"type":38,"tag":39,"props":1017,"children":1018},{},[1019,1021,1027],{"type":51,"value":1020},"A few seconds later there's a report in ",{"type":38,"tag":171,"props":1022,"children":1024},{"className":1023},[],[1025],{"type":51,"value":1026},"reports/",{"type":51,"value":1028},". This is real output from the example agent:",{"type":38,"tag":1030,"props":1031,"children":1032},"blockquote",{},[1033,1043,1072],{"type":38,"tag":39,"props":1034,"children":1035},{},[1036,1041],{"type":38,"tag":59,"props":1037,"children":1038},{},[1039],{"type":51,"value":1040},"GitHub Issue Opened — acme/shop #42",{"type":51,"value":1042}," · Severity: info",{"type":38,"tag":39,"props":1044,"children":1045},{},[1046,1048,1054,1056,1062,1064,1070],{"type":51,"value":1047},"A new GitHub issue was opened on the ",{"type":38,"tag":171,"props":1049,"children":1051},{"className":1050},[],[1052],{"type":51,"value":1053},"acme/shop",{"type":51,"value":1055}," repository by ",{"type":38,"tag":171,"props":1057,"children":1059},{"className":1058},[],[1060],{"type":51,"value":1061},"octocat",{"type":51,"value":1063},". Although classified as ",{"type":38,"tag":171,"props":1065,"children":1067},{"className":1066},[],[1068],{"type":51,"value":1069},"info",{"type":51,"value":1071},", the subject matter is noteworthy — dropped payment webhook events during deploys could indicate a reliability risk worth investigating proactively.",{"type":38,"tag":39,"props":1073,"children":1074},{},[1075,1080],{"type":38,"tag":180,"props":1076,"children":1077},{},[1078],{"type":51,"value":1079},"Suggested next action:",{"type":51,"value":1081}," review and assign issue #42 to the payments team; investigate whether there is a known deploy-window gap in webhook event handling.",{"type":38,"tag":39,"props":1083,"children":1084},{},[1085],{"type":51,"value":1086},"Point GitHub, Stripe, Shopify or your monitoring at the same URL and every event flows through — the agent reads the payload, decides a severity and writes up what happened and what to do next.",{"type":38,"tag":145,"props":1088,"children":1090},{"id":1089},"running-it-on-a-raspberry-pi-or-mac-mini",[1091],{"type":51,"value":1092},"Running it on a Raspberry Pi or Mac mini",{"type":38,"tag":39,"props":1094,"children":1095},{},[1096,1098,1104,1106,1111,1113,1119],{"type":51,"value":1097},"The whole stack is one ",{"type":38,"tag":171,"props":1099,"children":1101},{"className":1100},[],[1102],{"type":51,"value":1103},"docker compose up --build -d",{"type":51,"value":1105}," — the agent container plus the ",{"type":38,"tag":171,"props":1107,"children":1109},{"className":1108},[],[1110],{"type":51,"value":176},{"type":51,"value":1112}," sidecar. The image is built on multi-arch ",{"type":38,"tag":171,"props":1114,"children":1116},{"className":1115},[],[1117],{"type":51,"value":1118},"node:24-slim",{"type":51,"value":1120},", so the same compose file works on a Raspberry Pi (arm64), a Mac mini acting as a quiet home AI server, a NAS or a VM behind a corporate firewall.",{"type":38,"tag":39,"props":1122,"children":1123},{},[1124],{"type":51,"value":1125},"Two things worth turning on for hardware that lives at home:",{"type":38,"tag":1127,"props":1128,"children":1129},"ul",{},[1130,1145],{"type":38,"tag":1131,"props":1132,"children":1133},"li",{},[1134,1143],{"type":38,"tag":59,"props":1135,"children":1136},{},[1137],{"type":38,"tag":78,"props":1138,"children":1140},{"href":1139},"/features/durable-retries",[1141],{"type":51,"value":1142},"Durable retries",{"type":51,"value":1144}," on the bucket — if the machine sleeps, reboots or loses connectivity, events queue in the cloud and deliver when the sidecar reconnects, for up to 30 days.",{"type":38,"tag":1131,"props":1146,"children":1147},{},[1148,1150,1156],{"type":51,"value":1149},"A ",{"type":38,"tag":171,"props":1151,"children":1153},{"className":1152},[],[1154],{"type":51,"value":1155},"WEBHOOK_SECRET",{"type":51,"value":1157}," (supported by the example) so the agent's channel rejects anything that didn't come through your relay.",{"type":38,"tag":39,"props":1159,"children":1160},{},[1161,1163,1169,1171,1177,1179,1185,1187,1193,1195,1201],{"type":51,"value":1162},"The ",{"type":38,"tag":78,"props":1164,"children":1166},{"href":80,"rel":1165},[82],[1167],{"type":51,"value":1168},"README",{"type":51,"value":1170}," has the full quick start: create a bucket with an internal output pointing at ",{"type":38,"tag":171,"props":1172,"children":1174},{"className":1173},[],[1175],{"type":51,"value":1176},"http://agent:3000/eve/v1/webhook",{"type":51,"value":1178},", drop your token into ",{"type":38,"tag":171,"props":1180,"children":1182},{"className":1181},[],[1183],{"type":51,"value":1184},".env.local",{"type":51,"value":1186},", and compose up. For local development you can skip Docker entirely and forward the bucket to ",{"type":38,"tag":171,"props":1188,"children":1190},{"className":1189},[],[1191],{"type":51,"value":1192},"eve dev",{"type":51,"value":1194}," with the ",{"type":38,"tag":78,"props":1196,"children":1198},{"href":1197},"/docs/installation/cli",[1199],{"type":51,"value":1200},"relay CLI",{"type":51,"value":680},{"type":38,"tag":39,"props":1203,"children":1204},{},[1205,1207,1213],{"type":51,"value":1206},"The example triages events because it's a useful default that needs no extra API keys — but the wiring is the point. Swap ",{"type":38,"tag":171,"props":1208,"children":1210},{"className":1209},[],[1211],{"type":51,"value":1212},"agent/instructions.md",{"type":51,"value":1214}," and the tools, and the same skeleton becomes a code-review agent, an on-call assistant or the brain of your home automation: any webhook in the world can now reach the agent in your drawer.",{"type":38,"tag":1216,"props":1217,"children":1218},"style",{},[1219],{"type":51,"value":1220},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":280,"depth":280,"links":1222},[1223,1224,1225,1226],{"id":147,"depth":247,"text":150},{"id":197,"depth":247,"text":200},{"id":924,"depth":247,"text":927},{"id":1089,"depth":247,"text":1092},"content:blog:self-hosted-ai-agent-webhooks.md","content","blog/self-hosted-ai-agent-webhooks.md","blog/self-hosted-ai-agent-webhooks","md",{"loc":4},1782992702616]