> ## Documentation Index
> Fetch the complete documentation index at: https://iii.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> API reference for the iii SDK for Go.

## Installation

```bash theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
go get github.com/iii-hq/iii/sdk/packages/go/iii@latest
```

## Initialization

### RegisterWorker

RegisterWorker creates a `Client` for the engine at url and starts the connection lifecycle in the background, returning immediately. An empty url means `EngineURLFromEnv`, as with `New`. It is the idiomatic entry point, matching registerWorker in the Node SDK and register\_worker in the Rust SDK. Register functions and observers on the returned client; they are (re)sent on each connection. Call `Client.Close` to stop.

To wait for the first connection (or fail fast on a bad URL), call `Client.Connect` instead of (or after) RegisterWorker; it blocks until connected, ctx is done, or the reconnect budget is exhausted. Use `New` if you want to build a client without starting the connection.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func RegisterWorker(url string, opts ...Option) *Client
```

> [tab] Parameters
>
> **url** `string` *required*
>
> **opts** `...Option`
>
> **`Option` fields**
>
> **WithDescription** `func(description string) Option` *required*
>
> WithDescription sets a one-line, human/LLM-readable summary of what this worker does. Surfaces in engine::workers::list / engine::workers::info.
>
> **WithHeaders** `func(h http.Header) Option` *required*
>
> WithHeaders sets HTTP headers sent on the WebSocket handshake of the main connection and of channels created with `Client.CreateChannel`. The III\_TOKEN environment variable, when set and not blank, adds "Authorization: Bearer \<token>" unless h already carries an Authorization header.
>
> **WithInvocationTimeout** `func(d time.Duration) Option` *required*
>
> WithInvocationTimeout sets how long `Client.Call` waits for a result when `CallRequest`.Timeout is zero, in place of `DefaultInvocationTimeout`. It is the invocationTimeoutMs option of the Node SDK's registerWorker. A d of zero or less keeps the default.
>
> **WithMetricsReporting** `func(enabled bool) Option` *required*
>
> WithMetricsReporting turns the worker resource gauges on or off (Node enableMetricsReporting): the iii.worker.memory.\*, iii.worker.cpu.\*, iii.worker.event\_loop.lag\_ms and iii.worker.uptime\_seconds metrics, tagged with worker.id and worker.name. The default is true. The gauges start when the engine acknowledges the worker and stop when the connection drops; they need telemetry and its metrics to be enabled (see `OtelConfig`).
>
> **WithName** `func(name string) Option` *required*
>
> WithName sets the worker name reported to the engine. A non-empty III\_WORKER\_NAME takes precedence. The default is "hostname:pid".
>
> **WithNamespace** `func(namespace string) Option` *required*
>
> WithNamespace sets the namespace this worker registers into. Takes precedence over the III\_NAMESPACE environment variable.
>
> **WithOtel** `func(cfg OtelConfig) Option` *required*
>
> WithOtel configures the client's OpenTelemetry pipeline (the otel option of Node registerWorker). Telemetry starts with the connection lifecycle (`RegisterWorker` or `Client.Connect`) and `Client.Close` flushes and stops it. Without this option the defaults documented on `OtelConfig` apply, and they enable telemetry.
>
> **WithReconnectConfig** `func(c ReconnectConfig) Option` *required*
>
> WithReconnectConfig overrides the default reconnect schedule.
>
> **WithTelemetry** `func(t TelemetryOptions) Option` *required*
>
> WithTelemetry sets what the worker reports about its language, project and framework on every connection. Blank fields keep their defaults (see `TelemetryOptions`).

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.RegisterWorker(iii.DefaultEngineURL) // ws://127.0.0.1:49134
>
> greet, err := client.RegisterFunction("hello::greet", func(ctx context.Context, data json.RawMessage) (any, error) {
> 	var req struct {
> 		Body struct {
> 			Name string `json:"name"`
> 		} `json:"body"`
> 	}
> 	if err := json.Unmarshal(data, &req); err != nil {
> 		return nil, err
> 	}
> 	return map[string]any{
> 		"status_code": 200,
> 		"body":        map[string]string{"message": "Hello, " + req.Body.Name + "!"},
> 	}, nil
> })
> if err != nil {
> 	log.Fatal(err)
> }
>
> if _, err := client.Observe(iii.Source{
> 	FunctionID: "http::on-request",
> 	Args:       iii.Args{"method": "POST", "path": "/greet"},
> }, greet.ID); err != nil {
> 	log.Fatal(err)
> }
>
> if err := client.Connect(context.Background()); err != nil {
> 	log.Fatal(err)
> }
> defer client.Close()
> ```

## Methods

### Client.RegisterFunction

RegisterFunction registers a function this worker can handle and returns its handle; `FunctionRef.Unregister` removes it. It may be called before or after Connect; the registration is sent on the next (re)connect. As in the Node, Python and Rust SDKs, an empty id is refused ("id is required") and so is an id this client already registered ("function id already registered"): to replace a function, unregister it first. Pass a single `RegisterFunctionOptions` value for a description, request and response formats, metadata, args\_format, private, observable or the local dispatch setting.

The handler's return value is what observers observe. Every call of the function, local or routed, runs the handler and is then delivered to the observers that want it (`Client.OnObservationMatchFor`; by default all of them): by the function's deliver hook (`Client.OnObservationDeliverFor`), or by iii, to those whose capture and filters match. Set hooks by id before registering: the SDK acks no observer before RegisterFunction returns, but a hook set on the returned handle can miss observers already accepted, and is caught up for them.

Handlers read the optional per-invocation metadata sidecar from their ctx with `MetadataFromContext`.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) RegisterFunction(id string, handler Handler, opts ...RegisterFunctionOptions) (*FunctionRef, error)
```

> [tab] Parameters
>
> **id** `string` *required*
>
> **handler** `Handler` *required*
>
> **opts** `...RegisterFunctionOptions`
>
> **`RegisterFunctionOptions` fields**
>
> **Metadata** `json.RawMessage`
>
> Metadata is arbitrary JSON attached to the function registration. It is stored with the function and is distinct from the per-invocation metadata passed to handlers.
>
> **LocalDispatch** `*bool`
>
> LocalDispatch false keeps every Call of this function on the engine path; nil or true lets Call run it in-process once the engine confirms it (see `Client.Call`: observers see snapshots of the payload and metadata taken at call start, and a handler may outlive a caller whose deadline passed).
>
> **Description** `string`
>
> Description is a human/LLM-readable summary of the function, sent as "description" and surfaced by engine::functions::list and engine::functions::info. Empty omits it.
>
> **RequestFormat** `json.RawMessage`
>
> RequestFormat is the JSON Schema of the function's request, sent as "request\_format". It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Req.
>
> **ResponseFormat** `json.RawMessage`
>
> ResponseFormat is the JSON Schema of the function's response, sent as "response\_format": the handler's return value, which is what observers observe. It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Resp.
>
> **ArgsFormat** `json.RawMessage`
>
> ArgsFormat is the JSON Schema of the observers' args, sent as "args\_format": the engine validates every observer's args against it (INVALID\_ARGS), and refuses a schema that is not valid (INVALID\_ARGS\_FORMAT). It must be valid JSON; nil means the function takes no args.
>
> **Private** `bool`
>
> Private lets only this worker call the function (the engine answers anyone else with FORBIDDEN); other workers may observe it, and it cannot be an observer's target. A call this worker makes to it passes the input and the result by reference: the handler and the hooks treat both as read-only.
>
> **Observable** `*bool`
>
> Observable false refuses every observer of the function (NOT\_OBSERVABLE), for a function whose payloads carry credentials. nil means true.

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.New(iii.DefaultEngineURL)
>
> handler := func(ctx context.Context, data json.RawMessage) (any, error) {
> 	return map[string]any{"ok": true}, nil
> }
>
> client.RegisterFunction("orders::create", handler, iii.RegisterFunctionOptions{
> 	Metadata: json.RawMessage(`{"owner":"billing-team","priority":"high"}`),
> })
> ```

***

### Client.Call

Call invokes a function. With the default (nil) or an enqueue action it waits for the matching invocation result and returns it, mapping a remote error to `InvocationError` and a missed deadline to `ErrTimeout`. With a `VoidAction` it is fire-and-forget and returns immediately with a nil result. With `CallRequest`.Observer it calls through that observer.

ctx bounds the wait independently of `CallRequest.Timeout`: if ctx is cancelled first, its error is returned and the pending entry is reclaimed. Inside a handler or a deliver hook, pass its ctx: the call then carries that invocation as its caller (and the observe depth), which observers' filters and the engine's loop guard read.

A function this worker hosts runs in-process, without touching the wire, once the engine confirmed it for local dispatch, unless the action is enqueue or Dispatch is `DispatchRemote`. The handler gets its own copy of the payload and the caller its own encoding of the result, except for a `RegisterFunctionOptions`.Private function, whose input and result pass by reference; errors and timeouts look as on the remote path. The call is delivered to the function's observers as a routed one is, from snapshots of the payload and metadata taken when it started. The handler runs on a goroutine of its own, so the caller always returns on its Timeout or ctx deadline, even when the handler blocks and ignores its ctx; the handler then runs to completion (as a remote one would on its worker), its result is discarded, and the call is still counted and delivered. A handler that can block for long should honor its ctx (cancelled on Close), or opt out with `RegisterFunctionOptions`.LocalDispatch.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Call(ctx context.Context, req CallRequest) (json.RawMessage, error)
```

> [tab] Parameters
>
> **ctx** `context.Context` *required*
>
> **req** `CallRequest` *required*
>
> **`CallRequest` fields**
>
> **FunctionID** `string`
>
> FunctionID is the function to invoke (e.g. an EngineFunctions constant). Empty with Observer.
>
> **Observer** `*Observer`
>
> Observer calls through an observer of a function this worker hosts: the engine runs the observer's target with Payload as given, after checking that this worker hosts the observer's function (NOT\_FUNCTION\_OWNER) and that the observer exists and is active (OBSERVER\_NOT\_FOUND, OBSERVER\_NOT\_ACTIVE), and applies its policy (max\_fires, expiry, max\_in\_flight: OVERFLOW), metadata sidecar and loop guard. The call goes out on the current connection, after the acks already sent on it; FunctionID, Namespace and an enqueue action do not apply. It takes no Metadata: the target's metadata is the observer's registration metadata, and Call refuses a request with Metadata before anything is sent. A call through an observer belongs in the function's `DeliverHook`, which applies the observer's filters with `Observer.Matches`; to send one observer something outside delivery (its initial state), call the function with metadata \{"for\_observer": \<obs.ID>} (see `Observer.Accepted`).
>
> **Payload** `json.RawMessage`
>
> Payload is the JSON input of the function (payload in the Node, Python and Rust SDKs). Nil is sent as \{}.
>
> **Data** `json.RawMessage`
>
> **Deprecated.** Use Payload. Data is the JSON input of the function, used only when Payload is nil.
>
> **Metadata** `json.RawMessage`
>
> Metadata is optional per-invocation metadata (any JSON value) sent alongside the payload on the wire's metadata channel, not folded into it. nil and JSON null are no metadata: omitted, and the handler's MetadataFromContext reports none.
>
> **Action** `*CallAction`
>
> Action selects void/enqueue semantics; nil means the default await path.
>
> **Timeout** `time.Duration`
>
> Timeout bounds an await/enqueue call. Zero means the client's invocation timeout: `WithInvocationTimeout`, else `DefaultInvocationTimeout`.
>
> **Namespace** `string`
>
> Namespace routes the invocation to a specific namespace. Empty means this worker's namespace, except engine::\* and configuration::\* builtins, which resolve in the engine's default namespace.
>
> **Dispatch** `Dispatch`
>
> Dispatch selects the path to a function this worker hosts: the zero value (or `DispatchAuto`) runs it in-process once the engine confirmed it for local dispatch; `DispatchRemote` always goes through the engine.

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.RegisterWorker(iii.DefaultEngineURL)
> defer client.Close()
>
> res, err := client.Call(context.Background(), iii.CallRequest{
> 	FunctionID: "orders::create",
> 	Payload:    json.RawMessage(`{"item":"widget"}`),
> })
> switch {
> case errors.Is(err, iii.ErrTimeout):
> 	fmt.Println("timed out")
> case err != nil:
> 	var ie *iii.InvocationError
> 	if errors.As(err, &ie) {
> 		fmt.Println("remote error:", ie.Code)
> 	}
> default:
> 	fmt.Printf("result: %s\n", res)
> }
> ```

***

### Client.Observe

Observe observes a function: when source.FunctionID runs, target runs, for every call that the function's worker delivers to the observer and that passes source.Filters. target is a function registered by some worker (until it is, the observer stays pending). source.Args go to the worker that hosts the function, which reads them in its hooks (a route, a table).

It returns the handle immediately, even before the connection is up; the observer is replayed with the same id on every reconnect. Errors from the engine (INVALID\_ARGS, INVALID\_FILTER, FORBIDDEN, NOT\_OBSERVABLE, SELF\_TARGET, the host's rejection, and UNSUPPORTED against an engine without observe) show up as state error and fail `ObserverHandle.Ready`. An observeresult in state error is the engine's refusal of that observe and the handle's final answer: the engine holds no observer for it (it stores nothing for a refused observe, and disposes an observer it holds in error as it answers that observer's replay), the handle is not sent again on a later connection, and its Dispose sends nothing. An observer that turns error after its observe was answered (the host's rejection, OWNER\_TIMEOUT) is held by the engine: the handle is replayed, and its Dispose unobserves it. The returned error is for a request that cannot be sent at all: a missing function or target, a blank namespace, args or filters that are not JSON, and an id a handle of this client holds. A handle holds its id until it is disposed (by `ObserverHandle.Dispose` or by the engine); an Observe of a held id sends nothing, leaves the holder alone and returns an \*ObserveRejectionError with code OBSERVER\_ID\_CONFLICT. Once the holder is disposed the id is free at once.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Observe(source Source, target string, opts ...ObserverOptions) (*ObserverHandle, error)
```

> [tab] Parameters
>
> **source** `Source` *required*
>
> **`Source` fields**
>
> **FunctionID** `string` *required*
>
> FunctionID is the observed function.
>
> **Namespace** `string`
>
> Namespace defaults to this worker's (engine::\* and configuration::\* resolve to the engine's default namespace).
>
> **Args** `Args` *required*
>
> Args go to the worker that hosts the function, which reads them in its hooks (a cron expression, a route, a table). A function without args\_format takes none.
>
> **Filters** `Filters`
>
> Filters decide which calls reach the target; nil matches every call.
>
> **Capture** `[]Capture`
>
> Capture is what the target's payload carries, and with it which outcomes fire the target. nil means the default (input, output, caller), which fires on success. Input and output fire on success, error fires on error; caller and metadata only add to the payload, so a non-empty Capture must name at least one of input, output, error (INVALID\_ARGS otherwise). An empty, non-nil slice fires on success with the top-level fields only. \{error, caller} fires on errors, \{input, error} on both.
>
> **target** `string` *required*
>
> **opts** `...ObserverOptions`
>
> **`ObserverOptions` fields**
>
> **ID** `string`
>
> ID defaults to a random UUID, stable across replays.
>
> **MaxFires** `uint64` *required*
>
> MaxFires disposes the observer after this many activations.
>
> **ExpiresIn** `time.Duration` *required*
>
> ExpiresIn disposes the observer after this long (millisecond precision).
>
> **MaxInFlight** `uint64` *required*
>
> MaxInFlight bounds the observer's concurrent activations.
>
> **Metadata** `json.RawMessage`
>
> Metadata is delivered as the metadata sidecar of every activation (a JSON object).
>
> **TargetNamespace** `string`
>
> TargetNamespace is the namespace of the target function; it defaults to this worker's.

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client.RegisterFunction("alerts::payment-failed", notify)
> failures, err := client.Observe(iii.Source{
>     FunctionID: "payments::charge",
>     Capture:    []iii.Capture{iii.CaptureError, iii.CaptureCaller}, // fires on errors only
>     Filters:    iii.Filters{"input.amount": iii.Op{"$gte": 500}},
> }, "alerts::payment-failed")
> if err == nil {
>     err = failures.Ready(ctx)
> }
> ```

***

### Client.Trigger

> [!WARNING]
>
> **Deprecated.** Use `Client.Call`.

Trigger is `Client.Call` under its 0.25 name.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Trigger(ctx context.Context, req TriggerRequest) (json.RawMessage, error)
```

#### Parameters

**ctx** `context.Context` *required*

**req** `TriggerRequest` *required*

***

### Client.AddConnectionStateListener

AddConnectionStateListener calls listener with the current `ConnectionState`, then with every later change of state (`StateConnecting`, `StateConnected`, `StateReconnecting`, `StateDisconnected`, `StateFailed`), in the order the changes happen. Calls are made one at a time on a goroutine of the client's own, never on the subscribing goroutine or on the SDK's read loop, so a listener may call any Client method; it should return promptly, since later changes wait for it. A state is reported once per change: the same state is never delivered twice in a row. A listener that panics is logged and keeps its subscription.

It returns the function that unsubscribes listener: changes not yet delivered when it returns are dropped for this listener (a call already under way completes). Calling it more than once does nothing. It mirrors addConnectionStateListener in the browser SDK and add\_connection\_state\_listener in the Python SDK.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) AddConnectionStateListener(listener func(ConnectionState)) (unsubscribe func())
```

#### Parameters

**listener** `func(ConnectionState)` *required*

**`ConnectionState` fields**

**StateDisconnected** `'disconnected'` *required*

**StateConnecting** `'connecting'` *required*

**StateConnected** `'connected'` *required*

**StateReconnecting** `'reconnecting'` *required*

**StateFailed** `'failed'` *required*

***

### Client.Address

Address returns the engine WebSocket base URL this client dials (e.g. "ws\://127.0.0.1:49134"): the url given to `New` or `RegisterWorker`, else `EngineURLFromEnv`. Channel readers/writers build their own URLs from it.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Address() string
```

***

### Client.Close

Close shuts the client down at once: it stops the reconnect loop, gives buffered observe reports and telemetry a short, fixed time to go out (250ms for the reports, at most 10s for the telemetry flush), cancels all pending calls with ErrNotConnected, and drops the socket without a closing handshake. It does not send unregister frames (matching the reference SDKs). Use `Client.Shutdown` to wait for the flushes under a deadline of your own and close the socket gracefully.

Close is idempotent and implements io.Closer; it always returns nil. Called while a Shutdown runs, it cuts that Shutdown short (as http.Server.Close does) and returns once it has finished.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Close() error
```

***

### Client.Connect

Connect starts the connection lifecycle (if not already started) and blocks until the first connection is established, ctx is cancelled, or the reconnect budget is exhausted. The supervisor goroutine then keeps the connection alive (reconnecting with backoff) until Close. Connect is safe to call multiple times and from multiple goroutines; only one supervisor ever runs.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Connect(ctx context.Context) error
```

#### Parameters

**ctx** `context.Context` *required*

***

### Client.ConnectionState

ConnectionState returns the current state of the connection to the engine. It mirrors getConnectionState in the Node SDK; `Client.AddConnectionStateListener` reports every change.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) ConnectionState() ConnectionState
```

***

### Client.CreateChannel

CreateChannel asks the engine to allocate a streaming channel and returns both ends. bufferSize bounds the engine's internal queue; nil uses the engine default.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) CreateChannel(ctx context.Context, bufferSize *int) (*Channel, error)
```

#### Parameters

**ctx** `context.Context` *required*

**bufferSize** `*int` *required*

***

### Client.FatalError

FatalError returns the terminal registration rejection that stopped this worker, or nil while healthy. Mirrors the Node/Python/Rust SDKs' fatal-error accessors.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) FatalError() error
```

***

### Client.IsObserved

IsObserved reports whether functionID, registered by this worker, has active observers, so a source can skip building an event nobody wants. It takes no lock: a lookup of functionID and one atomic load. `FunctionRef.IsObserved` is the load alone.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) IsObserved(functionID string) bool
```

#### Parameters

**functionID** `string` *required*

***

### Client.IsObservedFor

IsObservedFor reports whether call of functionID, registered by this worker, would reach an observer. On a function matched by keys (`MatchKeys`) it is true when an observer is filed under one of the call's keys (one lookup per key); on any other function call is ignored, and it is `Client.IsObserved`. Set what the keys read (Input, Metadata).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) IsObservedFor(functionID string, call *Call) bool
```

