Overview
By default, AI agents return natural language responses in markdown format. To get structured data, use the outputType parameter to specify the desired format.
Output Types:
Type Description Schema Format textMarkdown formatted text (default) Not required structured_jsonValidated JSON output JSON Schema object structured_csvCSV formatted data Array of column names
Benefits:
Native validation for JSON and CSV formats
Guaranteed valid output structure
Easy parsing and integration
Type safety in your application
Direct integration with databases and APIs
Webhook notifications for completed tasks
Output Type: text
The default output type returns natural language responses formatted as markdown. No outputSchema is required.
Request Example
const session = await fetch ( "https://connect.webrun.ai/start/start-session" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
mode: "default" ,
initialTask: {
taskDetails: "Give me the latest prices of the top performing cryptos" ,
taskId: "3e0f4258511fc909775b9f1a74656004" ,
maxDuration: 5 ,
maxInputTokens: 400000 ,
maxOutputTokens: 200000 ,
avoidDomains: [],
outputType: "text"
}
})
}). then ( r => r . json ());
Response
The response contains markdown-formatted text:
{
"success" : true ,
"type" : "task_completed" ,
"data" : {
"message" : "## Top Cryptocurrency Prices \n\n | Name | Price | \n |------|-------| \n | Bitcoin | $95,360.97 | \n | Ethereum | $3,325.94 | \n ..." ,
"prompt_tokens" : 8500 ,
"completion_tokens" : 2300 ,
"total_tokens" : 10800
}
}
Output Type: structured_json
Use structured_json to get validated JSON output. The outputSchema parameter accepts a standard JSON Schema object that defines the expected response structure.
Supported JSON Schema Types
string - Text values
number - Numeric values (integers and decimals)
boolean - True/false values
object - Nested objects with defined properties
array - Arrays with defined item structure
Request Example
const session = await fetch ( "https://connect.webrun.ai/start/start-session" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
mode: "default" ,
initialTask: {
taskDetails: "Give me the latest prices of the top performing cryptos" ,
taskId: "2444180416485ba80be9f12ef51134dc" ,
maxDuration: 60 ,
maxInputTokens: 4000 ,
maxOutputTokens: 2000 ,
avoidDomains: [],
outputType: "structured_json" ,
outputSchema: {
type: "object" ,
properties: {
top: {
type: "array" ,
items: {
type: "object" ,
properties: {
name: { type: "string" },
price: { type: "string" }
}
}
}
},
additionalProperties: false
}
}
})
}). then ( r => r . json ());
Response
The response is guaranteed to match your schema:
{
"success" : true ,
"type" : "task_completed" ,
"data" : {
"message" : "{ \" top \" :[{ \" name \" : \" Bitcoin \" , \" price \" : \" $95,187.71 \" },{ \" name \" : \" Ethereum \" , \" price \" : \" $3,319.17 \" },{ \" name \" : \" Tether \" , \" price \" : \" $0.9995 \" },{ \" name \" : \" XRP \" , \" price \" : \" $2.15 \" },{ \" name \" : \" BNB \" , \" price \" : \" $942.50 \" },{ \" name \" : \" Solana \" , \" price \" : \" $145.42 \" },{ \" name \" : \" USDC \" , \" price \" : \" $0.9997 \" },{ \" name \" : \" TRON \" , \" price \" : \" $0.3064 \" },{ \" name \" : \" Dogecoin \" , \" price \" : \" $0.1482 \" },{ \" name \" : \" Cardano \" , \" price \" : \" $0.4199 \" }]}" ,
"prompt_tokens" : 8500 ,
"completion_tokens" : 2300 ,
"total_tokens" : 10800
}
}
When using outputType: "structured_json", the response is guaranteed to match your schema. The data.message field contains a valid JSON string that can be directly parsed.
Output Type: structured_csv
Use structured_csv to get CSV formatted output. The outputSchema parameter accepts an array of column names that define the CSV structure.
Request Example
const session = await fetch ( "https://connect.webrun.ai/start/start-session" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
mode: "default" ,
initialTask: {
taskDetails: "Give me the latest prices of the top performing cryptos" ,
taskId: "3e0f4258511fc909775b9f1a74656004" ,
maxDuration: 5 ,
maxInputTokens: 400000 ,
maxOutputTokens: 200000 ,
avoidDomains: [],
outputType: "structured_csv" ,
outputSchema: [ "name" , "price" ]
}
})
}). then ( r => r . json ());
Response
The response contains CSV formatted data with the specified columns:
{
"success" : true ,
"type" : "task_completed" ,
"data" : {
"message" : "name,price \n Bitcoin,$95,360.97 \n Ethereum,$3,325.94 \n Tether,$0.9994 \n XRP,$2.16 \n BNB,$943.70 \n Solana,$145.38 \n USDC,$0.9996 \n TRON,$0.3070 \n Dogecoin,$0.1483 \n Cardano,$0.4220" ,
"prompt_tokens" : 8500 ,
"completion_tokens" : 2300 ,
"total_tokens" : 10800
}
}
Parsing CSV Response
const result = await pollForResult ( sessionId , taskId );
const csv = result . data . message ;
const lines = csv . trim (). split ( ' \n ' );
const headers = lines [ 0 ]. split ( ',' );
const data = lines . slice ( 1 ). map ( line => {
const values = line . match ( / ( " . *? " | [ ^ , ] + )(?= \s * , | \s * $ ) / g );
const obj = {};
headers . forEach (( header , i ) => {
obj [ header . trim ()] = values [ i ]?. replace ( / ^ " | " $ / g , '' ). trim ();
});
return obj ;
});
console . log ( data );
// [
// { name: "Bitcoin", price: "$95,360.97" },
// { name: "Ethereum", price: "$3,325.94" },
// ...
// ]
When using outputType: "structured_csv", the outputSchema must be an array of column name strings. The first row of the output will be the header row containing these column names.
Webhook Integration
Combine structured output with webhooks to automatically deliver validated data to your systems when tasks complete.
const session = await fetch ( "https://connect.webrun.ai/start/start-session" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
mode: "default" ,
initialTask: {
taskDetails: "Extract product information from the page" ,
startingPoint: "https://example.com/product" ,
outputType: "structured_json" ,
outputSchema: {
type: "object" ,
properties: {
title: { type: "string" },
price: { type: "number" },
inStock: { type: "boolean" }
},
required: [ "title" , "price" ],
additionalProperties: false
},
webhook: {
name: "Product Webhook" ,
url: "https://api.mysite.com/products" ,
auth: "Bearer token123" ,
submittedData: "ai_response"
}
}
})
}). then ( r => r . json ());
Use submittedData: "ai_response" to receive only the AI’s output (text, structured_json, or structured_csv). See the Webhooks guide for complete documentation.
Legacy Approach (Deprecated)
The approach of embedding schema descriptions in taskDetails is deprecated. Use the native outputType and outputSchema parameters instead for guaranteed validation and consistent output.
The examples below show the legacy pattern for reference. New implementations should use the Native JSON Schema Support documented above.
JSON Output Pattern (Legacy)
The most common pattern for structured output is requesting JSON in your task description.
Basic Example
const session = await fetch ( "https://connect.webrun.ai/start/start-session" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
taskDetails: `Go to amazon.com and search for "wireless keyboard".
Return the results as a JSON array with this format:
[
{ "title": "product name", "price": "19.99", "rating": "4.5" }
]` ,
startingUrl: "https://amazon.com"
})
}). then ( r => r . json ());
Expected Response:
{
"success" : true ,
"type" : "task_completed" ,
"data" : {
"message" : "[{ \" title \" : \" Logitech K380 \" , \" price \" : \" 29.99 \" , \" rating \" : \" 4.5 \" },{ \" title \" : \" Arteck Wireless Keyboard \" , \" price \" : \" 19.99 \" , \" rating \" : \" 4.3 \" }]" ,
"prompt_tokens" : 8500 ,
"completion_tokens" : 2300 ,
"total_tokens" : 10800
}
}
Parsing the Result
async function getProductData ( sessionId , taskId ) {
// Poll for result
const result = await pollForResult ( sessionId , taskId );
if ( result . type === "task_completed" ) {
try {
// Parse JSON from message
const products = JSON . parse ( result . data . message );
return products ;
} catch ( error ) {
console . error ( "Failed to parse JSON:" , error );
console . log ( "Raw message:" , result . data . message );
return null ;
}
}
return null ;
}
// Usage
const products = await getProductData ( session . sessionId , task . taskId );
console . log ( products );
// [
// { title: "Logitech K380", price: "29.99", rating: "4.5" },
// { title: "Arteck Wireless Keyboard", price: "19.99", rating: "4.3" }
// ]
Array Results
Request arrays for lists of items.
Example: Product Search
const taskDetails = `
Search Google for "best mechanical keyboards 2024".
Extract the top 5 results and return as a JSON array:
[
{
"title": "page title",
"url": "https://...",
"snippet": "brief description"
}
]
` ;
const task = await fetch ( "https://connect.webrun.ai/start/send-message" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
sessionId: session . sessionId ,
message: {
actionType: "newTask" ,
newState: "start" ,
taskDetails
}
})
}). then ( r => r . json ());
Complex Nested Structures
Request nested JSON for complex data.
Example: E-commerce Product Details
const taskDetails = `
Go to the Amazon product page at [URL].
Extract the following information and return as JSON:
{
"product": {
"title": "product name",
"price": "19.99",
"currency": "USD",
"inStock": true,
"rating": {
"stars": 4.5,
"count": 1234
},
"images": ["url1", "url2"],
"features": ["feature 1", "feature 2"],
"specifications": {
"brand": "...",
"model": "...",
"dimensions": "..."
}
}
}
` ;
const result = await sendTaskAndWait ( session . sessionId , taskDetails );
const productData = JSON . parse ( result . data . message );
console . log ( productData . product . title );
console . log ( productData . product . rating . stars );
Validation with Zod (TypeScript)
Use Zod for runtime validation and type safety.
Install Zod
Define Schema
import { z } from "zod" ;
// Define expected structure
const ProductSchema = z . object ({
title: z . string (),
price: z . string (),
rating: z . string (),
url: z . string (). url (),
inStock: z . boolean ()
});
const ProductArraySchema = z . array ( ProductSchema );
// Type inference
type Product = z . infer < typeof ProductSchema >;
Validate Response
async function getValidatedProducts ( sessionId : string , taskId : string ) : Promise < Product []> {
const result = await pollForResult ( sessionId , taskId );
if ( result . type !== "task_completed" ) {
throw new Error ( "Task not completed" );
}
try {
// Parse JSON
const rawData = JSON . parse ( result . data . message );
// Validate with Zod
const products = ProductArraySchema . parse ( rawData );
return products ; // Type-safe products array
} catch ( error ) {
if ( error instanceof z . ZodError ) {
console . error ( "Validation failed:" , error . errors );
throw new Error ( `Invalid product data: ${ error . message } ` );
}
throw error ;
}
}
// Usage (TypeScript knows the shape)
const products = await getValidatedProducts ( sessionId , taskId );
products . forEach ( product => {
console . log ( product . title ); // Type-safe
console . log ( product . price ); // Type-safe
});
Handling Parse Errors
Sometimes the AI returns malformed JSON. Implement fallback strategies:
function extractJSON ( text ) {
// Try direct parse first
try {
return JSON . parse ( text );
} catch ( error ) {
// Try to find JSON within text
const jsonMatch = text . match ( / \[ [ \s\S ] * \] | \{ [ \s\S ] * \} / );
if ( jsonMatch ) {
try {
return JSON . parse ( jsonMatch [ 0 ]);
} catch ( innerError ) {
console . error ( "Failed to extract JSON:" , innerError );
}
}
}
return null ;
}
// Usage
const result = await pollForResult ( sessionId , taskId );
const data = extractJSON ( result . data . message );
if ( data ) {
console . log ( "Parsed:" , data );
} else {
console . log ( "Could not parse, raw response:" , result . data . message );
}
async function getStructuredDataWithRetry ( sessionId , originalTaskId ) {
const result = await pollForResult ( sessionId , originalTaskId );
// Try to parse
try {
return JSON . parse ( result . data . message );
} catch ( error ) {
console . log ( "Initial parse failed, requesting reformatting..." );
// Send follow-up task to reformat
const reformatTask = await fetch ( "https://connect.webrun.ai/start/send-message" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
sessionId ,
message: {
actionType: "newTask" ,
newState: "start" ,
taskDetails: `Take the previous response and format it as valid JSON: ${ result . data . message } `
}
})
}). then ( r => r . json ());
const reformatResult = await pollForResult ( sessionId , reformatTask . taskId );
return JSON . parse ( reformatResult . data . message );
}
}
Best Practices
1. Be Specific in Schema Description
// Bad - vague
taskDetails : "Return product info as JSON"
// Good - explicit schema
taskDetails : `
Return product information as JSON with this exact structure:
{
"title": "string",
"price": "number as string (e.g., '29.99')",
"inStock": "boolean (true/false)",
"rating": "number as string (e.g., '4.5')"
}
`
2. Request Array Length Limits
Prevent unexpectedly large responses:
taskDetails : `
Search for wireless keyboards and return the top 10 results (no more than 10) as JSON array:
[
{ "title": "...", "price": "...", "url": "..." }
]
`
3. Specify Data Types Clearly
taskDetails : `
Return product data as JSON:
{
"title": "string",
"price": "string in format '19.99' (no currency symbol)",
"inStock": "boolean (true or false, not string)",
"rating": "number (e.g., 4.5, not string)",
"reviewCount": "integer (e.g., 123, not string)"
}
`
4. Handle Missing Data
Specify how to handle missing fields:
taskDetails : `
Return product data as JSON:
{
"title": "string (required)",
"price": "string or null if not available",
"rating": "number or null if no ratings",
"inStock": "boolean or null if unknown"
}
`
5. Use TypeScript for Type Safety
interface Product {
title : string ;
price : string | null ;
rating : number | null ;
inStock : boolean | null ;
}
async function getProducts ( sessionId : string , taskId : string ) : Promise < Product []> {
const result = await pollForResult ( sessionId , taskId );
const products : Product [] = JSON . parse ( result . data . message );
return products ;
}
Complete Example: Product Comparison
const API_KEY = "YOUR_API_KEY" ;
const BASE = "https://connect.webrun.ai" ;
async function compareProducts ( searchQuery ) {
// 1. Create session
const session = await fetch ( ` ${ BASE } /start/start-session` , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Authorization" : `Bearer ${ API_KEY } `
},
body: JSON . stringify ({
taskDetails: `
Go to Amazon and search for " ${ searchQuery } ".
Return the top 5 results as a JSON array with this structure:
[
{
"title": "product title",
"price": "price as string without currency symbol (e.g., '29.99')",
"rating": "average rating as number (e.g., 4.5)",
"reviewCount": "number of reviews as integer",
"url": "product URL",
"isPrime": "boolean indicating Prime eligibility"
}
]
Return ONLY the JSON array, no additional text.
` ,
startingUrl: "https://amazon.com" ,
terminateOnCompletion: true
})
}). then ( r => r . json ());
console . log ( "Session created:" , session . sessionId );
// 2. Poll for result
const result = await pollForResult ( session . sessionId , session . taskId );
// 3. Parse and validate
try {
const products = JSON . parse ( result . data . message );
// Validate structure
if ( ! Array . isArray ( products )) {
throw new Error ( "Expected array of products" );
}
// Process results
const processed = products . map ( p => ({
... p ,
price: parseFloat ( p . price ),
pricePerStar: ( parseFloat ( p . price ) / p . rating ). toFixed ( 2 )
}));
// Sort by value (price per rating star)
processed . sort (( a , b ) => a . pricePerStar - b . pricePerStar );
console . log ( "Best value products:" );
processed . forEach (( p , i ) => {
console . log ( ` ${ i + 1 } . ${ p . title } ` );
console . log ( ` Price: $ ${ p . price } , Rating: ${ p . rating } ( ${ p . reviewCount } reviews)` );
console . log ( ` Value: $ ${ p . pricePerStar } per star` );
console . log ( ` URL: ${ p . url } ` );
console . log ();
});
return processed ;
} catch ( error ) {
console . error ( "Failed to parse product data:" , error );
console . log ( "Raw response:" , result . data . message );
return null ;
}
}
async function pollForResult ( sessionId , taskId ) {
const maxAttempts = 60 ;
const interval = 2000 ;
for ( let i = 0 ; i < maxAttempts ; i ++ ) {
const res = await fetch ( ` ${ BASE } /task/ ${ sessionId } / ${ taskId } ` , {
headers: { "Authorization" : `Bearer ${ API_KEY } ` }
});
const data = await res . json ();
if ( data . type === "task_completed" ) return data ;
if ( data . type === "guardrail_trigger" ) throw new Error ( `Guardrail: ${ data . data . value } ` );
if ( data . status === "failed" ) throw new Error ( data . error );
if ( data . pending ) {
await new Promise ( r => setTimeout ( r , interval ));
continue ;
}
return data ;
}
throw new Error ( "Polling timeout" );
}
// Run comparison
compareProducts ( "wireless mechanical keyboard" );
CSV Output
For tabular data, CSV can be simpler than JSON:
const taskDetails = `
Search Amazon for "standing desk" and return the top 10 results as CSV with these columns:
title,price,rating,reviewCount,url
Example:
"FlexiSpot Standing Desk","299.99","4.5","2341","https://..."
"FEZIBO Electric Desk","249.99","4.3","1823","https://..."
Return ONLY the CSV data with header row, no additional text.
` ;
// Parse CSV
const result = await pollForResult ( sessionId , taskId );
const csv = result . data . message ;
const lines = csv . trim (). split ( ' \n ' );
const headers = lines [ 0 ]. split ( ',' );
const products = lines . slice ( 1 ). map ( line => {
const values = line . match ( / ( " . *? " | [ ^ , ] + )(?= \s * , | \s * $ ) / g );
const obj = {};
headers . forEach (( header , i ) => {
obj [ header . trim ()] = values [ i ]. replace ( / ^ " | " $ / g , '' ). trim ();
});
return obj ;
});
console . log ( products );