How to Get JSON Structured Output from OpenClaw
LLM responses fail JSON parsing 8 to 15 percent of the time without schema enforcement. OpenClaw solves this with two built-in methods: the llm-task plugin, which forces JSON-only output with optional JSON Schema validation, and code-mode's json() function, which lets you capture structured data alongside text during script execution. This guide walks through both approaches with working examples and configuration details.
Why Raw LLM Output Breaks Automation Pipelines
A TokenMix.ai analysis of 2 million API calls found that LLM JSON responses fail parsing 8 to 15 percent of the time when no structured output enforcement is applied. With proper schema enforcement, that rate drops below 0.1 percent. The gap between those two numbers is where most agent automation projects stall.
The failure modes are predictable. Models wrap JSON in markdown code fences. They add conversational preamble before the opening brace. They hallucinate extra fields, omit required ones, or produce valid JSON that doesn't match the schema your downstream code expects. Every one of these failures requires a retry, a fallback, or manual intervention.
OpenClaw addresses this at the tool level rather than leaving it to prompt engineering. Instead of appending "respond only in JSON" to every prompt and hoping for the best, you declare the output format as a tool parameter. The model receives explicit JSON-only instructions, and the output is parsed and validated before it reaches your workflow.
Two mechanisms handle different use cases. The llm-task plugin runs standalone LLM calls that return nothing but validated JSON. Code-mode's json() function captures structured data during script execution when you need both text output and structured results from the same run.
How to Use the llm-task Plugin for JSON-Only Calls
The llm-task plugin is the primary way to get structured JSON from OpenClaw. It runs a dedicated LLM call that produces only JSON output, with no code fences, no commentary, and no tools exposed to the model during the call. The result lands in details.json within your workflow.
Enabling llm-task
Two configuration steps are required. First, enable the plugin in your gateway configuration's plugins section by setting "llm-task": { "enabled": true }. Then allow the tool in your tool policy by adding "llm-task" to either tools.alsoAllow (default mode) or tools.allow (restrictive mode). The official configuration reference covers the full set of plugin and tool policy options.
Basic Usage with JSON Schema Validation Here's a complete example that classifies an email and drafts a response, validated against a JSON Schema:
openclaw.invoke --tool llm-task --action json --args-json '{
"prompt": "Given the input email, return the sender intent and a draft reply.",
"input": {
"subject": "Project update request",
"body": "Can you send the latest build artifacts?"
},
"schema": {
"type": "object",
"properties": {
"intent": { "type": "string" },
"draft": { "type": "string" }
},
"required": ["intent", "draft"]
}
}'
The schema parameter accepts any valid JSON Schema object. When provided, OpenClaw validates the parsed output against it before returning. If validation fails, the call errors rather than passing malformed data downstream.
Tool Parameters
The llm-task plugin accepts these parameters:
- prompt (required): The instruction for the LLM. Keep it focused on what JSON structure you want back.
- input (optional): Any JSON value passed as context to the model alongside the prompt.
- schema (optional): A JSON Schema object. When present, output is validated against it.
- provider and model (optional): Override the default LLM. Use
provider/modelformat likeanthropic/claude-sonnet-4-6oropenai/gpt-5.5. - temperature (optional): Control output randomness. Lower values produce more deterministic JSON.
- maxTokens (optional): Cap the response length. Default is 800 in the example config.
- timeoutMs (optional): Set a per-call timeout. Default is 30000 (30 seconds).
- thinking (optional): Enable reasoning presets like
"low"or"medium"for complex extraction tasks. - authProfileId (optional): Select which API key profile to use for the call.
Method 2: Code-Mode json() for Inline Structured Output
Code-mode runs JavaScript in OpenClaw's QuickJS-WASI sandbox. When you need structured data as part of a larger script, the json() function captures it without a separate LLM call.
How json() Works
The function appends a structured output item to the execution result. Each call produces an entry with type: "json" and your serialized value:
const results = await tools.search("quarterly revenue data");
json({
toolsFound: results.length,
entries: results
});
The output type system distinguishes between text and structured data:
type CodeModeOutput =
| { type: "text"; text: string }
| { type: "json"; value: unknown };
You can mix text() and json() calls in the same script. Text output goes to the human-readable stream while JSON output is captured separately for programmatic consumption.
When to Use json() vs. llm-task
The two methods serve different purposes:
- llm-task: Best for standalone LLM calls where the entire output should be structured JSON. Classification, extraction, and summarization tasks where you want schema validation.
- json(): Best for capturing structured results during code execution. Use it when your script already has the data and you need to pass it downstream in a typed format.
json() does not call an LLM. It serializes whatever JavaScript value you pass it. If you need the LLM to produce structured output within code-mode, call llm-task from the script and capture the result.
Store and share your agent's structured output
Fast.io gives your OpenClaw workflows 50 GB of free storage with built-in AI indexing. Upload JSON extraction results through the MCP server, search by meaning, and share with your team. No credit card, no expiration.
How to Configure LLM Providers for Structured Output
OpenClaw's gateway configuration controls which models handle your llm-task calls. The configuration uses JSON5 format, so comments and trailing commas are valid.
Setting Default Provider and Model
The llm-task plugin accepts its own defaults:
{
"plugins": {
"llm-task": {
"enabled": true,
"defaultProvider": "anthropic",
"defaultModel": "claude-sonnet-4-6",
"maxTokens": 800,
"timeoutMs": 30000
}
}
}
You can override these per-call using the provider and model parameters in the tool invocation.
Model Allowlists
For teams that need to control which models agents can use, the allowedModels array restricts access:
{
"plugins": {
"llm-task": {
"enabled": true,
"allowedModels": [
"anthropic/claude-sonnet-4-6",
"openai/gpt-5.5",
"openai/gpt-5.4"
]
}
}
}
Any call that requests a model outside this list will be rejected.
API Key Management
Three approaches work for credentials. Inline environment variables are the simplest:
{
"env": {
"ANTHROPIC_API_KEY": "sk-ant-..."
}
}
For better security, use substitution syntax to reference system environment variables: "${ANTHROPIC_API_KEY}". Or use SecretRef objects for explicit sourcing:
{
"models": {
"providers": {
"anthropic": {
"apiKey": { "source": "env", "id": "ANTHROPIC_API_KEY" }
}
}
}
}
Run openclaw doctor --fix after changing configuration to validate the file and catch issues before they surface at runtime.
Building a Practical Extraction Pipeline
Here's how the pieces fit together in a real workflow. Say you're processing inbound support emails and need to extract ticket metadata, classify priority, and route to the right team.
Step 1: Extract Structured Metadata
Use llm-task with a schema that matches your ticketing system:
openclaw.invoke --tool llm-task --action json --args-json '{
"prompt": "Extract ticket metadata from this support email.",
"input": {
"subject": "API rate limits hitting us in production",
"body": "We are seeing 429 errors on the /upload endpoint..."
},
"schema": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "account", "feature-request"]
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"summary": { "type": "string", "maxLength": 200 },
"affected_endpoint": { "type": "string" }
},
"required": ["category", "priority", "summary"]
},
"temperature": 0.1
}'
Low temperature (0.1) reduces variance in classification tasks. The enum constraints in the schema ensure the model picks from your valid values rather than inventing categories.
Step 2: Store Results for Team Access
Once you have structured output, the data needs to go somewhere your team can access it. Local file storage works for single-developer setups, but teams need shared access.
Fast.io workspaces give agents and humans the same view of extracted data. Upload JSON results through the MCP server or API, and team members can browse, search, and query the files through the web interface. Intelligence Mode auto-indexes uploaded JSON files, making the extracted metadata searchable by meaning rather than just filename.
For comparison, S3 or Google Cloud Storage work if you only need programmatic access. Google Drive and Dropbox handle basic file sharing but lack the AI indexing layer. Fast.io's free agent plan includes 50 GB of storage, 5,000 monthly credits, and 5 workspaces with no credit card required.
What to Watch For When Using Structured Output
Output Size Limits
Code-mode's json() function is subject to the maxOutputBytes cap, which defaults to 65,536 bytes. If your serialized JSON exceeds this, the output will be truncated or error. For large extraction results, break the work into smaller chunks or increase the cap in your configuration.
The llm-task plugin is bounded by maxTokens (default 800 in the example config). Complex schemas with many fields may need a higher limit. Monitor for truncated responses and adjust accordingly.
Schema Validation Behavior
When a schema is provided, llm-task validates the parsed JSON before returning it. A validation failure causes the call to error. This is intentional: it's better to fail fast than to pass invalid data to the next step in your pipeline.
If you're seeing frequent validation failures, the problem is usually one of three things: the schema is too strict for what the model can reliably produce, the prompt doesn't describe the expected output enough, or the model needs more tokens to complete the response.
Security Considerations
The llm-task plugin does not expose tools to the model during execution. This means the LLM can only produce text (constrained to JSON), not take actions. Validate output with schemas, and always require human approval before any side-effecting operation based on LLM-extracted data.
Serialization Requirements
The json() function requires JSON-compatible values. Passing functions, circular references, or other non-serializable JavaScript values will produce errors or unexpected string conversions. Stick to plain objects, arrays, strings, numbers, booleans, and null.
Frequently Asked Questions
How do I get JSON output from OpenClaw?
Enable the llm-task plugin in your gateway configuration, then invoke it with a prompt and optional JSON Schema. The plugin instructs the model to output only JSON (no code fences or commentary) and returns the parsed result in details.json. For structured data during code execution, use code-mode's json() function instead.
Does OpenClaw support JSON Schema validation?
Yes. The llm-task plugin accepts a schema parameter containing any valid JSON Schema object. When provided, OpenClaw validates the parsed JSON output against the schema before returning it. If validation fails, the call errors rather than passing invalid data downstream.
How do I use OpenClaw's llm-task plugin?
First, enable it in your gateway configuration's plugins section with enabled set to true. Then allow the tool via tools.alsoAllow or tools.allow. Invoke it with openclaw.invoke using the tool name llm-task, action json, and your prompt, input, and schema in the args-json payload.
Can OpenClaw return structured data?
Yes, through two mechanisms. The llm-task plugin produces JSON-only LLM responses with optional schema validation. Code-mode's json() function captures structured data during JavaScript execution in the QuickJS-WASI sandbox. Both produce typed output items that downstream workflow steps can consume programmatically.
What is the difference between llm-task and code-mode json()?
llm-task makes a dedicated LLM call that returns only validated JSON. It's for classification, extraction, and summarization tasks. Code-mode's json() serializes JavaScript values you already have during script execution, without calling an LLM. Use llm-task when you need the model to produce structured output; use json() when your script already has the data.
Can I use different LLM providers with llm-task?
Yes. Set defaultProvider and defaultModel in the plugin configuration for workspace-wide defaults, or override per-call with the provider and model parameters. OpenClaw supports provider/model format like anthropic/claude-sonnet-4-6 or openai/gpt-5.5. Use the allowedModels array to restrict which models agents can access.
Related Resources
Store and share your agent's structured output
Fast.io gives your OpenClaw workflows 50 GB of free storage with built-in AI indexing. Upload JSON extraction results through the MCP server, search by meaning, and share with your team. No credit card, no expiration.