How to Create Custom HTTP Endpoints with the iii-http Worker and Configure api_path

The iii-http worker exposes registered iii functions as REST endpoints by binding them to specific HTTP methods and URL paths via the api_path configuration, automatically handling request parsing and response serialization on port 3111.

The iii repository provides a built-in HTTP server called the iii-http worker that transforms registered functions into RESTful APIs without requiring external web frameworks. When enabled in the default engine configuration found at engine/src/workers/rest_api/iii.worker.yaml, this worker runs an Axum server that routes incoming requests to your registered http triggers. Understanding how to register functions and properly configure the api_path parameter allows you to build scalable HTTP endpoints directly within the iii ecosystem.

How the iii-http Worker Processes Requests

The iii-http worker acts as a bridge between raw HTTP traffic and your registered iii functions. According to the worker definition in engine/src/workers/rest_api/iii.worker.yaml, the server listens on port 3111 by default and processes requests through a structured message-passing pipeline.

When an HTTP request arrives, the worker performs the following steps:

  1. Parses the request to extract path parameters (declared as :name segments in api_path), query strings, headers, and body content
  2. Constructs an HttpRequest object containing the extracted data in a structured format
  3. Routes to the registered function associated with the matching api_path and HTTP method pair
  4. Serializes the returned HttpResponse back to the client, including status codes, headers, and body content

The trigger system resolves routing conflicts by keeping the most recently registered route when duplicate api_path and http_method combinations exist.

Registering a Custom HTTP Endpoint

Creating a functional endpoint requires three distinct operations: defining the handler function, registering it with the iii runtime, and binding it to an HTTP trigger configuration.

First, write a function that accepts an HttpRequest payload and returns an HttpResponse envelope. The handler extracts data from request.path_params or other request properties and returns a structured response object.

Second, register the function using your SDK's registration method, such as iii.registerFunction in JavaScript or iii.register_function in Python. Assign a unique function_id following the namespace convention (e.g., greetings::hello).

Third, create an HTTP trigger with type: 'http' that maps the function to a URL path. The config object must include:

  • api_path: A string starting with / that defines the URL route (e.g., /users/:id)
  • http_method: The HTTP verb (GET, POST, PUT, DELETE, etc.)

As documented in engine/src/workers/rest_api/skills/skills/http/reactive-triggers.md, the api_path supports dynamic segments using the :parameter syntax.

Implementation Examples by SDK

JavaScript and TypeScript

The Node SDK uses iii.registerFunction and iii.registerTrigger to set up endpoints. This example from sdk/packages/node/iii-example/src/index.ts demonstrates a parameterized greeting endpoint:

import { iii } from 'iii-sdk';

async function greet(req, logger) {
  const name = req.path_params?.name ?? 'world';
  return {
    status_code: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: `Hello, ${name}!` }),
  };
}

iii.registerFunction({ id: 'greetings::hello', handler: greet });

iii.registerTrigger({
  type: 'http',
  function_id: 'greetings::hello',
  config: { api_path: '/hello/:name', http_method: 'GET' },
});

Python

In Python, use iii.register_function and iii.register_trigger with dictionary configurations. This pattern appears in sdk/packages/python/iii-example/src/main.py:

from iii import iii

def greet(request, logger):
    name = request.get('path_params', {}).get('name', 'world')
    return {
        'status_code': 200,
        'headers': {'Content-Type': 'application/json'},
        'body': f'{{"message": "Hello, {name}!"}}',
    }

iii.register_function({'id': 'greetings::hello', 'handler': greet})

iii.register_trigger({
    'type': 'http',
    'function_id': 'greetings::hello',
    'config': {'api_path': '/hello/:name', 'http_method': 'GET'},
})

Rust

The Rust SDK provides typed builders for function and trigger registration. As shown in sdk/packages/rust/iii/tests/api_triggers.rs:

use iii_sdk::{iii, RegisterFunction, RegisterTrigger, HttpMethod, json};