#### Parameters

**functionID** `string` *required*

**call** `*Call` *required*

**`Call` fields**

**FunctionID** `string` *required*

**InvocationID** `string` *required*

**Status** `string`

Status is "success" or "error". Empty reads as "error" when Error is set, and "success" otherwise.

**Input** `json.RawMessage`

Input is the call's input, as the handler received it.

**Output** `json.RawMessage`

Output is the handler's return value as JSON: what observers observe. Nil on an error.

**Error** `*ErrorBody`

Error is the error the handler returned. Nil on success.

**Caller** `*Caller`

Caller is who made the call; nil for a call made outside any handler.

**Metadata** `json.RawMessage`

Metadata is the call's metadata sidecar; nil when it carries none.

***

### Client.LocalDispatchActive

LocalDispatchActive reports whether Call runs functionID in-process: it is registered here, the engine confirmed it with local\_dispatch, and RegisterFunctionOptions.LocalDispatch did not opt out.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) LocalDispatchActive(functionID string) bool
```

#### Parameters

**functionID** `string` *required*

***

### Client.ObserveFunction

ObserveFunction observes functionID with target, every call and the observer defaults: `Client.Observe` with Source\{FunctionID: functionID}.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) ObserveFunction(functionID, target string, opts ...ObserverOptions) (*ObserverHandle, error)
```

> [tab] Parameters
>
> **functionID** `string` *required*
>
> **target** `string` *required*
>
> **opts** `...ObserverOptions`
>
> **`ObserverOptions` fields**
>
> **ID** `string`
>
> ID defaults to a random UUID, stable across replays.
>
> **MaxFires** `uint64` *required*
>
> MaxFires disposes the observer after this many activations.
>
> **ExpiresIn** `time.Duration` *required*
>
> ExpiresIn disposes the observer after this long (millisecond precision).
>
> **MaxInFlight** `uint64` *required*
>
> MaxInFlight bounds the observer's concurrent activations.
>
> **Metadata** `json.RawMessage`
>
> Metadata is delivered as the metadata sidecar of every activation (a JSON object).
>
> **TargetNamespace** `string`
>
> TargetNamespace is the namespace of the target function; it defaults to this worker's.

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.RegisterWorker(iii.DefaultEngineURL)
> defer client.Close()
>
> client.RegisterFunction("alerts::new-order", func(ctx context.Context, data json.RawMessage) (any, error) {
> 	var event iii.Observed
> 	if err := json.Unmarshal(data, &event); err != nil {
> 		return nil, err
> 	}
> 	log.Printf("order created: %s", event.Output)
> 	return nil, nil
> })
> if _, err := client.ObserveFunction("orders::create", "alerts::new-order"); err != nil {
> 	log.Fatal(err)
> }
> ```

***

### Client.Observers

Observers returns the active observers of functionID, registered by this worker, as its host sees them.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Observers(functionID string) []*Observer
```

#### Parameters

**functionID** `string` *required*

***

### Client.OnObservationDeliverFor

OnObservationDeliverFor sets the hook that delivers each call of functionID itself; nil restores iii's delivery, which delivers each call to every matched observer whose capture and filters accept it, and gives the caller the handler's result. It is stored by id, as `Client.OnObserverAddedFor` is. A function with a deliver hook needs an engine with observe.v2: against an older one it stays unregistered, and the SDK logs UNSUPPORTED once. A deliver hook does not run for a function registered with `Client.RegisterHTTPFunction`: the engine delivers its calls. It returns the client, for chaining.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OnObservationDeliverFor(functionID string, hook DeliverHook) *Client
```

> [tab] Parameters
>
> **functionID** `string` *required*
>
> **hook** `DeliverHook` *required*

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.RegisterWorker(iii.DefaultEngineURL)
> defer client.Close()
>
> commandOf := func(call *iii.Call) string {
> 	var req struct {
> 		Command string `json:"command"`
> 	}
> 	_ = json.Unmarshal(call.Input, &req)
> 	return req.Command
> }
> client.OnObservationMatchFor("chat::on-command", iii.MatchFunc(func(obs *iii.Observer, call *iii.Call) bool {
> 	return obs.Args["command"] == commandOf(call)
> }))
> client.OnObservationDeliverFor("chat::on-command", func(ctx context.Context, call *iii.Call, observers []*iii.Observer) (any, error) {
> 	if len(observers) == 0 {
> 		return map[string]string{"reply": "unknown command /" + commandOf(call)}, nil
> 	}
> 	obs := observers[0]
> 	if !obs.Matches(call) {
> 		return map[string]string{"reply": "/" + commandOf(call) + " is not available"}, nil
> 	}
> 	// what this hook returns is what the caller gets back
> 	return client.Call(ctx, iii.CallRequest{Observer: obs, Payload: call.Input})
> })
>
> if _, err := client.RegisterFunction("chat::on-command", iii.Identity, iii.FunctionOptions{
> 	ArgsFormat: json.RawMessage(`{"type":"object","required":["command"],"properties":{"command":{"type":"string"}}}`),
> 	Private:    true,
> }); err != nil {
> 	log.Fatal(err)
> }
> ```

***

### Client.OnObservationMatchFor

OnObservationMatchFor sets which observers want each call of functionID: `MatchFunc` or `MatchKeys`; nil restores the default, where every observer wants every call. It is stored by id, as `Client.OnObserverAddedFor` is. A match hook set on a registered function takes effect on the function's hook queue, after the hooks queued before it, and calls use the previous one until then. A hook by keys set while the function has active observers rebuilds the index from them; an observer whose keys panic is late-rejected with OBSERVE\_REJECTED. A function with a match hook needs an engine with observe.v2: against an older one it stays unregistered, and the SDK logs UNSUPPORTED once. It returns the client, for chaining.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OnObservationMatchFor(functionID string, m *MatchHook) *Client
```

#### Parameters

**functionID** `string` *required*

**m** `*MatchHook` *required*

**`MatchHook` fields**

**MatchFunc** `func(predicate func(obs *Observer, call *Call) bool) *MatchHook` *required*

MatchFunc is a match hook that asks predicate, once per active observer per call, whether the observer wants the call. A predicate that panics counts as no match, and the SDK logs it once per function. A nil predicate gives a nil MatchHook.

**MatchKeys** `func(observerKeys func(obs *Observer) []string, callKeys func(call *Call) []string) *MatchHook` *required*

MatchKeys is a match hook by keys: an observer wants a call when they share a key, and the SDK keeps an index of the active observers by key instead of asking each of them. observerKeys says what an observer listens for: the SDK computes it once, when the observer is accepted, files the observer under each key and unfiles it when it is removed. An observer whose observerKeys panics is rejected with OBSERVE\_REJECTED. callKeys says what a call is: the call matches the observers filed under any of its keys, each once; a callKeys that panics matches nothing, and the SDK logs it once per function. Keys express wildcards by listing them: an observer of every table is filed under "primary|\*|insert", and a change matches "primary|orders|insert", "primary|orders|\*", "primary|\*|insert" and "primary|\*|\*". A nil function gives a nil MatchHook.

***

### Client.OnObserverAdded

OnObserverAdded sets the hook that runs when an observer is added to any function this worker hosts, before the function's own; nil removes it. A hook set while functions have active observers runs for each of them at once (see `Client.OnObserverAddedFor`). It returns the client, for chaining.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OnObserverAdded(hook AddedHook) *Client
```

#### Parameters

**hook** `AddedHook` *required*

***

### Client.OnObserverAddedFor

OnObserverAddedFor sets the hook that runs when an observer is added to functionID, after the worker's; nil removes it. Hooks are stored by function id: set them before RegisterFunction, so they run for every observer, and they stay across `FunctionRef.Unregister` and a later registration of the id.

A hook set once the function already has active observers is caught up: it runs for each of them at once, an error late-rejects that observer, and the SDK logs one warning naming the function and how many observers were active without it. It returns the client, for chaining.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OnObserverAddedFor(functionID string, hook AddedHook) *Client
```

#### Parameters

**functionID** `string` *required*

**hook** `AddedHook` *required*

***

### Client.OnObserverRemoved

OnObserverRemoved sets the hook that runs when an observer is removed from any function this worker hosts, after the function's own; nil removes it. It returns the client, for chaining.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OnObserverRemoved(hook RemovedHook) *Client
```

#### Parameters

**hook** `RemovedHook` *required*

***

### Client.OnObserverRemovedFor

OnObserverRemovedFor sets the hook that runs when an observer is removed from functionID, before the worker's; nil removes it. It is stored by function id, as `Client.OnObserverAddedFor` is. It returns the client, for chaining.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OnObserverRemovedFor(functionID string, hook RemovedHook) *Client
```

#### Parameters

**functionID** `string` *required*

**hook** `RemovedHook` *required*

***

### Client.OpenReader

OpenReader returns a reader for the read end described by ref (typically received in a call payload), against this client's engine address. Like the ends returned by `Client.CreateChannel`, its handshake sends the client's `WithHeaders` plus "Authorization: Bearer \$III\_TOKEN" when that variable is set and the headers have no Authorization header. The socket connects on first read.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OpenReader(ref StreamChannelRef) *ChannelReader
```

#### Parameters

**ref** `StreamChannelRef` *required*

**`StreamChannelRef` fields**

**ChannelID** `string` *required*

**AccessKey** `string` *required*

**Direction** `ChannelDirection` *required*

***

### Client.OpenWriter

OpenWriter returns a writer for the write end described by ref (typically received in a call payload), against this client's engine address. Like the ends returned by `Client.CreateChannel`, its handshake sends the client's `WithHeaders` plus "Authorization: Bearer \$III\_TOKEN" when that variable is set and the headers have no Authorization header. The socket connects on first send.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) OpenWriter(ref StreamChannelRef) *ChannelWriter
```

#### Parameters

**ref** `StreamChannelRef` *required*

**`StreamChannelRef` fields**

**ChannelID** `string` *required*

**AccessKey** `string` *required*

**Direction** `ChannelDirection` *required*

***

### Client.RegisterHTTPFunction

RegisterHTTPFunction registers a function that has no handler in this process: the engine invokes it by sending an HTTP request as described by invocation, which travels as the registration's "invocation" field. invocation.URL is required; an empty Method means POST, as in the Node SDK. invocation.Auth, when set, must be valid JSON; build it with `HMACAuth`, `BearerAuth` or `APIKeyAuth`. Pass at most one `RegisterFunctionOptions` for the description, formats, metadata, args\_format, private or observable; LocalDispatch does not apply, since there is nothing to run in-process. The engine delivers the function's calls itself, so a deliver hook does not run for it; the added and removed hooks do.

Like every registration it is kept in memory and sent again on each reconnect. An empty id, or one this client already registered, is refused as in `Client.RegisterFunction`; `FunctionRef.Unregister` on the returned handle removes it.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) RegisterHTTPFunction(id string, invocation HTTPInvocationRef, opts ...RegisterFunctionOptions) (*FunctionRef, error)
```

#### Parameters

**id** `string` *required*

**invocation** `HTTPInvocationRef` *required*

**`HTTPInvocationRef` fields**

**URL** `string` *required*

URL is the endpoint the engine calls. Required.

**Method** `string`

Method is the HTTP method (GET, POST, PUT, PATCH or DELETE). RegisterHTTPFunction sends POST when it is empty.

**TimeoutMs** `*uint64`

TimeoutMs bounds each request, in milliseconds; nil uses the engine default.

**Headers** `map[string]string`

Headers are sent with every request.

**Auth** `json.RawMessage`

Auth is how the engine authenticates its requests; nil sends none.

**opts** `...RegisterFunctionOptions`

**`RegisterFunctionOptions` fields**

**Metadata** `json.RawMessage`

Metadata is arbitrary JSON attached to the function registration. It is stored with the function and is distinct from the per-invocation metadata passed to handlers.

**LocalDispatch** `*bool`

LocalDispatch false keeps every Call of this function on the engine path; nil or true lets Call run it in-process once the engine confirms it (see `Client.Call`: observers see snapshots of the payload and metadata taken at call start, and a handler may outlive a caller whose deadline passed).

**Description** `string`

Description is a human/LLM-readable summary of the function, sent as "description" and surfaced by engine::functions::list and engine::functions::info. Empty omits it.

**RequestFormat** `json.RawMessage`

RequestFormat is the JSON Schema of the function's request, sent as "request\_format". It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Req.

**ResponseFormat** `json.RawMessage`

ResponseFormat is the JSON Schema of the function's response, sent as "response\_format": the handler's return value, which is what observers observe. It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Resp.

**ArgsFormat** `json.RawMessage`

ArgsFormat is the JSON Schema of the observers' args, sent as "args\_format": the engine validates every observer's args against it (INVALID\_ARGS), and refuses a schema that is not valid (INVALID\_ARGS\_FORMAT). It must be valid JSON; nil means the function takes no args.

**Private** `bool`

Private lets only this worker call the function (the engine answers anyone else with FORBIDDEN); other workers may observe it, and it cannot be an observer's target. A call this worker makes to it passes the input and the result by reference: the handler and the hooks treat both as read-only.

**Observable** `*bool`

Observable false refuses every observer of the function (NOT\_OBSERVABLE), for a function whose payloads carry credentials. nil means true.

***

### Client.Shutdown

Shutdown shuts the client down gracefully, as the Node SDK's shutdown does, bounded by ctx like http.Server.Shutdown: it stops the reconnect loop, waits until the buffered observe reports and counters are written, stops the worker gauges, flushes and stops the telemetry exporters, cancels all pending calls with ErrNotConnected, and closes the socket with a normal-closure handshake.

When ctx is done before those steps finish, the remaining work is cut short (the socket is closed at once, also when the engine never answers the close handshake) and ctx.Err() is returned; the client is shut down either way and nil means everything was delivered. A `Client.Close` called meanwhile cuts it short the same way, and Shutdown then returns context.Canceled. Like Close it does not send unregister frames. Shutdown is idempotent: a later Shutdown returns once the first shutdown has finished, or ctx.Err() if its own ctx ends first.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) Shutdown(ctx context.Context) error
```

> [tab] Parameters
>
> **ctx** `context.Context` *required*

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.RegisterWorker(iii.DefaultEngineURL)
>
> ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
> defer cancel()
> if err := client.Shutdown(ctx); err != nil {
> 	log.Printf("shutdown cut short: %v", err)
> }
> ```

***

### Client.State

> [!WARNING]
>
> **Deprecated.** Use `Client.ConnectionState`.

State returns the current connection state.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) State() ConnectionState
```

***

### Client.WaitUntilRegistered

WaitUntilRegistered blocks until the engine has acknowledged this worker on the current connection, with the WorkerRegistered frame it sends on each connection, and then returns nil. While the client is disconnected it waits for the next connection's acknowledgement.

It returns the error of `Client.FatalError` when the worker was terminally rejected (for example a WORKER\_NAMESPACE\_CONFLICT, or a blank `WithNamespace`), and ctx.Err() when ctx is done first. Like the Rust SDK's wait\_until\_registered, which it mirrors with ctx in place of a timeout, it returns `ErrNotConnected` when the client was never started (call it after `Client.Connect` or `RegisterWorker`), has been closed, or gave up reconnecting (`ReconnectConfig`.MaxRetries).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) WaitUntilRegistered(ctx context.Context) error
```

#### Parameters

**ctx** `context.Context` *required*

***

### Client.WorkerID

WorkerID returns the worker id the engine assigned with its last WorkerRegistered, or "" before the first one. The id is kept across a disconnect (the next connection presents it to reattach) until the engine assigns the next one. It mirrors worker\_id in the Python SDK.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func (c *Client) WorkerID() string
```

## Functions

### APIKeyAuth

APIKeyAuth returns the `HTTPInvocationRef`.Auth that has the engine send an API key in the request header named header, the key being held in the engine's environment variable valueKey: \{"type":"api\_key","header":header,"value\_key":valueKey}. Same shape as the api\_key variant of the Node SDK's HttpAuthConfig.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func APIKeyAuth(header, valueKey string) json.RawMessage
```

#### Parameters

**header** `string` *required*

**valueKey** `string` *required*

***

### BearerAuth

