> ## Documentation Index
> Fetch the complete documentation index at: https://iii.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Triggers (deprecated)

> Calling functions directly or by binding them to events in your iii project.

> [!NOTE]
>
> Calling a function directly with `worker.trigger` or `iii trigger` are documented in [Using iii /
> Functions](https://iii.dev/docs/0-25-0/using-iii/functions.md#triggering-invoking-functions). This page covers specifics about Triggers
> such as registering (binding) Triggers to Functions, gating triggers, and unregistering them.

> [!WARNING]
>
> `registerTrigger` is deprecated in 0.25 and will be removed in a future release. It keeps working
> and logs one warning per process. It is still how you bind functions to the http, cron, queue,
> state, stream, pubsub, and database workers, which publish trigger types in 0.25. For engine events
> and any other function, use [observers](https://iii.dev/docs/0-25-0/using-iii/observers.md) (`iii.observe`). See [Upgrading from
> 0.24.x](https://iii.dev/docs/0-25-0/upgrading/from-0-24-x.md).

## Register a trigger

> [!NOTE]
>
> If you're authoring a worker, you'll want to refer to [Creating Workers /
> Triggers](https://iii.dev/docs/0-25-0/creating-workers/triggers.md#bind-a-function-to-an-existing-trigger-type) to learn the
> difference between registering a trigger, and registering a trigger type.

Functions can also run when a trigger is satisfied. A trigger can be any event that happens such as
a request to an `http` endpoint, a `cron` job, a change in `state`, or any other trigger that a
worker supports. You can also [write your own](https://iii.dev/docs/0-25-0/creating-workers/triggers.md).

You bind triggers to functions via the `function_id`. The trigger declares its `type`, its `config`
(defined by each type), and the function to invoke.

> [tab] Node / TypeScript
>
> ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> 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, { namespace: "orders" });
>
> worker.registerTrigger({
>   type: "http",
>   function_id: "math::add",
>   config: { api_path: "/math/add", http_method: "POST" },
> });
> ```

> [tab] Python
>
> ```python theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> import os
> from iii import register_worker, InitOptions
>
> worker = register_worker(
>     os.environ.get("III_URL"),
>     InitOptions(worker_name="my-worker", namespace="orders"),
> )
>
> worker.register_trigger({
>     "type": "http",
>     "function_id": "math::add",
>     "config": {"api_path": "/math/add", "http_method": "POST"},
> })
> ```

> [tab] Rust
>
> ```rust theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> use iii_sdk::protocol::RegisterTriggerInput;
> use iii_sdk::{InitOptions, register_worker};
> use serde_json::json;
>
> let url = std::env::var("III_URL").expect("III_URL must be set");
> let worker = register_worker(
>     &url,
>     InitOptions {
>         namespace: Some("orders".into()),
>         ..Default::default()
>     },
> );
>
> worker.register_trigger(RegisterTriggerInput::new(
>     "http",
>     "math::add",
>     json!({ "api_path": "/math/add", "http_method": "POST" }),
> ))?;
> ```

Per-type configuration is documented in each worker's Worker Docs (e.g.
[http](https://workers.iii.dev/workers/http) for the `http` type).

### Trigger metadata

The optional `metadata` field on a trigger registration (omitted in the examples above) is
arbitrary JSON stored with the trigger and delivered to the receiving function as a distinct
argument alongside the payload. It is useful for providing contextual information about the trigger
or execution context to the receiving function.

A target function shared by many triggers can use it to recover which registration fired and with
what context. See
[Receive per-invocation metadata](https://iii.dev/docs/0-25-0/creating-workers/functions.md#receive-per-invocation-metadata) for
handler-side access in each SDK.

> [!NOTE]
>
> `metadata` can be provided both via `registerTrigger` and direct `trigger()` invocations

## Registering before a trigger type is available

Registration is order-independent. If you register a trigger whose type is not active in the project
yet (for example, the worker that publishes `http` has not connected), the engine stores the
registration optimistically and activates it automatically once that trigger type becomes available.
The engine logs a pending notice while a registration waits, and re-establishes stored registrations
when a publishing worker restarts, so a worker that comes up after its consumers receives trigger
registrations as expected.

Bind `math::add` to the `http` type before the http worker is running. The engine accepts the
registration and stores it:

> [tab] Node / TypeScript
>
> ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> // http worker not started
> worker.registerTrigger({
>   type: "http",
>   function_id: "math::add",
>   config: { api_path: "/math/add", http_method: "POST" },
> });
> ```

> [tab] Python
>
> ```python theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> # http worker not started
> worker.register_trigger({
>     "type": "http",
>     "function_id": "math::add",
>     "config": {"api_path": "/math/add", "http_method": "POST"},
> })
> ```

> [tab] Rust
>
> ```rust theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> use iii_sdk::protocol::RegisterTriggerInput;
> use serde_json::json;
>
> // http worker not started
> worker.register_trigger(RegisterTriggerInput::new(
>     "http",
>     "math::add",
>     json!({ "api_path": "/math/add", "http_method": "POST" }),
> ))?;
> ```

Start the http worker afterward. The engine activates the stored binding automatically, and the
endpoint serves requests without re-registering. The `-n` flag is the documented short form of
`--namespace` for [`iii trigger`](../cli-reference/index#iii-trigger):

```bash theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
# http worker started after registration
iii trigger -n dev compose::add worker=http

# the stored binding is now live on the http worker's default port (3111)
curl -X POST http://localhost:3111/math/add \
  -H 'Content-Type: application/json' \
  -d '{"a": 2, "b": 3}'
# 200 OK: the request reaches math::add through the activated trigger
```

For known trigger types (ex. `http`, `state`, `durable:subscriber`, `stream`), the pending notice
includes the Compose command for the worker that provides the type. Replace `dev` with the
namespace of the running Compose daemon. For other types, find the worker that exposes the needed
type at [workers.iii.dev](https://workers.iii.dev).

A registration fails when an active provider rejects the config it was given (for example, an
invalid trigger config). The engine then sends a `TriggerRegistrationResult` with an `error` body
back to the worker that initiated the request and logs it.

When no worker provides the type the old way, but a function declares it as its
`legacy_trigger_type`, the engine turns the registration into an observer of that function and
answers with its outcome. The registration keeps working unchanged; see
[Accept old triggers](https://iii.dev/docs/0-25-0/creating-workers/observable-functions.md#accept-old-triggers).

## Bind multiple triggers to one function

It's valid to bind multiple triggers to the same `function_id` and this can be done across any
number of types. Register a second trigger with the same `function_id` and a different type or
config; the function runs unchanged whether the call arrives over HTTP, on a cron schedule, or from
a queue message.

> [tab] Node / TypeScript
>
> ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> // Same handler runs for an HTTP POST and a weekly cron tick.
> worker.registerTrigger({
>   type: "http",
>   function_id: "reports::generate",
>   config: { api_path: "/reports/generate", http_method: "POST" },
> });
>
> worker.registerTrigger({
>   type: "cron",
>   function_id: "reports::generate",
>   config: { expression: "0 0 9 * * 1" }, // Every Monday at 09:00
> });
> ```

> [tab] Python
>
> ```python theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> worker.register_trigger({
>     "type": "http",
>     "function_id": "reports::generate",
>     "config": {"api_path": "/reports/generate", "http_method": "POST"},
> })
>
> worker.register_trigger({
>     "type": "cron",
>     "function_id": "reports::generate",
>     "config": {"expression": "0 0 9 * * 1"},  # Every Monday at 09:00
> })
> ```

> [tab] Rust
>
> ```rust theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> use iii_sdk::protocol::RegisterTriggerInput;
> use serde_json::json;
>
> worker.register_trigger(RegisterTriggerInput::new(
>     "http",
>     "reports::generate",
>     json!({ "api_path": "/reports/generate", "http_method": "POST" }),
> ))?;
>
> worker.register_trigger(RegisterTriggerInput::new(
>     "cron",
>     "reports::generate",
>     json!({ "expression": "0 0 9 * * 1" }), // Every Monday at 09:00
> ))?;
> ```

## Gate a trigger with a condition

A trigger can carry an optional `condition_function_id` (set inside the trigger's `config`). When
the trigger fires, the engine invokes the condition function first with the same payload the handler
would receive; the target `function_id` only runs when the condition returns truthy. The condition
is a regular registered function.

> [!TIP]
>
> When an observer's source is a function, a condition you can state over the event becomes a
> declarative [filter](https://iii.dev/docs/0-25-0/reference/filters.md), evaluated without an extra function call:
> `filters: { "input.customer_tier": { "$eq": "gold" } }`. A condition that needs code (a lookup, an
> auth check) has no filter form: keep `condition_function_id`, which works in 0.25, or move the
> check into the target function.

> [tab] Node / TypeScript
>
> ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> worker.registerFunction(
>   "orders::is-priority",
>   async (payload: { customer_tier: string }) => payload.customer_tier === "gold",
> );
>
> worker.registerTrigger({
>   type: "http",
>   function_id: "orders::expedite",
>   config: {
>     api_path: "/orders/expedite",
>     http_method: "POST",
>     condition_function_id: "orders::is-priority",
>   },
> });
> ```

> [tab] Python
>
> ```python theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> def is_priority(payload: dict) -> bool:
>     return payload.get("customer_tier") == "gold"
>
> worker.register_function("orders::is-priority", is_priority)
>
> worker.register_trigger({
>     "type": "http",
>     "function_id": "orders::expedite",
>     "config": {
>         "api_path": "/orders/expedite",
>         "http_method": "POST",
>         "condition_function_id": "orders::is-priority",
>     },
> })
> ```

> [tab] Rust
>
> ```rust theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> use iii_sdk::protocol::RegisterTriggerInput;
> use iii_sdk::RegisterFunction;
> use schemars::JsonSchema;
> use serde::Deserialize;
> use serde_json::json;
>
> #[derive(Deserialize, JsonSchema)]
> struct Payload { customer_tier: String }
>
> worker.register_function(RegisterFunction::new(
>     "orders::is-priority",
>     |input: Payload| -> Result<bool, String> {
>         Ok(input.customer_tier == "gold")
>     },
> ));
>
> worker.register_trigger(RegisterTriggerInput::new(
>     "http",
>     "orders::expedite",
>     json!({
>         "api_path": "/orders/expedite",
>         "http_method": "POST",
>         "condition_function_id": "orders::is-priority",
>     }),
> ))?;
> ```

## Unregister a trigger

Trigger registration returns a handle with an `unregister()` method. Call it to drop the trigger at
runtime; when the worker disconnects, all of its triggers are removed automatically.

> [tab] Node / TypeScript
>
> ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> const trigger = worker.registerTrigger({
>   type: "http",
>   function_id: "math::add",
>   config: { api_path: "/math/add", http_method: "POST" },
> });
>
> trigger.unregister();
> ```

> [tab] Python
>
> ```python theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> trigger = worker.register_trigger({
>     "type": "http",
>     "function_id": "math::add",
>     "config": {"api_path": "/math/add", "http_method": "POST"},
> })
>
> trigger.unregister()
> ```

> [tab] Rust
>
> ```rust theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
> use iii_sdk::protocol::RegisterTriggerInput;
> use serde_json::json;
>
> let trigger = worker.register_trigger(RegisterTriggerInput::new(
>     "http",
>     "math::add",
>     json!({ "api_path": "/math/add", "http_method": "POST" }),
> ))?;
>
> trigger.unregister();
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.