Adding HTTP Endpoints to a Scala Golem Agent
Overview
Golem agents can be exposed over HTTP using code-first route definitions. This involves:
- Adding a
mountparameter to@agentDefinition - Annotating methods with
@endpoint - Adding an
httpApideployment section togolem.yaml(See thegolem-configure-api-domainguide)
Related Guides
| Guide | Description |
|---|---|
golem-http-params-scala | Path/query/header variable mapping, body mapping, supported types, response mapping |
golem-make-http-request-scala | Making outgoing HTTP requests from agent code, especially when calling other Golem agent endpoints (required for correct JSON body formatting) |
golem-add-http-auth-scala | Enabling authentication |
golem-add-cors-scala | Configuring CORS allowed origins |
golem-configure-api-domain | Setting up httpApi in golem.yaml, security schemes, domain deployments |
Steps
- Add
mount = "/path/{param}"to@agentDefinition(...) - Add
@endpoint(method = "GET", path = "/...")to trait methods - Add
httpApideployment togolem.yaml(seegolem-configure-api-domainguide) - Build and deploy
Mount Path
The mount parameter on @agentDefinition defines the base HTTP path. Path variables in {braces} map to constructor parameters defined in the class Id(...):
import golem.runtime.annotations.{agentDefinition, endpoint}
import golem.BaseAgent
import scala.concurrent.Future
@agentDefinition(mount = "/api/tasks/{name}")
trait TaskAgent extends BaseAgent {
class Id(val name: String)
// methods...
}Constructor Parameter Naming in Scala
The path variable names must exactly match the class Id parameter names — do not use kebab-case in path variables. Use the same camelCase (or single-word) identifier that appears in the class Id:
- Single parameter:
class Id(val name: String)→ use{name}in path - Multiple parameters:
class Id(val arg0: String, val arg1: Int)→ use{arg0},{arg1}in path - Custom Id class with
@id: use the parameter names from the annotated class
// ✅ Correct — path variable matches parameter name exactly
@agentDefinition(mount = "/api/tasks/{taskName}")
trait TaskAgent extends BaseAgent {
class Id(val taskName: String)
}
// ❌ Wrong — {task-name} does not match parameter name taskName
@agentDefinition(mount = "/api/tasks/{task-name}")
trait TaskAgent extends BaseAgent {
class Id(val taskName: String)
}// Named parameters with @id annotation
import golem.runtime.annotations.id
@agentDefinition(mount = "/api/catalog/{region}/{catalog}")
trait CatalogAgent extends BaseAgent {
@id
class CatalogParams(val region: String, val catalog: String)
}Rules:
- Path must start with
/ - Every constructor parameter must appear as a
{variable}in the mount path - Every
{variable}must exactly match a constructor parameter name (same casing) - Catch-all
{*rest}variables are not allowed in mount paths
Endpoint Annotation
The @endpoint annotation marks a method as an HTTP endpoint. Specify the HTTP method and path:
@endpoint(method = "GET", path = "/items")
def listItems(): Future[List[Item]]
@endpoint(method = "POST", path = "/items")
def createItem(name: String, count: Int): Future[Item]
@endpoint(method = "PUT", path = "/items/{id}")
def updateItem(id: String, name: String): Future[Item]
@endpoint(method = "DELETE", path = "/items/{id}")
def deleteItem(id: String): Future[Unit]Endpoint paths are relative to the mount path. Supported HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT, TRACE, or any custom string.
For details on how path variables, query parameters, headers, and request bodies map to method parameters, See the golem-http-params-scala guide.
Phantom Agents
Set phantomAgent = true to create a new agent instance for each HTTP request, enabling fully parallel processing:
@agentDefinition(
mount = "/webhook/{agent-type}/{value}",
phantomAgent = true
)
trait WebhookHandler extends BaseAgent {
class Id(val value: String)
// Each HTTP request gets its own agent instance
}Custom Types
All types used in endpoint parameters and return values must have a zio.blocks.schema.Schema instance:
import zio.blocks.schema.Schema
final case class Task(id: String, title: String, done: Boolean) derives SchemaFor collections, use List[T] instead of Array[T]. Array does not have automatic Schema derivation support.
Complete Example
import golem.runtime.annotations.{agentDefinition, agentImplementation, endpoint, header}
import golem.BaseAgent
import zio.blocks.schema.Schema
import scala.concurrent.Future
final case class Task(id: String, title: String, done: Boolean) derives Schema
@agentDefinition(mount = "/task-agents/{name}")
trait TaskAgent extends BaseAgent {
class Id(val name: String)
@endpoint(method = "GET", path = "/tasks")
def getTasks(): Future[List[Task]]
@endpoint(method = "POST", path = "/tasks")
def createTask(title: String): Future[Task]
@endpoint(method = "GET", path = "/tasks/{id}")
def getTask(id: String): Future[Option[Task]]
@endpoint(method = "POST", path = "/report")
def submitReport(@header("X-Tenant") tenantId: String, data: String): Future[String]
}import golem.runtime.annotations.agentImplementation
import scala.concurrent.Future
@agentImplementation()
final class TaskAgentImpl(private val name: String) extends TaskAgent {
private var tasks: List[Task] = Nil
override def getTasks(): Future[List[Task]] =
Future.successful(tasks)
override def createTask(title: String): Future[Task] = Future.successful {
val task = Task(id = (tasks.length + 1).toString, title = title, done = false)
tasks = tasks :+ task
task
}
override def getTask(id: String): Future[Option[Task]] =
Future.successful(tasks.find(_.id == id))
override def submitReport(tenantId: String, data: String): Future[String] =
Future.successful(s"Report from $tenantId: $data")
}# golem.yaml (add to existing file)
httpApi:
deployments:
local:
- domain: my-app.localhost:9006
agents:
TaskAgent: {}Key Constraints
- A
mountpath is required on@agentDefinitionbefore any@endpointannotations can be used - All constructor parameters (from
class Id) must be provided via mount path variables - Path/query variable names must exactly match method parameter names
- Header parameters use
@header("Header-Name")annotation on individual method parameters - Catch-all path variables
{*name}can only appear as the last path segment - The endpoint path must start with
/ - Custom types require a
zio.blocks.schema.Schemainstance (usederives Schemain Scala 3) - The
scalacOptions += "-experimental"flag is required for macro annotations