BearerAuth returns the `HTTPInvocationRef`.Auth that has the engine send "Authorization: Bearer \<token>", the token being held in the engine's environment variable tokenKey: \{"type":"bearer","token\_key":tokenKey}. Same shape as the bearer variant of the Node SDK's HttpAuthConfig.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func BearerAuth(tokenKey string) json.RawMessage
```

#### Parameters

**tokenKey** `string` *required*

***

### DefaultReconnectConfig

DefaultReconnectConfig is the reconnect schedule shared with the Node SDK: start at 1s, double each time, cap at 30s, ±30% jitter, retry forever.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func DefaultReconnectConfig() ReconnectConfig
```

***

### EngineURLFromEnv

EngineURLFromEnv returns the engine address a worker connects to when its code names none: the III\_URL environment variable, trimmed, when it is not blank, else `DefaultEngineURL`. The supervisor that spawned the process (iii compose, a container runtime, systemd) sets III\_URL the same way it sets III\_NAMESPACE and III\_WORKER\_NAME. `New` and `RegisterWorker` call it when their url argument is empty, so an explicit address wins, then III\_URL, then the default, as in the Node SDK.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func EngineURLFromEnv() string
```

***

### EnqueueAction

EnqueueAction routes an invocation through a named queue; the engine acknowledges with an enqueue receipt rather than the function's result.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func EnqueueAction(queue string) *CallAction
```

#### Parameters

**queue** `string` *required*

***

### ExtractChannelRefs

ExtractChannelRefs recursively walks a JSON value and returns every StreamChannelRef found, keyed by its dotted path (e.g. "reader", "data.writer"). A node is a ref iff it is an object with string channel\_id, access\_key, and direction fields. Mirrors extract\_channel\_refs and is\_channel\_ref in the Rust SDK: the receiving worker uses it to find a ref another worker embedded in a call payload.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func ExtractChannelRefs(data json.RawMessage) (map[string]StreamChannelRef, error)
```

#### Parameters

**data** `json.RawMessage` *required*

***

### HMACAuth

HMACAuth returns the `HTTPInvocationRef`.Auth that has the engine sign each request with HMAC, using the secret held in the engine's environment variable secretKey: \{"type":"hmac","secret\_key":secretKey}. Same shape as the hmac variant of the Node SDK's HttpAuthConfig.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func HMACAuth(secretKey string) json.RawMessage
```

#### Parameters

**secretKey** `string` *required*

***

### Identity

Identity is a handler that returns the input it is called with. An event source registers it: its observers observe each event as both input and output.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func Identity(_ context.Context, data json.RawMessage) (any, error)
```

#### Parameters

**\_** `context.Context` *required*

**data** `json.RawMessage` *required*

***

### InferSchema

InferSchema reflects a JSON Schema for T, exposed for callers that want to build a registration manually or inspect the schema the typed API would send.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func InferSchema[T any]() (json.RawMessage, error)
```

***

### InvocationFromContext

InvocationFromContext returns the invocation a handler's ctx belongs to, remote or local. ok is false outside any handler.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func InvocationFromContext(ctx context.Context) (Invocation, bool)
```

> [tab] Parameters
>
> **ctx** `context.Context` *required*

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> func handler(ctx context.Context, data json.RawMessage) (any, error) {
>     if inv, ok := iii.InvocationFromContext(ctx); ok {
>         log.Printf("%s called by %+v", inv.InvocationID, inv.Caller)
>     }
>     ...
> }
> ```

***

### MarshalMessage

MarshalMessage encodes a variant struct as a wire frame, injecting the "type" tag. Pass one of the \*Message structs (by pointer or value) from this file.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func MarshalMessage(msg any) ([]byte, error)
```

#### Parameters

**msg** `any` *required*

***

### MatchFunc

MatchFunc is a match hook that asks predicate, once per active observer per call, whether the observer wants the call. A predicate that panics counts as no match, and the SDK logs it once per function. A nil predicate gives a nil MatchHook.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func MatchFunc(predicate func(obs *Observer, call *Call) bool) *MatchHook
```

> [tab] Parameters
>
> **predicate** `func(obs *Observer, call *Call) bool` *required*
>
> **`Call` fields**
>
> **FunctionID** `string` *required*
>
> **InvocationID** `string` *required*
>
> **Status** `string`
>
> Status is "success" or "error". Empty reads as "error" when Error is set, and "success" otherwise.
>
> **Input** `json.RawMessage`
>
> Input is the call's input, as the handler received it.
>
> **Output** `json.RawMessage`
>
> Output is the handler's return value as JSON: what observers observe. Nil on an error.
>
> **Error** `*ErrorBody`
>
> Error is the error the handler returned. Nil on success.
>
> **Caller** `*Caller`
>
> Caller is who made the call; nil for a call made outside any handler.
>
> **Metadata** `json.RawMessage`
>
> Metadata is the call's metadata sidecar; nil when it carries none.
>
> **`Observer` fields**
>
> **ID** `string` *required*
>
> **FunctionID** `string` *required*
>
> FunctionID is the observed function (the one this worker hosts).
>
> **Args** `map[string]any`
>
> Args are the observer's args, validated against args\_format by the engine (numbers decode as float64; see DecodeArgs).
>
> **Filters** `map[string]any`
>
> Filters are the observer's filters in normal form; iii's delivery and Matches apply them.
>
> **Target** `string` *required*
>
> Target is the target function id.
>
> **Capture** `[]Capture`
>
> Capture is what the target's payload carries, and the outcomes that fire it.
>
> **Accepted** `func(ctx context.Context) error` *required*
>
> Accepted waits until the SDK has acked the observer as accepted, and returns nil. A call made after that reaches an active observer, since the engine handles a connection's frames in order: a host sends a new observer its initial state this way, with a call of its own function whose metadata is \{"for\_observer": \<obs.ID>}. The function's `MatchHook` gives that call to this observer alone, and iii delivers it like every call. It returns an \*`ObserveRejectionError` when the observer is rejected first (code OBSERVER\_NOT\_ACTIVE when it is removed, or its function released, first), and ctx's error when ctx is done first. Once accepted, it returns nil at once.
>
> **DecodeArgs** `func(v any) error` *required*
>
> DecodeArgs unmarshals the observer's args into v (a struct, typically).
>
> **Matches** `func(call *Call) bool` *required*
>
> Matches reports whether call reaches the observer: it fires on the call's status (its view's fires\_on) and its filters match the call. A deliver hook that calls targets itself checks it before calling through the observer; iii's delivery applies it on its own. On a call the SDK hands a hook, the observers share the call's decoded document and its values at the paths of the function's filter index, so each costs its own filter, and an observer costs a comparison when the call fails the condition it is filed under.
>
> **Reject** `func(code, message string)` *required*
>
> Reject sends a late rejection for this observer (e.g. after a config reload removed what it needs). It leaves the compiled set at once, so calls stop reaching it, and its removed hooks run next on the function's hook queue, connected or not; the engine's observerremoved that follows runs none again. The rejection is sent again when the engine replays the observer after a reconnect. Called while the observer's added hooks run, it rejects the observer once they return, and the removed hook of each level whose added hook resolved runs.

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> // an observer of expression X gets the ticks of expression X
> client.OnObservationMatchFor("cron::on-tick", iii.MatchFunc(func(obs *iii.Observer, call *iii.Call) bool {
> 	var tick struct{ Expression string `json:"expression"` }
> 	_ = json.Unmarshal(call.Input, &tick)
> 	return obs.Args["expression"] == tick.Expression
> }))
> ```

***

### MatchKeys

MatchKeys is a match hook by keys: an observer wants a call when they share a key, and the SDK keeps an index of the active observers by key instead of asking each of them. observerKeys says what an observer listens for: the SDK computes it once, when the observer is accepted, files the observer under each key and unfiles it when it is removed. An observer whose observerKeys panics is rejected with OBSERVE\_REJECTED. callKeys says what a call is: the call matches the observers filed under any of its keys, each once; a callKeys that panics matches nothing, and the SDK logs it once per function. Keys express wildcards by listing them: an observer of every table is filed under "primary|\*|insert", and a change matches "primary|orders|insert", "primary|orders|\*", "primary|\*|insert" and "primary|\*|\*". A nil function gives a nil MatchHook.

On a function matched by keys, `Client.IsObservedFor` answers with one lookup per call key.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func MatchKeys(observerKeys func(obs *Observer) []string, callKeys func(call *Call) []string) *MatchHook
```

> [tab] Parameters
>
> **observerKeys** `func(obs *Observer) []string` *required*
>
> **`Observer` fields**
>
> **ID** `string` *required*
>
> **FunctionID** `string` *required*
>
> FunctionID is the observed function (the one this worker hosts).
>
> **Args** `map[string]any`
>
> Args are the observer's args, validated against args\_format by the engine (numbers decode as float64; see DecodeArgs).
>
> **Filters** `map[string]any`
>
> Filters are the observer's filters in normal form; iii's delivery and Matches apply them.
>
> **Target** `string` *required*
>
> Target is the target function id.
>
> **Capture** `[]Capture`
>
> Capture is what the target's payload carries, and the outcomes that fire it.
>
> **Accepted** `func(ctx context.Context) error` *required*
>
> Accepted waits until the SDK has acked the observer as accepted, and returns nil. A call made after that reaches an active observer, since the engine handles a connection's frames in order: a host sends a new observer its initial state this way, with a call of its own function whose metadata is \{"for\_observer": \<obs.ID>}. The function's `MatchHook` gives that call to this observer alone, and iii delivers it like every call. It returns an \*`ObserveRejectionError` when the observer is rejected first (code OBSERVER\_NOT\_ACTIVE when it is removed, or its function released, first), and ctx's error when ctx is done first. Once accepted, it returns nil at once.
>
> **DecodeArgs** `func(v any) error` *required*
>
> DecodeArgs unmarshals the observer's args into v (a struct, typically).
>
> **Matches** `func(call *Call) bool` *required*
>
> Matches reports whether call reaches the observer: it fires on the call's status (its view's fires\_on) and its filters match the call. A deliver hook that calls targets itself checks it before calling through the observer; iii's delivery applies it on its own. On a call the SDK hands a hook, the observers share the call's decoded document and its values at the paths of the function's filter index, so each costs its own filter, and an observer costs a comparison when the call fails the condition it is filed under.
>
> **Reject** `func(code, message string)` *required*
>
> Reject sends a late rejection for this observer (e.g. after a config reload removed what it needs). It leaves the compiled set at once, so calls stop reaching it, and its removed hooks run next on the function's hook queue, connected or not; the engine's observerremoved that follows runs none again. The rejection is sent again when the engine replays the observer after a reconnect. Called while the observer's added hooks run, it rejects the observer once they return, and the removed hook of each level whose added hook resolved runs.
>
> **callKeys** `func(call *Call) []string` *required*
>
> **`Call` fields**
>
> **FunctionID** `string` *required*
>
> **InvocationID** `string` *required*
>
> **Status** `string`
>
> Status is "success" or "error". Empty reads as "error" when Error is set, and "success" otherwise.
>
> **Input** `json.RawMessage`
>
> Input is the call's input, as the handler received it.
>
> **Output** `json.RawMessage`
>
> Output is the handler's return value as JSON: what observers observe. Nil on an error.
>
> **Error** `*ErrorBody`
>
> Error is the error the handler returned. Nil on success.
>
> **Caller** `*Caller`
>
> Caller is who made the call; nil for a call made outside any handler.
>
> **Metadata** `json.RawMessage`
>
> Metadata is the call's metadata sidecar; nil when it carries none.

> [tab] Example
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.RegisterWorker(iii.DefaultEngineURL)
> defer client.Close()
>
> type timerArgs struct {
> 	EveryMs int64 `json:"every_ms"`
> }
> keyOf := func(everyMs int64) string { return fmt.Sprint(everyMs) }
> stops := map[string]chan struct{}{} // one ticker per interval, used on the hook queue only
>
> client.OnObserverAddedFor("timer::on-tick", func(ctx context.Context, obs *iii.Observer) error {
> 	var args timerArgs
> 	if err := obs.DecodeArgs(&args); err != nil || args.EveryMs <= 0 {
> 		return iii.ObserveRejection("INVALID_ARGS", "every_ms must be a positive integer")
> 	}
> 	if _, running := stops[keyOf(args.EveryMs)]; running {
> 		return nil
> 	}
> 	stop := make(chan struct{})
> 	stops[keyOf(args.EveryMs)] = stop
> 	go func() {
> 		ticker := time.NewTicker(time.Duration(args.EveryMs) * time.Millisecond)
> 		defer ticker.Stop()
> 		for {
> 			select {
> 			case <-stop:
> 				return
> 			case now := <-ticker.C:
> 				tick, _ := json.Marshal(map[string]int64{"every_ms": args.EveryMs, "fired_at_ms": now.UnixMilli()})
> 				if !client.IsObservedFor("timer::on-tick", &iii.Call{Input: tick}) {
> 					continue
> 				}
> 				client.Call(context.Background(), iii.CallRequest{FunctionID: "timer::on-tick", Payload: tick, Action: iii.VoidAction()})
> 			}
> 		}
> 	}()
> 	return nil
> })
> client.OnObserverRemovedFor("timer::on-tick", func(ctx context.Context, obs *iii.Observer) {
> 	var args timerArgs
> 	_ = obs.DecodeArgs(&args)
> 	for _, other := range client.Observers("timer::on-tick") {
> 		var o timerArgs
> 		if other != obs && other.DecodeArgs(&o) == nil && o.EveryMs == args.EveryMs {
> 			return // the interval is still observed
> 		}
> 	}
> 	if stop, ok := stops[keyOf(args.EveryMs)]; ok {
> 		close(stop)
> 		delete(stops, keyOf(args.EveryMs))
> 	}
> })
> client.OnObservationMatchFor("timer::on-tick", iii.MatchKeys(
> 	func(obs *iii.Observer) []string { // what the observer listens for
> 		var args timerArgs
> 		_ = obs.DecodeArgs(&args)
> 		return []string{keyOf(args.EveryMs)}
> 	},
> 	func(call *iii.Call) []string { // what the call is: the tick carries every_ms too
> 		var tick timerArgs
> 		_ = json.Unmarshal(call.Input, &tick)
> 		return []string{keyOf(tick.EveryMs)}
> 	},
> ))
>
> if _, err := client.RegisterFunction("timer::on-tick", iii.Identity, iii.FunctionOptions{
> 	Description: "One call per tick. Observe it with args { every_ms }.",
> 	ArgsFormat:  json.RawMessage(`{"type":"object","required":["every_ms"],"properties":{"every_ms":{"type":"integer","minimum":1}}}`),
> 	Private:     true,
> }); err != nil {
> 	log.Fatal(err)
> }
> ```

***

### MetadataFromContext

MetadataFromContext returns the per-invocation metadata attached to a handler's ctx. The bool is false (and the value nil) when the invocation carried no metadata, letting a handler distinguish "no metadata" from a present-but-empty value. Metadata is any JSON value; JSON null is no metadata.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func MetadataFromContext(ctx context.Context) (json.RawMessage, bool)
```

> [tab] Parameters
>
> **ctx** `context.Context` *required*

> [tab] Examples
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> func handler(ctx context.Context, data json.RawMessage) (any, error) {
>     if md, ok := iii.MetadataFromContext(ctx); ok {
>         // ... use md (raw JSON) ...
>     }
>     ...
> }
> ```
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.New(iii.DefaultEngineURL)
>
> client.RegisterFunction("orders::create",
> 	func(ctx context.Context, data json.RawMessage) (any, error) {
> 		metadata, ok := iii.MetadataFromContext(ctx)
> 		return map[string]any{"ok": true, "has_metadata": ok, "metadata": metadata}, nil
> 	})
> ```

***

### New

New creates a `Client` for the engine at url (e.g. `DefaultEngineURL`). An empty url means `EngineURLFromEnv`: III\_URL when set, else DefaultEngineURL. It does not connect; call `Client.Connect` to start the connection lifecycle, or use `RegisterWorker` to create and connect in one step.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func New(url string, opts ...Option) *Client
```

#### Parameters

**url** `string` *required*

**opts** `...Option`

**`Option` fields**

**WithDescription** `func(description string) Option` *required*

WithDescription sets a one-line, human/LLM-readable summary of what this worker does. Surfaces in engine::workers::list / engine::workers::info.

**WithHeaders** `func(h http.Header) Option` *required*

WithHeaders sets HTTP headers sent on the WebSocket handshake of the main connection and of channels created with `Client.CreateChannel`. The III\_TOKEN environment variable, when set and not blank, adds "Authorization: Bearer \<token>" unless h already carries an Authorization header.

**WithInvocationTimeout** `func(d time.Duration) Option` *required*

WithInvocationTimeout sets how long `Client.Call` waits for a result when `CallRequest`.Timeout is zero, in place of `DefaultInvocationTimeout`. It is the invocationTimeoutMs option of the Node SDK's registerWorker. A d of zero or less keeps the default.

**WithMetricsReporting** `func(enabled bool) Option` *required*

WithMetricsReporting turns the worker resource gauges on or off (Node enableMetricsReporting): the iii.worker.memory.\*, iii.worker.cpu.\*, iii.worker.event\_loop.lag\_ms and iii.worker.uptime\_seconds metrics, tagged with worker.id and worker.name. The default is true. The gauges start when the engine acknowledges the worker and stop when the connection drops; they need telemetry and its metrics to be enabled (see `OtelConfig`).

**WithName** `func(name string) Option` *required*

WithName sets the worker name reported to the engine. A non-empty III\_WORKER\_NAME takes precedence. The default is "hostname:pid".

**WithNamespace** `func(namespace string) Option` *required*

WithNamespace sets the namespace this worker registers into. Takes precedence over the III\_NAMESPACE environment variable.

**WithOtel** `func(cfg OtelConfig) Option` *required*

WithOtel configures the client's OpenTelemetry pipeline (the otel option of Node registerWorker). Telemetry starts with the connection lifecycle (`RegisterWorker` or `Client.Connect`) and `Client.Close` flushes and stops it. Without this option the defaults documented on `OtelConfig` apply, and they enable telemetry.

**WithReconnectConfig** `func(c ReconnectConfig) Option` *required*

WithReconnectConfig overrides the default reconnect schedule.

**WithTelemetry** `func(t TelemetryOptions) Option` *required*

WithTelemetry sets what the worker reports about its language, project and framework on every connection. Blank fields keep their defaults (see `TelemetryOptions`).

***

### ObserveRejection

ObserveRejection builds the error an `AddedHook` returns to reject an observer with code (any other error rejects it with OBSERVE\_REJECTED).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func ObserveRejection(code, message string) error
```

#### Parameters

**code** `string` *required*

