Adding HTTP Endpoints to a TypeScript Golem Agent
SkillAI & modelsLets your agent turn a TypeScript Golem agent into a web service by exposing its methods at a URL.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Adding HTTP Endpoints to a TypeScript Golem Agent skill
About this skill
Exposing a TypeScript Golem agent over HTTP. Use when the user asks to add HTTP endpoints, mount an agent to a URL path, or expose agent methods as a REST API.
What this skill tells your AI
The instructions your AI receives, as published by golemcloud/golem in golem-skills/skills/ts/golem-add-http-endpoint-ts/SKILL.md and read by ahel’s review.
Overview
Golem agents can be exposed over HTTP using code-first route definitions. This involves:
- Adding an
httpmount todefineAgent(...)withhttp.mount(...) - Attaching
http: http.<verb>(...)endpoints to methods declared withmethod(...) - Adding an
httpApideployment section togolem.yaml(load thegolem-configure-api-domainskill)
Related Skills
| Skill | When to Load |
|---|---|
golem-http-params-ts | Path/query/header variable mapping, body mapping, supported types, response mapping |
golem-make-http-request-ts | Making outgoing HTTP requests from agent code, especially when calling other Golem agent endpoints (required for correct JSON body formatting) |
golem-add-http-auth-ts | Enabling authentication and receiving Principal |
golem-add-cors-ts | Configuring CORS allowed origins |
golem-configure-api-domain | Setting up httpApi in golem.yaml, security schemes, domain deployments, and subdomain versus domain choices |
Steps
- Add
http: http.mount(...)todefineAgent(...) - Add
http: http.<verb>(...)to the methods you want to expose - Add
httpApideployment togolem.yaml(seegolem-configure-api-domainskill) - Build and deploy
Mount Path
The http field on defineAgent(...) defines the base HTTP path via http.mount(...). Path variables in {braces} map to the agent's id record fields:
import { z } from 'zod';
import { defineAgent, method, http } from '@golemcloud/golem-ts-sdk';
export const TaskAgent = defineAgent({
name: 'TaskAgent',
id: { name: z.string() },
http: http.mount('/api/tasks/{name}'),
methods: {
// ...
},
});
Rules:
- Path must start with
/ - Every
idfield must appear as a{variable}in the mount path (enforced at compile time) - Every
{variable}must match anidfield name - Catch-all
{*rest}variables are not allowed in mount paths
The mount path template is template-literal typed: a {var} that does not match an id field (or an id field with no matching {var}) is a tsc error, not just a runtime failure. Passing a dynamically built (non-literal) string widens to string and defers to the runtime checks.
Endpoint Declaration
Attach an http endpoint to a method with one of the verb builders (http.get, http.post, http.put, http.del, http.patch, or http.custom):
methods: {
listItems: method({ input: {}, returns: z.array(Item), http: http.get('/items') }),
createItem: method({
input: { name: z.string(), count: z.number() },
returns: Item,
http: http.post('/items'),
}),
updateItem: method({
input: { id: z.string(), name: z.string() },
returns: Item,
http: http.put('/items/{id}'),
}),
deleteItem: method({ input: { id: z.string() }, returns: z.void(), http: http.del('/items/{id}') }),
patchItem: method({
input: { id: z.string(), patch: PatchData },
returns: Item,
http: http.custom('PATCH', '/items/{id}'),
}),
}
Endpoint paths are relative to the mount path. To expose a method under multiple routes, pass an array: http: [http.get('/items'), http.get('/all')].
For details on how path variables, query parameters, headers, and request bodies map to method inputs, load the golem-http-params-ts skill.
Durable Stream Route Customization
For a method with stream inputs or outputs, pass durableStreams in the endpoint options:
http: http.post('/events', {
durableStreams: {
slots: [
{ source: 'input', slot: 'input', name: 'uploads' },
{
source: 'output',
slot: '$result',
name: 'events',
contentType: 'application/vnd.example.events',
},
],
allowExternalWrites: true,
allowStreamDelete: false,
allowInvocationDelete: false,
load: {
maxConcurrentReadersPerStream: 8,
maxAppendRequestsPerSecondPerStream: 25,
},
},
}),
sourceandslotselect canonical top-level stream slots.$resultis the only implicit result selector.namechanges the public URL and OpenAPI name; the canonical name is no longer accepted in the public URL.contentTypeis allowed only for a direct byte stream (stream<u8>) and must be a concrete non-text, non-JSON MIME type without parameters or wildcards. JSON-shaped streams stayapplication/json; do not usetext/plainforstream<string>. SSE still usestext/event-streamand base64 data.- The three
allow*options default totrue. Setting one tofalseremoves that protocol operation and produces405with an exactAllowheader. - The live-reader limit is 1 through 16 and applies to long-poll and SSE. The append limit must be positive and cannot be set when external writes are disabled. A route-local limit rejection is
429withRetry-After: 1. - Limits are maintained per route and worker-service node, not as a cluster-wide quota.
Phantom Agents
Set phantomAgent: true on the mount to create a fresh ephemeral agent instance for each HTTP request. This enables fully parallel request processing:
export const GatewayAgent = defineAgent({
name: 'GatewayAgent',
id: { name: z.string() },
http: http.mount('/gateway/{name}', { phantomAgent: true }),
methods: { /* each HTTP request gets its own agent instance */ },
});
Return Type to HTTP Response Mapping
Golem maps a method's returned value to HTTP status codes and response bodies according to the table below. This mapping is currently not configurable. In the TypeScript SDK the return shape is declared by the method's returns schema.
Returned value / returns schema | HTTP Status | Response Body |
|---|---|---|
z.void() / no value | 204 No Content | empty |
any schema T | 200 OK | JSON-serialized T |
T.nullable() / T.optional() | 200 OK if value, 404 Not Found if null / undefined | JSON T or empty |
s.result(ok, err) returning Result.ok / Result.err | 200 OK if Ok, 500 Internal Server Error if Err | JSON ok or JSON err |
s.unstructuredBinary() | 200 OK | Raw binary with Content-Type |
Complete Example
import { z } from 'zod';
import { defineAgent, method, http, s, Result } from '@golemcloud/golem-ts-sdk';
const Task = z.object({ id: z.string(), title: z.string(), done: z.boolean() });
type Task = z.infer<typeof Task>;
export const TaskAgent = defineAgent({
name: 'TaskAgent',
id: { name: z.string() },
http: http.mount('/task-agents/{name}'),
methods: {
getTasks: method({ input: {}, returns: z.array(Task), http: http.get('/tasks') }),
createTask: method({ input: { title: z.string() }, returns: Task, http: http.post('/tasks') }),
getTask: method({ input: { id: z.string() }, returns: Task.nullable(), http: http.get('/tasks/{id}') }),
completeTask: method({
input: { id: z.string() },
returns: s.result(Task, z.object({ error: z.string() })),
http: http.post('/tasks/{id}/complete'),
}),
},
});
export const TaskAgentImpl = TaskAgent.implement({
init: () => ({ tasks: [] as Task[] }),
methods: {
getTasks() {
return this.tasks;
},
createTask({ title }) {
const task: Task = { id: String(this.tasks.length + 1), title, done: false };
this.tasks.push(task);
return task;
},
getTask({ id }) {
return this.tasks.find((t) => t.id === id) ?? null;
},
completeTask({ id }) {
const task = this.tasks.find((t) => t.id === id);
if (!task) return Result.err({ error: 'not found' });
task.done = true;
return Result.ok(task);
},
},
});
# golem.yaml (add to existing file)
httpApi:
deployments:
local:
- subdomain: my-app # resolves to my-app.localhost:9006 by default
agents:
TaskAgent: {}
Key Constraints
- An
http.mount(...)is required ondefineAgent(...)before any method can declare anhttpendpoint - Every
idfield must be provided via a mount path variable (or a header variable — seegolem-http-params-ts) - Path/query/header variable names must exactly match
idfields (mount) or method input keys (endpoint) - Catch-all path variables
{*name}can only appear as the last endpoint path segment and are not allowed in mount paths - The endpoint path must start with
/ - Use exactly one verb builder per endpoint; pass an array of endpoints to expose a method under multiple routes
Signals
- GitHub stars
- 2k
- Forks
- 210
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
golem-add-http-endpoint-ts- Source
- github.com/golemcloud/golem