Getting Started with iii
iii replaces your API framework, task queue, cron scheduler, pub/sub, state store, and observability pipeline with a single engine and three primitives: Function, Trigger, Worker.
Step 1: Install the Engine
bashcurl -fsSL https://install.iii.dev/iii/main/install.sh | sh
Verify it installed:
bashiii --version
Step 2: Create a Project
bashiii project init my-app # barebones project iii project init my-app -t harness # or: the harness template (agent + console UI) cd my-app
iii project init --learn-iii scaffolds the harness template and starts it in one step; the
installer offers the same when you answer "y" at its prompt. iii project init --template quickstart
scaffolds the Quickstart with a Python and a TypeScript worker.
Step 3: Start the Project
bashiii compose --namespace dev --up --file worker-compose.yaml
The file's engine: section starts the engine; containers: starts project workers. The engine
commonly listens on ws://localhost:49134. Keep this foreground supervisor running.
--namespace dev names the daemon and becomes III_NAMESPACE for every worker it starts, so your
workers register and call each other in dev; that is why the iii trigger commands below pass
-n dev. Engine-owned functions (engine::*, configuration::*) stay in default; when a worker
in dev calls one of those, pass namespace: "default" on the call (see iii-sdk-reference).
Step 4: Install the SDK
Pick your language:
bash# TypeScript / Node.js pnpm add iii-sdk @iii-dev/helpers # Python pip install iii-sdk iii-helpers # Rust cargo add iii-sdk iii-helpers
Step 5: Write Your First Worker
TypeScript
typescriptimport { registerWorker, TriggerAction } from "iii-sdk"; import { Logger } from "@iii-dev/helpers/observability"; const iii = registerWorker(process.env.III_URL ?? "ws://localhost:49134"); iii.registerFunction( "hello::greet", async (input) => { const logger = new Logger(); const name = input?.name ?? "world"; logger.info("Greeting user", { name }); return { message: `Hello, ${name}!` }; }, { description: "Greet a user by name" }, ); iii.registerTrigger({ type: "http", function_id: "hello::greet", config: { api_path: "/hello", http_method: "POST" }, });
Python
pythonfrom iii import register_worker, InitOptions from iii_helpers.observability import Logger iii = register_worker(address="ws://localhost:49134", options=InitOptions(worker_name="hello-worker")) def greet(data): logger = Logger() name = data.get("name", "world") if isinstance(data, dict) else "world" logger.info("Greeting user", {"name": name}) return {"message": f"Hello, {name}!"} iii.register_function("hello::greet", greet, description="Greet a user by name") iii.register_trigger({"type": "http", "function_id": "hello::greet", "config": {"api_path": "/hello", "http_method": "POST"}})
Rust
rustuse iii_sdk::{register_worker, InitOptions, RegisterFunction}; use iii_sdk::protocol::RegisterTriggerInput; use iii_helpers::observability::Logger; use serde_json::json; let iii = register_worker("ws://127.0.0.1:49134", InitOptions::default()); iii.register_function( "hello::greet", RegisterFunction::new(|input: serde_json::Value| -> Result<serde_json::Value, iii_sdk::Error> { let logger = Logger::new(); let name = input["name"].as_str().unwrap_or("world"); logger.info("Greeting user", Some(json!({ "name": name }))); Ok(json!({ "message": format!("Hello, {}!", name) })) }).description("Greet a user by name"), ); iii.register_trigger(RegisterTriggerInput { trigger_type: "http".into(), function_id: "hello::greet".into(), config: json!({ "api_path": "/hello", "http_method": "POST" }), metadata: None, })?;
Step 6: Test It
The http trigger type comes from the http worker. Add it once through the running Compose
daemon, then call your endpoint:
bashiii trigger -n dev compose::add worker=http
bashcurl -X POST http://localhost:3111/hello \ -H "Content-Type: application/json" \ -d '{"name": "iii"}'
Expected response:
json{ "message": "Hello, iii!" }
Add Existing Workers
To add a capability that already exists, browse https://workers.iii.dev/ and add it through the
running Compose daemon:
bashiii trigger -n dev compose::add worker=state iii trigger -n dev compose::add worker=queue iii trigger -n dev compose::add worker=image-resize@0.1.2
compose::add resolves dependencies, writes exact versions to worker-compose.yaml, and restarts
the affected project. A local worker can be declared as a path:// container or added by path.
Install Agent Skills
Get all iii skills for your AI coding agent:
bashnpx skills add iii-hq/iii/skills
Skills teach your agent the top-level iii model: functions, triggers, workers, registry access, SDKs, engine configuration, architecture patterns, and error handling. Worker-backed capabilities live with the worker docs and registry entries.
Adapting This Pattern
- Add more functions to the same worker — each gets its own
registerFunction+registerTriggercalls - Use
::separator for function IDs to namespace them:orders::create,orders::validate - Add cron triggers with
{ type: 'cron', config: { expression: '0 0 9 * * * *' } }(7-field: sec min hour day month weekday year) - Add queue triggers with
{ type: 'durable:subscriber', config: { topic: 'my-queue' } } - Use
iii.trigger()to invoke other functions from within a function - Use
state::get/state::setto persist data across function calls - Use
iii trigger -n <daemon> compose::add worker=<name>when the capability already exists in the worker registry
Recommended Next Steps
After getting your first worker running:
- Register functions, triggers, and workers — See
iii-core-primitives - Choose the right SDK APIs — See
iii-sdk-reference - Configure the engine — See
iii-engine-config - Explore backend patterns — See
iii-architecture-patterns - Handle failures well — See
iii-error-handling
Key Resources
- Quickstart Guide
- SDK Reference — Node.js
- SDK Reference — Python
- SDK Reference — Rust
- Compose
- Configuration
- Console
- Every docs page is also available as Markdown by adding a
.mdsuffix to its URL.
Pattern Boundaries
- For function and trigger registration patterns, worker creation, worker registry access, trigger
payload schemas, invocation modes, channels, custom triggers, and HTTP-invoked functions, prefer
iii-core-primitives - For language-specific SDK APIs, prefer
iii-sdk-reference - For engine configuration, prefer
iii-engine-config - For worker-backed HTTP, cron, queue, pubsub, state, stream, and observability behavior, use the matching worker page on https://workers.iii.dev/
- Stay with
iii-getting-startedfor installation, initial setup, and first-worker guidance
When to Use
- Use this skill when the task is about installing iii, creating a new project, or writing a first worker.
- Triggers when the request asks for setup help, quickstart guidance, or getting started with iii.
Boundaries
- Never use this skill as a generic fallback for unrelated tasks.
- You must not apply this skill when a more specific iii skill is a better fit.
- Always verify environment and safety constraints before applying examples from this skill.


