Skip to main content
Actions are the verbs of a resource. Each one runs inside your app when someone in your team (or an agent, or a triage rule) runs it from Pult. Pult never touches your database; it asks, and your code decides.
The key (ban) is the action’s identity. It’s referred to elsewhere as user.actions.ban.

Running

run receives:
  • record: the record, freshly fetched with get.
  • input: the form values, validated against input and typed from it.
  • actor: who ran it, with name, type (user, agent or triage) and their role.
  • item: the id of the item it was run from, if any.
Return nothing, a message, or a message with blocks:
The result shows in a toast and in the item’s or record’s history. Throw an error to fail: its message is shown to the person and recorded too. Actions always wait for your app. The console says it’s waiting for your app to confirm, and if your app is offline the action is queued and marked as waiting until your app answers.

Forms

input is a schema, and Pult builds the form from it: text fields, numbers, dates, switches, single and multiple choice from enums, and multi-line boxes for strings longer than 200 characters. .describe() adds a hint under a field. An action without input runs straight away (unless it needs confirmation). defaults(record) prefills the form from the record:
pick turns a field into a searchable record picker, and choices fills a dropdown from your app:
Choices can be strings or { value, label } pairs. The history shows a picked record by its title.

When an action applies

when(record) hides the action unless it applies right now: Ban only for users who aren’t banned, Unban only for those who are. Your app checks it again when the action runs, so an action that stopped applying in the meantime is refused with a clear message.

Guardrails

danger: "confirm" asks for confirmation first. danger: "reauth" also asks the person to sign in again if they haven’t in the last ten minutes. Dangerous actions are shown in red. approval: true means one person asks and someone else approves. The request waits under Approvals; the person who asked can’t approve it themselves, and approving a reauth action needs a fresh sign-in too. The history shows who asked, who approved and what ran. preview shows what would happen before anyone commits:

Background actions

Long work (exports, migrations, bulk changes in your own system) can run in the background:
The console doesn’t wait. The history shows a progress bar with your notes, then the result. progress takes a fraction between 0 and 1 (or null when you don’t know) and an optional note.

Actions without a record

Some work isn’t about one record: purging a cache, re-running last night’s payouts, sending a digest. Define those on their own:
They take the same options as any action except when, and record is null when they run. Grant them in a role like other actions. People run them from ⌘K, from pages and from any block with b.actions(purgeCache).

On a schedule

An action without a record can also run by itself:
schedule is a cron expression, or an object with one and a timezone. Without a timezone it runs in UTC. Each environment runs its own schedule, and every run is in the audit log under “Schedule” with its result. If your app can’t be reached when a run is due, that run is recorded as failed instead of piling up for later. People can still run a scheduled action by hand. pult check makes sure scheduled actions need no input without a default and no approval, since nobody is there to give them.

Triage and agents

Triage rules can run actions that are harmless (danger: "none") or that you mark automatable: true. They never run actions that need an approval. Agents run actions through their role like people do, but can’t run reauth actions, since they can’t sign in again.

Running on many at once

People can run an action on many records from a resource’s table, or on the records of many items from a list. Each one runs separately in your app and the result says how many succeeded.