> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
last_verified: 2026-10-09
title: Functions
description: Define functions with fn(), configure retries and schemas, and call them from workflows
category: TypeScript SDK
hideRightSidebar: true
---

A function is one unit of work with retries and tracing. Define it with `fn(name)`, configure it on the returned builder, and finish with `.run(handler)`. For a guided introduction, see [Functions](/docs/build/functions.md).

## `fn(name)`

```typescript
import { fn } from '@agnt5/sdk';
import type { Context } from '@agnt5/sdk';

export const sendEmail = fn('send_email').run(
  async (ctx: Context, input: { to: string; subject: string }) => {
    ctx.logger.info('Sending email', { to: input.to });
    return `Sent to ${input.to}`;
  },
);
```

`fn<TInput, TOutput>(name)` returns a `FunctionBuilder`. `name` is the name the worker registers and the name you pass to `agnt5 run` or the API. The type parameters type the handler's input and return value:

```typescript
export const add = fn<{ a: number; b: number }, number>('add').run(
  async (ctx: Context, input) => input.a + input.b,
);
```

A function registers when its module is imported, so import every module that defines components from your worker's entry point. See [Worker](/sdk/typescript/worker.md).

## Builder methods

Chain these before `.run()`:

| Method | Description |
|---|---|
| `.retry(policy: RetryPolicy)` | How many times to try, and how long to wait between tries. |
| `.backoff(policy: BackoffPolicy)` | How the wait grows between tries. |
| `.description(text)` | What the function does. Used as the tool description when the function is [published over MCP](/docs/build/mcp.md#expose-agnt5-over-mcp). |
| `.inputSchema(schema)`, `.outputSchema(schema)` | JSON Schema for the input and output. TypeScript types are erased at runtime, so a schema is how AGNT5 learns the shape. |
| `.maxConcurrency(limit)`, `.priority(priority)` | Concurrency limit and scheduling priority recorded in the function's manifest. |
| `.flowControl(policy)` | Flow-control policy for [serverless endpoints](/docs/run/serverless-endpoints.md). |
| `.run(handler)` | Finish the definition and return the callable function. |

```typescript
export const sendEmail = fn<{ to: string }, string>('send_email')
  .retry({ maxAttempts: 5, initialIntervalMs: 500, maxIntervalMs: 30000 })
  .backoff({ type: 'exponential', multiplier: 2 })
  .description('Send a transactional email')
  .inputSchema({
    type: 'object',
    properties: { to: { type: 'string' } },
    required: ['to'],
  })
  .outputSchema({ type: 'string' })
  .run(async (ctx: Context, input) => `Sent to ${input.to}`);
```

### `RetryPolicy`

| Field | Default | Description |
|---|---|---|
| `maxAttempts` | `3` | Total tries, including the first. |
| `initialIntervalMs` | `1000` | Wait before the first retry. |
| `maxIntervalMs` | `60000` | Longest wait between retries. |

When every try fails, the run fails with the last error. `ctx.attempt` is `0` on the first try, `1` on the first retry, and so on.

### `BackoffPolicy`

| Field | Default | Description |
|---|---|---|
| `type` | `'exponential'` | `'constant'`, `'linear'`, or `'exponential'`. |
| `multiplier` | `2.0` | How fast the wait grows for `'linear'` and `'exponential'`. |

## The handler

```typescript
type FunctionHandler<TInput, TOutput> =
  (ctx: Context, ...args: TInput[]) => Promise<TOutput> | TOutput;
```

The first parameter is always the [`Context`](/sdk/typescript/context.md), which the runtime passes in. The second is the input: the JSON object you send to `agnt5 run --input` or the API. The handler can be `async` or synchronous, and its return value must be JSON-serializable.

| `Context` member | Description |
|---|---|
| `ctx.runId` | The ID of the current run. |
| `ctx.attempt` | Which try this is, starting at `0`. |
| `ctx.logger` | Structured logger: `ctx.logger.info('message', { key: value })`. |

## Call a function

`.run()` returns the function itself, typed as `(ctx, input) => Promise<TOutput>`. Call it with a context and an input:

| From | Call | Checkpointed |
|---|---|---|
| A workflow | `await ctx.step('send_email', () => sendEmail(ctx, input))` | Yes. On replay the saved result is returned. |
| Another function | `await sendEmail(ctx, input)` | Runs as part of the calling function. |
| The CLI | `agnt5 run send_email --input '{"to": "a@example.com"}'` | Its own run. |

```typescript
import { workflow } from '@agnt5/sdk';

export const welcome = workflow('welcome', async (ctx: Context, input: { email: string }) => {
  const sent = await ctx.step('send_email', () => sendEmail(ctx, { to: input.email }));
  return { sent };
});
```

See [Workflows](/sdk/typescript/workflows.md) for `ctx.step()` options.

## Next steps

* [Workflows](/sdk/typescript/workflows.md): run functions as checkpointed steps.
* [Context API](/sdk/typescript/context.md): everything a handler can do with `ctx`.
* [Functions guide](/docs/build/functions.md): retries, timeouts, and calling functions in practice.
