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:
| Type | Effect Machine | Purpose |
|---|---|---|
http | @mashin/actions/http/* | HTTP requests (GET, POST, PUT, DELETE) |
db | @mashin/actions/db/* | Database queries and transactions |
call | Direct machine invocation | Call 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
| Config | Required | Description |
|---|---|---|
method | No | HTTP method: "GET", "POST", "PUT", "DELETE". Default: "GET". |
url | Yes | Request URL. Supports ${expr} interpolation. |
headers | No | Request headers as a map. |
body | No | Request body (for POST/PUT). Map or string. |
timeout | No | Request timeout in milliseconds. |
action ... db
| Config | Required | Description |
|---|---|---|
query | Yes | SQL query string. Use $1, $2, etc. for parameterized values. |
params | No | Array of parameter values (SQL injection safe). |
action ... call
| Config | Required | Description |
|---|---|---|
machine | Yes | Machine path (e.g., "@mashin/actions/shipping/create", "@myorg/billing/charge"). |
| Additional keys | Varies | Passed as input to the called machine. |
action ... exec
| Config | Required | Description |
|---|---|---|
language | Yes | Execution language: "python", "javascript", "shell". |
code | Yes | Code string to execute. |
| Additional keys | Varies | Passed as data to the code execution environment. |
action ... file
| Config | Required | Description |
|---|---|---|
operation | Yes | File operation: "read", "write", "append", "delete". |
path | Yes | File path. |
content | For write/append | Content 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_channelPolyglot 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.datasetHTTP 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):
- Permission check: the machine must declare the appropriate capability in
ensures > permissions(e.g.,http,db_read,db_write,compute.exec) - Directive emission: the action emits a directive that is mediated by the governance interpreter
- Behavioral ledger: the action type, target, and timing are recorded
- Cost tracking: for metered effects (API calls, compute time), costs are tracked per execution
- 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
| Language | Keyword |
|---|---|
| English | action |
| Spanish | accion |
| French | action |
| German | Aktion |
| 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