$worker

web

v1.2.3

Outbound HTTP client on the iii bus (web::fetch).

  • macOS: arm64 · x64
  • Linux: arm64 · armv7 · x64
  • Windows: arm64 · x64 · x86

skill doc

web

index
SKILL.md

Callable id: web::fetch — pass to agent_trigger { function: "web::fetch" } (NOT the iii://... skill path; that's docs, not a function id). Use this instead of shell::exec with curl/wget for any HTTP request: you get a parsed { ok, status, headers, body } envelope, enforced size/timeout caps, and server-side SSRF protection a shell curl doesn't have.

Decide fast

You want to… Call
Read a web page (docs, articles) { "url": "https://…", "format": "markdown" }
GET a page/API { "url": "https://…" }
Parse a JSON API response { "url": "https://…", "response_format": "json" }
POST/PUT JSON { "url": "…", "method": "post", "json": { … } }
Send a raw body / form { "url": "…", "method": "post", "body": "…", "headers": { "content-type": "…" } }
Download binary { "url": "…", "response_format": "base64" }
Run a shell curl ❌ stop — use web::fetch

Minimal call

// agent_trigger { function: "web::fetch", payload: { "url": "https://api.github.com/zen" } }
// → { "ok": true, "status": 200, "body": "Design for failure.", ... }

That's the whole T0 path: a URL is the only required field. Everything else has a default.

Read the result correctly (the #1 trap)

ok tells you if the request COMPLETED, not whether the server was happy.

  • A 404 or 500 is a successful fetchok: true, status: 404. Branch on status for HTTP outcomes.
  • ok: false means the request never produced a response (bad URL, blocked host, timeout, transport failure). Branch on error for those.
if (!r.ok)            → fetch failed; look at r.error  (see table below)
else if (r.status>=400) → server returned an HTTP error; look at r.status / r.body
else                  → success; use r.body or r.json

Do not treat ok: true as "2xx". Always check status too.

One exception: in page-reading mode (format set), an image/* response returns the image itself plus a one-line text summary instead of this envelope — there is no top-level ok/status to branch on (see "Images in page-reading mode" below).

Request fields

Field Default Notes
url (required) absolute http(s)://
method GET case-insensitive ("post" works); GET HEAD POST PUT PATCH DELETE OPTIONS
headers {} object; keys as you write them
json structured payload; auto-stringified + sets content-type: application/json. Wins over body.
body raw string body; use for non-JSON. Ignored on GET/HEAD.
response_format "text" "text" | "base64" (binary) | "json" (also parses into json)
format page-reading mode: "markdown" (HTML→Markdown) | "text" (HTML→plain text) | "html" (raw). Sends a browser UA + matching Accept; retries once with the honest UA on a Cloudflare challenge; images come back viewable. Forces text transport (response_format ignored).
timeout_ms 30000 clamped DOWN to the worker ceiling (120000 by default); can't raise past it
max_bytes 5 MiB (256 KiB in format mode) raw fetches default to the 5 MiB ceiling; page-reading mode (format set) uses a context-safe 256 KiB. Pass an explicit value to override (up to the 5 MiB ceiling). Over-cap body is truncated, not errored
follow_redirects true each hop re-checked against the SSRF blocklist

Content pipeline

Four request controls for page-reading mode (format set):

Field Effect
content_filter { type: "pruning"|"bm25", query?, threshold?, threshold_type?, min_word_threshold? } — filters body to signal content. Pruning default threshold 0.48; BM25 default 1.0. query falls back to page </code>/<code class="readme-inline-code"><meta description></code> if omitted.</td> </tr> <tr> <td><code class="readme-inline-code">target_elements</code></td> <td>Restrict body to matching regions. Selector subset: tag, <code class="readme-inline-code">.class</code>, <code class="readme-inline-code">#id</code> — other forms are silently ignored.</td> </tr> <tr> <td><code class="readme-inline-code">excluded_tags</code></td> <td>Drop matching elements before rendering (e.g. <code class="readme-inline-code">["nav", "footer"]</code>).</td> </tr> <tr> <td><code class="readme-inline-code">include_links</code></td> <td><code class="readme-inline-code">true</code> → adds <code class="readme-inline-code">links: { internal, external }</code> arrays (absolute URLs, classified by host).</td> </tr> <tr> <td><code class="readme-inline-code">include_media</code></td> <td><code class="readme-inline-code">true</code> → adds <code class="readme-inline-code">media: { images, videos, audios }</code> arrays (absolute URLs).</td> </tr> </tbody></table> <p>When <code class="readme-inline-code">content_filter</code> is set, <code class="readme-inline-code">body</code> <strong>is</strong> the filtered output — no separate field. Feed it straight to a model.</p> <h1 id="response" class="scroll-mt-20">Response</h1> <p><strong>Success</strong> (<code class="readme-inline-code">ok: true</code>) — returned for <em>any</em> completed response, 2xx through 5xx:</p> <pre><code>{ "ok": true, "status": 200, "status_text": "OK", "headers": { "content-type": "application/json", ... }, // keys LOWER-CASED; set-cookie joined with ", " "body": "<utf8 text | base64 when response_format=base64>", "json": { ... }, // only when response_format="json" AND parse succeeded AND not truncated "parse_error": "…", // only when response_format="json" AND JSON.parse failed (body still set) "response_format": "json", "bytes_truncated": false, // true when body hit max_bytes (NOT an error) "redirect_chain": ["https://…/a"], // omitted when no redirects "content_type": "text/html", // only in page-reading mode (format set) "transformed": "markdown" // only when an HTML transform actually ran }</code></pre><p><strong>Failure</strong> (<code class="readme-inline-code">ok: false</code>) — the fetch never produced a response:</p> <pre><code>{ "ok": false, "error": "blocked_host", "message": "…" } // no status field</code></pre><h2 id="error-cause-fix" class="scroll-mt-20">error → cause → fix</h2> <table> <thead> <tr> <th><code class="readme-inline-code">error</code></th> <th>Cause</th> <th>Fix</th> </tr> </thead> <tbody><tr> <td><code class="readme-inline-code">invalid_payload</code></td> <td>Payload failed schema (bad <code class="readme-inline-code">method</code>, wrong types). <code class="readme-inline-code">message</code> lists the bad fields.</td> <td>Correct the named fields.</td> </tr> <tr> <td><code class="readme-inline-code">invalid_url</code></td> <td><code class="readme-inline-code">url</code> isn't a parseable absolute <code class="readme-inline-code">http(s)://</code> URL.</td> <td>Pass a full absolute URL incl. scheme.</td> </tr> <tr> <td><code class="readme-inline-code">blocked_host</code></td> <td>Target resolves to a private / link-local / cloud-metadata IP (SSRF guard).</td> <td>Don't target internal/metadata hosts. For loopback in dev, the operator sets <code class="readme-inline-code">web.allow_loopback</code>.</td> </tr> <tr> <td><code class="readme-inline-code">timeout</code></td> <td>Slower than <code class="readme-inline-code">timeout_ms</code> (30 s default; 120 s ceiling).</td> <td>Raise <code class="readme-inline-code">timeout_ms</code> (up to ceiling) or shrink work via <code class="readme-inline-code">max_bytes</code>.</td> </tr> <tr> <td><code class="readme-inline-code">too_many_redirects</code></td> <td>More than <code class="readme-inline-code">max_redirects</code> (5) hops.</td> <td>Use the final URL directly, or set <code class="readme-inline-code">follow_redirects: false</code> and read the <code class="readme-inline-code">location</code> header.</td> </tr> <tr> <td><code class="readme-inline-code">transport_error</code></td> <td>Connection refused/reset, TLS failure, DNS failure, or a redirect <code class="readme-inline-code">Location</code> that won't parse.</td> <td>Check host/port/cert; retry if transient.</td> </tr> </tbody></table> <p>There is <strong>no <code class="readme-inline-code">too_large</code> error</strong> — oversize responses come back <code class="readme-inline-code">ok: true</code> with <code class="readme-inline-code">bytes_truncated: true</code>. Branch on the flag, not on an error.</p> <h1 id="page-reading-vs-api-fetch" class="scroll-mt-20">Page reading vs API fetch</h1> <p>Two orthogonal knobs — pick ONE:</p> <ul> <li><strong><code class="readme-inline-code">response_format</code></strong> = transport encoding for APIs/binaries (<code class="readme-inline-code">text</code>/<code class="readme-inline-code">base64</code>/<code class="readme-inline-code">json</code>). The body is returned untouched.</li> <li><strong><code class="readme-inline-code">format</code></strong> = page-reading mode (<code class="readme-inline-code">markdown</code>/<code class="readme-inline-code">text</code>/<code class="readme-inline-code">html</code>). The request goes out with a browser User-Agent + format-matched <code class="readme-inline-code">Accept</code> header, and <code class="readme-inline-code">text/html</code> responses are transformed (<code class="readme-inline-code">markdown</code> is the right default for reading pages — far fewer tokens than raw HTML). Non-HTML bodies pass through unchanged. If Cloudflare answers <code class="readme-inline-code">403</code> with a challenge, the worker retries once with its honest UA (beats the UA-fingerprint rule only, not full JS challenges). For large pages, lower <code class="readme-inline-code">max_bytes</code> — conversion runs on the capped body.</li> </ul> <p>Don't combine them: when <code class="readme-inline-code">format</code> is set, <code class="readme-inline-code">response_format</code> is ignored (treated as <code class="readme-inline-code">"text"</code>).</p> <p><strong>Images in page-reading mode:</strong> a viewable <code class="readme-inline-code">image/*</code> response returns the actual image (plus a one-line text summary like <code class="readme-inline-code">Image fetched (image/png, 8123 bytes)</code>) instead of the JSON envelope — providers that support tool-result images (Anthropic) show it to the model; others see the text line. "Viewable" means jpeg/png/gif/webp, 2xx status, complete (not truncated), and non-empty — anything else (svg, error pages served as images, truncated bytes) comes back as the normal envelope with <code class="readme-inline-code">response_format: "base64"</code> so a hostile image can't fail the provider request. Without <code class="readme-inline-code">format</code>, use <code class="readme-inline-code">response_format: "base64"</code> as before.</p> <p><strong>Transform bounds:</strong> the HTML→markdown/text conversion runs only on bodies ≤ <code class="readme-inline-code">web.max_transform_bytes</code> (1 MiB default) — larger pages come back raw with <code class="readme-inline-code">transformed</code> unset (lower <code class="readme-inline-code">max_bytes</code> to read huge pages). If the body was truncated at <code class="readme-inline-code">max_bytes</code>, the transformed text ends with a visible <code class="readme-inline-code">[Content truncated at max_bytes — …]</code> line.</p> <h1 id="rules-that-save-a-turn" class="scroll-mt-20">Rules that save a turn</h1> <ul> <li><strong><code class="readme-inline-code">json</code> vs <code class="readme-inline-code">body</code>: set exactly one.</strong> <code class="readme-inline-code">json</code> wins if both are present and forces <code class="readme-inline-code">content-type: application/json</code>. Use <code class="readme-inline-code">body</code> + your own <code class="readme-inline-code">content-type</code> for form/text/XML.</li> <li><strong>GET/HEAD ignore the body.</strong> Putting <code class="readme-inline-code">json</code>/<code class="readme-inline-code">body</code> on a GET sends nothing — switch <code class="readme-inline-code">method</code>.</li> <li><strong><code class="readme-inline-code">response_format: "json"</code> can succeed with <code class="readme-inline-code">parse_error</code>.</strong> Non-JSON (or truncated) bodies return <code class="readme-inline-code">ok: true</code>, no <code class="readme-inline-code">json</code>, <code class="readme-inline-code">parse_error</code> set; the raw text is still in <code class="readme-inline-code">body</code>. Check for <code class="readme-inline-code">json</code> before reading it.</li> <li><strong>Caps only go down.</strong> <code class="readme-inline-code">timeout_ms</code>/<code class="readme-inline-code">max_bytes</code> above the worker ceiling are clamped; you can't request more than the operator allows.</li> <li><strong>Response header keys are lower-cased.</strong> Index <code class="readme-inline-code">headers["content-type"]</code>, never <code class="readme-inline-code">"Content-Type"</code>.</li> <li><strong>Auth headers don't survive a cross-origin redirect</strong> (see below) — for an authed endpoint, hit the final URL directly.</li> </ul> <h1 id="ssrf-guard-server-side-not-optional" class="scroll-mt-20">SSRF guard (server-side, not optional)</h1> <p>DNS is resolved <strong>once</strong>, every resolved address is checked, then the request is dialed at the <strong>validated IP</strong> (pinned <code class="readme-inline-code">lookup</code> + TLS <code class="readme-inline-code">servername</code>) — so the IP that passed the check is the IP connected to (no DNS-rebind window).</p> <p>Blocked <strong>unconditionally</strong> → <code class="readme-inline-code">error: "blocked_host"</code>:</p> <ul> <li>Private RFC1918 (<code class="readme-inline-code">10/8</code>, <code class="readme-inline-code">172.16/12</code>, <code class="readme-inline-code">192.168/16</code>)</li> <li>Link-local incl. <strong>cloud metadata</strong> <code class="readme-inline-code">169.254.169.254</code></li> <li>IPv6 ULA (<code class="readme-inline-code">fc00::/7</code>), link-local (<code class="readme-inline-code">fe80::/10</code>), multicast</li> <li><strong><code class="readme-inline-code">::ffff:</code>-mapped IPv4</strong> in both dotted (<code class="readme-inline-code">::ffff:169.254.169.254</code>) and hex (<code class="readme-inline-code">::ffff:a9fe:a9fe</code>) forms</li> </ul> <p>Loopback (<code class="readme-inline-code">127.0.0.0/8</code>, <code class="readme-inline-code">::1</code>, <code class="readme-inline-code">localhost</code>) is <strong>allowed by default</strong> (dev convenience); operators flip <code class="readme-inline-code">web.allow_loopback: false</code> for prod, after which loopback returns <code class="readme-inline-code">blocked_host</code>.</p> <p>On each 3xx the <code class="readme-inline-code">Location</code> is re-resolved and <strong>re-validated</strong> before following, and <code class="readme-inline-code">Authorization</code> / <code class="readme-inline-code">Cookie</code> / <code class="readme-inline-code">Proxy-Authorization</code> are <strong>stripped</strong> when the redirect changes host or downgrades <code class="readme-inline-code">https → http</code>.</p> <h1 id="examples" class="scroll-mt-20">Examples</h1> <pre><code>// Read a documentation page as Markdown { "url": "https://docs.example.com/guide", "format": "markdown" } // → { ok:true, status:200, body:"# Guide\n\n…", transformed:"markdown", content_type:"text/html" } // Parse a JSON API { "url": "https://api.example.com/status", "response_format": "json" } // → { ok:true, status:200, json:{ healthy:true } } // POST JSON (content-type set for you) { "url": "https://api.example.com/things", "method": "post", "json": { "name": "x" }, "response_format": "json" } // Authenticated GET (auth stays only because there's no cross-host redirect) { "url": "https://api.example.com/me", "headers": { "authorization": "Bearer TOKEN" }, "response_format": "json" } // Bounded download of a maybe-large file { "url": "https://example.com/big.log", "max_bytes": 65536 } // → { ok:true, body:"<first 64 KiB>", bytes_truncated:true }</code></pre><h1 id="authoritative-schema" class="scroll-mt-20">Authoritative schema</h1> <p>This page covers behavior the schema can't. For exact field types, call <code class="readme-inline-code">engine::functions::info { function_id: "web::fetch" }</code> — the live schema wins if it ever disagrees with this page.</p> <h1 id="related" class="scroll-mt-20">Related</h1> <ul> <li><a href="iii://shell"><code class="readme-inline-code">shell/index</code></a> — local filesystem + process ops; <code class="readme-inline-code">web::fetch</code> is the network counterpart (use it, not <code class="readme-inline-code">shell::exec curl</code>).</li> <li><a href="iii://sandbox"><code class="readme-inline-code">sandbox/index</code></a> — code <em>inside</em> a sandbox reaches the host engine via the boot-time <code class="readme-inline-code">III_ENGINE_URL</code> rewrite, not <code class="readme-inline-code">web::fetch</code>.</li> </ul> </div></div></section></div><aside class="flex flex-col gap-y-4 @4xl:sticky @4xl:top-4 @4xl:self-start @4xl:max-h-[calc(100dvh-2rem)] @4xl:overflow-y-auto"><div class="border border-rule bg-bg"><div class="bg-panel px-3.5 py-2.5 border-b border-rule"><span class="font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint">details</span></div><div class="p-4 flex flex-col gap-y-2.5"><div class="flex items-baseline justify-between font-mono text-[13px] gap-x-3"><span class="text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]">version</span><span class="text-ink truncate text-right lowercase tabular-nums">v1.2.3</span></div><div class="flex items-baseline justify-between font-mono text-[13px] gap-x-3"><span class="text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]">type</span><span class="text-ink truncate text-right lowercase ">binary</span></div><div class="flex items-baseline justify-between font-mono text-[13px] gap-x-3 min-w-0"><span class="text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]">repo</span><a href="https://github.com/iii-hq/workers" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink lowercase hover:text-accent transition-colors min-w-0 text-right justify-end"><span class="truncate">iii-hq/workers</span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a></div></div></div><div class="border border-rule bg-bg"><div class="bg-panel px-3.5 py-2.5 border-b border-rule"><span class="font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint">skills</span></div><div class="max-h-[min(60dvh,28rem)] overflow-y-auto"><section aria-labelledby="skills-tree-web"><div id="skills-tree-web" class="flex items-baseline justify-between px-3.5 py-2 bg-paper-2 border-b border-rule-2"><span class="font-mono text-[11px] font-medium uppercase tracking-[0.06em] text-ink-faint">files</span><span class="font-mono text-[11px] uppercase tracking-[0.06em] text-ink-ghost tabular-nums">1</span></div><nav aria-label="skill files" class="px-3 pt-2 pb-3"><ul class="flex flex-col gap-y-0.5"><li style="padding-left:0"><a class="block font-mono text-[12px] py-0.5 px-1.5 -mx-1.5 lowercase truncate transition-colors bg-ink text-bg" href="/workers/web?tab=skills&skill=SKILL.md">SKILL.md</a></li></ul></nav></section></div></div><div class="border border-rule bg-bg"><div class="bg-panel px-3.5 py-2.5 border-b border-rule"><span class="font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint">author</span></div><div class="p-4 flex flex-col gap-y-2.5"><div class="flex items-center gap-x-2 font-mono text-[13px]"><img src="https://iii.dev/docs/_mintlify/favicons/motiadev/AFkVbz_UcSL_5jsm/_generated/favicon/apple-touch-icon.png" alt="" class="size-5 object-cover"/><span class="text-ink lowercase">iii</span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-check size-3.5 text-accent" aria-hidden="true"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"></path><path d="m9 12 2 2 4-4"></path></svg></div></div></div></aside></div></div></div><!--$--><!--/$--></main><footer class="border-t border-rule bg-bg"><div class="flex items-center justify-between flex-wrap gap-x-6 gap-y-3 px-9 py-4.5"><a aria-label="iii worker — registry home" class="inline-flex items-center gap-x-2 text-ink" href="/"><svg viewBox="0 0 1075.74 1075.69" aria-hidden="true" focusable="false" class="size-[14px]" fill="currentColor"><rect x="0" y="0.05" width="268.94" height="268.94"></rect><rect x="403.4" y="0.05" width="268.94" height="268.94"></rect><rect x="806.81" y="0.05" width="268.94" height="268.94"></rect><rect x="0" y="403.45" width="268.94" height="672.24"></rect><rect x="403.4" y="403.45" width="268.94" height="672.24"></rect><rect x="806.81" y="403.45" width="268.94" height="672.24"></rect></svg><span class="font-mono text-[13px] font-medium tracking-[-0.02em] lowercase">iii / worker</span></a><nav aria-label="footer" class="flex items-center flex-wrap gap-x-6 gap-y-2"><a href="https://iii.dev" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">iii.dev<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a><a href="https://iii.dev/docs" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">docs<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a><a class="font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors" href="/badge-preview">badge preview</a><a href="https://discord.gg/iiidev" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">discord<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a><a href="https://github.com/iii-hq/iii" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">github<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a></nav></div></footer></div></div><script src="/_next/static/chunks/0b_t-9oq5.7ee.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n3:I[46610,[\"/_next/static/chunks/05w7h3fxzucf~.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0~_d9nztcm4i3.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/06i73iynkubsx.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"\"]\n5:I[8808,[\"/_next/static/chunks/05w7h3fxzucf~.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0~_d9nztcm4i3.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"ThemeToggle\"]\n6:I[90280,[\"/_next/static/chunks/04ylibgf-0e6d.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0.vqct1a~k736.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"default\"]\n7:I[69616,[\"/_next/static/chunks/04ylibgf-0e6d.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0.vqct1a~k736.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"default\"]\n11:I[50609,[\"/_next/static/chunks/05w7h3fxzucf~.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0~_d9nztcm4i3.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/02eqejg_b4k2y.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"default\"]\n13:I[41074,[\"/_next/static/chunks/05w7h3fxzucf~.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0~_d9nztcm4i3.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/06i73iynkubsx.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"default\"]\n15:I[51557,[\"/_next/static/chunks/04ylibgf-0e6d.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0.vqct1a~k736.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"OutletBoundary\"]\n16:\"$Sreact.suspense\"\n19:I[51557,[\"/_next/static/chunks/04ylibgf-0e6d.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0.vqct1a~k736.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"ViewportBoundary\"]\n1b:I[51557,[\"/_next/static/chunks/04ylibgf-0e6d.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0.vqct1a~k736.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"MetadataBoundary\"]\n:HL[\"/_next/static/chunks/0wq3-zxiue07d.css?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"style\"]\n:HL[\"/_next/static/media/387ee14c0e0fe675-s.p.16njqniuj6huf.woff2?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:[\"$\",\"iframe\",null,{\"src\":\"https://www.googletagmanager.com/ns.html?id=GTM-N8DCTFB8\",\"height\":\"0\",\"width\":\"0\",\"style\":{\"display\":\"none\",\"visibility\":\"hidden\"},\"title\":\"Google Tag Manager\"}]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"workers\",\"web?tab=skills\"],\"q\":\"?tab=skills\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"workers\",{\"children\":[[\"slug\",\"web\",\"d\",null],{\"children\":[\"__PAGE__?{\\\"tab\\\":\\\"skills\\\"}\",{}]}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0wq3-zxiue07d.css?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/05w7h3fxzucf~.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0~_d9nztcm4i3.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"chivo_mono_90504305-module__244I6q__variable antialiased\",\"data-theme\":\"light\",\"suppressHydrationWarning\":true,\"children\":[\"$\",\"body\",null,{\"className\":\"bg-bg text-ink font-sans min-h-full flex flex-col\",\"children\":[[[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n try {\\n var stored = localStorage.getItem('iii-theme');\\n if (stored === 'dark' || stored === 'light') {\\n document.documentElement.dataset.theme = stored;\\n } else {\\n document.documentElement.dataset.theme = 'light';\\n }\\n } catch (_) {\\n document.documentElement.dataset.theme = 'light';\\n }\\n\"}}],[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':\\n new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],\\n j=d.createElement(s),dl=l!='dataLayer'?'\u0026l='+l:'';j.async=true;j.src=\\n 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);\\n })(window,document,'script','dataLayer','GTM-N8DCTFB8');\\n\"}}]],[\"$\",\"noscript\",null,{\"children\":\"$2\"}],[\"$\",\"div\",null,{\"className\":\"mx-auto w-full max-w-[1200px] @container border-x border-rule h-screen bg-bg flex flex-col\",\"children\":[[\"$\",\"header\",null,{\"className\":\"sticky top-0 z-30 bg-bg border-b border-rule\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex items-center justify-between gap-x-3 px-5 py-4.5 sm:px-9\",\"children\":[[\"$\",\"div\",null,{\"className\":\"inline-flex min-w-0 items-center gap-x-3 text-ink\",\"children\":[[\"$\",\"a\",null,{\"href\":\"https://iii.dev\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"aria-label\":\"iii.dev\",\"className\":\"inline-flex shrink-0\",\"children\":[\"$\",\"svg\",null,{\"viewBox\":\"0 0 1075.74 1075.69\",\"aria-hidden\":\"true\",\"focusable\":\"false\",\"className\":\"size-[18px]\",\"fill\":\"currentColor\",\"children\":[[\"$\",\"rect\",null,{\"x\":\"0\",\"y\":\"0.05\",\"width\":\"268.94\",\"height\":\"268.94\"}],[\"$\",\"rect\",null,{\"x\":\"403.4\",\"y\":\"0.05\",\"width\":\"268.94\",\"height\":\"268.94\"}],[\"$\",\"rect\",null,{\"x\":\"806.81\",\"y\":\"0.05\",\"width\":\"268.94\",\"height\":\"268.94\"}],[\"$\",\"rect\",null,{\"x\":\"0\",\"y\":\"403.45\",\"width\":\"268.94\",\"height\":\"672.24\"}],[\"$\",\"rect\",null,{\"x\":\"403.4\",\"y\":\"403.45\",\"width\":\"268.94\",\"height\":\"672.24\"}],[\"$\",\"rect\",null,{\"x\":\"806.81\",\"y\":\"403.45\",\"width\":\"268.94\",\"height\":\"672.24\"}]]}]}],[\"$\",\"$L3\",null,{\"href\":\"/\",\"aria-label\":\"iii worker — registry home\",\"className\":\"truncate font-mono text-[18px] leading-[18px] font-semibold tracking-[-0.02em] lowercase\",\"children\":\"iii / worker\"}]]}],[\"$\",\"div\",null,{\"className\":\"inline-flex shrink-0 items-center gap-x-2.5 sm:gap-x-3\",\"children\":[\"$L4\",[\"$\",\"$L5\",null,{}]]}]]}]}],[\"$\",\"div\",null,{\"className\":\"flex-1 overflow-y-auto flex flex-col\",\"children\":[[\"$\",\"main\",null,{\"className\":\"flex-1\",\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"div\",null,{\"className\":\"page-transition\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex items-end justify-between flex-wrap gap-x-6 gap-y-4 px-9 py-12\",\"children\":[[\"$\",\"div\",null,{\"className\":\"min-w-0\",\"children\":[[\"$\",\"div\",null,{\"className\":\"font-mono text-[11px] uppercase tracking-[0.06em] text-ink-faint mb-3\",\"children\":[\"$\",\"span\",null,{\"className\":\"font-mono text-accent\",\"children\":[\"$$\",[\"$\",\"span\",null,{\"className\":\"text-ink ml-2\",\"children\":\"error\"}]]}]}],[\"$\",\"div\",null,{\"className\":\"flex items-baseline flex-wrap gap-x-3 gap-y-1\",\"children\":[[\"$\",\"h1\",null,{\"className\":\"font-mono text-[28px] font-medium tracking-[-0.01em] text-ink lowercase\",\"children\":\"$L8\"}],null]}],\"$L9\"]}],\"$La\"]}]}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}],\"$Lb\"]}]]}]]}]}]]}],{\"children\":[\"$Lc\",{\"children\":[\"$Ld\",{\"children\":[\"$Le\",{},null,false,null]},null,false,\"$@f\"]},null,false,\"$@f\"]},null,false,null],\"$L10\",false]],\"m\":\"$undefined\",\"G\":[\"$11\",[\"$L12\"]],\"S\":false,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"8:[\"$\",\"span\",null,{\"className\":\"inline-flex items-baseline gap-x-2 tabular-nums\",\"children\":[\"404\",[\"$\",\"span\",null,{\"aria-hidden\":true,\"className\":\"blink inline-block w-[6px] h-[13px] bg-ink align-middle translate-y-[1px]\"}]]}]\n9:[\"$\",\"p\",null,{\"className\":\"mt-3 font-mono text-[14px] leading-[1.7] text-ink-faint max-w-[60ch] lowercase\",\"children\":\"that worker is not in the registry. it may have been renamed or never published.\"}]\na:[\"$\",\"div\",null,{\"className\":\"flex items-center gap-x-3\",\"children\":[\"$\",\"$L3\",null,{\"href\":\"/\",\"className\":\"inline-flex items-center justify-center gap-x-2 whitespace-nowrap font-mono lowercase rounded-none transition-[background-color,color,border-color] duration-150 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent disabled:pointer-events-none disabled:opacity-40 select-none bg-ink text-bg border border-ink hover:bg-bg hover:text-ink h-9 px-5 text-[13px]\",\"children\":\"back to registry\"}]}]\n"])</script><script>self.__next_f.push([1,"b:[\"$\",\"footer\",null,{\"className\":\"border-t border-rule bg-bg\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex items-center justify-between flex-wrap gap-x-6 gap-y-3 px-9 py-4.5\",\"children\":[[\"$\",\"$L3\",null,{\"href\":\"/\",\"aria-label\":\"iii worker — registry home\",\"className\":\"inline-flex items-center gap-x-2 text-ink\",\"children\":[[\"$\",\"svg\",null,{\"viewBox\":\"0 0 1075.74 1075.69\",\"aria-hidden\":\"true\",\"focusable\":\"false\",\"className\":\"size-[14px]\",\"fill\":\"currentColor\",\"children\":[[\"$\",\"rect\",null,{\"x\":\"0\",\"y\":\"0.05\",\"width\":\"268.94\",\"height\":\"268.94\"}],[\"$\",\"rect\",null,{\"x\":\"403.4\",\"y\":\"0.05\",\"width\":\"268.94\",\"height\":\"268.94\"}],[\"$\",\"rect\",null,{\"x\":\"806.81\",\"y\":\"0.05\",\"width\":\"268.94\",\"height\":\"268.94\"}],[\"$\",\"rect\",null,{\"x\":\"0\",\"y\":\"403.45\",\"width\":\"268.94\",\"height\":\"672.24\"}],[\"$\",\"rect\",null,{\"x\":\"403.4\",\"y\":\"403.45\",\"width\":\"268.94\",\"height\":\"672.24\"}],[\"$\",\"rect\",null,{\"x\":\"806.81\",\"y\":\"403.45\",\"width\":\"268.94\",\"height\":\"672.24\"}]]}],[\"$\",\"span\",null,{\"className\":\"font-mono text-[13px] font-medium tracking-[-0.02em] lowercase\",\"children\":\"iii / worker\"}]]}],[\"$\",\"nav\",null,{\"aria-label\":\"footer\",\"className\":\"flex items-center flex-wrap gap-x-6 gap-y-2\",\"children\":[[\"$\",\"a\",\"https://iii.dev\",{\"href\":\"https://iii.dev\",\"target\":\"_blank\",\"rel\":\"noreferrer\",\"className\":\"inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors\",\"children\":[\"iii.dev\",[\"$\",\"$L13\",null,{\"ref\":\"$undefined\",\"iconNode\":[[\"path\",{\"d\":\"M15 3h6v6\",\"key\":\"1q9fwt\"}],[\"path\",{\"d\":\"M10 14 21 3\",\"key\":\"gplh6r\"}],[\"path\",{\"d\":\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\",\"key\":\"a6xqqp\"}]],\"className\":\"lucide-external-link size-3 shrink-0\",\"aria-hidden\":true}]]}],[\"$\",\"a\",\"https://iii.dev/docs\",{\"href\":\"https://iii.dev/docs\",\"target\":\"_blank\",\"rel\":\"noreferrer\",\"className\":\"inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors\",\"children\":[\"docs\",[\"$\",\"$L13\",null,{\"ref\":\"$undefined\",\"iconNode\":\"$b:props:children:props:children:1:props:children:0:props:children:1:props:iconNode\",\"className\":\"lucide-external-link size-3 shrink-0\",\"aria-hidden\":true}]]}],[\"$\",\"$L3\",\"/badge-preview\",{\"href\":\"/badge-preview\",\"className\":\"font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors\",\"children\":\"badge preview\"}],[\"$\",\"a\",\"https://discord.gg/iiidev\",{\"href\":\"https://discord.gg/iiidev\",\"target\":\"_blank\",\"rel\":\"noreferrer\",\"className\":\"inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors\",\"children\":[\"discord\",[\"$\",\"$L13\",null,{\"ref\":\"$undefined\",\"iconNode\":\"$b:props:children:props:children:1:props:children:0:props:children:1:props:iconNode\",\"className\":\"lucide-external-link size-3 shrink-0\",\"aria-hidden\":true}]]}],[\"$\",\"a\",\"https://github.com/iii-hq/iii\",{\"href\":\"https://github.com/iii-hq/iii\",\"target\":\"_blank\",\"rel\":\"noreferrer\",\"className\":\"inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors\",\"children\":[\"github\",[\"$\",\"$L13\",null,{\"ref\":\"$undefined\",\"iconNode\":\"$b:props:children:props:children:1:props:children:0:props:children:1:props:iconNode\",\"className\":\"lucide-external-link size-3 shrink-0\",\"aria-hidden\":true}]]}]]}]]}]}]\n"])</script><script>self.__next_f.push([1,"c:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\nd:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\ne:[\"$\",\"$1\",\"c\",{\"children\":[\"$L14\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/06i73iynkubsx.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L15\",null,{\"children\":[\"$\",\"$16\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@17\"}]}]]}]\n18:[]\nf:\"$W18\"\n10:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L19\",null,{\"children\":\"$L1a\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L1b\",null,{\"children\":[\"$\",\"$16\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L1c\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\n12:[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0wq3-zxiue07d.css?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]\n1a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"4:[\"$\",\"a\",null,{\"href\":\"https://github.com/iii-hq/iii\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"aria-label\":\"iii-hq/iii on GitHub — 18,481 stars\",\"className\":\"inline-flex shrink-0 items-center gap-x-2 border border-rule px-2.5 py-1 font-mono text-[13px] text-ink-faint transition-colors hover:text-ink\",\"children\":[[\"$\",\"svg\",null,{\"viewBox\":\"0 0 16 16\",\"aria-hidden\":\"true\",\"focusable\":\"false\",\"className\":\"size-3.5 shrink-0\",\"fill\":\"currentColor\",\"children\":[\"$\",\"path\",null,{\"d\":\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z\"}]}],[\"$\",\"span\",null,{\"className\":\"hidden sm:inline lowercase\",\"children\":\"stars\"}],[\"$\",\"span\",null,{\"className\":\"tabular-nums text-ink\",\"aria-hidden\":true,\"children\":\"18.5k\"}]]}]\n"])</script><script>self.__next_f.push([1,"1d:I[18418,[\"/_next/static/chunks/04ylibgf-0e6d.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0.vqct1a~k736.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"IconMark\"]\n17:null\n"])</script><script>self.__next_f.push([1,"1c:[[\"$\",\"title\",\"0\",{\"children\":\"web - iii worker\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Outbound HTTP client on the iii bus (web::fetch).\"}],[\"$\",\"meta\",\"2\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"link\",\"3\",{\"rel\":\"canonical\",\"href\":\"https://workers.iii.dev/workers/web\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:title\",\"content\":\"web - iii worker\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:description\",\"content\":\"Outbound HTTP client on the iii bus (web::fetch).\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:url\",\"content\":\"https://workers.iii.dev/workers/web\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:image\",\"content\":\"https://workers.iii.dev/workers/web/opengraph-image?bced171a25d0f70f\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:image:type\",\"content\":\"image/png\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:image:alt\",\"content\":\"iii worker\"}],[\"$\",\"meta\",\"12\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"13\",{\"name\":\"twitter:title\",\"content\":\"web - iii worker\"}],[\"$\",\"meta\",\"14\",{\"name\":\"twitter:description\",\"content\":\"Outbound HTTP client on the iii bus (web::fetch).\"}],[\"$\",\"meta\",\"15\",{\"name\":\"twitter:image\",\"content\":\"https://workers.iii.dev/workers/web/opengraph-image?bced171a25d0f70f\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:image:alt\",\"content\":\"iii worker\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:image:type\",\"content\":\"image/png\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"20\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0n6vou73am7b3.ico\",\"sizes\":\"48x48\",\"type\":\"image/x-icon\"}],[\"$\",\"$L1d\",\"21\",{}]]\n"])</script><script>self.__next_f.push([1,"1e:I[33516,[\"/_next/static/chunks/05w7h3fxzucf~.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/0~_d9nztcm4i3.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\",\"/_next/static/chunks/06i73iynkubsx.js?dpl=dpl_56oQi1b4GitRCmiqWKY2DLN1xkri\"],\"Image\"]\n"])</script><script>self.__next_f.push([1,"14:[\"$\",\"div\",null,{\"className\":\"page-transition\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-end justify-between flex-wrap gap-x-6 gap-y-4 px-9 py-12\",\"children\":[[\"$\",\"div\",null,{\"className\":\"min-w-0\",\"children\":[[\"$\",\"div\",null,{\"className\":\"font-mono text-[11px] uppercase tracking-[0.06em] text-ink-faint mb-3\",\"children\":[\"$\",\"span\",null,{\"className\":\"font-mono text-accent\",\"children\":[\"$$\",[\"$\",\"span\",null,{\"className\":\"text-ink ml-2\",\"children\":\"worker\"}]]}]}],[\"$\",\"div\",null,{\"className\":\"flex items-baseline flex-wrap gap-x-3 gap-y-1\",\"children\":[[\"$\",\"h1\",null,{\"className\":\"font-mono text-[28px] font-medium tracking-[-0.01em] text-ink lowercase\",\"children\":\"web\"}],[\"$\",\"span\",null,{\"className\":\"font-mono text-[11px] uppercase tracking-[0.06em] text-ink-ghost tabular-nums\",\"children\":\"v1.2.3\"}]]}],[\"$\",\"p\",null,{\"className\":\"mt-3 font-mono text-[14px] leading-[1.7] text-ink-faint max-w-[60ch] lowercase\",\"children\":\"Outbound HTTP client on the iii bus (web::fetch).\"}]]}],null]}],[\"$\",\"div\",null,{\"className\":\"px-9 pb-12 flex flex-col gap-y-6\",\"children\":[[\"$\",\"ul\",null,{\"aria-label\":\"Supported platforms\",\"className\":\"flex flex-wrap items-center font-mono text-[12px] gap-x-2 gap-y-1.5 \",\"children\":[[\"$\",\"li\",\"apple\",{\"title\":\"macOS: arm64, x64\",\"className\":\"inline-flex items-center border border-rule bg-bg gap-x-2 px-2 py-1\",\"children\":[[\"$\",\"$L1e\",null,{\"src\":\"/icons/apple_dark.svg\",\"alt\":\"\",\"aria-hidden\":true,\"width\":209,\"height\":256,\"className\":\"shrink-0 [filter:brightness(0)] dark:[filter:brightness(0)_invert(1)] h-4 w-auto\"}],[\"$\",\"span\",null,{\"className\":\"sr-only\",\"children\":[\"macOS\",\": \"]}],[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"arm64 · x64\"}]]}],[\"$\",\"li\",\"linux\",{\"title\":\"Linux: arm64, armv7, x64\",\"className\":\"inline-flex items-center border border-rule bg-bg gap-x-2 px-2 py-1\",\"children\":[[\"$\",\"$L1e\",null,{\"src\":\"/icons/linux.svg\",\"alt\":\"\",\"aria-hidden\":true,\"width\":256,\"height\":295,\"className\":\"shrink-0 [filter:brightness(0)] dark:[filter:brightness(0)_invert(1)] h-4 w-auto\"}],[\"$\",\"span\",null,{\"className\":\"sr-only\",\"children\":[\"Linux\",\": \"]}],[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"arm64 · armv7 · x64\"}]]}],[\"$\",\"li\",\"windows\",{\"title\":\"Windows: arm64, x64, x86\",\"className\":\"inline-flex items-center border border-rule bg-bg gap-x-2 px-2 py-1\",\"children\":[[\"$\",\"$L1e\",null,{\"src\":\"/icons/windows.svg\",\"alt\":\"\",\"aria-hidden\":true,\"width\":88,\"height\":88,\"className\":\"shrink-0 [filter:brightness(0)] dark:[filter:brightness(0)_invert(1)] h-4 w-auto\"}],[\"$\",\"span\",null,{\"className\":\"sr-only\",\"children\":[\"Windows\",\": \"]}],[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"arm64 · x64 · x86\"}]]}]]}],false,[\"$\",\"div\",null,{\"className\":\"grid grid-cols-1 @4xl:grid-cols-[1fr_280px] gap-x-8 gap-y-8\",\"children\":[[\"$\",\"div\",null,{\"className\":\"min-w-0 flex flex-col gap-y-6\",\"children\":[[\"$\",\"nav\",null,{\"aria-label\":\"sections\",\"className\":\"overflow-x-auto\",\"children\":[\"$\",\"div\",null,{\"role\":\"tablist\",\"className\":\"inline-flex border border-rule p-[2px] min-w-max\",\"children\":[[\"$\",\"$L3\",\"readme\",{\"href\":\"/workers/web\",\"scroll\":false,\"role\":\"tab\",\"aria-selected\":false,\"aria-current\":\"$undefined\",\"className\":\"inline-flex items-center font-mono text-[13px] px-3 py-1 lowercase transition-colors bg-transparent text-ink-faint hover:text-ink\",\"children\":\"readme\"}],[\"$\",\"$L3\",\"api\",{\"href\":\"/workers/web?tab=api\",\"scroll\":false,\"role\":\"tab\",\"aria-selected\":false,\"aria-current\":\"$undefined\",\"className\":\"inline-flex items-center font-mono text-[13px] px-3 py-1 lowercase transition-colors bg-transparent text-ink-faint hover:text-ink\",\"children\":\"api reference\"}],[\"$\",\"$L3\",\"skills\",{\"href\":\"/workers/web?tab=skills\",\"scroll\":false,\"role\":\"tab\",\"aria-selected\":true,\"aria-current\":\"page\",\"className\":\"inline-flex items-center font-mono text-[13px] px-3 py-1 lowercase transition-colors bg-ink text-bg\",\"children\":\"skills\"}],[\"$\",\"$L3\",\"agent-context\",{\"href\":\"/workers/web?tab=agent-context\",\"scroll\":false,\"role\":\"tab\",\"aria-selected\":false,\"aria-current\":\"$undefined\",\"className\":\"inline-flex items-center font-mono text-[13px] px-3 py-1 lowercase transition-colors bg-transparent text-ink-faint hover:text-ink\",\"children\":\"agent context\"}],\"$L1f\",\"$L20\"]}]}],false,false,\"$L21\",false,false,false,false]}],\"$L22\"]}]]}]]}]\n"])</script><script>self.__next_f.push([1,":HL[\"https://iii.dev/docs/_mintlify/favicons/motiadev/AFkVbz_UcSL_5jsm/_generated/favicon/apple-touch-icon.png\",\"image\"]\n1f:[\"$\",\"$L3\",\"versions\",{\"href\":\"/workers/web?tab=versions\",\"scroll\":false,\"role\":\"tab\",\"aria-selected\":false,\"aria-current\":\"$undefined\",\"className\":\"inline-flex items-center font-mono text-[13px] px-3 py-1 lowercase transition-colors bg-transparent text-ink-faint hover:text-ink\",\"children\":\"versions\"}]\n20:[\"$\",\"$L3\",\"deps\",{\"href\":\"/workers/web?tab=deps\",\"scroll\":false,\"role\":\"tab\",\"aria-selected\":false,\"aria-current\":\"$undefined\",\"className\":\"inline-flex items-center font-mono text-[13px] px-3 py-1 lowercase transition-colors bg-transparent text-ink-faint hover:text-ink\",\"children\":\"dependencies\"}]\n"])</script><script>self.__next_f.push([1,"22:[\"$\",\"aside\",null,{\"className\":\"flex flex-col gap-y-4 @4xl:sticky @4xl:top-4 @4xl:self-start @4xl:max-h-[calc(100dvh-2rem)] @4xl:overflow-y-auto\",\"children\":[[\"$\",\"div\",null,{\"className\":\"border border-rule bg-bg\",\"children\":[[\"$\",\"div\",null,{\"className\":\"bg-panel px-3.5 py-2.5 border-b border-rule\",\"children\":[\"$\",\"span\",null,{\"className\":\"font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint\",\"children\":\"details\"}]}],[\"$\",\"div\",null,{\"className\":\"p-4 flex flex-col gap-y-2.5\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-baseline justify-between font-mono text-[13px] gap-x-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]\",\"children\":\"version\"}],[\"$\",\"span\",null,{\"className\":\"text-ink truncate text-right lowercase tabular-nums\",\"children\":\"v1.2.3\"}]]}],[\"$\",\"div\",null,{\"className\":\"flex items-baseline justify-between font-mono text-[13px] gap-x-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]\",\"children\":\"type\"}],[\"$\",\"span\",null,{\"className\":\"text-ink truncate text-right lowercase \",\"children\":\"binary\"}]]}],[\"$\",\"div\",null,{\"className\":\"flex items-baseline justify-between font-mono text-[13px] gap-x-3 min-w-0\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]\",\"children\":\"repo\"}],[\"$\",\"a\",null,{\"href\":\"https://github.com/iii-hq/workers\",\"target\":\"_blank\",\"rel\":\"noreferrer\",\"className\":\"inline-flex items-center gap-x-1 font-mono text-[13px] text-ink lowercase hover:text-accent transition-colors min-w-0 text-right justify-end\",\"children\":[[\"$\",\"span\",null,{\"className\":\"truncate\",\"children\":\"iii-hq/workers\"}],[\"$\",\"$L13\",null,{\"ref\":\"$undefined\",\"iconNode\":\"$b:props:children:props:children:1:props:children:0:props:children:1:props:iconNode\",\"className\":\"lucide-external-link size-3 shrink-0\",\"aria-hidden\":true}]]}]]}],\"$undefined\"]}]]}],[\"$\",\"div\",null,{\"className\":\"border border-rule bg-bg\",\"children\":[[\"$\",\"div\",null,{\"className\":\"bg-panel px-3.5 py-2.5 border-b border-rule\",\"children\":[\"$\",\"span\",null,{\"className\":\"font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint\",\"children\":\"skills\"}]}],[\"$\",\"div\",null,{\"className\":\"max-h-[min(60dvh,28rem)] overflow-y-auto\",\"children\":[[\"$\",\"section\",null,{\"aria-labelledby\":\"skills-tree-web\",\"children\":[[\"$\",\"div\",null,{\"id\":\"skills-tree-web\",\"className\":\"flex items-baseline justify-between px-3.5 py-2 bg-paper-2 border-b border-rule-2\",\"children\":[[\"$\",\"span\",null,{\"className\":\"font-mono text-[11px] font-medium uppercase tracking-[0.06em] text-ink-faint\",\"children\":\"files\"}],[\"$\",\"span\",null,{\"className\":\"font-mono text-[11px] uppercase tracking-[0.06em] text-ink-ghost tabular-nums\",\"children\":1}]]}],[\"$\",\"nav\",null,{\"aria-label\":\"skill files\",\"className\":\"px-3 pt-2 pb-3\",\"children\":[\"$\",\"ul\",null,{\"className\":\"flex flex-col gap-y-0.5\",\"children\":[[\"$\",\"li\",\"SKILL.md\",{\"style\":{\"paddingLeft\":0},\"children\":[\"$\",\"$L3\",null,{\"href\":\"/workers/web?tab=skills\u0026skill=SKILL.md\",\"scroll\":false,\"className\":\"block font-mono text-[12px] py-0.5 px-1.5 -mx-1.5 lowercase truncate transition-colors bg-ink text-bg\",\"children\":\"SKILL.md\"}]}]]}]}]]}],null]}]]}],[\"$\",\"div\",null,{\"className\":\"border border-rule bg-bg\",\"children\":[[\"$\",\"div\",null,{\"className\":\"bg-panel px-3.5 py-2.5 border-b border-rule\",\"children\":[\"$\",\"span\",null,{\"className\":\"font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint\",\"children\":\"author\"}]}],[\"$\",\"div\",null,{\"className\":\"p-4 flex flex-col gap-y-2.5\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex items-center gap-x-2 font-mono text-[13px]\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://iii.dev/docs/_mintlify/favicons/motiadev/AFkVbz_UcSL_5jsm/_generated/favicon/apple-touch-icon.png\",\"alt\":\"\",\"className\":\"size-5 object-cover\"}],[\"$\",\"span\",null,{\"className\":\"text-ink lowercase\",\"children\":\"iii\"}],[\"$\",\"$L13\",null,{\"ref\":\"$undefined\",\"iconNode\":[[\"path\",{\"d\":\"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z\",\"key\":\"3c2336\"}],[\"path\",{\"d\":\"m9 12 2 2 4-4\",\"key\":\"dzmm74\"}]],\"className\":\"lucide-badge-check size-3.5 text-accent\"}]]}]}]]}]]}]\n"])</script><script>self.__next_f.push([1,"21:[\"$\",\"section\",null,{\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center justify-between mb-3\",\"children\":[\"$\",\"h2\",null,{\"className\":\"font-mono text-[12px] font-medium uppercase tracking-[0.18em] text-ink-faint\",\"children\":\"skill doc\"}]}],\"$L23\"]}]\n"])</script><script>self.__next_f.push([1,"24:T5453,"])</script><script>self.__next_f.push([1,"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eCallable id:\u003c/strong\u003e \u003ccode class=\"readme-inline-code\"\u003eweb::fetch\u003c/code\u003e — pass to \u003ccode class=\"readme-inline-code\"\u003eagent_trigger { function: \"web::fetch\" }\u003c/code\u003e (NOT the \u003ccode class=\"readme-inline-code\"\u003eiii://...\u003c/code\u003e skill path; that\u0026#39;s docs, not a function id). Use this \u003cstrong\u003einstead of\u003c/strong\u003e \u003ccode class=\"readme-inline-code\"\u003eshell::exec\u003c/code\u003e with \u003ccode class=\"readme-inline-code\"\u003ecurl\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003ewget\u003c/code\u003e for any HTTP request: you get a parsed \u003ccode class=\"readme-inline-code\"\u003e{ ok, status, headers, body }\u003c/code\u003e envelope, enforced size/timeout caps, and server-side SSRF protection a shell \u003ccode class=\"readme-inline-code\"\u003ecurl\u003c/code\u003e doesn\u0026#39;t have.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"decide-fast\" class=\"scroll-mt-20\"\u003eDecide fast\u003c/h1\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eYou want to…\u003c/th\u003e\n\u003cth\u003eCall\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eRead a web page\u003c/strong\u003e (docs, articles)\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{ \"url\": \"https://…\", \"format\": \"markdown\" }\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eGET a page/API\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{ \"url\": \"https://…\" }\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eParse a JSON API response\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{ \"url\": \"https://…\", \"response_format\": \"json\" }\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003ePOST/PUT JSON\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{ \"url\": \"…\", \"method\": \"post\", \"json\": { … } }\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSend a raw body / form\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{ \"url\": \"…\", \"method\": \"post\", \"body\": \"…\", \"headers\": { \"content-type\": \"…\" } }\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDownload binary\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{ \"url\": \"…\", \"response_format\": \"base64\" }\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eRun a shell \u003ccode class=\"readme-inline-code\"\u003ecurl\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e❌ stop — use \u003ccode class=\"readme-inline-code\"\u003eweb::fetch\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\u003ch1 id=\"minimal-call\" class=\"scroll-mt-20\"\u003eMinimal call\u003c/h1\u003e\n\u003cpre\u003e\u003ccode\u003e// agent_trigger { function: \u0026quot;web::fetch\u0026quot;, payload: { \u0026quot;url\u0026quot;: \u0026quot;https://api.github.com/zen\u0026quot; } }\n// → { \u0026quot;ok\u0026quot;: true, \u0026quot;status\u0026quot;: 200, \u0026quot;body\u0026quot;: \u0026quot;Design for failure.\u0026quot;, ... }\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThat\u0026#39;s the whole T0 path: a URL is the only required field. Everything else has a default.\u003c/p\u003e\n\u003ch1 id=\"read-the-result-correctly-the-1-trap\" class=\"scroll-mt-20\"\u003eRead the result correctly (the #1 trap)\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode class=\"readme-inline-code\"\u003eok\u003c/code\u003e tells you if the request COMPLETED, not whether the server was happy.\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA 404 or 500 is a \u003cstrong\u003esuccessful fetch\u003c/strong\u003e → \u003ccode class=\"readme-inline-code\"\u003eok: true\u003c/code\u003e, \u003ccode class=\"readme-inline-code\"\u003estatus: 404\u003c/code\u003e. Branch on \u003ccode class=\"readme-inline-code\"\u003estatus\u003c/code\u003e for HTTP outcomes.\u003c/li\u003e\n\u003cli\u003e\u003ccode class=\"readme-inline-code\"\u003eok: false\u003c/code\u003e means the request never produced a response (bad URL, blocked host, timeout, transport failure). Branch on \u003ccode class=\"readme-inline-code\"\u003eerror\u003c/code\u003e for those.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre\u003e\u003ccode\u003eif (!r.ok) → fetch failed; look at r.error (see table below)\nelse if (r.status\u0026gt;=400) → server returned an HTTP error; look at r.status / r.body\nelse → success; use r.body or r.json\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDo \u003cstrong\u003enot\u003c/strong\u003e treat \u003ccode class=\"readme-inline-code\"\u003eok: true\u003c/code\u003e as \u0026quot;2xx\u0026quot;. Always check \u003ccode class=\"readme-inline-code\"\u003estatus\u003c/code\u003e too.\u003c/p\u003e\n\u003cp\u003eOne exception: in page-reading mode (\u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e set), an \u003ccode class=\"readme-inline-code\"\u003eimage/*\u003c/code\u003e response returns the image itself plus a one-line text summary instead of this envelope — there is no top-level \u003ccode class=\"readme-inline-code\"\u003eok\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003estatus\u003c/code\u003e to branch on (see \u0026quot;Images in page-reading mode\u0026quot; below).\u003c/p\u003e\n\u003ch1 id=\"request-fields\" class=\"scroll-mt-20\"\u003eRequest fields\u003c/h1\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eDefault\u003c/th\u003e\n\u003cth\u003eNotes\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eurl\u003c/code\u003e \u003cem\u003e(required)\u003c/em\u003e\u003c/td\u003e\n\u003ctd\u003e—\u003c/td\u003e\n\u003ctd\u003eabsolute \u003ccode class=\"readme-inline-code\"\u003ehttp(s)://\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003emethod\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eGET\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ecase-insensitive (\u003ccode class=\"readme-inline-code\"\u003e\"post\"\u003c/code\u003e works); GET HEAD POST PUT PATCH DELETE OPTIONS\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eheaders\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{}\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eobject; keys as you write them\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e—\u003c/td\u003e\n\u003ctd\u003estructured payload; auto-stringified + sets \u003ccode class=\"readme-inline-code\"\u003econtent-type: application/json\u003c/code\u003e. \u003cstrong\u003eWins over \u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e.\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e—\u003c/td\u003e\n\u003ctd\u003eraw string body; use for non-JSON. Ignored on GET/HEAD.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eresponse_format\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e\"text\"\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e\"text\"\u003c/code\u003e | \u003ccode class=\"readme-inline-code\"\u003e\"base64\"\u003c/code\u003e (binary) | \u003ccode class=\"readme-inline-code\"\u003e\"json\"\u003c/code\u003e (also parses into \u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e—\u003c/td\u003e\n\u003ctd\u003epage-reading mode: \u003ccode class=\"readme-inline-code\"\u003e\"markdown\"\u003c/code\u003e (HTML→Markdown) | \u003ccode class=\"readme-inline-code\"\u003e\"text\"\u003c/code\u003e (HTML→plain text) | \u003ccode class=\"readme-inline-code\"\u003e\"html\"\u003c/code\u003e (raw). Sends a browser UA + matching \u003ccode class=\"readme-inline-code\"\u003eAccept\u003c/code\u003e; retries once with the honest UA on a Cloudflare challenge; images come back viewable. Forces text transport (\u003ccode class=\"readme-inline-code\"\u003eresponse_format\u003c/code\u003e ignored).\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etimeout_ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e30000\u003c/td\u003e\n\u003ctd\u003eclamped DOWN to the worker ceiling (120000 by default); can\u0026#39;t raise past it\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003emax_bytes\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e5 MiB (256 KiB in \u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e mode)\u003c/td\u003e\n\u003ctd\u003eraw fetches default to the 5 MiB ceiling; page-reading mode (\u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e set) uses a context-safe 256 KiB. Pass an explicit value to override (up to the 5 MiB ceiling). Over-cap body is truncated, not errored\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003efollow_redirects\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etrue\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eeach hop re-checked against the SSRF blocklist\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\u003ch1 id=\"content-pipeline\" class=\"scroll-mt-20\"\u003eContent pipeline\u003c/h1\u003e\n\u003cp\u003eFour request controls for page-reading mode (\u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e set):\u003c/p\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eField\u003c/th\u003e\n\u003cth\u003eEffect\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003econtent_filter\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003e{ type: \"pruning\"|\"bm25\", query?, threshold?, threshold_type?, min_word_threshold? }\u003c/code\u003e — filters \u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e to signal content. Pruning default threshold 0.48; BM25 default 1.0. \u003ccode class=\"readme-inline-code\"\u003equery\u003c/code\u003e falls back to page \u003ccode class=\"readme-inline-code\"\u003e\u003ctitle\u003e\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003e\u003cmeta description\u003e\u003c/code\u003e if omitted.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etarget_elements\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eRestrict body to matching regions. Selector subset: tag, \u003ccode class=\"readme-inline-code\"\u003e.class\u003c/code\u003e, \u003ccode class=\"readme-inline-code\"\u003e#id\u003c/code\u003e — other forms are silently ignored.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eexcluded_tags\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eDrop matching elements before rendering (e.g. \u003ccode class=\"readme-inline-code\"\u003e[\"nav\", \"footer\"]\u003c/code\u003e).\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003einclude_links\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etrue\u003c/code\u003e → adds \u003ccode class=\"readme-inline-code\"\u003elinks: { internal, external }\u003c/code\u003e arrays (absolute URLs, classified by host).\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003einclude_media\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etrue\u003c/code\u003e → adds \u003ccode class=\"readme-inline-code\"\u003emedia: { images, videos, audios }\u003c/code\u003e arrays (absolute URLs).\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\u003cp\u003eWhen \u003ccode class=\"readme-inline-code\"\u003econtent_filter\u003c/code\u003e is set, \u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e \u003cstrong\u003eis\u003c/strong\u003e the filtered output — no separate field. Feed it straight to a model.\u003c/p\u003e\n\u003ch1 id=\"response\" class=\"scroll-mt-20\"\u003eResponse\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003eSuccess\u003c/strong\u003e (\u003ccode class=\"readme-inline-code\"\u003eok: true\u003c/code\u003e) — returned for \u003cem\u003eany\u003c/em\u003e completed response, 2xx through 5xx:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e{\n \u0026quot;ok\u0026quot;: true,\n \u0026quot;status\u0026quot;: 200,\n \u0026quot;status_text\u0026quot;: \u0026quot;OK\u0026quot;,\n \u0026quot;headers\u0026quot;: { \u0026quot;content-type\u0026quot;: \u0026quot;application/json\u0026quot;, ... }, // keys LOWER-CASED; set-cookie joined with \u0026quot;, \u0026quot;\n \u0026quot;body\u0026quot;: \u0026quot;\u0026lt;utf8 text | base64 when response_format=base64\u0026gt;\u0026quot;,\n \u0026quot;json\u0026quot;: { ... }, // only when response_format=\u0026quot;json\u0026quot; AND parse succeeded AND not truncated\n \u0026quot;parse_error\u0026quot;: \u0026quot;…\u0026quot;, // only when response_format=\u0026quot;json\u0026quot; AND JSON.parse failed (body still set)\n \u0026quot;response_format\u0026quot;: \u0026quot;json\u0026quot;,\n \u0026quot;bytes_truncated\u0026quot;: false, // true when body hit max_bytes (NOT an error)\n \u0026quot;redirect_chain\u0026quot;: [\u0026quot;https://…/a\u0026quot;], // omitted when no redirects\n \u0026quot;content_type\u0026quot;: \u0026quot;text/html\u0026quot;, // only in page-reading mode (format set)\n \u0026quot;transformed\u0026quot;: \u0026quot;markdown\u0026quot; // only when an HTML transform actually ran\n}\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eFailure\u003c/strong\u003e (\u003ccode class=\"readme-inline-code\"\u003eok: false\u003c/code\u003e) — the fetch never produced a response:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e{ \u0026quot;ok\u0026quot;: false, \u0026quot;error\u0026quot;: \u0026quot;blocked_host\u0026quot;, \u0026quot;message\u0026quot;: \u0026quot;…\u0026quot; } // no status field\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"error-cause-fix\" class=\"scroll-mt-20\"\u003eerror → cause → fix\u003c/h2\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e\u003ccode class=\"readme-inline-code\"\u003eerror\u003c/code\u003e\u003c/th\u003e\n\u003cth\u003eCause\u003c/th\u003e\n\u003cth\u003eFix\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003einvalid_payload\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ePayload failed schema (bad \u003ccode class=\"readme-inline-code\"\u003emethod\u003c/code\u003e, wrong types). \u003ccode class=\"readme-inline-code\"\u003emessage\u003c/code\u003e lists the bad fields.\u003c/td\u003e\n\u003ctd\u003eCorrect the named fields.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003einvalid_url\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eurl\u003c/code\u003e isn\u0026#39;t a parseable absolute \u003ccode class=\"readme-inline-code\"\u003ehttp(s)://\u003c/code\u003e URL.\u003c/td\u003e\n\u003ctd\u003ePass a full absolute URL incl. scheme.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003eblocked_host\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eTarget resolves to a private / link-local / cloud-metadata IP (SSRF guard).\u003c/td\u003e\n\u003ctd\u003eDon\u0026#39;t target internal/metadata hosts. For loopback in dev, the operator sets \u003ccode class=\"readme-inline-code\"\u003eweb.allow_loopback\u003c/code\u003e.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etimeout\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eSlower than \u003ccode class=\"readme-inline-code\"\u003etimeout_ms\u003c/code\u003e (30 s default; 120 s ceiling).\u003c/td\u003e\n\u003ctd\u003eRaise \u003ccode class=\"readme-inline-code\"\u003etimeout_ms\u003c/code\u003e (up to ceiling) or shrink work via \u003ccode class=\"readme-inline-code\"\u003emax_bytes\u003c/code\u003e.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etoo_many_redirects\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eMore than \u003ccode class=\"readme-inline-code\"\u003emax_redirects\u003c/code\u003e (5) hops.\u003c/td\u003e\n\u003ctd\u003eUse the final URL directly, or set \u003ccode class=\"readme-inline-code\"\u003efollow_redirects: false\u003c/code\u003e and read the \u003ccode class=\"readme-inline-code\"\u003elocation\u003c/code\u003e header.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode class=\"readme-inline-code\"\u003etransport_error\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eConnection refused/reset, TLS failure, DNS failure, or a redirect \u003ccode class=\"readme-inline-code\"\u003eLocation\u003c/code\u003e that won\u0026#39;t parse.\u003c/td\u003e\n\u003ctd\u003eCheck host/port/cert; retry if transient.\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\u003cp\u003eThere is \u003cstrong\u003eno \u003ccode class=\"readme-inline-code\"\u003etoo_large\u003c/code\u003e error\u003c/strong\u003e — oversize responses come back \u003ccode class=\"readme-inline-code\"\u003eok: true\u003c/code\u003e with \u003ccode class=\"readme-inline-code\"\u003ebytes_truncated: true\u003c/code\u003e. Branch on the flag, not on an error.\u003c/p\u003e\n\u003ch1 id=\"page-reading-vs-api-fetch\" class=\"scroll-mt-20\"\u003ePage reading vs API fetch\u003c/h1\u003e\n\u003cp\u003eTwo orthogonal knobs — pick ONE:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode class=\"readme-inline-code\"\u003eresponse_format\u003c/code\u003e\u003c/strong\u003e = transport encoding for APIs/binaries (\u003ccode class=\"readme-inline-code\"\u003etext\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003ebase64\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e). The body is returned untouched.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e\u003c/strong\u003e = page-reading mode (\u003ccode class=\"readme-inline-code\"\u003emarkdown\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003etext\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003ehtml\u003c/code\u003e). The request goes out with a browser User-Agent + format-matched \u003ccode class=\"readme-inline-code\"\u003eAccept\u003c/code\u003e header, and \u003ccode class=\"readme-inline-code\"\u003etext/html\u003c/code\u003e responses are transformed (\u003ccode class=\"readme-inline-code\"\u003emarkdown\u003c/code\u003e is the right default for reading pages — far fewer tokens than raw HTML). Non-HTML bodies pass through unchanged. If Cloudflare answers \u003ccode class=\"readme-inline-code\"\u003e403\u003c/code\u003e with a challenge, the worker retries once with its honest UA (beats the UA-fingerprint rule only, not full JS challenges). For large pages, lower \u003ccode class=\"readme-inline-code\"\u003emax_bytes\u003c/code\u003e — conversion runs on the capped body.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eDon\u0026#39;t combine them: when \u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e is set, \u003ccode class=\"readme-inline-code\"\u003eresponse_format\u003c/code\u003e is ignored (treated as \u003ccode class=\"readme-inline-code\"\u003e\"text\"\u003c/code\u003e).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImages in page-reading mode:\u003c/strong\u003e a viewable \u003ccode class=\"readme-inline-code\"\u003eimage/*\u003c/code\u003e response returns the actual image (plus a one-line text summary like \u003ccode class=\"readme-inline-code\"\u003eImage fetched (image/png, 8123 bytes)\u003c/code\u003e) instead of the JSON envelope — providers that support tool-result images (Anthropic) show it to the model; others see the text line. \u0026quot;Viewable\u0026quot; means jpeg/png/gif/webp, 2xx status, complete (not truncated), and non-empty — anything else (svg, error pages served as images, truncated bytes) comes back as the normal envelope with \u003ccode class=\"readme-inline-code\"\u003eresponse_format: \"base64\"\u003c/code\u003e so a hostile image can\u0026#39;t fail the provider request. Without \u003ccode class=\"readme-inline-code\"\u003eformat\u003c/code\u003e, use \u003ccode class=\"readme-inline-code\"\u003eresponse_format: \"base64\"\u003c/code\u003e as before.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTransform bounds:\u003c/strong\u003e the HTML→markdown/text conversion runs only on bodies ≤ \u003ccode class=\"readme-inline-code\"\u003eweb.max_transform_bytes\u003c/code\u003e (1 MiB default) — larger pages come back raw with \u003ccode class=\"readme-inline-code\"\u003etransformed\u003c/code\u003e unset (lower \u003ccode class=\"readme-inline-code\"\u003emax_bytes\u003c/code\u003e to read huge pages). If the body was truncated at \u003ccode class=\"readme-inline-code\"\u003emax_bytes\u003c/code\u003e, the transformed text ends with a visible \u003ccode class=\"readme-inline-code\"\u003e[Content truncated at max_bytes — …]\u003c/code\u003e line.\u003c/p\u003e\n\u003ch1 id=\"rules-that-save-a-turn\" class=\"scroll-mt-20\"\u003eRules that save a turn\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e vs \u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e: set exactly one.\u003c/strong\u003e \u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e wins if both are present and forces \u003ccode class=\"readme-inline-code\"\u003econtent-type: application/json\u003c/code\u003e. Use \u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e + your own \u003ccode class=\"readme-inline-code\"\u003econtent-type\u003c/code\u003e for form/text/XML.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eGET/HEAD ignore the body.\u003c/strong\u003e Putting \u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e on a GET sends nothing — switch \u003ccode class=\"readme-inline-code\"\u003emethod\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode class=\"readme-inline-code\"\u003eresponse_format: \"json\"\u003c/code\u003e can succeed with \u003ccode class=\"readme-inline-code\"\u003eparse_error\u003c/code\u003e.\u003c/strong\u003e Non-JSON (or truncated) bodies return \u003ccode class=\"readme-inline-code\"\u003eok: true\u003c/code\u003e, no \u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e, \u003ccode class=\"readme-inline-code\"\u003eparse_error\u003c/code\u003e set; the raw text is still in \u003ccode class=\"readme-inline-code\"\u003ebody\u003c/code\u003e. Check for \u003ccode class=\"readme-inline-code\"\u003ejson\u003c/code\u003e before reading it.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCaps only go down.\u003c/strong\u003e \u003ccode class=\"readme-inline-code\"\u003etimeout_ms\u003c/code\u003e/\u003ccode class=\"readme-inline-code\"\u003emax_bytes\u003c/code\u003e above the worker ceiling are clamped; you can\u0026#39;t request more than the operator allows.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eResponse header keys are lower-cased.\u003c/strong\u003e Index \u003ccode class=\"readme-inline-code\"\u003eheaders[\"content-type\"]\u003c/code\u003e, never \u003ccode class=\"readme-inline-code\"\u003e\"Content-Type\"\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAuth headers don\u0026#39;t survive a cross-origin redirect\u003c/strong\u003e (see below) — for an authed endpoint, hit the final URL directly.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"ssrf-guard-server-side-not-optional\" class=\"scroll-mt-20\"\u003eSSRF guard (server-side, not optional)\u003c/h1\u003e\n\u003cp\u003eDNS is resolved \u003cstrong\u003eonce\u003c/strong\u003e, every resolved address is checked, then the request is dialed at the \u003cstrong\u003evalidated IP\u003c/strong\u003e (pinned \u003ccode class=\"readme-inline-code\"\u003elookup\u003c/code\u003e + TLS \u003ccode class=\"readme-inline-code\"\u003eservername\u003c/code\u003e) — so the IP that passed the check is the IP connected to (no DNS-rebind window).\u003c/p\u003e\n\u003cp\u003eBlocked \u003cstrong\u003eunconditionally\u003c/strong\u003e → \u003ccode class=\"readme-inline-code\"\u003eerror: \"blocked_host\"\u003c/code\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePrivate RFC1918 (\u003ccode class=\"readme-inline-code\"\u003e10/8\u003c/code\u003e, \u003ccode class=\"readme-inline-code\"\u003e172.16/12\u003c/code\u003e, \u003ccode class=\"readme-inline-code\"\u003e192.168/16\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003eLink-local incl. \u003cstrong\u003ecloud metadata\u003c/strong\u003e \u003ccode class=\"readme-inline-code\"\u003e169.254.169.254\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eIPv6 ULA (\u003ccode class=\"readme-inline-code\"\u003efc00::/7\u003c/code\u003e), link-local (\u003ccode class=\"readme-inline-code\"\u003efe80::/10\u003c/code\u003e), multicast\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode class=\"readme-inline-code\"\u003e::ffff:\u003c/code\u003e-mapped IPv4\u003c/strong\u003e in both dotted (\u003ccode class=\"readme-inline-code\"\u003e::ffff:169.254.169.254\u003c/code\u003e) and hex (\u003ccode class=\"readme-inline-code\"\u003e::ffff:a9fe:a9fe\u003c/code\u003e) forms\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLoopback (\u003ccode class=\"readme-inline-code\"\u003e127.0.0.0/8\u003c/code\u003e, \u003ccode class=\"readme-inline-code\"\u003e::1\u003c/code\u003e, \u003ccode class=\"readme-inline-code\"\u003elocalhost\u003c/code\u003e) is \u003cstrong\u003eallowed by default\u003c/strong\u003e (dev convenience); operators flip \u003ccode class=\"readme-inline-code\"\u003eweb.allow_loopback: false\u003c/code\u003e for prod, after which loopback returns \u003ccode class=\"readme-inline-code\"\u003eblocked_host\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eOn each 3xx the \u003ccode class=\"readme-inline-code\"\u003eLocation\u003c/code\u003e is re-resolved and \u003cstrong\u003ere-validated\u003c/strong\u003e before following, and \u003ccode class=\"readme-inline-code\"\u003eAuthorization\u003c/code\u003e / \u003ccode class=\"readme-inline-code\"\u003eCookie\u003c/code\u003e / \u003ccode class=\"readme-inline-code\"\u003eProxy-Authorization\u003c/code\u003e are \u003cstrong\u003estripped\u003c/strong\u003e when the redirect changes host or downgrades \u003ccode class=\"readme-inline-code\"\u003ehttps → http\u003c/code\u003e.\u003c/p\u003e\n\u003ch1 id=\"examples\" class=\"scroll-mt-20\"\u003eExamples\u003c/h1\u003e\n\u003cpre\u003e\u003ccode\u003e// Read a documentation page as Markdown\n{ \u0026quot;url\u0026quot;: \u0026quot;https://docs.example.com/guide\u0026quot;, \u0026quot;format\u0026quot;: \u0026quot;markdown\u0026quot; }\n// → { ok:true, status:200, body:\u0026quot;# Guide\\n\\n…\u0026quot;, transformed:\u0026quot;markdown\u0026quot;, content_type:\u0026quot;text/html\u0026quot; }\n\n// Parse a JSON API\n{ \u0026quot;url\u0026quot;: \u0026quot;https://api.example.com/status\u0026quot;, \u0026quot;response_format\u0026quot;: \u0026quot;json\u0026quot; }\n// → { ok:true, status:200, json:{ healthy:true } }\n\n// POST JSON (content-type set for you)\n{ \u0026quot;url\u0026quot;: \u0026quot;https://api.example.com/things\u0026quot;, \u0026quot;method\u0026quot;: \u0026quot;post\u0026quot;,\n \u0026quot;json\u0026quot;: { \u0026quot;name\u0026quot;: \u0026quot;x\u0026quot; }, \u0026quot;response_format\u0026quot;: \u0026quot;json\u0026quot; }\n\n// Authenticated GET (auth stays only because there's no cross-host redirect)\n{ \u0026quot;url\u0026quot;: \u0026quot;https://api.example.com/me\u0026quot;,\n \u0026quot;headers\u0026quot;: { \u0026quot;authorization\u0026quot;: \u0026quot;Bearer TOKEN\u0026quot; }, \u0026quot;response_format\u0026quot;: \u0026quot;json\u0026quot; }\n\n// Bounded download of a maybe-large file\n{ \u0026quot;url\u0026quot;: \u0026quot;https://example.com/big.log\u0026quot;, \u0026quot;max_bytes\u0026quot;: 65536 }\n// → { ok:true, body:\u0026quot;\u0026lt;first 64 KiB\u0026gt;\u0026quot;, bytes_truncated:true }\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"authoritative-schema\" class=\"scroll-mt-20\"\u003eAuthoritative schema\u003c/h1\u003e\n\u003cp\u003eThis page covers behavior the schema can\u0026#39;t. For exact field types, call\n\u003ccode class=\"readme-inline-code\"\u003eengine::functions::info { function_id: \"web::fetch\" }\u003c/code\u003e — the live schema wins if it ever disagrees with this page.\u003c/p\u003e\n\u003ch1 id=\"related\" class=\"scroll-mt-20\"\u003eRelated\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"iii://shell\"\u003e\u003ccode class=\"readme-inline-code\"\u003eshell/index\u003c/code\u003e\u003c/a\u003e — local filesystem + process ops; \u003ccode class=\"readme-inline-code\"\u003eweb::fetch\u003c/code\u003e is the network counterpart (use it, not \u003ccode class=\"readme-inline-code\"\u003eshell::exec curl\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"iii://sandbox\"\u003e\u003ccode class=\"readme-inline-code\"\u003esandbox/index\u003c/code\u003e\u003c/a\u003e — code \u003cem\u003einside\u003c/em\u003e a sandbox reaches the host engine via the boot-time \u003ccode class=\"readme-inline-code\"\u003eIII_ENGINE_URL\u003c/code\u003e rewrite, not \u003ccode class=\"readme-inline-code\"\u003eweb::fetch\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n"])</script><script>self.__next_f.push([1,"23:[\"$\",\"div\",null,{\"className\":\"border border-rule bg-bg\",\"children\":[[\"$\",\"div\",null,{\"className\":\"bg-panel px-3.5 py-2.5 border-b border-rule flex flex-col gap-y-1\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-x-2 min-w-0\",\"children\":[[\"$\",\"h3\",null,{\"className\":\"font-mono text-[16px] font-semibold tracking-[-0.01em] text-ink truncate min-w-0\",\"children\":\"web\"}],[\"$\",\"span\",null,{\"className\":\"inline-flex items-center border border-rule-2 px-1.5 pt-0.5 pb-0.4 font-mono text-[11px] uppercase tracking-[0.1em] font-medium bg-bg text-ink-faint leading-1.1\",\"children\":\"index\"}]]}],[\"$\",\"div\",null,{\"className\":\"font-mono text-[11px] uppercase tracking-[0.06em] text-ink-ghost truncate\",\"children\":\"SKILL.md\"}]]}],[\"$\",\"div\",null,{\"className\":\"px-7 py-6 readme-prose font-mono\",\"dangerouslySetInnerHTML\":{\"__html\":\"$24\"}}]]}]\n"])</script></body></html>