Skip to main content
The Smart Actions API was redesigned to be more composable and explicit. The legacy API mixed declaration, form definition, and execution in a single object; the new agent splits these concerns and ties everything to a fluent collection-customization API.

API cheatsheet

Before (Node.js, forest-express-sequelize)

After (Node.js, @forestadmin/agent)

Before (Ruby, forest-rails)

After (Ruby, forest_admin_rails)

Customizations live inside ForestAdminRails::CreateAgent.customize in app/lib/forest_admin_rails/create_agent.rb:

Result types

The new agent supports the same result types as v1, plus a few new ones, all returned via resultBuilder: See Action result types for details.

Form fields

Forms in the new agent are typed and support dynamic behavior more cleanly:
See Action forms for the full API.

Approval workflows

If your legacy action used the approval system, the configuration moves from the action declaration to Project Settings → Roles. The action code itself doesn’t need changes. Forest’s UI handles the approval gating around your execute function.

Bulk and global actions

Bulk actions can use context.getRecordIds() to retrieve every selected record’s primary key, or context.getRecords(fields) to fetch the full records.

Common conversions

v1 used download: true and the route returned a file via res. v2 returns the file via resultBuilder.file(buffer, filename, mimeType) and sets generateFile: true in the action declaration.
v1 supported change hooks on form fields. v2 uses the if and defaultValue properties, which receive a context and the current form values. See Action forms.
v1 used Liana.ensureAuthenticated middleware and custom permission checks. v2 uses the standard role and team permission system configured in the UI. For dynamic checks (e.g. only the assigned rep can run an action), use action visibility conditions or check inside execute and return resultBuilder.error(...).
Direct port: execute calls your webhook URL the same way the v1 route handler did. The axios.post(...) (or Net::HTTP.post(...)) line is unchanged.

Migration checklist

1

List every Smart Action in your project

Pull from your forest/ (Node.js) or app/services/forest_liana/actions/ (Ruby) directory.
2

For each action, port the declaration

Replace Liana.collection(...).actions = [...] with agent.customizeCollection(...).addAction(...).
3

Port the form definition

Convert each field to a form entry. Update widget names (camelCase → PascalCase: 'text area''TextArea').
4

Port the execute logic

Move the route handler body into the execute function. Replace req.body.data.attributes.values with context.formValues.
5

Test in parallel

Run both agents and confirm each action behaves the same.

Next step

Migrate Smart Fields

Convert computed fields to the new addField API with explicit dependencies.