# createActionHandler (/docs/reference/agents/action-handler)



```ts
import { createActionHandler } from "@nylorun/agents";
```

Guides: [Action endpoints](/docs/run/action-endpoints),
[App server](/docs/deploy/app-server#action-endpoint).

## `createActionHandler` [#createactionhandler]

```ts
createActionHandler(options: ActionHandlerOptions): ActionHandler
```

Returns one HTTP handler for the agents' Actions. The Runtime POSTs each tool
call, hook, and flow `fn`, `verify`, or tool step to it. Each request's
delivery token (`Nylorun-Signature`) is verified before any code runs: it must
be signed by the Tenant's key, for this Tenant, URL, Action, generation, and
exact body. Sandbox tools run in the Runtime instead.

```ts
const actions = createActionHandler({ agents, url, onError: console.error });
createServer(actions.node).listen(3001);
await actions.register({ url });
```

### `ActionHandlerOptions` [#actionhandleroptions]

| Name                    | Type                                        | Required | Description                                                                                                                     |
| ----------------------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `agents`                | `readonly (AgentSource \| BuiltWorkflow)[]` | Yes      | Agents and flow agents served. Agents they embed are served too.                                                                |
| `client`                | `AgentsClient \| Promise<AgentsClient>`     | No       | Application client for `register`, and for reading public keys when `runtime` is unset. Default: `createClient()`.              |
| `runtime`               | `{ url: string; tenant: string; fetch? }`   | No       | Where to read the Tenant's public keys without a key. Set it for a process that only serves Actions.                            |
| `jwks`                  | `{ keys: readonly JsonWebKey[] }`           | No       | The Tenant's public keys, instead of reading them from the Runtime.                                                             |
| `url`                   | `string`                                    | No       | The registered URL every delivery token must name. Default: the URL passed to `register` in this process.                       |
| `implementationVersion` | `string`                                    | No       | Registered with each endpoint. Default: `NYLORUN_IMPLEMENTATION_VERSION`, then `dev`.                                           |
| `onError`               | `(error: unknown) => void`                  | No       | Receives errors answered with a status instead of thrown.                                                                       |
| `waitUntil`             | `(work: Promise<unknown>) => void`          | No       | Keeps a background tool's work alive after its `202` on platforms that end work with the response. A Node server needs nothing. |

### `ActionHandler` [#actionhandler]

| Member     | Type                                                            | Description                                                                                    |
| ---------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `fetch`    | `(request: Request) => Promise<Response>`                       | Web-standard handler: Hono, Next.js route handlers, Workers, Bun, Deno.                        |
| `node`     | Node request listener                                           | The same handler for `node:http` and Express.                                                  |
| `register` | `(options: RegisterOptions) => Promise<EndpointPingResponse[]>` | Saves definitions, registers `url` for every served agent, and pings each through the Runtime. |

`register` needs an application key and a Runtime with the `action-endpoints`
feature; it refuses an older Runtime before sending anything.

### `RegisterOptions` [#registeroptions]

| Name              | Type          | Required | Description                                                              |
| ----------------- | ------------- | -------- | ------------------------------------------------------------------------ |
| `url`             | `string`      | Yes      | The URL the Runtime calls, e.g. `http://localhost:3001/nylorun/actions`. |
| `saveDefinitions` | `boolean`     | No       | Save the definitions first. Default `true`.                              |
| `timeoutMs`       | `number`      | No       | How long one inline delivery may take. Default 60 000; at most 840 000.  |
| `maxConcurrent`   | `number`      | No       | In-flight deliveries per agent. Default 16.                              |
| `signal`          | `AbortSignal` | No       | Aborts registration.                                                     |

## Responses [#responses]

| Status | When                                                                                        |
| ------ | ------------------------------------------------------------------------------------------- |
| `200`  | The outcome, tagged with `Nylorun-Outcome: 1`, or a ping answer.                            |
| `400`  | The body is not an Action delivery.                                                         |
| `202`  | A `background: true` tool; the handler heartbeats and posts the result itself.              |
| `401`  | The delivery token is missing, invalid, or for another delivery.                            |
| `404`  | The agent or tool is not served here.                                                       |
| `405`  | Any method other than `POST`.                                                               |
| `409`  | A flow Action for another version of the flow. The Runtime retries.                         |
| `503`  | Signed with a key the handler has not seen yet, just after a rotation. The Runtime retries. |

## Delegation limits [#delegation-limits]

Agents used as tools cannot declare `approval`. `ctx.ask`, `ctx.approve`,
`ctx.sleep`, and `ctx.waitFor` inside a delegated agent fail with
`delegation.interaction-unsupported`.
