Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
iii-hq avatar

Iii Http

  • 11 installs
  • 18.6k repo stars
  • Updated August 5, 2026
  • iii-hq/iii

Expose registered iii functions as HTTP/REST endpoints with a preHandler middleware chain for auth, rate limiting, and logging, without standing up a separate web server.

About

The iii-http worker runs an HTTP server that matches inbound requests against http triggers bound to registered functions and serializes the handler's response envelope. A developer uses it to serve REST endpoints on iii instead of running their own Express or Fastify server.

  • Middleware chain runs before handlers for auth, rate limiting, and logging
  • Default port 3111; the handler sets the HTTP status explicitly

Iii Http by the numbers

  • 11 all-time installs (skills.sh)
  • Ranked #3,574 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/iii-hq/iii --skill iii-http

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs11
repo stars18.6k
Last updatedAugust 5, 2026
Repositoryiii-hq/iii

What it does

Expose registered iii functions as HTTP/REST endpoints with a preHandler middleware chain for auth, rate limiting, and logging, without standing up a separate web server.

Files

SKILL.mdMarkdownGitHub ↗

iii-http

The iii-http worker exposes registered functions as HTTP endpoints. It runs an HTTP server on the configured host/port, matches each inbound request against the http triggers you bind, and invokes the bound function with the request. The handler returns a response envelope that the worker serializes to the wire — JSON, plain text, or bytes, depending on the Content-Type the handler sets.

The worker has no callable functions of its own — you never invoke an http::* id. Its surface is the http trigger TYPE plus a middleware chain that runs before each handler. You expose an endpoint by registering a normal function and binding an http trigger to it.

Do NOT stand up your own web server

This is the way to serve HTTP on iii, and the only one: register a function, then bind an http trigger to it. Do NOT write or run your own web server — no express, no http.createServer, no fastify, no framework listening on a port. iii does not route to a server you start yourself; such a process is unreachable as an iii endpoint, and running it as a foreground process (e.g. inside a sandbox) just hangs. If you are reaching for a web framework or a listen() call, stop — that instinct is from another ecosystem. The iii equivalent is one iii.registerTrigger({ type: 'http', ... }) per route; this worker is the server.

Get the contract from the engine

This page explains WHEN and HOW to serve HTTP; the engine is the source of truth for the exact shapes. Before you bind a trigger or write a handler, fetch the contract and build to it — do not guess field names, types, or defaults from memory:

engine::triggers::info { id: "http" }

returns three schemas: configuration_schema (the route fields — path, method, optional route gating, per-route middleware), request_schema (the request envelope your handler receives), and response_schema (the response envelope your handler must return). Build to all three. Use engine::triggers::list to discover trigger types in the first place.

Set the right HTTP status

YOUR handler chooses the HTTP status code — it is a field on the response envelope whose exact name is in the response_schema from engine::triggers::info { id: "http" } (read it; do not guess the field name from memory). The status is NOT inferred from the body, so a handler that just returns a value or an error object yields 200 by default. For error cases, set the matching status explicitly: a not-found path returns 404, a bad request 400, and so on. Returning 200 with an { error: ... } body is a bug — it "isn't a 500," but it also isn't the status the requirement asked for, and callers (and tests) will read it as success. Avoiding a crash is not the same as returning the right status.

When to Use

  • Expose a function as a REST endpoint without standing up a separate HTTP server.
  • You need URL path parameters or query strings parsed and handed to the handler.
  • You want condition-gated routes or per-route preHandler middleware.
  • You need a single not-found handler for unmatched routes.

Boundaries

  • No callable functions — the worker is never reached through an http::* id; routes are bound with iii.registerTrigger, not called.
  • Two routes on the same path + method conflict; the router resolves to the most recently registered one.
  • Query-string parsing is single-valued — a repeated key keeps only the last value; pre-encode multi-valued params yourself.
  • Middleware runs BEFORE body parsing, so it sees headers, path, and query but not the body — use it for preconditions, not response logic (that belongs in the handler).

Binding a route

Register your handler as a normal function, then bind an http trigger to it with iii.registerTrigger using the http trigger type. Shape the trigger config to what engine::triggers::info { id: "http" } describes — the route path and method, an optional gating function evaluated before the handler, and per-route middleware. Path segments you declare in the route arrive as string path parameters on the request; the gating function, if set, can veto a request before the handler runs.

Where your route is served

Your bound route is served by the iii-http worker on its configured host and port — NOT on a port you pick. The default port is 3111 (host 0.0.0.0), so a route with path /example is reachable at http://localhost:3111/example on a default deployment. Do NOT guess other ports (3000, 8080, the engine's WS port): if the deployment overrides the default, read the live host/port from the iii-http worker's configuration (config.yaml) or its status rather than probing. Verify a live endpoint with web::fetch against that base URL, never curl.

Middleware

Register a middleware function when the same logic must run before multiple routes — authentication, rate limiting, request logging, header normalization. A middleware is a regular registered function that receives a subset of the request (without the body) and either lets the request proceed (optionally enriching its context) or short-circuits with an immediate response. Target middleware two ways:

  • Globally — via the worker's middleware config in config.yaml; runs on every matched route, in priority order.
  • Per-route — via the trigger's middleware list; runs after the global chain, in the order given.

The full preHandler order per request: route match → global middleware → route gating function → per-route middleware → body parsing → handler.

Configuration

The worker is configured in config.yaml: the listen host and port, request timeout, concurrency and body-size limits, proxy trust and request-id header, trailing-slash handling, a not-found handler function, CORS, and the global middleware chain. For the full set of keys and their defaults, read the worker's configuration rather than hardcoding values from memory.

Related skills

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.