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

> ## Agent Instructions
> Automate.ax automations are TypeScript programs.
> Use Bun for installation and command examples.
> Use Automate.ax for the product name and automate.ax for the package, CLI, and domain.
> Treat documented public APIs as current; do not invent transitional or deprecated names.

# Utilities

> Transform, interpolate, route, and coordinate signals in an automation.

The utilities exported from `automate.ax` let you derive values and control which work becomes ready without awaiting a [signal](/concepts/signals).

## Interpolate text with `t`

Use the `t` tagged template to combine signals with static text or in-memory values:

```ts automations/request-notification.automation.ts theme={null}
import { automation, onHttpRequest, sendEmail, t } from "automate.ax"

export default automation("Report HTTP requests", () => {
  const request = onHttpRequest()

  sendEmail({
    subject: t`${request.method} request received`,
    text: t`Received ${request.method} at ${request.path}.`,
  })
})
```

`t` returns a `Signal<string>` that depends on every interpolated signal. It converts each resolved signal and ordinary interpolated value with `String()`.

## Transform values

Every signal has a `.transform()` method for deriving a value from one signal:

```ts theme={null}
const normalizedPath = request.path.transform((path) => path.toLowerCase())
```

Use the top-level `transform` function when a result depends on several signals:

```ts theme={null}
import { transform } from "automate.ax"

const requestLine = transform(
  [request.method, request.path],
  (method, path) => `${method} ${path}`,
)
```

The transformer runs after all input signals materialize and returns a signal for its result. Keep transformers pure: use actions for external work.

## Route values conditionally

Use `filter` to preserve a signal only when a predicate matches:

```ts theme={null}
import { filter } from "automate.ax"

const apiRequest = filter(request, ({ path }) => path.startsWith("/api/"))
```

`filter(signal, predicate)` emits the original value when the predicate returns `true`. Otherwise, it closes without a value, so work that depends on it does not run.

Use `partition` when both outcomes need separate paths:

```ts theme={null}
import { partition } from "automate.ax"

const [apiRequest, otherRequest] = partition(request, ({ path }) =>
  path.startsWith("/api/"),
)
```

Exactly one returned signal emits the original value; the other closes.

Use `gate` when you already have a boolean signal:

```ts theme={null}
import { gate } from "automate.ax"

const isApiRequest = request.path.transform((path) => path.startsWith("/api/"))
const apiRequest = gate(request, isApiRequest)
const otherRequest = gate(request, isApiRequest, false)
```

`gate(value, condition, expected)` emits `value` when `condition` equals `expected` and closes otherwise. `expected` defaults to `true`.

## Branch automation work

Use `branch` when different actions should run for the two outcomes of a boolean signal:

```ts automations/route-request.automation.ts theme={null}
import { automation, branch, onHttpRequest, sendEmail, t } from "automate.ax"

export default automation("Route an HTTP request", () => {
  const request = onHttpRequest()
  const isApiRequest = request.path.transform((path) =>
    path.startsWith("/api/"),
  )

  branch(
    isApiRequest,
    () => {
      sendEmail({ subject: "API request", text: t`Path: ${request.path}` })
    },
    () => {
      sendEmail({ subject: "Other request", text: t`Path: ${request.path}` })
    },
  )
})
```

Automate.ax traverses both callbacks while composing the automation, but only actions in the selected branch become ready during execution. If either callback returns a signal, both callbacks must return signals; `branch` then returns a signal for the selected result. When only the true callback is provided and it returns a signal, that result closes when the condition is false.

## Add an ordering dependency

Use `group` when actions must wait for signals that they do not consume as inputs:

```ts theme={null}
import { group } from "automate.ax"

const archived = archiveGmailMessages({ messageIds: message.messageId })

group(archived, () => {
  sendEmail({
    subject: "Message archived",
    text: "The message was archived successfully.",
  })
})
```

`group(dependencies, callback)` makes every action called synchronously inside the callback wait for one signal or an array of signals. It returns the callback's result unchanged.

## Handle closure and failure

A signal can emit a value, close without one, or fail. `closed(signal)` and `failed(signal)` project those lifecycle outcomes as ordinary signals:

```ts theme={null}
import { closed, failed, group } from "automate.ax"

const failure = failed(result)

group(failure, () => {
  sendEmail({ subject: "Action failed", text: failure.message })
})

group(closed(result), () => {
  sendEmail({
    subject: "No result",
    text: "The action closed without a value.",
  })
})
```

`failed(signal)` emits `{ name: string, message: string }` only when the source fails. `closed(signal)` emits `void` only when the source closes without a value. Each lifecycle signal closes for every other outcome.

## Inspect a value

`isSignal(value)` is a TypeScript type guard for reusable helpers that accept ordinary values or signals:

```ts theme={null}
import { isSignal, type Signal } from "automate.ax"

function normalize(value: string | Signal<string>) {
  return isSignal(value) ? value.transform((text) => text.trim()) : value.trim()
}
```

Most automations do not need `isSignal`; action inputs already accept compatible signals.
