| title | Action |
|---|---|
| description | Action protocol schemas |
{/*
Action Parameter Schema
Defines inputs required before executing an action.
Two declaration modes:
- Field-backed (preferred) — reference an existing object field; the
runtime resolves the field's label (i18n), type, validation rules,
options, placeholder, help text, and widget mapping from object
metadata. Cross-object references use objectOverride.
params: [
\{ field: 'email' \}, // same object
\{ field: 'role', objectOverride: 'sys_member' \}, // different object
]- Inline (legacy / bespoke) — declare
name,label,typeetc.
inline when no matching object field exists. Inline values may also be
used alongside field to override individual properties. A lookup /
master_detail param declared this way MUST name its target object via
reference — there is no field to inherit it from:
params: [
\{ name: 'inspector', label: 'Inspector', type: 'lookup', reference: 'sys_user' \},
]name is required unless field is provided (in which case it defaults
to the field name and is used as the request-body key).
**Source:** `packages/spec/src/ui/action.zod.ts`import { Action, ActionAi, ActionLocation, ActionParam, ActionType } from '@objectstack/spec/ui';
import type { Action, ActionAi, ActionLocation, ActionParam, ActionType } from '@objectstack/spec/ui';
// Validate data
const result = Action.parse(data);| Property | Type | Required | Description |
|---|---|---|---|
| name | string |
✅ | Machine name (lowercase snake_case) |
| label | string |
✅ | Display label |
| objectName | string |
optional | Target object this action belongs to. When set, the action is auto-merged into the object's actions array by defineStack(). |
| icon | string |
optional | Icon name |
| locations | Enum<'list_toolbar' | 'list_item' | 'record_header' | 'record_more' | 'record_related' | 'record_section' | 'global_nav'>[] |
optional | Locations where this action is visible |
| component | Enum<'action:button' | 'action:icon' | 'action:menu' | 'action:group'> |
optional | Visual component override |
| type | Enum<'script' | 'url' | 'modal' | 'flow' | 'api' | 'form'> |
optional | Action functionality type |
| target | string |
optional | URL, Script Name, Flow ID, or API Endpoint. Supports ${param.X} and ${ctx.X} interpolation. |
| openIn | Enum<'self' | 'new-tab'> |
optional | For type:'url' — where to open target. 'new-tab' opens a new browser tab; 'self' navigates in place. When omitted, external/absolute URLs open in a new tab and relative URLs navigate in place. Static execution option — keep it OUT of params (which is user-input-collection only). |
| body | { language: 'expression'; source: string } | { language: 'js'; source: string; capabilities?: Enum<'api.read' | 'api.write' | 'api.transaction' | 'crypto.uuid' | 'crypto.hash' | 'log'>[]; timeoutMs?: integer; … } |
optional | Action body — expression (L1) or sandboxed JS (L2). Only used when type is script. |
| execute | string |
optional | @deprecated — Use target instead. Auto-migrated to target during parsing. |
| params | { name?: string; field?: string; objectOverride?: string; label?: string; … }[] |
optional | Input parameters required from user |
| variant | Enum<'primary' | 'secondary' | 'danger' | 'ghost' | 'link'> |
optional | Button visual variant for styling (primary = highlighted, danger = destructive, ghost = transparent) |
| order | number |
optional | Sort order within a location group (lower = higher). Promotes/demotes an action toward the record_header primary button; stable, so actions without order keep their registration order. |
| confirmText | string |
optional | Confirmation message before execution |
| successMessage | string |
optional | Success message to show after execution |
| errorMessage | string |
optional | Error message to show when the action fails (overrides the raw error). |
| refreshAfter | boolean |
optional | Refresh view after execution |
| undoable | boolean |
optional | Offer an Undo affordance after this single-record update action succeeds. |
| resultDialog | { title?: string; description?: string; acknowledge?: string; format?: Enum<'qrcode' | 'code-list' | 'secret' | 'text' | 'json'>; … } |
optional | Render API response in a one-shot reveal dialog (suppresses successMessage when set). |
| visible | string | { dialect: Enum<'cel' | 'cron' | 'template'>; source?: string; ast?: any; meta?: object } |
optional | Visibility predicate (CEL). |
| requiresFeature | Enum<'twoFactor' | 'passkeys' | 'magicLink' | 'organization' | 'multiOrgEnabled' | 'degradedTenancy' | 'oidcProvider' | 'sso' | 'ssoEnforced' | 'deviceAuthorization' | 'admin' | 'phoneNumber' | 'phoneNumberOtp'> |
optional | Public auth feature flag gating this action; lowered into visible at parse time. |
| disabled | boolean | string | { dialect: Enum<'cel' | 'cron' | 'template'>; source?: string; ast?: any; meta?: object } |
optional | Boolean or predicate (CEL) — action is disabled when TRUE. |
| requiredPermissions | string[] |
optional | [ADR-0066 D4] Capabilities required to invoke this action (server-enforced 403 + UI hide/disable). |
| shortcut | string |
optional | Keyboard shortcut to trigger this action (e.g., "Ctrl+S") |
| bulkEnabled | boolean |
optional | Whether this action can be applied to multiple selected records |
| ai | { exposed?: boolean; description?: string; category?: Enum<'data' | 'action' | 'flow' | 'integration' | 'vector_search' | 'analytics' | 'utility'>; paramHints?: Record<string, { description?: string; enum?: string | number[]; examples?: any[] }>; … } |
optional | AI exposure (opt-in). Set ai.exposed=true + ai.description to make this callable by agents. |
| recordIdParam | string |
optional | Body key to inject the row id into when running from a list_item context. |
| recordIdField | string |
optional | Row field whose value seeds recordIdParam. Defaults to "id". |
| bodyShape | 'flat' | { wrap: string } |
optional | Body wrapping: flat (default) or { wrap: key } to nest user-collected params under a key. |
| method | Enum<'POST' | 'PATCH' | 'PUT' | 'DELETE'> |
optional | HTTP method for type:"api" actions. Defaults to POST. |
| bodyExtra | Record<string, any> |
optional | Constant body fields merged into the API request (applied last; overrides user params). |
| mode | Enum<'create' | 'edit' | 'delete' | 'custom'> |
optional | Semantic mode of the action. |
| opensInNewTab | boolean |
optional | Open the action result in a new tab. The renderer pre-opens the tab synchronously on click (popup-blocker-safe) and navigates it to the handler's redirectUrl. |
| newTabUrl | string |
optional | Direct new-tab URL template ({recordId} placeholder). When set with opensInNewTab, the renderer navigates the pre-opened tab here immediately — no action POST. The endpoint must enforce auth itself. |
| aria | { ariaLabel?: string; ariaDescribedBy?: string; role?: string } |
optional | ARIA accessibility attributes |
| Property | Type | Required | Description |
|---|---|---|---|
| exposed | boolean |
✅ | Expose this action to AI agents. Requires description when true. |
| description | string |
optional | LLM-facing description (≥40 chars). Required when exposed. |
| category | Enum<'data' | 'action' | 'flow' | 'integration' | 'vector_search' | 'analytics' | 'utility'> |
optional | Tool category override (defaults to "action"). |
| paramHints | Record<string, { description?: string; enum?: string | number[]; examples?: any[] }> |
optional | Per-parameter AI hints keyed by param name. |
| outputSchema | Record<string, any> |
optional | JSON Schema for the action return value. |
| requiresConfirmation | boolean |
optional | Override HITL confirmation for AI invocations. |
list_toolbarlist_itemrecord_headerrecord_morerecord_relatedrecord_sectionglobal_nav
| Property | Type | Required | Description |
|---|---|---|---|
| name | string |
optional | |
| field | string |
optional | Snake case identifier (lowercase with underscores only) |
| objectOverride | string |
optional | Snake case identifier (lowercase with underscores only) |
| label | string |
optional | Display label (plain string; i18n keys are auto-generated by the framework) |
| type | Enum<'text' | 'textarea' | 'email' | 'url' | 'phone' | 'password' | 'secret' | 'markdown' | 'html' | 'richtext' | 'number' | 'currency' | 'percent' | 'date' | 'datetime' | 'time' | 'boolean' | 'toggle' | 'select' | 'multiselect' | 'radio' | 'checkboxes' | 'lookup' | 'master_detail' | 'tree' | 'user' | 'image' | 'file' | 'avatar' | 'video' | 'audio' | 'formula' | 'summary' | 'autonumber' | 'composite' | 'repeater' | 'record' | 'location' | 'address' | 'code' | 'json' | 'color' | 'rating' | 'slider' | 'signature' | 'qrcode' | 'progress' | 'tags' | 'vector'> |
optional | |
| required | boolean |
optional | |
| options | { label: string; value: string }[] |
optional | |
| placeholder | string |
optional | |
| helpText | string |
optional | |
| defaultValue | any |
optional | |
| multiple | boolean |
optional | Allow multiple values (array value shape); mirrors FieldSchema.multiple. |
| accept | string[] |
optional | Accepted upload types (MIME types / extensions) for file/image params. |
| maxSize | integer |
optional | Max upload size in bytes for file/image params. |
| reference | string |
optional | Reference target object for inline lookup/master_detail params; mirrors FieldSchema.reference. |
| defaultFromRow | boolean |
optional | |
| visible | string | { dialect: Enum<'cel' | 'cron' | 'template'>; source?: string; ast?: any; meta?: object } |
optional | Param visibility predicate (CEL); omits the param when false. |
| requiresFeature | Enum<'twoFactor' | 'passkeys' | 'magicLink' | 'organization' | 'multiOrgEnabled' | 'degradedTenancy' | 'oidcProvider' | 'sso' | 'ssoEnforced' | 'deviceAuthorization' | 'admin' | 'phoneNumber' | 'phoneNumberOtp'> |
optional | Public auth feature flag gating this param; lowered into visible at parse time. |
scripturlmodalflowapiform