Skip to content

action

action

Execute a side effect through an effect machine. The action step is the primary way machines interact with the outside world: making HTTP requests, querying databases, calling other machines, executing shell commands, or writing files. Every action is governed: the runtime checks permissions, mediates the call through the governance interpreter, and records the operation in the behavioral ledger.

The core principle: code computes, machines effect. Compute steps have no I/O capability by construction. All I/O happens through action steps that route to effect machines.

When to use

Use action when you need to:

  • Make HTTP requests to external APIs
  • Query or write to a database
  • Call another machine as a subroutine
  • Execute external code (Python, JavaScript, shell)
  • Read or write files

Use ask ... from as an alternative syntax for calling effect machines with a returns schema. Use ask ... using when the task requires LLM reasoning rather than a deterministic effect. Use compute for pure data transformation with no I/O.

Syntax

action <name> <type>
<config key>: <value>
...

The <type> determines which effect machine handles the action. Built-in types map to stdlib effect machines:

TypeEffect MachinePurpose
http@mashin/actions/http/*HTTP requests (GET, POST, PUT, DELETE)
db@mashin/actions/db/*Database queries and transactions
callDirect machine invocationCall another machine by name or path
exec@mashin/actions/exec/*Shell command execution
file@mashin/actions/file/*File read/write operations
dataframe@mashin/actions/data/*DataFrame operations

Configuration by type

action ... http

ConfigRequiredDescription
methodNoHTTP method: "GET", "POST", "PUT", "DELETE". Default: "GET".
urlYesRequest URL. Supports ${expr} interpolation.
headersNoRequest headers as a map.
bodyNoRequest body (for POST/PUT). Map or string.
timeoutNoRequest timeout in milliseconds.

action ... db

ConfigRequiredDescription
queryYesSQL query string. Use $1, $2, etc. for parameterized values.
paramsNoArray of parameter values (SQL injection safe).

action ... call

ConfigRequiredDescription
machineYesMachine path (e.g., "@mashin/actions/shipping/create", "@myorg/billing/charge").
Additional keysVariesPassed as input to the called machine.

action ... exec

ConfigRequiredDescription
languageYesExecution language: "python", "javascript", "shell".
codeYesCode string to execute.
Additional keysVariesPassed as data to the code execution environment.

action ... file

ConfigRequiredDescription
operationYesFile operation: "read", "write", "append", "delete".
pathYesFile path.
contentFor write/appendContent to write.

Examples

HTTP API call

machine weather_checker
accepts
city as text, is required
responds with
temperature as number
conditions as text
implements
action fetch_weather http
method: "GET"
url: "https://api.weather.com/v1/current?city=${input.city}"
headers: {"Authorization": "Bearer ${secrets.WEATHER_API_KEY}"}
timeout: 5000
compute format_result
{
temperature: fetch_weather.data.temp,
conditions: fetch_weather.data.conditions
}

Database query

action find_user db
query: "SELECT * FROM users WHERE email = $1 LIMIT 1"
params: [input.email]

Call another machine

action send_notification call
machine: "@mashin/actions/notifications/send"
channel: "slack"
message: "Order " + input.order_id + " has been processed"
recipient: input.team_channel

Polyglot execution (Python)

action analyze_data exec
language: "python"
code: "import pandas as pd\ndf = pd.DataFrame(data)\nresult = {'mean': float(df['value'].mean()), 'std': float(df['value'].std())}"
data: input.dataset

HTTP POST with body

machine stripe_charge
accepts
amount as number, is required
currency as text, default: "usd"
customer_id as text, is required
implements
action create_charge http
method: "POST"
url: "https://api.stripe.com/v1/charges"
headers: {
"Authorization": "Bearer ${secrets.STRIPE_KEY}",
"Content-Type": "application/x-www-form-urlencoded"
}
body: {
amount: input.amount,
currency: input.currency,
customer: input.customer_id
}
timeout: 10000
compute result
{
charge_id: create_charge.data.id,
status: create_charge.data.status
}

Governance

Every action step is governed (Inv 1: No Ambient Effects, Inv 3: Governance Mediation):

  1. Permission check: the machine must declare the appropriate capability in ensures > permissions (e.g., http, db_read, db_write, compute.exec)
  2. Directive emission: the action emits a directive that is mediated by the governance interpreter
  3. Behavioral ledger: the action type, target, and timing are recorded
  4. Cost tracking: for metered effects (API calls, compute time), costs are tracked per execution
  5. Trust ceiling: the machine’s trust level determines which effects are allowed

In test mode, use assuming in verifies tests to mock action responses. This avoids real HTTP calls, database writes, or other side effects during testing.

verifies
test "fetches weather data"
assuming fetch_weather {data: {temp: 72, conditions: "sunny"}}
given {city: "San Francisco"}
expect {temperature: 72, conditions: "sunny"}

Translations

LanguageKeyword
Englishaction
Spanishaccion
Frenchaction
GermanAktion
Japaneseアクション
Chinese动作
Korean행동

See also

  • ask … from - Alternative syntax for calling effect machines with returns
  • compute - Pure computation (no I/O)
  • implements - The section where action steps live
  • ensures - Permission declarations for effect capabilities