fn greet(req: iii::HttpRequest) -> iii::HttpResponse {
    let name = req.path_params
        .get("name")
        .and_then(|v| v.as_str())
        .unwrap_or("world");
    iii::HttpResponse::new()
        .status_code(200)
        .header("Content-Type", "application/json")
        .body(json!({ "message": format!("Hello, {}!", name) }))
}

iii.register_function(
    RegisterFunction::new("greetings::hello", greet)
        .description("Greeting endpoint")
);

iii.register_trigger(
    iii::Trigger::Http {
        api_path: "/hello/:name".into(),
        http_method: HttpMethod::Get,
    }
    .for_function("greetings::hello")
);

Configuring api_path and Path Parameters

The api_path configuration supports both static routes and dynamic path segments. When you define a segment like :name in the path string, the iii-http worker extracts the corresponding URL segment and populates request.path_params as a string key-value pair.

Key constraints from the reactive triggers documentation in engine/src/workers/rest_api/skills/skills/http/reactive-triggers.md:

  • The api_path must begin with a leading slash (/)
  • Path parameter names follow the colon immediately (e.g., /users/:id/posts)
  • Extracted parameters appear in path_params as strings, requiring manual type conversion in your handler

You can also configure optional middleware via middleware_function_ids in the trigger configuration, or set global middleware in iii-config.yaml to apply cross-cutting concerns like authentication.

Worker Configuration Options

While the default configuration runs on port 3111, you can customize the iii-http worker behavior by modifying engine/src/workers/rest_api/iii.worker.yaml or your local iii-config.yaml:

name: iii-http
port: 3111
host: 0.0.0.0
default_timeout: 30000
concurrency_request_limit: 1024
body_limit: 1048576
trust_proxy: false
request_id_header: x-request-id
ignore_trailing_slash: false
not_found_function: myApp::fallback
cors:
  allowed_origins: ["*"]
  allowed_methods: ["GET", "POST"]

The not_found_function setting allows you to specify a custom function that receives unmatched requests, enabling single-page application serving or custom 404 handling.

Summary

  • The iii-http worker automatically exposes registered iii functions as REST endpoints when you configure HTTP triggers with api_path and http_method pairs.
  • Path parameters use the :name syntax in api_path and appear as strings in request.path_params within your handler function.
  • Registration requires three steps: define the handler function, register it with a unique ID, and create an HTTP trigger binding it to a specific path.
  • The worker runs on port 3111 by default, with configuration options available in engine/src/workers/rest_api/iii.worker.yaml for port, host, CORS, request limits, and fallback handlers.
  • SDKs for JavaScript, Python, and Rust provide equivalent registration APIs that follow the same underlying protocol defined in the reactive triggers documentation.

Frequently Asked Questions

What format should the api_path value use?

The api_path must be a string that starts with a forward slash (/) followed by the route definition. For dynamic segments, insert a colon followed by the parameter name, such as /users/:id or /api/v1/resources/:resourceId. This value is defined in the trigger configuration alongside the http_method property as documented in engine/src/workers/rest_api/skills/skills/http/reactive-triggers.md.

How do I access URL parameters in my handler function?

Path parameters defined in api_path (e.g., :name) are extracted by the iii-http worker and placed into the path_params field of the HttpRequest object. In JavaScript, access them via request.path_params.name; in Python, use request.get('path_params', {}).get('name'); in Rust, access req.path_params.get("name").

Can I change the default port 3111?

Yes, modify the port field in the worker configuration file at engine/src/workers/rest_api/iii.worker.yaml or override it in your iii-config.yaml. The default configuration binds to port 3111, but you can specify any available port for the Axum server to listen on.

What happens if two triggers have the same api_path and method?

When conflicting routes exist with identical api_path and http_method values, the iii-http worker resolves the conflict by keeping the most recently registered trigger active. Earlier registrations for the same path and method combination are effectively overwritten by subsequent registrations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →