API cheatsheet
Before (Node.js, forest-express-sequelize)
After (Node.js, @forestadmin/agent)
Before (Ruby, forest-rails)
After (Ruby, forest_admin_rails)
Customizations live insideForestAdminRails::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 viaresultBuilder:
See Action result types for details.
Form fields
Forms in the new agent are typed and support dynamic behavior more cleanly: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 yourexecute 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
Action that returns a file download
Action that returns a file download
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.Action with dynamic form fields
Action with dynamic form fields
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.Action with custom permission logic
Action with custom permission logic
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(...).Action that triggers a webhook
Action that triggers a webhook
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.