**message** `string` *required*

***

### OpenReader

OpenReader returns a reader for the read end described by ref, against the given engine WS base (typically Client.Address()). The socket connects on first read and authenticates with III\_TOKEN, when set; channels from `Client.CreateChannel` also send the client's `WithHeaders`. Prefer `Client.OpenReader`, which uses the client's address and headers.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func OpenReader(engineWSBase string, ref StreamChannelRef) *ChannelReader
```

#### Parameters

**engineWSBase** `string` *required*

**ref** `StreamChannelRef` *required*

**`StreamChannelRef` fields**

**ChannelID** `string` *required*

**AccessKey** `string` *required*

**Direction** `ChannelDirection` *required*

***

### OpenWriter

OpenWriter returns a writer for the write end described by ref, against the given engine WS base (typically Client.Address()). The socket connects on first send and authenticates with III\_TOKEN, when set; channels from `Client.CreateChannel` also send the client's `WithHeaders`. Prefer `Client.OpenWriter`, which uses the client's address and headers.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func OpenWriter(engineWSBase string, ref StreamChannelRef) *ChannelWriter
```

#### Parameters

**engineWSBase** `string` *required*

**ref** `StreamChannelRef` *required*

**`StreamChannelRef` fields**

**ChannelID** `string` *required*

**AccessKey** `string` *required*

**Direction** `ChannelDirection` *required*

***

### RegisterFunctionTyped

RegisterFunctionTyped registers a function whose request and response schemas are inferred from the Req and Resp type parameters and advertised to the engine. It is the schema-aware counterpart of `Client.RegisterFunction`; reach for it when you want the engine (and its dashboard / typed callers) to know the function's contract.

It returns the function's handle, as `Client.RegisterFunction` does.

Use `Client.RegisterFunction` directly for schemaless functions. See `InferSchema` to obtain a type's schema on its own. Pass a single `RegisterFunctionOptions` value to attach a description, registration metadata, args\_format, private or observable; its RequestFormat and ResponseFormat, when set, replace the inferred schemas (that type is then not reflected). Its handle sets the function's hooks, as `Client.RegisterFunction`'s does.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func RegisterFunctionTyped[Req any, Resp any](c *Client, id string, handler TypedHandler[Req, Resp], opts ...RegisterFunctionOptions) (*FunctionRef, error)
```

> [tab] Parameters
>
> **c** `*Client` *required*
>
> **id** `string` *required*
>
> **handler** `TypedHandler[Req, Resp]` *required*
>
> **opts** `...RegisterFunctionOptions`
>
> **`RegisterFunctionOptions` fields**
>
> **Metadata** `json.RawMessage`
>
> Metadata is arbitrary JSON attached to the function registration. It is stored with the function and is distinct from the per-invocation metadata passed to handlers.
>
> **LocalDispatch** `*bool`
>
> LocalDispatch false keeps every Call of this function on the engine path; nil or true lets Call run it in-process once the engine confirms it (see `Client.Call`: observers see snapshots of the payload and metadata taken at call start, and a handler may outlive a caller whose deadline passed).
>
> **Description** `string`
>
> Description is a human/LLM-readable summary of the function, sent as "description" and surfaced by engine::functions::list and engine::functions::info. Empty omits it.
>
> **RequestFormat** `json.RawMessage`
>
> RequestFormat is the JSON Schema of the function's request, sent as "request\_format". It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Req.
>
> **ResponseFormat** `json.RawMessage`
>
> ResponseFormat is the JSON Schema of the function's response, sent as "response\_format": the handler's return value, which is what observers observe. It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Resp.
>
> **ArgsFormat** `json.RawMessage`
>
> ArgsFormat is the JSON Schema of the observers' args, sent as "args\_format": the engine validates every observer's args against it (INVALID\_ARGS), and refuses a schema that is not valid (INVALID\_ARGS\_FORMAT). It must be valid JSON; nil means the function takes no args.
>
> **Private** `bool`
>
> Private lets only this worker call the function (the engine answers anyone else with FORBIDDEN); other workers may observe it, and it cannot be an observer's target. A call this worker makes to it passes the input and the result by reference: the handler and the hooks treat both as read-only.
>
> **Observable** `*bool`
>
> Observable false refuses every observer of the function (NOT\_OBSERVABLE), for a function whose payloads carry credentials. nil means true.

> [tab] Examples
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> iii.RegisterFunctionTyped[CreateOrderRequest, OrderResult](client, "orders::create",
>     func(ctx context.Context, req CreateOrderRequest) (OrderResult, error) { ... })
> ```
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> client := iii.RegisterWorker(iii.DefaultEngineURL)
> defer client.Close()
>
> iii.RegisterFunctionTyped(client, "hello::greet",
> 	func(ctx context.Context, req greetRequest) (greetResponse, error) {
> 		return greetResponse{Message: "Hello, " + req.Name + "!"}, nil
> 	})
> ```

***

### UnmarshalMessage

UnmarshalMessage decodes a wire frame by peeking at "type" and then decoding into the matching variant struct. It is the Go counterpart of serde's tag dispatch.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func UnmarshalMessage(data []byte) (*DecodedMessage, error)
```

#### Parameters

**data** `[]byte` *required*

***

### VoidAction

VoidAction returns the fire-and-forget action. A call with this action carries no invocation\_id, so the engine sends no reply (see `Client.Call`).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func VoidAction() *CallAction
```

***

### WithDescription

WithDescription sets a one-line, human/LLM-readable summary of what this worker does. Surfaces in engine::workers::list / engine::workers::info.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithDescription(description string) Option
```

#### Parameters

**description** `string` *required*

***

### WithHeaders

WithHeaders sets HTTP headers sent on the WebSocket handshake of the main connection and of channels created with `Client.CreateChannel`. The III\_TOKEN environment variable, when set and not blank, adds "Authorization: Bearer \<token>" unless h already carries an Authorization header.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithHeaders(h http.Header) Option
```

#### Parameters

**h** `http.Header` *required*

***

### WithInvocationTimeout

WithInvocationTimeout sets how long `Client.Call` waits for a result when `CallRequest`.Timeout is zero, in place of `DefaultInvocationTimeout`. It is the invocationTimeoutMs option of the Node SDK's registerWorker. A d of zero or less keeps the default.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithInvocationTimeout(d time.Duration) Option
```

#### Parameters

**d** `time.Duration` *required*

***

### WithMetricsReporting

WithMetricsReporting turns the worker resource gauges on or off (Node enableMetricsReporting): the iii.worker.memory.\*, iii.worker.cpu.\*, iii.worker.event\_loop.lag\_ms and iii.worker.uptime\_seconds metrics, tagged with worker.id and worker.name. The default is true. The gauges start when the engine acknowledges the worker and stop when the connection drops; they need telemetry and its metrics to be enabled (see `OtelConfig`).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithMetricsReporting(enabled bool) Option
```

#### Parameters

**enabled** `bool` *required*

***

### WithName

WithName sets the worker name reported to the engine. A non-empty III\_WORKER\_NAME takes precedence. The default is "hostname:pid".

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithName(name string) Option
```

#### Parameters

**name** `string` *required*

***

### WithNamespace

WithNamespace sets the namespace this worker registers into. Takes precedence over the III\_NAMESPACE environment variable.

A namespace named and left blank is refused, not read as "no namespace": the client is put in a terminal failed state, so `Client.Connect` returns the reason and `Client.FatalError` reports it. Absent and blank ask for opposite things: absent asks for the engine's `default`, blank names a namespace and gives nothing to name it with. Read as absent, the worker registers in `default`, and since a worker's calls and observers follow its namespace, the whole project quietly serves from a place its declaration never named.

To register in the engine's default namespace, do not call this.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithNamespace(namespace string) Option
```

#### Parameters

**namespace** `string` *required*

***

### WithOtel

WithOtel configures the client's OpenTelemetry pipeline (the otel option of Node registerWorker). Telemetry starts with the connection lifecycle (`RegisterWorker` or `Client.Connect`) and `Client.Close` flushes and stops it. Without this option the defaults documented on `OtelConfig` apply, and they enable telemetry.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithOtel(cfg OtelConfig) Option
```

