What “writing a function” means
A worker contributes capabilities to an iii system by registering functions. Each function has anid of the form service::name, a handler that receives the payload and returns a result, and
optional JSON Schemas that describe the request and response shape.
The function_id is the contract. Callers invoke the function, and observers run a target on each
call; see Using iii / Functions.
Register a function
Inside the worker, register the function with the SDK. Theid is what callers and observers use as
a function_id.
A function that is an observer’s target receives what the observed function’s documentation
describes. For example, a target of
cron::on-tick receives the tick as its payload’s input, and
a target of http::on-request receives an HTTP request with body, headers, and other
properties. Each worker’s page at workers.iii.dev lists its functions,
their input, and their args.- Node / TypeScript
- Python
- Rust
- Go
Attach request and response schemas
Attach JSON Schemas to the registration so the request and response shape are documented alongside the function. The schemas are stored with the function and surface in the iii console,iii call <function_id> --help, and elsewhere.
Runtime validation is not yet supported. Attached schemas are metadata only; the engine does not
enforce a specific schema, reject payloads, nor handler return values that don’t match the
schemas. Treat the schemas as contract documentation for function invocations, agents, and the
console.
- Node / TypeScript
- Python
- Rust
- Go
Attach metadata
metadata is an arbitrary JSON object stored with the function at registration. The engine never
interprets it, it’s stored with the function so other parts of the system can act on it. It surfaces
in engine::functions::info, the iii console, and is commonly used with iii-worker-manager / Role
based access control.
Because the shape is entirely yours, metadata is a facility that can enable and enhance other
functionality. For example,
worker-manager RBAC allowlists can expose functions
by a metadata: selector, so tagging a function { public: true } or { tier: "premium" } lets
you gate access on the fields you define.
- Node / TypeScript
- Python
- Rust
- Go
Receive per-invocation metadata
Each function can also receive optional per-invocationmetadata: any JSON value. A caller sets it
on the call, and an observer sets it once for every activation of its target, to give the receiving
function execution context (see Policy and metadata).
A call through an observer takes no metadata of its own: its target receives the observer’s.
null is the same as no metadata. A call that carries no metadata reaches the handler with none,
whether it runs in the same worker or goes through the engine.
- Node / TypeScript
- Python
- Rust
- Go
Registration metadata is a different field from per-invocation metadata. It is set once on the
registration and the engine stores it with the function; the per-invocation value travels on each
call and reaches the handler.
HTTP-invoked functions
A worker can register a function that the engine invokes over HTTP. The registration carries anHttpInvocationConfig instead of a handler: the engine makes the HTTP call whenever the function is
invoked, and the worker declares only the endpoint.
This is the mechanism that surfaces an existing API gateway, webhook, serverless platform (Lambda,
Azure Functions, Google Cloud Functions), or third-party API as a regular iii function.
iii-http-functions is one worker that uses this mechanism; its
Worker Docs describe its configuration and
security controls.
- Node / TypeScript
- Python
- Rust
- Go
HttpInvocationConfig fields
A handler function takes an id and a handler. An HTTP-invoked function takes an id and an
HttpInvocationConfig, and no handler runs in the worker.
Auth fields (
token_key, secret_key, value_key) are specified as the names of environment
variables, not the secrets themselves. The engine resolves them from its own process environment
at registration time, so secrets stay on the engine host and never travel over the SDK WebSocket.HTTP error handling
The engine sends the invocation payload as the JSON request body. It treats every response with a status below 400 as a success, and every other response, or a network error, as an invocation failure that propagates back to the caller. A 1xx or 3xx response is a success with no function result. HTTP-invoked functions appear inengine::functions::list and are discoverable
through the console exactly like in-process handlers.
Return values and errors
A handler returns a value that matches its documented response schema, or an error. An error raised in the handler reaches the caller as an invocation error, with the handler’serror.stack attached in Node. The
engine does not swallow it.
Return a structured value for an expected failure, and throw for an unexpected one.
Observers of a function
Every registered function is observable unless the registration setsobservable: false, which
makes the engine refuse every observer with NOT_OBSERVABLE; the function stays callable.
private: true allows calls only from the connection that hosts the function, and other workers can
still observe it. args_format is the JSON Schema of the args an observer passes.
See Observable functions for the hooks that run around observation.
Unregister a function
registerFunction returns a handle with an unregister() method that removes the function from the
engine at runtime. When the worker disconnects, all of its functions are removed automatically and
pending invocations error out.
- Node / TypeScript
- Python
- Rust
- Go