Already using the Anthropic or OpenAI SDK? Ship your agent to production in one call.

See the quickstart

Jettson HTTP

Outbound HTTP requests with SSRF guards. The right tool when the data lives behind an API, not a web page.

When the data an agent needs lives behind an HTTP API — Stripe, GitHub, Slack, your own services — it reaches out with jettson_http_request. Private IP ranges and Jettson-owned domains are refused at the proxy boundary.

jettson_http_request

| Field | Type | Description | | --- | --- | --- | | method (required) | string | HTTP method. GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. | | url (required) | string | Absolute http(s) URL. | | headers | object | Optional request headers. String values only. | | body | string | Optional UTF-8 request body. JSON-stringify structured data before passing. | | timeout_seconds | number | Per-call timeout (default 30). Capped server-side at 30. |

Returns:

json
{
  "status_code": 200,
  "headers": { "content-type": "application/json", "..." },
  "body": "{ \"items\": [...] }",
  "truncated": false
}

truncated: true if the response body exceeded 100 KB. The first 100 KB is returned; for larger payloads use jettson_shell_run with curl (subject to the shell's 10 MB cap).

What's blocked

  • Non-http(s) schemes (no file://, no ssh://, no data:)
  • Hostnames resolving to RFC 1918 / loopback / link-local / multicast / reserved IP space
  • Any hostname ending in .jettson.dev (no calling our own API as yourself)

Blocked requests return:

json
{ "error": "Jettson HTTP rejected the request: destination resolves to a private/internal address." }

The SSRF guard runs against DNS resolution before the request leaves the container — there's no way to bypass it via DNS rebinding either.

Examples

Calling a public API

text
jettson_http_request({
  method: "GET",
  url: "https://api.github.com/repos/jettson/jettson-runtime",
  headers: { "Authorization": "token <token-from-task-context>", "Accept": "application/vnd.github.v3+json" }
})

POSTing to a webhook

text
jettson_http_request({
  method: "POST",
  url: "https://hooks.slack.com/services/T.../B.../...",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: "Triage summary: 5 events, 1 critical." })
})

Walking pagination

The Mind handles pagination itself — each jettson_http_request call gets a 100KB response and the loop continues until the cursor runs out.

text
1. jettson_http_request({ method: "GET", url: "https://api.example.com/items?limit=100" })
2. (Mind sees `next_cursor` in body, repeats with `?limit=100&cursor=...`)
3. (...until no next_cursor)

Headers worth knowing

Jettson HTTP follows redirects by default (up to the underlying library's limit, usually 30). To inspect the redirect chain, send HEAD first or set a custom header — there's no follow_redirects: false knob today.

The Content-Length of the request body is set automatically from the body parameter — don't set it manually.

Failure modes

| Situation | Result | | --- | --- | | Bad scheme / not http(s) | error: "only http(s) URLs are permitted." | | DNS resolution → private IP | error: "destination resolves to a private/internal address." | | Hostname is *.jettson.dev | error: "destination host is not allowed." | | Connection refused | error: "could not connect to the destination." | | Wall-clock timeout (30s) | error: "request timed out after 30s." | | Response > 100 KB | Body truncated; truncated: true. Use jettson_shell_run for larger pulls. |

The HTTP tool doesn't retry. If the agent prompts asks for retries, the Mind will loop — but jettson_http_request itself is one-shot.