Skip to main content

Documentation Index

Fetch the complete documentation index at: https://motiadev-docs-phase-2.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

What “writing a function” means

A worker contributes capabilities to a iii system by registering functions. Each function has an id 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. For how callers invoke functions (worker.trigger / iii trigger / event-bound triggers), see Using iii / Triggers. This page is about the authoring new functions.

Register a function

Inside the worker, register the function with the SDK. The id is what triggers will use as a function_id.
Each type of trigger has its own expected argument structure for a given function. For example cron will call functions without arguments, while http will provide a standard http-style payload that includes body, headers, and other properties. For each worker visit their respective page at workers.iii.dev for their expected payload.
import { registerWorker } from "iii-sdk";

const url = process.env.III_URL;
if (!url) throw new Error("III_URL must be set");
const worker = registerWorker(url);

worker.registerFunction("math::add", async (payload: { a: number; b: number }) => {
  return { c: payload.a + payload.b };
});

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 and the agent-readable skills.
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.
import { registerWorker } from "iii-sdk";

const url = process.env.III_URL;
if (!url) throw new Error("III_URL must be set");
const worker = registerWorker(url);

worker.registerFunction(
  "math::add",
  async (payload) => ({ c: payload.a + payload.b }),
  {
    request_format: {
      type: "object",
      properties: { a: { type: "number" }, b: { type: "number" } },
      required: ["a", "b"],
    },
    response_format: {
      type: "object",
      properties: { c: { type: "number" } },
      required: ["c"],
    },
  },
);
The schemas also feed the iii console and the agent-readable skills.

HTTP-invokable functions

You can also register an external HTTP endpoint as a function. The engine makes the HTTP call whenever the function is invoked, your worker only declares the endpoint. This is useful for delegating work to your existing API Gateways, webhooks, serverless platforms (Lambda, Azure Functions, Google Cloud Functions), or any third-party API you want to surface as a regular iii function. The function is then triggerable like any other: worker.trigger, iii trigger, and any bound trigger type (queue, cron, state, http) all work without any other changes.
import { registerWorker } from "iii-sdk";

const url = process.env.III_URL;
if (!url) throw new Error("III_URL must be set");
const worker = registerWorker(url);

worker.registerFunction(
  "notifications::send",
  {
    url: "https://hooks.provider.example.com/notify",
    method: "POST",
    timeout_ms: 5000,
    headers: { "X-Service": "iii-worker" },
    auth: { type: "bearer", token_key: "PROVIDER_API_TOKEN" },
  },
  { description: "POST a notification to the provider webhook" },
);

HttpInvocationConfig fields

While a normal function takes an id and a handler, http invokeable functions take an id and a HttpInvocationConfig.
FieldTypeDefaultDescription
urlstring(required)Endpoint the engine calls when the function is invoked.
method"GET" | "POST" | "PUT" | "PATCH" | "DELETE""POST"HTTP method.
timeout_msnumber30000Per-request timeout in milliseconds.
headersRecord<string, string>{}Headers added to every invocation.
authHttpAuthConfig(none)An object with two fields: bearer, hmac, or api_key AND token_key, secret_key, or value_key
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 and treats any non-2xx response or network error as an invocation failure that propagates back to the caller. HTTP-invoked functions appear in engine::functions::list and are discoverable through the console exactly like in-process handlers.

Return values and errors

A function returns either a value (which the handler is responsible for shaping to match its documented response schema) or an error. Errors raised inside the handler are propagated to the caller as invocation errors with the worker’s stack trace attached: Node forwards error.stack, Python forwards traceback.format_exc(), and Rust forwards the underlying error’s stack trace. The engine doesn’t swallow them. Use this distinction to express expected failures (return a structured error value) versus unexpected ones (throw / raise / return Err).

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.
const add = worker.registerFunction("math::add", async (payload) => {
  return { c: payload.a + payload.b };
});

add.unregister();