---
title: Action
description: Validate an operation and return its next interface state.
---

An action owns one operation on a resource. It validates every payload before
its handler runs. A form, an agent, or another client can call the same action
and get the same validation and response.

Register an action once and keep its definition. Forms use the definition as
their typed submission target.

## API

```ts
resource.action(name, schema, handler): ActionDefinition

resource.action(
  name,
  params: string[],
  schema,
  handler
): ActionDefinition
```

The schema can use any Standard Schema validator. These examples use Zod.

| Definition member   | Result                                                                       |
| ------------------- | ---------------------------------------------------------------------------- |
| `resource`          | Returns the resource name.                                                   |
| `name`              | Returns the action name.                                                     |
| `params`            | Returns the ordered parameter names.                                         |
| `schema`            | Returns the validation schema.                                               |
| `getAddress(args?)` | Creates a complete action address.                                           |
| `validate(input)`   | Returns the schema result without running the handler.                       |
| `execute(ctx)`      | Validates and runs the handler. Expected failures throw.                     |
| `attempt(ctx)`      | Validates and runs the handler. Expected failures become protocol responses. |

The Backlit app calls `attempt` through its request pipeline. Application code
usually registers the action and uses the handler context.

## Basic action

Return nothing from the handler for a successful operation with no additional
interface change.

<BacklitExample
  path="actions/basic"
  action="create"
  payload={{ customerName: 'Ada' }}
/>

## Validated data

The handler reads the schema output from `ctx.data`. It does not read the raw
payload. This example trims the submitted name before the handler uses it.

<BacklitExample
  path="actions/validated_data"
  action="create"
  payload={{ customerName: '  Ada  ' }}
/>

## Parameters

Declare address parameters in order. Read their values from `ctx.paramValues`.
The payload remains available separately through `ctx.data`.

<BacklitExample
  path="actions/params"
  action="update"
  args={['42']}
  payload={{ customerName: 'Ada' }}
/>

Call `action.getAddress(args)` to create the complete resource, action, string
arguments, and key. A missing argument causes an error.

## Notifications

Call `ctx.notify` to report success without a navigation change. Notifications
stay in call order. A notification can use text alone or add a title and tone.

<BacklitExample
  path="actions/notifications"
  action="save"
  payload={{ note: 'Ready' }}
/>

## Redirect

Call `ctx.redirectTo` with the target view definition. For a view with parameters,
add one argument for each parameter in declaration order. TypeScript checks the
target and argument count. A redirect can also carry notifications.

<BacklitExample
  path="actions/redirect"
  expectedStatus={302}
  action="create"
  payload={{ customerName: 'Ada' }}
/>

## Refresh the page

Call `ctx.refresh()` to request a refresh of the current page.

<BacklitExample
  path="actions/refresh_page"
  action="archive"
  payload={{ orderId: '42' }}
/>

## Refresh zones

Pass an array of zone names to `ctx.refreshZones` to refresh only those parts of
the current page. A refresh can also carry notifications.

<BacklitExample
  path="actions/refresh_zones"
  action="recalculate"
  payload={{ orderId: '42' }}
/>

## Schema failure

If the schema rejects the payload, Backlit does not run the handler. It returns
each issue as a validation error. Nested schema paths become dot-separated
protocol paths.

<BacklitExample
  path="actions/schema_failure"
  expectedStatus={422}
  action="create"
  payload={{ customerName: '' }}
/>

## Field failure

Call `ctx.fail` with a key-to-message object when the handler rejects one or
more submitted fields. Backlit returns an action failure with field paths.

<BacklitExample
  path="actions/field_failure"
  expectedStatus={422}
  action="create"
  payload={{ email: 'ada@example.com' }}
/>

## Payload failure

Call `ctx.fail` with text when the failure applies to the complete payload. Use
the optional code for a stable application error code.

<BacklitExample
  path="actions/payload_failure"
  expectedStatus={422}
  action="pay"
  payload={{ card: '4242' }}
/>

## Expected request error

Call `ctx.error` when the request cannot continue for a reason that is not a
validation failure. Set the HTTP status and a stable error code. The response
can include errors and notifications.

<BacklitExample
  path="actions/expected_error"
  expectedStatus={403}
  action="cancel"
  payload={{ orderId: '42' }}
/>

An unexpected exception continues to throw. Backlit does not convert it into
an action failure.