> [tab] Parameters
>
> **cfg** `OtelConfig` *required*
>
> **`OtelConfig` fields**
>
> **Enabled** `*bool`
>
> Enabled turns telemetry export on or off. Nil reads OTEL\_ENABLED ("false", "0", "no" or "off" disable it); the default is true.
>
> **ServiceName** `string`
>
> ServiceName is the service.name resource attribute and the instrumentation scope of the SDK's tracer, meter and logger. Empty reads OTEL\_SERVICE\_NAME; the default is "iii-go".
>
> **ServiceVersion** `string`
>
> ServiceVersion is the service.version resource attribute. Empty reads SERVICE\_VERSION; the default is "unknown".
>
> **ServiceNamespace** `string`
>
> ServiceNamespace is the service.namespace resource attribute, omitted when empty. Empty reads SERVICE\_NAMESPACE.
>
> **ServiceInstanceID** `string`
>
> ServiceInstanceID is the service.instance.id resource attribute. Empty reads SERVICE\_INSTANCE\_ID; the default is a random UUID.
>
> **MetricsEnabled** `*bool`
>
> MetricsEnabled turns metrics export on or off. Nil reads OTEL\_METRICS\_ENABLED ("false", "0", "no" or "off" disable it); the default is true.
>
> **MetricsExportInterval** `time.Duration`
>
> MetricsExportInterval is the period of the metrics export. Zero means 60 seconds.
>
> **SpansFlushInterval** `time.Duration`
>
> SpansFlushInterval is how long an ended span waits in the batch before it is sent. Zero reads OTEL\_SPANS\_FLUSH\_INTERVAL\_MS (milliseconds); the default is 100ms.
>
> **LogsFlushInterval** `time.Duration`
>
> LogsFlushInterval is how long a log record waits in the batch before it is sent. Zero reads OTEL\_LOGS\_FLUSH\_INTERVAL\_MS (milliseconds); the default is 100ms.
>
> **LogsBatchSize** `int`
>
> LogsBatchSize is the maximum number of log records per export. Zero reads OTEL\_LOGS\_BATCH\_SIZE; the default is 1.
>
> **FetchInstrumentationEnabled** `*bool`
>
> FetchInstrumentationEnabled turns the HTTP client spans of observability.NewTransport and observability.NewHTTPClient on or off (the Go counterpart of Node's fetch instrumentation). Nil means true.
>
> **ReconnectionConfig** `*ReconnectConfig`
>
> ReconnectionConfig is the reconnect schedule of the telemetry WebSocket. Nil means `DefaultReconnectConfig`; a non-nil value is used as given.

> [tab] Examples
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> c := iii.RegisterWorker(url, iii.WithOtel(iii.OtelConfig{ServiceName: "orders"}))
> ```
>
> ```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> // To turn telemetry off:
> off := false
> c := iii.RegisterWorker(url, iii.WithOtel(iii.OtelConfig{Enabled: &off}))
> ```

***

### WithReconnectConfig

WithReconnectConfig overrides the default reconnect schedule.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithReconnectConfig(c ReconnectConfig) Option
```

#### Parameters

**c** `ReconnectConfig` *required*

**`ReconnectConfig` fields**

**InitialDelay** `time.Duration` *required*

InitialDelay is the delay before the first reconnect attempt.

**MaxDelay** `time.Duration` *required*

MaxDelay caps the backoff.

**BackoffMultiplier** `float64` *required*

BackoffMultiplier scales the delay after each failed attempt.

**JitterFactor** `float64` *required*

JitterFactor (0..1) randomizes each delay by up to ±JitterFactor to avoid a thundering herd of workers reconnecting in lockstep.

**MaxRetries** `int`

MaxRetries caps reconnect attempts; -1 means retry forever (the default).

**DefaultReconnectConfig** `func() ReconnectConfig` *required*

DefaultReconnectConfig is the reconnect schedule shared with the Node SDK: start at 1s, double each time, cap at 30s, ±30% jitter, retry forever.

***

### WithTelemetry

WithTelemetry sets what the worker reports about its language, project and framework on every connection. Blank fields keep their defaults (see `TelemetryOptions`).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func WithTelemetry(t TelemetryOptions) Option
```

#### Parameters

**t** `TelemetryOptions` *required*

**`TelemetryOptions` fields**

**Language** `string`

Language reported for the worker. Default: the locale of LANG (else LC\_ALL) without its encoding, for example "en\_US" for LANG=en\_US.UTF-8, which is the Node SDK's fallback; omitted when neither variable is set.

**ProjectName** `string`

ProjectName is the project the worker belongs to. Default: the last element of the module path in the nearest go.mod, searched from the working directory upwards, skipping a major-version suffix such as /v2; omitted when there is no go.mod.

**Framework** `string`

Framework the worker is built with. Default: "iii-go". Surrounding spaces are trimmed.

***

### filter.Compile

Compile validates and compiles a filter given as a Go value. A json.RawMessage or \[]byte is read as JSON text; any other value is read through encoding/json, so what is validated is exactly what travels on the wire. It returns an \*Error with code INVALID\_FILTER when the filter is not valid.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func Compile(src any) (*Filter, error)
```

#### Parameters

**src** `any` *required*

***

### filter.MatchAll

MatchAll returns the filter that matches every document (\{}).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func MatchAll() *Filter
```

***

### filter.Parse

Parse validates and compiles a filter given as JSON text. It returns an \*Error with code INVALID\_FILTER when the filter is not valid.

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func Parse(data []byte) (*Filter, error)
```

#### Parameters

**data** `[]byte` *required*

***

### filter.ParseNormalized

ParseNormalized compiles a filter in the normal form the engine pushes (functionregistered, observeradded). Every rule applies except the encoded size limit, which the engine checked on the filter as written (a normal form can be longer).

**Signature**

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
func ParseNormalized(data []byte) (*Filter, error)
```

#### Parameters

**data** `[]byte` *required*

## Constants

### iii

EngineFunctions are the engine's built-in function paths a worker can invoke with `Client.Call`. Mirrors EngineFunctions in the Node SDK. A function row of engine::functions::list and engine::functions::info carries observer\_count, and args\_format, private and observable when set.

| Name | Value | Description |
| - | - | - |
| `FnListFunctions` | `"engine::functions::list"` | - |
| `FnInfoFunctions` | `"engine::functions::info"` | - |
| `FnListWorkers` | `"engine::workers::list"` | - |
| `FnInfoWorkers` | `"engine::workers::info"` | - |
| `FnRegisterWorker` | `"engine::workers::register"` | - |
| `FnCreateChannel` | `"engine::channels::create"` | - |

Engine-hosted sources: private functions of the engine whose handler returns the event, so input and output are both the event. Observe them with `Client.Observe` and filters on input; only the engine calls them. `EngineSources` names them as the other SDKs do.

| Name | Value | Description |
| - | - | - |
| `FnOnFunctionsChanged` | `"engine::on-functions-changed"` | FnOnFunctionsChanged is called when the function set changes: \{ event: "functions\_changed", functions: \[...] }. |
| `FnOnWorkersChanged` | `"engine::on-workers-changed"` | FnOnWorkersChanged is called when a worker connects, disconnects or updates its metadata: \{ event, worker\_id }. |
| `FnOnObserversChanged` | `"engine::on-observers-changed"` | FnOnObserversChanged is called once per observer state change: \{ observer\_id, function\_id, target, state, reason? }. |
| `FnOnLog` | `"observability::on-log"` | FnOnLog is called once per log record. |
| `FnOnSpan` | `"observability::on-span"` | FnOnSpan is called once per window of recorded spans: \{ trace\_ids }. |
| `FnOnConfigurationUpdate` | `"configuration::on-updated"` | FnOnConfigurationUpdate is called when a configuration entry is registered, updated or deleted. |

Log function paths. Mirrors LogFunctions in the Node SDK. Call these with `VoidAction` (fire-and-forget) to write to the engine log.

| Name | Value | Description |
| - | - | - |
| `LogInfo` | `"engine::log::info"` | - |
| `LogWarn` | `"engine::log::warn"` | - |
| `LogError` | `"engine::log::error"` | - |
| `LogDebug` | `"engine::log::debug"` | - |

Features this SDK declares in its engine::workers::register payload. The engine advertises the first three in WorkerRegistered: observe.v2 includes observe.v1, and with it this worker runs the match and deliver hooks of the functions it hosts for their routed calls, whose observers' capture and filters the engine evaluates. FeatureLargeMessagesV1 is declared by the SDK only: it receives JSON messages up to 64 MiB, so the engine sends it messages of that size whatever version it reports.

| Name | Value | Description |
| - | - | - |
| `FeatureObserveV1` | `"observe.v1"` | - |
| `FeatureObserveV2` | `"observe.v2"` | - |
| `FeatureObserveCapture` | `"observe.capture"` | FeatureObserveCapture: the host reads the outcomes that fire an observer from the fires\_on of its observer views, never from capture, and sends no source.on. An engine with it announces fires\_on on every view. |
| `FeatureLocalDispatchV1` | `"local-dispatch.v1"` | - |
| `FeatureLargeMessagesV1` | `"large-messages.v1"` | - |

Engine functions of the observer registry.

| Name | Value | Description |
| - | - | - |
| `FnListObservers` | `"engine::observers::list"` | - |
| `FnGetObserver` | `"engine::observers::get"` | - |
| `FnDisposeObserver` | `"engine::observers::dispose"` | - |

| Name | Value | Description |
| - | - | - |
| `DefaultEngineURL` | `"ws://127.0.0.1:49134"` | DefaultEngineURL is the worker WebSocket endpoint of a locally-running engine, used when neither an explicit address nor III\_URL is set (see `EngineURLFromEnv`). The engine listens for workers here by default; its HTTP API is on :3111. The IPv4 loopback is spelled out on purpose: "localhost" can resolve to ::1 on a host whose engine only listens on IPv4. Same value as DEFAULT\_ENGINE\_URL in the Node, Python and Rust SDKs. |
| `DefaultInvocationTimeout` | `30 * time.Second` | DefaultInvocationTimeout bounds how long Call waits for an InvocationResult before failing with ErrTimeout, unless `WithInvocationTimeout` or `CallRequest`.Timeout says otherwise. Same default (30000 ms) as DEFAULT\_INVOCATION\_TIMEOUT\_MS in the Node SDK. |

### iii/filter

Structural limits of a filter (the conformance vectors define how they count).

| Name | Value | Description |
| - | - | - |
| `MaxDepth` | `8` | MaxDepth is the deepest a filter may nest; the top-level filter is at depth 1. |
| `MaxNodes` | `64` | MaxNodes bounds the operator and $or / $not nodes of one filter. |
| `MaxSetItems` | `256` | MaxSetItems bounds the items of one $in / $nin array. |
| `MaxPathSegments` | `16` | MaxPathSegments bounds the segments after a path's root. |
| `MaxEncodedBytes` | `8192` | MaxEncodedBytes bounds the UTF-8 size of the filter's compact JSON encoding. |
| `MaxSafeLiteral` | `1 << 53` | MaxSafeLiteral bounds numeric literals: \|n\| \<= 2^53. |
| `MaxLiteralDepth` | `32` | MaxLiteralDepth bounds the arrays / objects nested in one $eq / $ne operand or one $in / $nin item (a scalar is 0, \[] is 1). |
| `MaxJSONDepth` | `120` | MaxJSONDepth bounds the arrays / objects nested in the whole filter value, \$and wrappers and literals included (\{} is 1). |

## Variables

### iii

Sentinel errors for the two failure modes that carry no payload. Compare with errors.Is. These line up with the Rust IIIError::NotConnected / ::Timeout variants.

| Name | Value | Description |
| - | - | - |
| `ErrNotConnected` | `errors.New("iii: not connected")` | ErrNotConnected is returned by Call when the client has no live connection and the call cannot be buffered (a call through an observer, or one cancelled by shutdown). |
| `ErrTimeout` | `errors.New("iii: invocation timed out")` | ErrTimeout is returned when no InvocationResult arrives within the call's timeout (DefaultInvocationTimeout unless overridden). |

| Name | Value | Description |
| - | - | - |
| `EngineSources` | `struct { FunctionsChanged string; WorkersChanged string; ObserversChanged string; Log string; Span string; ConfigurationUpdated string }{FunctionsChanged: FnOnFunctionsChanged, WorkersChanged: FnOnWorkersChanged, ObserversChanged: FnOnObserversChanged, Log: FnOnLog, Span: FnOnSpan, ConfigurationUpdated: FnOnConfigurationUpdate}` | EngineSources are the ids of the engine-hosted sources under the names every SDK gives them (EngineSources in the Node, browser, Python and Rust SDKs): EngineSources.FunctionsChanged is `FnOnFunctionsChanged`, and so on. `client.ObserveFunction(iii.EngineSources.FunctionsChanged, "catalog::refresh")` |

## Types

### iii

Package iii is a Go SDK for the iii worker protocol.

[`Activation`](#activation) · [`AddedHook`](#addedhook) · [`Args`](#args) · [`Call`](#call) · [`CallAction`](#callaction) · [`CallRequest`](#callrequest) · [`Caller`](#caller) · [`Capture`](#capture) · [`Channel`](#channel) · [`ChannelDirection`](#channeldirection) · [`ChannelReader`](#channelreader) · [`ChannelWriter`](#channelwriter) · [`ConnectionState`](#connectionstate) · [`DecodedMessage`](#decodedmessage) · [`DeliverHook`](#deliverhook) · [`Dispatch`](#dispatch) · [`DroppedReports`](#droppedreports) · [`ErrorBody`](#errorbody) · [`Filters`](#filters) · [`FunctionOptions`](#functionoptions) · [`FunctionRef`](#functionref) · [`FunctionRegisteredMessage`](#functionregisteredmessage) · [`HTTPInvocationRef`](#httpinvocationref) · [`Handler`](#handler) · [`Invocation`](#invocation) · [`InvocationError`](#invocationerror) · [`InvocationResultMessage`](#invocationresultmessage) · [`InvocationsObservedMessage`](#invocationsobservedmessage) · [`InvokeFunctionMessage`](#invokefunctionmessage) · [`LocalFunctionStats`](#localfunctionstats) · [`LocalInvocationStatsMessage`](#localinvocationstatsmessage) · [`MatchHook`](#matchhook) · [`MessageType`](#messagetype) · [`MiddlewareFunctionInput`](#middlewarefunctioninput) · [`ObserveMessage`](#observemessage) · [`ObserveMessageSource`](#observemessagesource) · [`ObserveMessageTarget`](#observemessagetarget) · [`ObserveReason`](#observereason) · [`ObserveRejectionError`](#observerejectionerror) · [`ObserveResultMessage`](#observeresultmessage) · [`Observed`](#observed) · [`ObservedItem`](#observeditem) · [`Observer`](#observer) · [`ObserverAckMessage`](#observerackmessage) · [`ObserverAddedMessage`](#observeraddedmessage) · [`ObserverHandle`](#observerhandle) · [`ObserverInfo`](#observerinfo) · [`ObserverOptions`](#observeroptions) · [`ObserverRemovedMessage`](#observerremovedmessage) · [`ObserverState`](#observerstate) · [`ObserverStateChangedMessage`](#observerstatechangedmessage) · [`Op`](#op) · [`Option`](#option) · [`OtelConfig`](#otelconfig) · [`PingMessage`](#pingmessage) · [`PongMessage`](#pongmessage) · [`ReattachMessage`](#reattachmessage) · [`ReconnectConfig`](#reconnectconfig) · [`RegisterFunctionMessage`](#registerfunctionmessage) · [`RegisterFunctionOptions`](#registerfunctionoptions) · [`RegisterServiceMessage`](#registerservicemessage) · [`RegistrationRejectedError`](#registrationrejectederror) · [`RegistrationRejectedMessage`](#registrationrejectedmessage) · [`RemovedHook`](#removedhook) · [`Source`](#source) · [`StreamChannelRef`](#streamchannelref) · [`TelemetryOptions`](#telemetryoptions) · [`TypedHandler`](#typedhandler) · [`UnobserveMessage`](#unobservemessage) · [`UnregisterFunctionMessage`](#unregisterfunctionmessage) · [`WorkerRegisteredMessage`](#workerregisteredmessage)

#### Activation

Activation marks the invocation of an observer's target.

| Name | Type | Required | Description |
| - | - | - | - |
| `ObserverID` | `string` | Yes | - |
| `ActivationID` | `string` | No | - |

***

#### AddedHook

AddedHook runs when an observer is added to a function this worker hosts, before the SDK acks it. Returning nil accepts the observer; an error built with `ObserveRejection` rejects it with that code, any other error with OBSERVE\_REJECTED. ctx is cancelled after 30 s, when the observer fails with OWNER\_TIMEOUT.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type AddedHook func(ctx context.Context, obs *Observer) error
```

***

#### Args

Args are an observer's arguments, for the worker that hosts the observed function: validated against the function's args\_format by the engine, and read by its hooks.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Args map[string]any
```

***

#### Call

Call is one call of a function, as its host sees it in a `MatchHook`, a `DeliverHook` and `Observer.Matches`. The fields of a call the SDK hands over are read-only.

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `InvocationID` | `string` | Yes | - |
| `Status` | `string` | No | Status is "success" or "error". Empty reads as "error" when Error is set, and "success" otherwise. |
| `Input` | `json.RawMessage` | No | Input is the call's input, as the handler received it. |
| `Output` | `json.RawMessage` | No | Output is the handler's return value as JSON: what observers observe. Nil on an error. |
| `Error` | \*[`ErrorBody`](#errorbody) | No | Error is the error the handler returned. Nil on success. |
| `Caller` | \*[`Caller`](#caller) | No | Caller is who made the call; nil for a call made outside any handler. |
| `Metadata` | `json.RawMessage` | No | Metadata is the call's metadata sidecar; nil when it carries none. |

***

#### CallAction

CallAction mirrors the engine's CallAction enum, a "type"-tagged lowercase union: \{"type":"void"} or \{"type":"enqueue","queue":"..."}. It rides on InvokeFunction.Action (the wire field "action") and selects the delivery semantics of a `Client.Call`.

| Name | Type | Required | Description |
| - | - | - | - |
| `Type` | `string` | Yes | Type is "void" (fire-and-forget) or "enqueue" (route via a named queue). |
| `Queue` | `string` | No | Queue is set only when Type == "enqueue". |
| `EnqueueAction` | func(queue string) \*[`CallAction`](#callaction) | Yes | EnqueueAction routes an invocation through a named queue; the engine acknowledges with an enqueue receipt rather than the function's result. |
| `VoidAction` | func() \*[`CallAction`](#callaction) | Yes | VoidAction returns the fire-and-forget action. A call with this action carries no invocation\_id, so the engine sends no reply (see `Client.Call`). |

***

#### CallRequest

CallRequest is the input to `Client.Call`. The Action field selects the delivery semantics: nil/await (default) waits for the result; `VoidAction` is fire-and-forget; `EnqueueAction` routes through a named queue and awaits its receipt.

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | No | FunctionID is the function to invoke (e.g. an EngineFunctions constant). Empty with Observer. |
| `Observer` | \*[`Observer`](#observer) | No | Observer calls through an observer of a function this worker hosts: the engine runs the observer's target with Payload as given, after checking that this worker hosts the observer's function (NOT\_FUNCTION\_OWNER) and that the observer exists and is active (OBSERVER\_NOT\_FOUND, OBSERVER\_NOT\_ACTIVE), and applies its policy (max\_fires, expiry, max\_in\_flight: OVERFLOW), metadata sidecar and loop guard. The call goes out on the current connection, after the acks already sent on it; FunctionID, Namespace and an enqueue action do not apply. It takes no Metadata: the target's metadata is the observer's registration metadata, and Call refuses a request with Metadata before anything is sent. A call through an observer belongs in the function's `DeliverHook`, which applies the observer's filters with `Observer.Matches`; to send one observer something outside delivery (its initial state), call the function with metadata \{"for\_observer": \<obs.ID>} (see `Observer.Accepted`). |
| `Payload` | `json.RawMessage` | No | Payload is the JSON input of the function (payload in the Node, Python and Rust SDKs). Nil is sent as \{}. |
| `Data` | `json.RawMessage` | No | **Deprecated.** Use Payload. Data is the JSON input of the function, used only when Payload is nil. |
| `Metadata` | `json.RawMessage` | No | Metadata is optional per-invocation metadata (any JSON value) sent alongside the payload on the wire's metadata channel, not folded into it. nil and JSON null are no metadata: omitted, and the handler's MetadataFromContext reports none. |
| `Action` | \*[`CallAction`](#callaction) | No | Action selects void/enqueue semantics; nil means the default await path. |
| `Timeout` | `time.Duration` | No | Timeout bounds an await/enqueue call. Zero means the client's invocation timeout: `WithInvocationTimeout`, else `DefaultInvocationTimeout`. |
| `Namespace` | `string` | No | Namespace routes the invocation to a specific namespace. Empty means this worker's namespace, except engine::\* and configuration::\* builtins, which resolve in the engine's default namespace. |
| `Dispatch` | [`Dispatch`](#dispatch) | No | Dispatch selects the path to a function this worker hosts: the zero value (or `DispatchAuto`) runs it in-process once the engine confirmed it for local dispatch; `DispatchRemote` always goes through the engine. |

***

#### Caller

Caller is who started an invocation: the worker (WorkerID / WorkerName, stamped by the engine and never forgeable), and the function and invocation the call was made from. Parent is the caller of that handler, one level only. Every field may be empty: a top-level call has no function.

| Name | Type | Required | Description |
| - | - | - | - |
| `WorkerID` | `string` | No | - |
| `WorkerName` | `string` | No | - |
| `FunctionID` | `string` | No | - |
| `InvocationID` | `string` | No | - |
| `Parent` | \*[`Caller`](#caller) | No | - |

***

#### Capture

Capture names a part of an observed invocation shipped to an observer's target. The default is input, output and caller. Capture also decides which outcomes fire the target: input and output fire on success, error on error; caller and metadata only add to the payload.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Capture string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `CaptureInput` | `"input"` | Yes | - |
| `CaptureOutput` | `"output"` | Yes | - |
| `CaptureError` | `"error"` | Yes | - |
| `CaptureCaller` | `"caller"` | Yes | - |
| `CaptureMetadata` | `"metadata"` | Yes | - |

***

#### Channel

Channel is a freshly created channel: a live writer/reader plus the refs for each end. Pass WriterRef or ReaderRef to another worker (in a call payload) so it opens the opposite end with `Client.OpenReader` / `Client.OpenWriter`. Created by `Client.CreateChannel`.

| Name | Type | Required | Description |
| - | - | - | - |
| `Writer` | \*[`ChannelWriter`](#channelwriter) | No | - |
| `Reader` | \*[`ChannelReader`](#channelreader) | No | - |
| `WriterRef` | [`StreamChannelRef`](#streamchannelref) | Yes | - |
| `ReaderRef` | [`StreamChannelRef`](#streamchannelref) | Yes | - |

***

#### ChannelDirection

ChannelDirection is the end of a streaming channel a StreamChannelRef refers to. On the wire it is lowercase: "read" or "write".

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type ChannelDirection string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `ChannelRead` | `"read"` | Yes | - |
| `ChannelWrite` | `"write"` | Yes | - |

***

#### ChannelReader

ChannelReader is the read end of a channel: a WebSocket opened lazily on first use. Binary frames are stream data (NextBinary / ReadAll); text frames are messages delivered to OnMessage callbacks. The reader reads from a single goroutine internally, so register OnMessage callbacks before the first NextBinary/ReadAll call.

| Name | Type | Required | Description |
| - | - | - | - |
| `OpenReader` | func(engineWSBase string, ref [`StreamChannelRef`](#streamchannelref)) \*[`ChannelReader`](#channelreader) | Yes | OpenReader returns a reader for the read end described by ref, against the given engine WS base (typically Client.Address()). The socket connects on first read and authenticates with III\_TOKEN, when set; channels from `Client.CreateChannel` also send the client's `WithHeaders`. Prefer `Client.OpenReader`, which uses the client's address and headers. |
| `Close` | `func() error` | Yes | Close closes the reader socket. A read it cuts short, and any read after it, ends as end of stream, not an error. It is idempotent and safe to call after ReadAll. |
| `NextBinary` | `func(ctx context.Context) (data []byte, ok bool, err error)` | Yes | NextBinary returns the next binary stream chunk. ok is false at end of stream (the writer closed cleanly, or this reader was closed). If the writer aborted or the connection dropped, err is non-nil; a close carries its code and reason (websocket.CloseStatus, errors.As with websocket.CloseError). Text frames encountered while waiting are dispatched to OnMessage callbacks and skipped, so a reader can interleave messages and stream data. |
| `OnMessage` | `func(cb func(string))` | Yes | OnMessage registers a callback for inbound text messages. Callbacks fire from the goroutine driving NextBinary/ReadAll, in registration order. |
| `ReadAll` | `func(ctx context.Context) ([]byte, error)` | Yes | ReadAll consumes the channel until the writer closes, concatenating all binary chunks. Text messages seen along the way are dispatched to OnMessage callbacks. On an error (an aborted writer included) it returns the bytes read so far with the error. |

***

#### ChannelWriter

ChannelWriter is the write end of a channel: a WebSocket opened lazily on first use. SendMessage emits a text frame; Write emits 64 KiB-chunked binary frames. Close finishes the stream. A ChannelWriter is safe for serial use by one goroutine; it is not designed for concurrent writers (mirroring the reference SDKs).

| Name | Type | Required | Description |
| - | - | - | - |
| `OpenWriter` | func(engineWSBase string, ref [`StreamChannelRef`](#streamchannelref)) \*[`ChannelWriter`](#channelwriter) | Yes | OpenWriter returns a writer for the write end described by ref, against the given engine WS base (typically Client.Address()). The socket connects on first send and authenticates with III\_TOKEN, when set; channels from `Client.CreateChannel` also send the client's `WithHeaders`. Prefer `Client.OpenWriter`, which uses the client's address and headers. |
| `Close` | `func() error` | Yes | Close finishes the stream: it pauses briefly so buffered frames flush, then closes the socket with code 1000, which the engine treats as a completed stream (the reader sees EOF). After a failed Write or SendMessage the stream is incomplete, so Close ends the socket without a close frame instead: the engine reads that as an abort and the reader gets an error, as it does when the process exits before Close. Close is idempotent. |
| `SendMessage` | `func(ctx context.Context, msg string) error` | Yes | SendMessage sends a discrete text message (a TEXT frame) on the channel. |
| `Write` | `func(ctx context.Context, data []byte) error` | Yes | Write streams bytes as BINARY frames, chunked at channelFrameSize. It implements the stream-data half of the channel; the reader receives the bytes via NextBinary/ReadAll. |

***

#### ConnectionState

ConnectionState is the lifecycle state of a Client's WebSocket. Mirrors IIIConnectionState in the Node SDK.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type ConnectionState string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `StateDisconnected` | `"disconnected"` | Yes | - |
| `StateConnecting` | `"connecting"` | Yes | - |
| `StateConnected` | `"connected"` | Yes | - |
| `StateReconnecting` | `"reconnecting"` | Yes | - |
| `StateFailed` | `"failed"` | Yes | - |

***

#### DecodedMessage

DecodedMessage is the result of UnmarshalMessage: the decoded "type" plus exactly one populated variant pointer (the rest are nil). Callers switch on Type, or check which pointer is non-nil.

| Name | Type | Required | Description |
| - | - | - | - |
| `Type` | [`MessageType`](#messagetype) | Yes | - |
| `RegisterFunction` | \*[`RegisterFunctionMessage`](#registerfunctionmessage) | No | - |
| `UnregisterFunction` | \*[`UnregisterFunctionMessage`](#unregisterfunctionmessage) | No | - |
| `InvokeFunction` | \*[`InvokeFunctionMessage`](#invokefunctionmessage) | No | - |
| `InvocationResult` | \*[`InvocationResultMessage`](#invocationresultmessage) | No | - |
| `RegisterService` | \*[`RegisterServiceMessage`](#registerservicemessage) | No | - |
| `WorkerRegistered` | \*[`WorkerRegisteredMessage`](#workerregisteredmessage) | No | - |
| `RegistrationRejected` | \*[`RegistrationRejectedMessage`](#registrationrejectedmessage) | No | - |
| `Ping` | \*[`PingMessage`](#pingmessage) | No | - |
| `Pong` | \*[`PongMessage`](#pongmessage) | No | - |
| `Observe` | \*[`ObserveMessage`](#observemessage) | No | - |
| `Unobserve` | \*[`UnobserveMessage`](#unobservemessage) | No | - |
| `ObserverAck` | \*[`ObserverAckMessage`](#observerackmessage) | No | - |
| `InvocationsObserved` | \*[`InvocationsObservedMessage`](#invocationsobservedmessage) | No | - |
| `LocalInvocationStats` | \*[`LocalInvocationStatsMessage`](#localinvocationstatsmessage) | No | - |
| `FunctionRegistered` | \*[`FunctionRegisteredMessage`](#functionregisteredmessage) | No | - |
| `ObserverAdded` | \*[`ObserverAddedMessage`](#observeraddedmessage) | No | - |
| `ObserverRemoved` | \*[`ObserverRemovedMessage`](#observerremovedmessage) | No | - |
| `ObserveResult` | \*[`ObserveResultMessage`](#observeresultmessage) | No | - |
| `ObserverStateChanged` | \*[`ObserverStateChangedMessage`](#observerstatechangedmessage) | No | - |
| `UnmarshalMessage` | func(data \[]byte) (\*[`DecodedMessage`](#decodedmessage), error) | Yes | UnmarshalMessage decodes a wire frame by peeking at "type" and then decoding into the matching variant struct. It is the Go counterpart of serde's tag dispatch. |

***

#### DeliverHook

DeliverHook delivers each call of one function itself. It receives the call and the observers that matched it (`MatchHook`, or every active observer), as they were when the call started. What it returns is the caller's result, encoded as a handler's is; the error it returns is the caller's error. iii delivers nothing for the function: the hook calls a target through its observer (`CallRequest`.Observer) and applies the observer's capture and filters with `Observer.Matches`. It runs inside the function's invocation, so calls made with ctx have that invocation as their caller.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type DeliverHook func(ctx context.Context, call *Call, observers []*Observer) (any, error)
```

***

#### Dispatch

Dispatch selects how `Client.Call` reaches a function this worker hosts.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Dispatch string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `DispatchAuto` | `"auto"` | Yes | DispatchAuto (the default, also the zero value) runs a function this worker hosts in-process once the engine confirmed it for local dispatch. |
| `DispatchRemote` | `"remote"` | Yes | DispatchRemote always goes through the engine. |

***

#### DroppedReports

DroppedReports counts report items the SDK dropped (BUFFER\_FULL, TOO\_LARGE, DISCONNECTED, INACTIVE).

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Reason` | `string` | Yes | - |
| `Count` | `uint64` | Yes | - |

***

#### ErrorBody

ErrorBody is the wire representation of a remote error: a code, a message and an optional stack trace. It is intentionally minimal; a `Client.Call` caller receives it as an `InvocationError` (read it with errors.As).

| Name | Type | Required | Description |
| - | - | - | - |
| `Code` | `string` | Yes | - |
| `Message` | `string` | Yes | - |
| `Stacktrace` | `*string` | No | - |
| `Error` | `func() string` | Yes | - |

***

#### Filters

Filters is a declarative filter over input, output, error, caller and metadata, with explicit operators only (the language of the filter subpackage):

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
iii.Filters{"input.amount": iii.Op{"$gte": 500}}
```

The engine evaluates them for the calls it routes, and the worker that hosts the function for the calls it makes to it in-process. Both match through an index of the observers' equality conditions: each observer is filed under one $eq of a scalar, or one $in of scalars, on a path of its filter's top-level \$and, and costs a call nothing when the call's value at that path is none of that condition's values. A call evaluates the whole filter of the observers filed under its value and of those with no such condition.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Filters map[string]any
```

***

#### FunctionOptions

FunctionOptions is another name for `RegisterFunctionOptions`, the one the cross-SDK specification uses.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type FunctionOptions = RegisterFunctionOptions
```

***

#### FunctionRef

FunctionRef is the handle of a registered function, returned by `Client.RegisterFunction`, `Client.RegisterHTTPFunction` and `RegisterFunctionTyped`. It mirrors the FunctionRef of the Node SDK: its hook methods set the function's hooks by its id, and return the handle for chaining.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | ID is the function id given at registration. |
| `IsObserved` | `func() bool` | Yes | IsObserved reports whether any observer is active on the function: one atomic load, so a source checks it before building an event nobody would receive. False for a handle that does not control the function's current registration. |
| `IsObservedFor` | func(call \*[`Call`](#call)) bool | Yes | IsObservedFor reports whether call would reach an observer, as `Client.IsObservedFor` does: on a function matched by keys (`MatchKeys`), whether an observer is filed under one of the call's keys; otherwise `FunctionRef.IsObserved`. False for a handle that does not control the function's current registration. |
| `Observers` | func() \[]\*[`Observer`](#observer) | Yes | Observers returns the active observers of the function, as its host sees them. Empty for a handle that does not control the function's current registration. |
| `OnObservationDeliver` | func(hook [`DeliverHook`](#deliverhook)) \*[`FunctionRef`](#functionref) | Yes | OnObservationDeliver sets the function's deliver hook, as `Client.OnObservationDeliverFor` does for its id, and returns the handle, for chaining. |
| `OnObservationMatch` | func(m \*[`MatchHook`](#matchhook)) \*[`FunctionRef`](#functionref) | Yes | OnObservationMatch sets the function's match hook, as `Client.OnObservationMatchFor` does for its id, and returns the handle, for chaining. |
| `OnObserverAdded` | func(hook [`AddedHook`](#addedhook)) \*[`FunctionRef`](#functionref) | Yes | OnObserverAdded sets the function's added hook, as `Client.OnObserverAddedFor` does for its id, and returns the handle, for chaining. |
| `OnObserverRemoved` | func(hook [`RemovedHook`](#removedhook)) \*[`FunctionRef`](#functionref) | Yes | OnObserverRemoved sets the function's removed hook, as `Client.OnObserverRemovedFor` does for its id, and returns the handle, for chaining. |
| `Unregister` | `func() error` | Yes | Unregister removes the function: the engine stops routing invocations to it, and a reconnect does not register it again. Its observe state is cleared as well: its observers are released (through the removed hooks), calls stop running in-process, and reports it still buffered are dropped. The hooks set for its id stay, for a later registration of the id. |

***

#### FunctionRegisteredMessage

FunctionRegisteredMessage is the RegisterFunction success ack, with the full current observer set. Always accepted: it replaces the set the SDK holds.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `Namespace` | `string` | Yes | - |
| `Revision` | `uint64` | Yes | - |
| `LocalDispatch` | `bool` | Yes | - |
| `Observers` | \[][`ObserverInfo`](#observerinfo) | Yes | - |

***

#### HTTPInvocationRef

HTTPInvocationRef describes an externally-hosted function the engine calls directly over HTTP: the Node SDK's HttpInvocationConfig. Register one with `Client.RegisterHTTPFunction`; it travels as the registration's "invocation" field. Auth is raw JSON because the engine's HttpAuthConfig is a tagged union: build it with `HMACAuth`, `BearerAuth` or `APIKeyAuth`.

| Name | Type | Required | Description |
| - | - | - | - |
| `URL` | `string` | Yes | URL is the endpoint the engine calls. Required. |
| `Method` | `string` | No | Method is the HTTP method (GET, POST, PUT, PATCH or DELETE). RegisterHTTPFunction sends POST when it is empty. |
| `TimeoutMs` | `*uint64` | No | TimeoutMs bounds each request, in milliseconds; nil uses the engine default. |
| `Headers` | `map[string]string` | No | Headers are sent with every request. |
| `Auth` | `json.RawMessage` | No | Auth is how the engine authenticates its requests; nil sends none. |

***

#### Handler

Handler is a registered function's implementation. data is the raw JSON the engine sent. The returned value is marshaled into the InvocationResult.result. Returning an error produces an InvocationResult.error; if the error is an \*InvocationError its code and stacktrace are preserved, otherwise it is reported with code "invocation\_failed" (matching the Rust and Node SDKs).

Per-invocation metadata, when the call carries any, rides on ctx rather than the signature (so adding it is non-breaking): read it with `MetadataFromContext`.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Handler func(ctx context.Context, data json.RawMessage) (any, error)
```

***

#### Invocation

Invocation is the invocation a handler runs in, remote or local (`InvocationFromContext`).

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `InvocationID` | `string` | Yes | - |
| `Caller` | \*[`Caller`](#caller) | No | Caller is who made the call; nil when the engine sent none (before 0.25). |
| `Activation` | \*[`Activation`](#activation) | No | Activation is set when this invocation is the activation of an observer's target. |
| `ObserveDepth` | `uint32` | Yes | ObserveDepth is the depth of the causal chain in observe hops. |
| `InvocationFromContext` | func(ctx context.Context) ([`Invocation`](#invocation), bool) | Yes | InvocationFromContext returns the invocation a handler's ctx belongs to, remote or local. ok is false outside any handler. |

***

#### InvocationError

InvocationError wraps a remote ErrorBody returned by the engine in InvocationResult.error, annotated with the function that was invoked. It is the Go counterpart of Node's IIIInvocationError: one error type across all remote failure modes (RBAC FORBIDDEN, handler error, …), disambiguated by Code.

Recover the details with errors.As:

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
var ie *iii.InvocationError
if errors.As(err, &ie) && ie.Code == "FORBIDDEN" { ... }
```

| Name | Type | Required | Description |
| - | - | - | - |
| `Code` | `string` | Yes | Code is the engine's machine-readable error code (e.g. "FORBIDDEN"). |
| `Message` | `string` | Yes | Message is the human-readable description. |
| `FunctionID` | `string` | No | FunctionID is the function whose invocation failed; empty if not known. |
| `Stacktrace` | `string` | Yes | Stacktrace is the remote stack trace, when the engine provides one. |
| `Error` | `func() string` | Yes | - |

***

#### InvocationResultMessage

InvocationResult is the worker's reply to a non-fire-and-forget InvokeFunction. InvocationID is REQUIRED (a value, not a pointer): a result with no id cannot be routed back to its caller. At most one of Result/Error is set.

| Name | Type | Required | Description |
| - | - | - | - |
| `InvocationID` | `uuid.UUID` | Yes | - |
| `FunctionID` | `string` | Yes | - |
| `Result` | `json.RawMessage` | No | - |
| `Error` | \*[`ErrorBody`](#errorbody) | No | - |
| `Traceparent` | `*string` | No | - |
| `Baggage` | `*string` | No | - |
| `Matched` | `[]string` | No | Matched lists, on an observe.v2 host's answer to a routed call of a function with a match hook, the observers the hook matched, each once (\[] when it matched none): the engine evaluates the capture and filters of those that are active observers of the function, with the call's input, caller and metadata and this answer. Absent (nil) when the host did not narrow the call's observers: the engine then evaluates every active observer of the function. Decoded leniently, as the engine reads it: null is absent, a value that is not an array is \[], and an id that is not a string is skipped. |
| `Delivered` | `bool` | No | Delivered is set on an observe.v2 host's answer to a routed call of a function whose deliver hook delivered the call: the engine activates no observer for it. It outweighs Matched. Only true sets it on decode. The engine strips both before forwarding a result to the caller. |
| `UnmarshalJSON` | `func(data []byte) error` | Yes | UnmarshalJSON decodes an InvocationResult, reading matched and delivered leniently, as the engine does: a malformed value never costs the result. |

***

#### InvocationsObservedMessage

InvocationsObservedMessage carries the matches of the local calls this worker delivered, batched. The engine delivers routed calls itself (InvocationResultMessage says which observers it evaluates).

| Name | Type | Required | Description |
| - | - | - | - |
| `Items` | \[][`ObservedItem`](#observeditem) | Yes | - |

***

#### InvokeFunctionMessage

InvokeFunction is the engine->worker (and worker->engine, via Call) call.

InvocationID is a POINTER and omitempty: when absent, the call is fire-and-forget and no InvocationResult is expected. Traceparent/Baggage carry W3C trace context across the hop. Action selects void/enqueue semantics.

Metadata is OPTIONAL per-invocation metadata (arbitrary JSON) that travels as its own channel alongside Data, NOT folded into the payload. Like the engine's skip\_serializing\_if optional fields it is omitted when nil; an inbound frame without a metadata key decodes to nil ("no metadata"), so older engines that never send it stay backward compatible. Distinct from RegisterFunction's registration-level Metadata: this one is scoped to a single call.

| Name | Type | Required | Description |
| - | - | - | - |
| `InvocationID` | `*uuid.UUID` | No | - |
| `FunctionID` | `string` | Yes | - |
| `Data` | `json.RawMessage` | Yes | - |
| `Metadata` | `json.RawMessage` | No | - |
| `Traceparent` | `*string` | No | - |
| `Baggage` | `*string` | No | - |
| `Action` | \*[`CallAction`](#callaction) | No | - |
| `Namespace` | `string` | No | Namespace routes this invocation to a specific namespace. Absent means the engine's default namespace, independent of the caller's own namespace. |
| `Caller` | \*[`Caller`](#caller) | No | Caller is who made the call. Worker -> engine: the handler the call was made from and its own caller; engine -> worker: also the calling worker, stamped by the engine. Sent and received only with observe.v1. |
| `ObserveDepth` | `uint32` | No | ObserveDepth is the depth of the causal chain in observe hops; 0 is omitted. |
| `Activation` | \*[`Activation`](#activation) | No | Activation marks an observer activation (engine -> worker) or a call through an observer (worker -> engine, ObserverID only). |
| `UnmarshalJSON` | `func(data []byte) error` | Yes | UnmarshalJSON decodes an InvokeFunction, reading the observe fields (caller, observe\_depth, activation) leniently: a malformed value is treated as absent rather than failing the frame, so the invocation is still answered. |

***

#### LocalFunctionStats

LocalFunctionStats counts one function's local invocations in a stats window.

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Count` | `uint64` | Yes | - |
| `Errors` | `uint64` | Yes | - |
| `DurationMsSum` | `float64` | Yes | - |

***

#### LocalInvocationStatsMessage

LocalInvocationStatsMessage reports local invocations and SDK-side report drops, periodically and only when non-zero.

| Name | Type | Required | Description |
| - | - | - | - |
| `WindowMs` | `uint64` | Yes | - |
| `Functions` | \[][`LocalFunctionStats`](#localfunctionstats) | No | - |
| `DroppedReports` | \[][`DroppedReports`](#droppedreports) | No | - |

***

#### MatchHook

MatchHook says which observers want each call of one function; without one, every observer wants every call. Build it with `MatchFunc` (a predicate) or `MatchKeys` (keys the SDK indexes). The observers it matches are what iii delivers the call to, after their capture and filters, or what the function's `DeliverHook` receives. A nil MatchHook, and the zero one, match every observer.

A host that sends one observer something of its own (its initial state) calls the function with metadata \{"for\_observer": \<observer id>}, and the hook gives such a call to that observer alone: a predicate compares the ids, and observerKeys also files each observer under "for\_observer:\<id>", the one key callKeys gives such a call.

| Name | Type | Required | Description |
| - | - | - | - |
| `MatchFunc` | func(predicate func(obs \*[`Observer`](#observer), call \*[`Call`](#call)) bool) \*[`MatchHook`](#matchhook) | Yes | MatchFunc is a match hook that asks predicate, once per active observer per call, whether the observer wants the call. A predicate that panics counts as no match, and the SDK logs it once per function. A nil predicate gives a nil MatchHook. |
| `MatchKeys` | func(observerKeys func(obs \*[`Observer`](#observer)) \[]string, callKeys func(call \*[`Call`](#call)) \[]string) \*[`MatchHook`](#matchhook) | Yes | MatchKeys is a match hook by keys: an observer wants a call when they share a key, and the SDK keeps an index of the active observers by key instead of asking each of them. observerKeys says what an observer listens for: the SDK computes it once, when the observer is accepted, files the observer under each key and unfiles it when it is removed. An observer whose observerKeys panics is rejected with OBSERVE\_REJECTED. callKeys says what a call is: the call matches the observers filed under any of its keys, each once; a callKeys that panics matches nothing, and the SDK logs it once per function. Keys express wildcards by listing them: an observer of every table is filed under "primary\|\*\|insert", and a change matches "primary\|orders\|insert", "primary\|orders\|\*", "primary\|\*\|insert" and "primary\|\*\|\*". A nil function gives a nil MatchHook. |

***

#### MessageType

MessageType is the value of the envelope "type" field: the lowercase name of the message variant, as the engine expects it.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type MessageType string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `MsgRegisterFunction` | `"registerfunction"` | Yes | - |
| `MsgUnregisterFunction` | `"unregisterfunction"` | Yes | - |
| `MsgInvokeFunction` | `"invokefunction"` | Yes | - |
| `MsgInvocationResult` | `"invocationresult"` | Yes | - |
| `MsgRegisterService` | `"registerservice"` | Yes | - |
| `MsgPing` | `"ping"` | Yes | - |
| `MsgPong` | `"pong"` | Yes | - |
| `MsgWorkerRegistered` | `"workerregistered"` | Yes | - |
| `MsgReattach` | `"reattach"` | Yes | - |
| `MsgRegistrationRejected` | `"registrationrejected"` | Yes | - |
| `MsgObserve` | `"observe"` | Yes | observe messages. The engine pushes functionregistered, observeradded and observerremoved only to connections that declared observe.v1. |
| `MsgUnobserve` | `"unobserve"` | Yes | - |
| `MsgObserveResult` | `"observeresult"` | Yes | - |
| `MsgObserverStateChanged` | `"observerstatechanged"` | Yes | - |
| `MsgFunctionRegistered` | `"functionregistered"` | Yes | - |
| `MsgObserverAdded` | `"observeradded"` | Yes | - |
| `MsgObserverRemoved` | `"observerremoved"` | Yes | - |
| `MsgObserverAck` | `"observerack"` | Yes | - |
| `MsgInvocationsObserved` | `"invocationsobserved"` | Yes | - |
| `MsgLocalInvocationStats` | `"localinvocationstats"` | Yes | - |

***

#### MiddlewareFunctionInput

MiddlewareFunctionInput is the payload the engine sends to the RBAC middleware function (the worker-manager rbac.middleware\_function\_id) on every function invocation that arrives through the RBAC port. The middleware can inspect, modify or reject the call before it reaches the target function: forward it with `Client.Call`, or return an error to reject it.

It mirrors MiddlewareFunctionInput in the Node and Rust SDKs field for field; the JSON names are the wire contract. Register the middleware with `RegisterFunctionTyped` to receive it decoded, or unmarshal the raw handler payload into it.

When forwarding, note that an empty Namespace means the engine's default namespace, whereas an empty `CallRequest.Namespace` means this worker's own namespace: pass "default" explicitly when Namespace is empty and the worker is namespaced.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
raw := []byte(`{"function_id":"orders::create","payload":{"item":"book"},"action":null,"context":{"source":"browser"},"namespace":"default"}`)

var in iii.MiddlewareFunctionInput
if err := json.Unmarshal(raw, &in); err != nil {
	panic(err)
}
fmt.Println(in.FunctionID, in.Namespace, in.Action == nil)
fmt.Println(string(in.Payload), string(in.Context))
```

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
client := iii.RegisterWorker(iii.DefaultEngineURL)
defer client.Close()

_, _ = iii.RegisterFunctionTyped(client, "auth::middleware",
	func(ctx context.Context, in iii.MiddlewareFunctionInput) (json.RawMessage, error) {
		var payload map[string]any
		if err := json.Unmarshal(in.Payload, &payload); err != nil {
			return nil, err
		}
		payload["_auth"] = in.Context
		data, err := json.Marshal(payload)
		if err != nil {
			return nil, err
		}
		namespace := in.Namespace
		if namespace == "" {
			namespace = "default" // absent means the engine's default namespace
		}
		return client.Call(ctx, iii.CallRequest{
			FunctionID: in.FunctionID,
			Payload:    data,
			Action:     in.Action,
			Namespace:  namespace,
		})
	})
```

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | FunctionID is the ID of the function being invoked. |
| `Payload` | `json.RawMessage` | Yes | Payload is the payload sent by the caller, verbatim. Forward it as `CallRequest.Payload` or decode it to inspect the call. |
| `Action` | \*[`CallAction`](#callaction) | No | Action is the routing action of the call, if any (nil for a synchronous request/response). Forward it as `CallRequest.Action` to keep enqueue/void semantics. |
| `Context` | `json.RawMessage` | Yes | Context is the auth context the RBAC auth function returned for this session (AuthResult.context in package helpers/workerconnection), as raw JSON. |
| `Namespace` | `string` | No | Namespace is the target namespace the invocation addressed; forward the call there to stay in the caller's namespace. Empty means the engine's default namespace. |

***

#### ObserveMessage

ObserveMessage creates, or idempotently replays, an observer owned by this connection: Source is the left side of `Client.Observe`, Target the right side.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `Source` | [`ObserveMessageSource`](#observemessagesource) | Yes | - |
| `Target` | [`ObserveMessageTarget`](#observemessagetarget) | Yes | - |
| `Metadata` | `json.RawMessage` | No | - |
| `MaxFires` | `*uint64` | No | - |
| `ExpiresInMs` | `*uint64` | No | - |
| `MaxInFlight` | `*uint64` | No | - |

***

#### ObserveMessageSource

ObserveMessageSource is what an observer observes. Capture is omitted when nil and sent when empty (capture: \[] is valid: a success with ids, status and timings only).

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Namespace` | `string` | No | - |
| `Args` | `json.RawMessage` | No | - |
| `Filters` | `json.RawMessage` | No | - |
| `Capture` | \[][`Capture`](#capture) | No | - |

***

#### ObserveMessageTarget

ObserveMessageTarget is the function an observer invokes.

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Namespace` | `string` | No | - |

***

#### ObserveReason

ObserveReason is why an observer is in its state: a machine-readable code and a message.

| Name | Type | Required | Description |
| - | - | - | - |
| `Code` | `string` | Yes | - |
| `Message` | `string` | Yes | - |

***

#### ObserveRejectionError

ObserveRejectionError is an observe error carrying its code: what `ObserverHandle.Ready` returns when the observer turns error or disposed, what `Client.Observe` returns for an id a handle of the client holds (OBSERVER\_ID\_CONFLICT), and what an `AddedHook` returns (built with `ObserveRejection`) to reject an observer with a code of its own. Recover it with errors.As.

| Name | Type | Required | Description |
| - | - | - | - |
| `Code` | `string` | Yes | - |
| `Message` | `string` | Yes | - |
| `Error` | `func() string` | Yes | - |

***

#### ObserveResultMessage

ObserveResultMessage is the engine's reply to an observe (also on a replay).

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `State` | `string` | Yes | - |
| `Error` | \*[`ErrorBody`](#errorbody) | No | - |
| `Warnings` | `[]string` | No | - |

***

#### Observed

Observed is what an observer's target receives when iii delivers: the top-level fields, plus whichever of the observer's capture apply to the outcome. Fields not captured, or that do not apply, are absent. Decode a target's payload into it:

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
var ev iii.Observed
_ = json.Unmarshal(data, &ev)
```

| Name | Type | Required | Description |
| - | - | - | - |
| `ObserverID` | `string` | Yes | - |
| `ActivationID` | `string` | Yes | - |
| `FunctionID` | `string` | Yes | - |
| `Namespace` | `string` | Yes | - |
| `InvocationID` | `string` | Yes | - |
| `Status` | `string` | Yes | - |
| `Input` | `json.RawMessage` | No | - |
| `Output` | `json.RawMessage` | No | - |
| `Error` | \*[`ErrorBody`](#errorbody) | No | - |
| `Caller` | \*[`Caller`](#caller) | No | - |
| `Metadata` | `json.RawMessage` | No | - |
| `StartedAtMs` | `int64` | Yes | - |
| `DurationMs` | `float64` | Yes | - |

***

#### ObservedItem

ObservedItem is one reported call. Input, Output, Error and Metadata keep JSON null apart from absent: "output": null is a function that returned null, a nil Output was not captured or equals Input (OutputIsInput).

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Namespace` | `string` | No | - |
| `InvocationID` | `string` | Yes | - |
| `Matched` | `[]string` | Yes | - |
| `Status` | `string` | Yes | - |
| `Input` | `json.RawMessage` | No | - |
| `Output` | `json.RawMessage` | No | - |
| `Error` | `json.RawMessage` | No | - |
| `Caller` | \*[`Caller`](#caller) | No | - |
| `Metadata` | `json.RawMessage` | No | - |
| `StartedAtMs` | `int64` | Yes | - |
| `DurationMs` | `float64` | Yes | - |
| `ObserveDepth` | `uint32` | Yes | - |
| `Traceparent` | `*string` | No | - |
| `Baggage` | `*string` | No | - |
| `OutputIsInput` | `bool` | No | OutputIsInput: the handler returned its input, so the item carries Input once and the engine fills output from it. Sent only to engines with observe.v2. |

***

#### Observer

Observer is an observer as the worker that hosts the observed function sees it (in its hooks and in `Client.Observers`): its args, filters, target and capture. Never its metadata, policy or the observing worker. Its fields are set when it is added; the SDK keeps its own copy of what it reads, so changing them changes nothing but the host's view. The pointer is stable while the engine announces the same view of the observer, so hosts keep it in their own index (expression -> observers, route table).

The engine may announce an observer again with the same args and other filters, target or capture (a replay): it stays the same observer and no hook runs. From then on it is served, and listed by `Client.Observers`, as a new \*Observer with that view; Reject and Accepted act on the observer through either pointer, and pointers handed out earlier keep the view they were made with.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `FunctionID` | `string` | Yes | FunctionID is the observed function (the one this worker hosts). |
| `Args` | `map[string]any` | No | Args are the observer's args, validated against args\_format by the engine (numbers decode as float64; see DecodeArgs). |
| `Filters` | `map[string]any` | No | Filters are the observer's filters in normal form; iii's delivery and Matches apply them. |
| `Target` | `string` | Yes | Target is the target function id. |
| `Capture` | \[][`Capture`](#capture) | No | Capture is what the target's payload carries, and the outcomes that fire it. |
| `Accepted` | `func(ctx context.Context) error` | Yes | Accepted waits until the SDK has acked the observer as accepted, and returns nil. A call made after that reaches an active observer, since the engine handles a connection's frames in order: a host sends a new observer its initial state this way, with a call of its own function whose metadata is \{"for\_observer": \<obs.ID>}. The function's `MatchHook` gives that call to this observer alone, and iii delivers it like every call. It returns an \*`ObserveRejectionError` when the observer is rejected first (code OBSERVER\_NOT\_ACTIVE when it is removed, or its function released, first), and ctx's error when ctx is done first. Once accepted, it returns nil at once. |
| `DecodeArgs` | `func(v any) error` | Yes | DecodeArgs unmarshals the observer's args into v (a struct, typically). |
| `Matches` | func(call \*[`Call`](#call)) bool | Yes | Matches reports whether call reaches the observer: it fires on the call's status (its view's fires\_on) and its filters match the call. A deliver hook that calls targets itself checks it before calling through the observer; iii's delivery applies it on its own. On a call the SDK hands a hook, the observers share the call's decoded document and its values at the paths of the function's filter index, so each costs its own filter, and an observer costs a comparison when the call fails the condition it is filed under. |
| `Reject` | `func(code, message string)` | Yes | Reject sends a late rejection for this observer (e.g. after a config reload removed what it needs). It leaves the compiled set at once, so calls stop reaching it, and its removed hooks run next on the function's hook queue, connected or not; the engine's observerremoved that follows runs none again. The rejection is sent again when the engine replays the observer after a reconnect. Called while the observer's added hooks run, it rejects the observer once they return, and the removed hook of each level whose added hook resolved runs. |

***

#### ObserverAckMessage

ObserverAckMessage is the host's answer to one added observer, sent once every OnObserverAdded hook that applies to it has returned. Error rejects it; a host may re-send it with an error for an active observer (a late rejection).

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Namespace` | `string` | No | - |
| `ID` | `string` | Yes | - |
| `Revision` | `uint64` | Yes | - |
| `Error` | \*[`ErrorBody`](#errorbody) | No | - |

***

#### ObserverAddedMessage

ObserverAddedMessage attaches one observer to a function this connection hosts.

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Namespace` | `string` | Yes | - |
| `Revision` | `uint64` | Yes | - |
| `Observer` | [`ObserverInfo`](#observerinfo) | Yes | - |

***

#### ObserverHandle

ObserverHandle is what `Client.Observe` returns: the observing side of one observer. It is kept for replay, with the same id, on every connection until it is disposed. Safe for concurrent use.

| Name | Type | Required | Description |
| - | - | - | - |
| `Dispose` | `func(ctx context.Context) error` | Yes | Dispose removes the observer (unobserve). It is not replayed on reconnect. An observer disposed while disconnected is unobserved on the next connection, before any observe is replayed, so a Reattach does not keep it alive. When the engine refused its observe (see `Client.Observe`), Dispose sends nothing. Its id is free at once: an Observe under it gets a new handle, which hears the engine's answers to its own observe only. ctx is reserved; Dispose does not wait for the engine. |
| `ID` | `func() string` | Yes | ID is the observer's id, stable across replays. |
| `OnStateChange` | func(listener func(state [`ObserverState`](#observerstate), reason \*[`ObserveReason`](#observereason))) (unsubscribe func()) | Yes | OnStateChange calls listener on every state change, in order of the changes and of registration, on a goroutine of the handle's own (one change at a time; never on the SDK's read loop, so a listener may call the client). It returns the function that unsubscribes it; a change already queued is not delivered to it afterwards. |
| `Ready` | `func(ctx context.Context) error` | Yes | Ready waits until the observer is active. It returns an \*ObserveRejectionError carrying the code when the observer turns error or disposed first (INVALID\_ARGS, INVALID\_FILTER, FORBIDDEN, SELF\_TARGET, an owner rejection, UNSUPPORTED against an engine without observe), and ctx's error when ctx is done first. |
| `Reason` | func() \*[`ObserveReason`](#observereason) | Yes | Reason is why the observer is in its state (error, suspended, disposed), or nil. |
| `State` | func() [`ObserverState`](#observerstate) | Yes | State is the observer's current state. |

***

#### ObserverInfo

ObserverInfo is an observer as the host sees it: never its metadata, policy or the observing worker. Filters are in normal form.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `Args` | `json.RawMessage` | No | - |
| `Filters` | `json.RawMessage` | No | - |
| `Target` | `string` | Yes | - |
| `Capture` | \[][`Capture`](#capture) | No | - |
| `FiresOn` | `[]string` | No | FiresOn is the outcomes that fire it ("success", "error"), from an engine with observe.capture; nil from an older one, whose observers fire as On says. |
| `On` | `json.RawMessage` | No | On is the legacy on of an engine without observe.capture (absent: success; "error"; "complete"), read only when FiresOn is nil. Deprecated. |

***

#### ObserverOptions

ObserverOptions are the observer's own settings: its id, policy and metadata. A zero field is unset.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | No | ID defaults to a random UUID, stable across replays. |
| `MaxFires` | `uint64` | Yes | MaxFires disposes the observer after this many activations. |
| `ExpiresIn` | `time.Duration` | Yes | ExpiresIn disposes the observer after this long (millisecond precision). |
| `MaxInFlight` | `uint64` | Yes | MaxInFlight bounds the observer's concurrent activations. |
| `Metadata` | `json.RawMessage` | No | Metadata is delivered as the metadata sidecar of every activation (a JSON object). |
| `TargetNamespace` | `string` | No | TargetNamespace is the namespace of the target function; it defaults to this worker's. |

***

#### ObserverRemovedMessage

ObserverRemovedMessage detaches one observer.

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | - |
| `Namespace` | `string` | Yes | - |
| `Revision` | `uint64` | Yes | - |
| `ID` | `string` | Yes | - |

***

#### ObserverState

ObserverState is an observer's lifecycle: pending (no host yet, waiting for the host's ack, or no target yet) -> active or error; suspended while its worker or target is away; disposed at the end.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type ObserverState string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `ObserverPending` | `"pending"` | Yes | - |
| `ObserverActive` | `"active"` | Yes | - |
| `ObserverSuspended` | `"suspended"` | Yes | - |
| `ObserverError` | `"error"` | Yes | - |
| `ObserverDisposed` | `"disposed"` | Yes | - |

***

#### ObserverStateChangedMessage

ObserverStateChangedMessage is a lifecycle transition of an observer this connection owns.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `State` | `string` | Yes | - |
| `Reason` | \*[`ErrorBody`](#errorbody) | No | - |
| `Fires` | `uint64` | Yes | - |

***

#### Op

Op is the operator object of one filter path: iii.Op\{"\$eq": "insert"}.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Op map[string]any
```

***

#### Option

Option configures a Client. Pass options to New.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Option func(*Client)
```

| Name | Type | Required | Description |
| - | - | - | - |
| `WithDescription` | func(description string) [`Option`](#option) | Yes | WithDescription sets a one-line, human/LLM-readable summary of what this worker does. Surfaces in engine::workers::list / engine::workers::info. |
| `WithHeaders` | func(h http.Header) [`Option`](#option) | Yes | WithHeaders sets HTTP headers sent on the WebSocket handshake of the main connection and of channels created with `Client.CreateChannel`. The III\_TOKEN environment variable, when set and not blank, adds "Authorization: Bearer \<token>" unless h already carries an Authorization header. |
| `WithInvocationTimeout` | func(d time.Duration) [`Option`](#option) | Yes | WithInvocationTimeout sets how long `Client.Call` waits for a result when `CallRequest`.Timeout is zero, in place of `DefaultInvocationTimeout`. It is the invocationTimeoutMs option of the Node SDK's registerWorker. A d of zero or less keeps the default. |
| `WithMetricsReporting` | func(enabled bool) [`Option`](#option) | Yes | WithMetricsReporting turns the worker resource gauges on or off (Node enableMetricsReporting): the iii.worker.memory.\*, iii.worker.cpu.\*, iii.worker.event\_loop.lag\_ms and iii.worker.uptime\_seconds metrics, tagged with worker.id and worker.name. The default is true. The gauges start when the engine acknowledges the worker and stop when the connection drops; they need telemetry and its metrics to be enabled (see `OtelConfig`). |
| `WithName` | func(name string) [`Option`](#option) | Yes | WithName sets the worker name reported to the engine. A non-empty III\_WORKER\_NAME takes precedence. The default is "hostname:pid". |
| `WithNamespace` | func(namespace string) [`Option`](#option) | Yes | WithNamespace sets the namespace this worker registers into. Takes precedence over the III\_NAMESPACE environment variable. |
| `WithOtel` | func(cfg [`OtelConfig`](#otelconfig)) [`Option`](#option) | Yes | WithOtel configures the client's OpenTelemetry pipeline (the otel option of Node registerWorker). Telemetry starts with the connection lifecycle (`RegisterWorker` or `Client.Connect`) and `Client.Close` flushes and stops it. Without this option the defaults documented on `OtelConfig` apply, and they enable telemetry. |
| `WithReconnectConfig` | func(c [`ReconnectConfig`](#reconnectconfig)) [`Option`](#option) | Yes | WithReconnectConfig overrides the default reconnect schedule. |
| `WithTelemetry` | func(t [`TelemetryOptions`](#telemetryoptions)) [`Option`](#option) | Yes | WithTelemetry sets what the worker reports about its language, project and framework on every connection. Blank fields keep their defaults (see `TelemetryOptions`). |

***

#### OtelConfig

OtelConfig configures the OpenTelemetry pipeline a `Client` runs: spans, metrics and logs exported to the engine over a dedicated WebSocket at \<engine address>/otel, the "execute \<function id>" span around every handler, and the worker resource gauges. It mirrors the Node SDK's OtelConfig (the otel option of registerWorker) field for field. The engine URL and the handshake headers are not part of it: the client uses its own address and its `WithHeaders` headers (plus the III\_TOKEN bearer), as Node does for engineWsUrl and engineWsHeaders.

Telemetry is enabled by default, as in the Node SDK: a client started without `WithOtel` exports unless OTEL\_ENABLED is "false", "0", "no" or "off". A zero field means "not set": the environment variable named on the field, then the default, fills it in.

OpenTelemetry globals: when telemetry starts, the client installs its tracer provider, its meter provider and the W3C Trace Context + Baggage propagator as the OpenTelemetry globals (otel.Tracer, otel.Meter, otel.GetTextMapPropagator), each only while that global is still the otel default. A provider or propagator the application installed earlier, or another client installed first, is never replaced: first registration wins, as in the Node SDK. `Client.Close` puts the default back into every global that still holds this client's. The client's invocation spans and worker gauges, and the observability helpers (which use the client started last), always use the client's own providers, so they reach the engine either way. Spans the application creates with its own provider are parented to the invocation span through ctx and exported wherever that provider exports. Call and inbound invocations always propagate W3C Trace Context and Baggage, whatever the global propagator.

| Name | Type | Required | Description |
| - | - | - | - |
| `Enabled` | `*bool` | No | Enabled turns telemetry export on or off. Nil reads OTEL\_ENABLED ("false", "0", "no" or "off" disable it); the default is true. |
| `ServiceName` | `string` | No | ServiceName is the service.name resource attribute and the instrumentation scope of the SDK's tracer, meter and logger. Empty reads OTEL\_SERVICE\_NAME; the default is "iii-go". |
| `ServiceVersion` | `string` | No | ServiceVersion is the service.version resource attribute. Empty reads SERVICE\_VERSION; the default is "unknown". |
| `ServiceNamespace` | `string` | No | ServiceNamespace is the service.namespace resource attribute, omitted when empty. Empty reads SERVICE\_NAMESPACE. |
| `ServiceInstanceID` | `string` | No | ServiceInstanceID is the service.instance.id resource attribute. Empty reads SERVICE\_INSTANCE\_ID; the default is a random UUID. |
| `MetricsEnabled` | `*bool` | No | MetricsEnabled turns metrics export on or off. Nil reads OTEL\_METRICS\_ENABLED ("false", "0", "no" or "off" disable it); the default is true. |
| `MetricsExportInterval` | `time.Duration` | No | MetricsExportInterval is the period of the metrics export. Zero means 60 seconds. |
| `SpansFlushInterval` | `time.Duration` | No | SpansFlushInterval is how long an ended span waits in the batch before it is sent. Zero reads OTEL\_SPANS\_FLUSH\_INTERVAL\_MS (milliseconds); the default is 100ms. |
| `LogsFlushInterval` | `time.Duration` | No | LogsFlushInterval is how long a log record waits in the batch before it is sent. Zero reads OTEL\_LOGS\_FLUSH\_INTERVAL\_MS (milliseconds); the default is 100ms. |
| `LogsBatchSize` | `int` | No | LogsBatchSize is the maximum number of log records per export. Zero reads OTEL\_LOGS\_BATCH\_SIZE; the default is 1. |
| `FetchInstrumentationEnabled` | `*bool` | No | FetchInstrumentationEnabled turns the HTTP client spans of observability.NewTransport and observability.NewHTTPClient on or off (the Go counterpart of Node's fetch instrumentation). Nil means true. |
| `ReconnectionConfig` | \*[`ReconnectConfig`](#reconnectconfig) | No | ReconnectionConfig is the reconnect schedule of the telemetry WebSocket. Nil means `DefaultReconnectConfig`; a non-nil value is used as given. |

***

#### PingMessage

PingMessage is the payloadless keepalive the engine sends; it serializes to \{"type":"ping"}. The client replies with a PongMessage.

***

#### PongMessage

PongMessage is the payloadless reply to a PingMessage; it serializes to \{"type":"pong"}.

***

#### ReattachMessage

Reattach is sent worker->engine as the first message of a reconnect, before the registration replay: PreviousWorkerID is the id the engine assigned via WorkerRegistered on the previous connection. The engine retires that connection so the replay lands on a clean slate instead of racing its cleanup.

| Name | Type | Required | Description |
| - | - | - | - |
| `PreviousWorkerID` | `string` | Yes | - |
| `ReattachToken` | `string` | No | ReattachToken authorizes retiring the previous connection: worker ids are publicly listable, the token was only ever sent over that connection's own socket. |

***

#### ReconnectConfig

ReconnectConfig controls the exponential-backoff-with-jitter reconnect loop. The defaults below match the Node SDK's DEFAULT\_BRIDGE\_RECONNECTION\_CONFIG exactly, so a Go worker reconnects on the same schedule as a Node one.

| Name | Type | Required | Description |
| - | - | - | - |
| `InitialDelay` | `time.Duration` | Yes | InitialDelay is the delay before the first reconnect attempt. |
| `MaxDelay` | `time.Duration` | Yes | MaxDelay caps the backoff. |
| `BackoffMultiplier` | `float64` | Yes | BackoffMultiplier scales the delay after each failed attempt. |
| `JitterFactor` | `float64` | Yes | JitterFactor (0..1) randomizes each delay by up to ±JitterFactor to avoid a thundering herd of workers reconnecting in lockstep. |
| `MaxRetries` | `int` | No | MaxRetries caps reconnect attempts; -1 means retry forever (the default). |
| `DefaultReconnectConfig` | func() [`ReconnectConfig`](#reconnectconfig) | Yes | DefaultReconnectConfig is the reconnect schedule shared with the Node SDK: start at 1s, double each time, cap at 30s, ±30% jitter, retry forever. |

***

#### RegisterFunctionMessage

RegisterFunction announces a function this worker can handle. RequestFormat and ResponseFormat are optional JSON Schemas; Invocation is set only for externally HTTP-hosted functions (`Client.RegisterHTTPFunction`).

Optional fields are omitted when unset, as the engine writes them (skip\_serializing\_if); the engine reads an absent format and a JSON null alike. ArgsFormat, Private and Observable describe the function to the engine's observer registry; private: false and observable: true are the defaults and are omitted.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `Description` | `*string` | No | - |
| `RequestFormat` | `json.RawMessage` | No | - |
| `ResponseFormat` | `json.RawMessage` | No | - |
| `Metadata` | `json.RawMessage` | No | - |
| `Invocation` | \*[`HTTPInvocationRef`](#httpinvocationref) | No | - |
| `ArgsFormat` | `json.RawMessage` | No | ArgsFormat is the JSON Schema of the observers' args; the engine validates them (INVALID\_ARGS). Absent: the function takes no args. |
| `Private` | `bool` | No | Private: only the connection that hosts the function may call it (FORBIDDEN for anyone else); other workers may observe it. |
| `Observable` | `*bool` | No | Observable false refuses every observer (NOT\_OBSERVABLE); absent means true. |

***

#### RegisterFunctionOptions

RegisterFunctionOptions configures a function registration. The zero value preserves the default registration shape. `FunctionOptions` is the same type.

ArgsFormat, Private and Observable false need an engine with observe.v2, as a match or deliver hook does: against an older one the function stays unregistered, and the SDK logs UNSUPPORTED once. What the worker does when the function is observed is set with hooks (`FunctionRef.OnObserverAdded` and its siblings).

| Name | Type | Required | Description |
| - | - | - | - |
| `Metadata` | `json.RawMessage` | No | Metadata is arbitrary JSON attached to the function registration. It is stored with the function and is distinct from the per-invocation metadata passed to handlers. |
| `LocalDispatch` | `*bool` | No | LocalDispatch false keeps every Call of this function on the engine path; nil or true lets Call run it in-process once the engine confirms it (see `Client.Call`: observers see snapshots of the payload and metadata taken at call start, and a handler may outlive a caller whose deadline passed). |
| `Description` | `string` | No | Description is a human/LLM-readable summary of the function, sent as "description" and surfaced by engine::functions::list and engine::functions::info. Empty omits it. |
| `RequestFormat` | `json.RawMessage` | No | RequestFormat is the JSON Schema of the function's request, sent as "request\_format". It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Req. |
| `ResponseFormat` | `json.RawMessage` | No | ResponseFormat is the JSON Schema of the function's response, sent as "response\_format": the handler's return value, which is what observers observe. It must be valid JSON; nil omits it. With `RegisterFunctionTyped` it replaces the schema inferred from Resp. |
| `ArgsFormat` | `json.RawMessage` | No | ArgsFormat is the JSON Schema of the observers' args, sent as "args\_format": the engine validates every observer's args against it (INVALID\_ARGS), and refuses a schema that is not valid (INVALID\_ARGS\_FORMAT). It must be valid JSON; nil means the function takes no args. |
| `Private` | `bool` | No | Private lets only this worker call the function (the engine answers anyone else with FORBIDDEN); other workers may observe it, and it cannot be an observer's target. A call this worker makes to it passes the input and the result by reference: the handler and the hooks treat both as read-only. |
| `Observable` | `*bool` | No | Observable false refuses every observer of the function (NOT\_OBSERVABLE), for a function whose payloads carry credentials. nil means true. |

***

#### RegisterServiceMessage

RegisterService groups functions under a service for the dashboard.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |
| `Name` | `string` | Yes | - |
| `Description` | `*string` | No | - |
| `ParentServiceID` | `*string` | No | - |

***

#### RegistrationRejectedError

RegistrationRejectedError is the terminal error the engine returns when a worker's registration is refused for good, typically a WORKER\_NAMESPACE\_CONFLICT, where another live worker already owns this name in the namespace. It is delivered to every pending invocation and reported by `Client.FatalError`; the worker stops and does not reconnect. Mirrors the Node/Python/Rust SDKs.

Recover the details with errors.As:

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
var re *iii.RegistrationRejectedError
if errors.As(err, &re) { ... re.Code, re.Namespace ... }
```

| Name | Type | Required | Description |
| - | - | - | - |
| `Code` | `string` | Yes | Code is the rejection code, e.g. "WORKER\_NAMESPACE\_CONFLICT". |
| `Namespace` | `string` | Yes | Namespace is the namespace the conflict occurred in. |
| `WorkerName` | `string` | Yes | WorkerName is the rejected worker name for a worker conflict. |
| `FunctionID` | `string` | Yes | FunctionID is the rejected function id for a function conflict. |
| `OwnerWorkerID` | `string` | Yes | OwnerWorkerID is the id of the worker that already owns the name. |
| `Error` | `func() string` | Yes | - |

***

#### RegistrationRejectedMessage

RegistrationRejectedMessage is the engine's refusal of a registration. A FUNCTION\_NAMESPACE\_CONFLICT costs one function and leaves the connection open; a WORKER\_NAMESPACE\_CONFLICT is terminal: another live worker owns this name in the namespace.

| Name | Type | Required | Description |
| - | - | - | - |
| `Code` | `string` | Yes | - |
| `Namespace` | `string` | Yes | - |
| `WorkerName` | `string` | No | - |
| `FunctionID` | `string` | No | - |
| `OwnerWorkerID` | `string` | Yes | - |

***

#### RemovedHook

RemovedHook runs once for every accepted observer when it is removed (unobserve, disposal, late rejection with `Observer.Reject`). For an observer rejected before it is accepted (by another level's `AddedHook`, or by `Observer.Reject` while the added hooks run), it runs for each level whose `AddedHook` resolved. A panic is logged; the observer is removed either way.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type RemovedHook func(ctx context.Context, obs *Observer)
```

***

#### Source

Source is the left side of `Client.Observe`: what is observed.

| Name | Type | Required | Description |
| - | - | - | - |
| `FunctionID` | `string` | Yes | FunctionID is the observed function. |
| `Namespace` | `string` | No | Namespace defaults to this worker's (engine::\* and configuration::\* resolve to the engine's default namespace). |
| `Args` | [`Args`](#args) | Yes | Args go to the worker that hosts the function, which reads them in its hooks (a cron expression, a route, a table). A function without args\_format takes none. |
| `Filters` | [`Filters`](#filters) | No | Filters decide which calls reach the target; nil matches every call. |
| `Capture` | \[][`Capture`](#capture) | No | Capture is what the target's payload carries, and with it which outcomes fire the target. nil means the default (input, output, caller), which fires on success. Input and output fire on success, error fires on error; caller and metadata only add to the payload, so a non-empty Capture must name at least one of input, output, error (INVALID\_ARGS otherwise). An empty, non-nil slice fires on success with the top-level fields only. \{error, caller} fires on errors, \{input, error} on both. |

***

#### StreamChannelRef

StreamChannelRef is a capability handle to one end of a streaming data channel. The engine's engine::channels::create returns a writer ref and a reader ref that share a channel\_id and access\_key; a ref travels as plain JSON inside a call payload or result, and the holder opens its own WebSocket to the channel with it (`Client.OpenReader`, `Client.OpenWriter`).

| Name | Type | Required | Description |
| - | - | - | - |
| `ChannelID` | `string` | Yes | - |
| `AccessKey` | `string` | Yes | - |
| `Direction` | [`ChannelDirection`](#channeldirection) | Yes | - |

***

#### TelemetryOptions

TelemetryOptions is what the worker reports about its language, project and framework in the telemetry object of engine::workers::register, shown by engine::workers::list and engine::workers::info. A blank field takes its default. Mirrors TelemetryOptions in the Node and Rust SDKs.

| Name | Type | Required | Description |
| - | - | - | - |
| `Language` | `string` | No | Language reported for the worker. Default: the locale of LANG (else LC\_ALL) without its encoding, for example "en\_US" for LANG=en\_US.UTF-8, which is the Node SDK's fallback; omitted when neither variable is set. |
| `ProjectName` | `string` | No | ProjectName is the project the worker belongs to. Default: the last element of the module path in the nearest go.mod, searched from the working directory upwards, skipping a major-version suffix such as /v2; omitted when there is no go.mod. |
| `Framework` | `string` | No | Framework the worker is built with. Default: "iii-go". Surrounding spaces are trimmed. |

***

#### TypedHandler

TypedHandler is a function handler with a typed request and response. The SDK unmarshals the invocation payload into Req before calling it and marshals the returned Resp into the result, so handlers work with concrete types instead of json.RawMessage.

Per-invocation metadata, when the call carries any, rides on ctx rather than the signature (so adding it is non-breaking): read it with `MetadataFromContext`.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type TypedHandler[Req any, Resp any] func(ctx context.Context, req Req) (Resp, error)
```

***

#### UnobserveMessage

UnobserveMessage removes an observer this connection owns.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |

***

#### UnregisterFunctionMessage

UnregisterFunction removes a previously registered function.

| Name | Type | Required | Description |
| - | - | - | - |
| `ID` | `string` | Yes | - |

***

#### WorkerRegisteredMessage

WorkerRegistered is sent engine->worker on connect to announce the worker's id. The worker treats it as informational.

| Name | Type | Required | Description |
| - | - | - | - |
| `WorkerID` | `string` | Yes | - |
| `ReattachToken` | `string` | No | ReattachToken is the secret to present in a ReattachMessage on reconnect; empty on older engines. |
| `Features` | `[]string` | No | Features the engine supports, e.g. "observe.v1" and "local-dispatch.v1". Absent on engines before 0.25: observe is then unsupported. |

### iii/filter

Package filter is the observer filter language (iii-filter v1): validation and evaluation with the same semantics as the engine's evaluator and the other SDKs, pinned down by the conformance vectors all of them share.

[`Doc`](#doc) · [`Error`](#error) · [`ErrorCode`](#errorcode) · [`Filter`](#filter) · [`Map`](#map) · [`Root`](#root) · [`Roots`](#roots)

#### Doc

Doc is the document a filter reads: the value of each root, and whether it is present. A missing root is absent; a present nil is JSON null.

| Name | Type | Required | Description |
| - | - | - | - |
| `Root` | func(root [`Root`](#root)) (value any, present bool) | Yes | - |

***

#### Error

Error is a filter the language rejects. Recover it with errors.As.

| Name | Type | Required | Description |
| - | - | - | - |
| `Code` | [`ErrorCode`](#errorcode) | Yes | - |
| `Message` | `string` | Yes | - |
| `Error` | `func() string` | Yes | - |

***

#### ErrorCode

ErrorCode is the protocol error code of a rejected filter.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type ErrorCode string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `CodeInvalidFilter` | `"INVALID_FILTER"` | Yes | CodeInvalidFilter is a filter the language rejects. |
| `CodeInvalidFilterPath` | `"INVALID_FILTER_PATH"` | Yes | CodeInvalidFilterPath is a path a closed schema does not have. Only the engine checks schemas; this package never returns it. |

***

#### Filter

Filter is a validated filter, compiled once into a closure tree. It is immutable and safe for concurrent use.

| Name | Type | Required | Description |
| - | - | - | - |
| `Compile` | func(src any) (\*[`Filter`](#filter), error) | Yes | Compile validates and compiles a filter given as a Go value. A json.RawMessage or \[]byte is read as JSON text; any other value is read through encoding/json, so what is validated is exactly what travels on the wire. It returns an \*Error with code INVALID\_FILTER when the filter is not valid. |
| `MatchAll` | func() \*[`Filter`](#filter) | Yes | MatchAll returns the filter that matches every document (\{}). |
| `Parse` | func(data \[]byte) (\*[`Filter`](#filter), error) | Yes | Parse validates and compiles a filter given as JSON text. It returns an \*Error with code INVALID\_FILTER when the filter is not valid. |
| `ParseNormalized` | func(data \[]byte) (\*[`Filter`](#filter), error) | Yes | ParseNormalized compiles a filter in the normal form the engine pushes (functionregistered, observeradded). Every rule applies except the encoded size limit, which the engine checked on the filter as written (a normal form can be longer). |
| `IsMatchAll` | `func() bool` | Yes | IsMatchAll reports whether the filter matches every document. |
| `Matches` | func(doc [`Doc`](#doc)) (matched bool) | Yes | Matches evaluates the filter on doc. A nil doc has every root absent. It is total: a host value whose JSON encoding panics reads as no match. |
| `Roots` | func() [`Roots`](#roots) | Yes | Roots reports which roots the filter's clauses read. |
| `Source` | `func() map[string]any` | Yes | Source returns a copy of the filter as decoded JSON (numbers as json.Number). |

***

#### Map

Map is a Doc backed by a map keyed by root name: a missing key is an absent root, a nil value is JSON null. Keys other than the five roots are ignored.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Map map[string]any
```

| Name | Type | Required | Description |
| - | - | - | - |
| `Root` | func(root [`Root`](#root)) (any, bool) | Yes | Root implements Doc. |

***

#### Root

Root is a path's first segment: the part of the document it reads.

```go theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
type Root string
```

| Name | Type | Required | Description |
| - | - | - | - |
| `RootInput` | `"input"` | Yes | - |
| `RootOutput` | `"output"` | Yes | - |
| `RootError` | `"error"` | Yes | - |
| `RootCaller` | `"caller"` | Yes | - |
| `RootMetadata` | `"metadata"` | Yes | - |

***

#### Roots

Roots tells which document roots a filter's clauses read.

| Name | Type | Required | Description |
| - | - | - | - |
| `Input` | `bool` | Yes | - |
| `Output` | `bool` | Yes | - |
| `Error` | `bool` | Yes | - |
| `Caller` | `bool` | Yes | - |
| `Metadata` | `bool` | Yes | - |
| `Has` | func(root [`Root`](#root)) bool | Yes | Has reports whether the filter reads root. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.