# Quick Reference: Action Output Format and Schema **Last Updated:** 2026-02-07 **Status:** Current standard for all actions ## TL;DR - ✅ **DO:** Set `output_format` to "text", "json", or "yaml" - ✅ **DO:** Define `output_schema` for structured outputs (json/yaml only) - ❌ **DON'T:** Include stdout/stderr/exit_code in output schema (captured automatically) - 💡 **Output schema** describes the shape of structured data sent to stdout ## Output Format Field All actions must specify an `output_format` field in their YAML definition: ```yaml name: my_action ref: mypack.my_action runner_type: shell entry_point: my_action.sh # Output format: text, json, or yaml output_format: text # or json, or yaml ``` ### Supported Formats | Format | Description | Worker Behavior | Use Case | |--------|-------------|-----------------|----------| | `text` | Plain text output | Stored as-is in execution result | Simple messages, logs, unstructured data | | `json` | JSON structured data | Parsed into JSONB field | APIs, structured results, complex data | | `yaml` | YAML structured data | Parsed into JSONB field | Configuration, human-readable structured data | ## Output Schema The `output_schema` field describes the **shape of structured data** written to stdout: - **Only applicable** for `output_format: json` or `output_format: yaml` - **Not needed** for `output_format: text` (no parsing occurs) - **Should NOT include** execution metadata (stdout/stderr/exit_code) ### Text Output Actions For actions that output plain text, omit the output schema: ```yaml name: echo ref: core.echo runner_type: shell entry_point: echo.sh # Output format: text (no structured data parsing) output_format: text parameters: type: object properties: message: type: string # Output schema: not applicable for text output format # The action outputs plain text to stdout ``` **Action script:** ```bash #!/bin/bash INPUT=$(cat) MESSAGE=$(echo "$INPUT" | jq -r '.message // ""') echo "$MESSAGE" # Plain text to stdout ``` ### JSON Output Actions For actions that output JSON, define the schema: ```yaml name: http_request ref: core.http_request runner_type: python entry_point: http_request.py # Output format: json (structured data parsing enabled) output_format: json parameters: type: object properties: url: type: string required: true # Output schema: describes the JSON structure written to stdout # Note: stdout/stderr/exit_code are captured automatically by the execution system output_schema: type: object properties: status_code: type: integer description: "HTTP status code" body: type: string description: "Response body as text" success: type: boolean description: "Whether the request was successful (2xx status)" ``` **Action script:** ```python #!/usr/bin/env python3 import json import sys def main(): params = json.loads(sys.stdin.read() or '{}') # Perform HTTP request logic result = { "status_code": 200, "body": "Response body", "success": True } # Output JSON to stdout (worker will parse and store in execution.result) print(json.dumps(result, indent=2)) if __name__ == "__main__": main() ``` ### YAML Output Actions For actions that output YAML: ```yaml name: get_config ref: mypack.get_config runner_type: shell entry_point: get_config.sh # Output format: yaml (structured data parsing enabled) output_format: yaml # Output schema: describes the YAML structure written to stdout output_schema: type: object properties: server: type: object properties: host: type: string port: type: integer database: type: object properties: url: type: string ``` **Action script:** ```bash #!/bin/bash cat <&2 # stderr exit 1 fi # Normal output to stdout echo "Success" ``` ### Exit Codes - **0:** Success - **Non-zero:** Failure - **Captured automatically:** Worker records exit code in execution record - **Don't output in JSON:** Exit code is metadata, not result data ## Pattern Examples ### Example 1: Simple Text Action ```yaml # echo.yaml name: echo output_format: text parameters: properties: message: type: string ``` ```bash # echo.sh #!/bin/bash INPUT=$(cat) MESSAGE=$(echo "$INPUT" | jq -r '.message // ""') echo "$MESSAGE" ``` ### Example 2: Structured JSON Action ```yaml # validate_json.yaml name: validate_json output_format: json parameters: properties: json_data: type: string output_schema: type: object properties: valid: type: boolean errors: type: array items: type: string ``` ```python # validate_json.py #!/usr/bin/env python3 import json import sys def main(): params = json.loads(sys.stdin.read() or '{}') json_data = params.get('json_data', '') errors = [] valid = False try: json.loads(json_data) valid = True except json.JSONDecodeError as e: errors.append(str(e)) result = {"valid": valid, "errors": errors} # Output JSON to stdout print(json.dumps(result)) if __name__ == "__main__": main() ``` ### Example 3: API Wrapper with JSON Output ```yaml # github_pr_info.yaml name: github_pr_info output_format: json parameters: properties: repo: type: string required: true pr_number: type: integer required: true output_schema: type: object properties: title: type: string state: type: string enum: [open, closed, merged] author: type: string created_at: type: string format: date-time ``` ## Migration from Old Pattern ### Before (Incorrect) ```yaml # DON'T DO THIS - includes execution metadata output_schema: type: object properties: stdout: # ❌ Execution metadata type: string stderr: # ❌ Execution metadata type: string exit_code: # ❌ Execution metadata type: integer result: type: object # ❌ Actual result unnecessarily nested ``` ### After (Correct) ```yaml # DO THIS - only describe the actual data structure your action outputs output_format: json output_schema: type: object properties: count: type: integer items: type: array items: type: string # No stdout/stderr/exit_code - those are captured automatically ``` ## Best Practices 1. **Choose the right format:** - Use `text` for simple messages, logs, or unstructured output - Use `json` for structured data, API responses, complex results - Use `yaml` for human-readable configuration or structured output 2. **Keep output schema clean:** - Only describe the actual data structure - Don't include execution metadata - Don't nest result under a "result" or "data" key unless semantic 3. **Use stderr for diagnostics:** - Error messages go to stderr, not stdout - Debugging output goes to stderr - Normal results go to stdout 4. **Exit codes matter:** - 0 = success (even if result indicates failure semantically) - Non-zero = execution failure (script error, crash, etc.) - Don't output exit code in JSON - it's captured automatically 5. **Validate your schema:** - Ensure output schema matches actual JSON/YAML structure - Test with actual action outputs - Use JSON Schema validation tools 6. **Document optional fields:** - Mark fields that may not always be present - Provide descriptions for all fields - Include examples in action documentation ## Testing ### Test Text Output ```bash echo '{"message": "test"}' | ./action.sh # Verify: Plain text output, no JSON structure ``` ### Test JSON Output ```bash echo '{"url": "https://example.com"}' | ./action.py | jq . # Verify: Valid JSON, matches schema ``` ### Test Error Handling ```bash echo '{}' | ./action.sh 2>&1 # Verify: Errors to stderr, proper exit code ``` ### Test Schema Compliance ```bash OUTPUT=$(echo '{"param": "value"}' | ./action.py) echo "$OUTPUT" | jq -e '.status and .data' > /dev/null # Verify: Output has required fields from schema ``` ## Common Pitfalls ### ❌ Pitfall 1: Including Execution Metadata ```yaml # WRONG output_schema: properties: exit_code: # ❌ Automatic type: integer stdout: # ❌ Automatic type: string ``` ### ❌ Pitfall 2: Missing output_format ```yaml # WRONG - no output_format specified name: my_action output_schema: # How should this be parsed? type: object ``` ### ❌ Pitfall 3: Text Format with Schema ```yaml # WRONG - text format doesn't need schema output_format: text output_schema: # ❌ Ignored for text format type: object ``` ### ❌ Pitfall 4: Unnecessary Nesting ```bash # WRONG - unnecessary "result" wrapper echo '{"result": {"count": 5, "name": "test"}}' # ❌ # RIGHT - output the data structure directly echo '{"count": 5, "name": "test"}' # ✅ ``` ## References - [Action Parameter Handling](./QUICKREF-action-parameters.md) - Stdin-based parameter delivery - [Core Pack Actions](../packs/core/actions/README.md) - Reference implementations - [Worker Service Architecture](./architecture/worker-service.md) - How worker processes actions ## See Also - Execution API endpoints (for retrieving results) - Workflow parameter mapping (for using action outputs) - Logging configuration (for stderr handling)