Inputs and Outputs
Every machine has a contract: what data it takes in (accepts) and what data it returns (responds with). The runtime validates incoming data against this contract before any step runs. If a required field is missing or a constraint is violated, execution is rejected immediately. No steps execute, no tokens are spent, no ledger entries are created.
Declaring inputs
accepts name as text, is required email as text, is required, format: "email" age as integer, min: 13 role as text, default: "member", choices: ["member", "admin", "moderator"]Each field has three parts: a name, a type (after as), and optional modifiers.
Types
| Type | Description |
|---|---|
text / string | Text value |
number | Numeric (integer or decimal) |
integer | Integer only |
decimal | Decimal/float |
boolean | True or false |
list | Array of values |
list of <type> | Typed list (e.g., list of text) |
map | Key-value object |
any | Untyped |
Modifiers
is required makes a field mandatory. default: <value> provides a fallback when the field is not supplied.
accepts query as text, is required limit as number, default: 10 include_archived as boolean, default: falseValidation constraints
Constraints catch bad data before it reaches your steps:
accepts username as text, is required, min_length: 3, max_length: 20, pattern: "^[a-zA-Z0-9_]+$" score as number, range: [0.0, 1.0] priority as text, choices: ["low", "medium", "high", "urgent"] tags as list, subset: ["bug", "feature", "docs"]| Constraint | Applies to | Meaning |
|---|---|---|
min: / max: | numbers | Value bounds |
min_length: / max_length: | text | String length bounds |
format: | text | Named format (e.g., "email") |
pattern: | text | Regex match |
choices: | text | Allowed values |
range: [lo, hi] | numbers | Value within range |
Declaring outputs
responds with summary as text confidence as number tags as list of textThe runtime validates that the machine’s final output matches this contract. Extra fields are stripped. Missing fields cause an error.
Source mapping with from
You can map output fields directly to step results instead of writing a final compute step:
responds with answer as text, from search sources as list, from search cached as boolean, from state.was_cached, default falseWithout from, output fields are populated from the last step’s return value by matching field names.
Accessing inputs in steps
Inside any step, use input.<field> to access input values:
compute greet {greeting: "Hello, " + input.name + "!"}Inside ask steps, interpolate inputs into the task string:
ask classify, using: "anthropic:claude-sonnet-4-6" with task "Classify this text: ${input.text}\nMax categories: ${input.limit}"A complete example
machine user_profile_validator
accepts email as text, is required, format: "email" username as text, is required, min_length: 3, max_length: 30 age as integer, is required, min: 13, max: 150 bio as text, max_length: 500 interests as list of text
responds with valid as boolean profile_summary as text warnings as list of text
implements compute validate_and_summarize let warnings = [] let bio_text = input.bio || "No bio provided" let interest_count = input.interests ? input.interests.length : 0 { valid: true, profile_summary: input.username + " (" + input.email + "), age " + input.age + ", " + interest_count + " interests", warnings: interest_count == 0 ? ["No interests listed"] : [] }The accepts section handles type checking and constraint validation. By the time compute runs, you know the data is clean.
Try it
Create a machine that accepts an email address (with format validation), a message body (with a max_length of 1000), and a priority field (with choices). Have it use a compute step to build a formatted output. Try invoking it with invalid data and observe the validation error.
Next steps
- Expressions - The expression language used inside steps
- Machine Anatomy - Where inputs and outputs fit in the structure
- accepts reference - Full input specification
- responds with reference - Full output specification