Introduction 
Everything you need to get started with the Wiro AI platform.
What is
?
Wiro is an AI model marketplace and API platform that lets you run AI models through a single, unified API. Instead of managing infrastructure for each model provider, you make one API call to Wiro and we handle the rest.
- Unified API — one interface for all models (image generation, LLMs, audio, video, and more)
- Pay-per-use pricing — only pay for what you consume, no upfront commitments
- Real-time WebSocket updates — stream task progress and outputs live
- 9 SDK languages — curl, Python, Node.js, PHP, C#, Swift, Dart, Kotlin, Go
Base URL
All API requests are made to:
https://api.wiro.ai/v1
WebSocket connections use:
wss://socket.wiro.ai/v1
Quick Start
- Sign up Create an account at wiro.ai
- Create a project Go to the Dashboard to get your API key
- Pick a model Browse the marketplace and choose a model
- Make your first API call See Code Examples for full end-to-end samples
Response Format
Every API response returns JSON with a consistent structure:
{
"result": true,
"errors": [],
"data": { ... }
}
When result is false, the errors
array contains human-readable messages describing what went wrong.
Rate Limits & Error Handling
API requests are rate-limited per project. If you exceed the limit, the API returns a 429 Too Many Requests status. Implement exponential backoff in your retry logic.
Common HTTP status codes:
200— Success400— Bad request (check parameters)401— Unauthorized (invalid or missing API key)403— Forbidden (signature mismatch or insufficient permissions)429— Rate limit exceeded500— Internal server error
Authentication
Secure your API requests with signature-based or simple key authentication.
Overview
Wiro supports two authentication methods. You choose the method when creating a project — it cannot be changed afterward.
Signature-Based Authentication
Uses HMAC-SHA256 to sign every request. The API secret never leaves your environment, making this method ideal for client-side applications where the key might be exposed.
How it works
- Generate a nonce Use a unix timestamp or random integer
- Concatenate Combine:
API_SECRET + NONCE - Create HMAC-SHA256 hash Use your
API_KEYas the secret key - Send as headers Include the signature, nonce, and API key in request headers
SIGNATURE = HMAC-SHA256(key=API_KEY, message=API_SECRET + NONCE)
Required Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
x-api-key | string | Yes | Your project API key |
x-signature | string | Yes | HMAC-SHA256(API_SECRET + NONCE, API_KEY) |
x-nonce | string | Yes | Unix timestamp or random integer |
API Key Only Authentication
For server-side applications where you control the environment, you can use the simpler API-key-only method. Just include the x-api-key header — no signature required.
Required Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
x-api-key | string | Yes | Your project API key |
Comparison
| Feature | Signature-Based | API Key Only |
|---|---|---|
| Security | High — secret never sent over the wire | Moderate — key sent in every request |
| Complexity | Requires HMAC computation | Single header |
| Best for | Client-side apps, mobile, public repos | Server-side, internal tools |
| Replay protection | Yes (via nonce) | No |
How to Choose
- Building a client-side or mobile app? Use Signature-Based.
- Running a server-side backend with controlled access? API Key Only is simpler.
- Unsure? Default to Signature-Based — it's always the safer option.
Projects
Organize your API access, billing, and usage with projects.
What is a Project?
A project is a container that holds your API keys, billing settings, and usage tracking. Each project gets its own API key and secret, letting you separate environments (development, staging, production) or different applications.
- Each project has its own API key and (optionally) API secret
- Usage and billing are tracked per project
- You can create multiple projects under one account
Creating a Project
- Go to the Dashboard Navigate to wiro.ai/panel
- Open Projects Go to Projects and click New Project
- Name your project Enter a descriptive project name
- Select authentication method Signature-Based — generates API key + secret | API Key Only — generates only an API key
- Create Click Create and copy your credentials immediately
API Credentials
After creating a project, your API key (and secret, if signature-based) are displayed once. Copy and store them securely — you won't be able to view the secret again.
Important: Treat your API secret like a password. Never commit it to version control or expose it in client-side code without signature-based authentication.
Managing Projects
From the Projects page in your Dashboard, you can:
- Update name — rename your project at any time
- Regenerate keys — invalidates existing keys and generates new ones
- View usage — see API calls, costs, and task history
- Delete project — permanently removes the project and revokes all keys
Regenerating keys immediately invalidates the old ones. Update your application with the new credentials before the old ones stop working.
Models
Browse and discover AI models available on the Wiro platform.
POST /Tool/List
Returns a paginated list of available models. Filter by categories, search by name, and sort results.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
start | string | No | Offset for pagination (default: "0") |
limit | string | No | Number of results to return (default: "20") |
search | string | No | Search query to filter models by name |
sort | string | No | Sort field: id, relevance |
order | string | No | Sort direction: ASC or DESC |
categories | string[] | No | Filter by categories (e.g. image-generation, llm, audio, video) |
tags | string[] | No | Filter by tags |
slugowner | string | No | Filter by model owner slug |
hideworkflows | boolean | No | Hide workflow models from results (recommended: true) |
summary | boolean | No | Return summarized model data (recommended for listings) |
Response
{
"result": true,
"errors": [],
"total": 2,
"tool": [
{
"id": "1611",
"title": "Virtual Try-on",
"slugowner": "wiro",
"slugproject": "Virtual Try-On",
"cleanslugowner": "wiro",
"cleanslugproject": "virtual-try-on",
"description": "Integrate the Wiro Virtual Try-On API...",
"image": "https://cdn.wiro.ai/uploads/models/...",
"computingtime": "10 seconds",
"categories": ["tool", "image-to-image", "image-editing"],
"tags": [],
"marketplace": 1,
"onlymembers": "1",
"averagepoint": "5.00",
"commentcount": "1",
"dynamicprice": "[{\"inputs\":{},\"price\":0.09,\"priceMethod\":\"cpr\"}]",
"taskstat": {
"runcount": 672,
"successcount": "254",
"errorcount": "198",
"lastruntime": "1774007585"
}
}
]
}
POST /Tool/Detail
Returns full details for a specific model, including its input parameters, pricing, categories, and configuration.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
slugowner | string | Yes | Model owner slug (e.g. stability-ai) |
slugproject | string | Yes | Model project slug (e.g. sdxl) |
summary | boolean | No | Return summarized data |
Response
{
"result": true,
"errors": [],
"tool": [{
"id": "1611",
"title": "Virtual Try-on",
"slugowner": "wiro",
"slugproject": "Virtual Try-On",
"cleanslugowner": "wiro",
"cleanslugproject": "virtual-try-on",
"description": "Integrate the Wiro Virtual Try-On API...",
"image": "https://cdn.wiro.ai/uploads/models/...",
"computingtime": "10 seconds",
"readme": "<p>The Wiro Virtual Try-On AI model...</p>",
"categories": ["tool", "image-to-image", "image-editing"],
"parameters": null,
"inspire": [
{
"inputImageHuman": "https://cdn.wiro.ai/uploads/sampleinputs/...",
"inputImageClothes": ["https://cdn.wiro.ai/..."]
}
],
"samples": ["https://cdn.wiro.ai/uploads/models/..."],
"tags": [],
"marketplace": 1,
"onlymembers": "1",
"dynamicprice": "[{\"inputs\":{},\"price\":0.09,\"priceMethod\":\"cpr\"}]",
"averagepoint": "5.00",
"commentcount": "1",
"ratedusercount": "3",
"taskstat": {
"runcount": 672,
"successcount": "254",
"errorcount": "198",
"lastruntime": "1774007585"
},
"seotitle": "AI Virtual Try-On: Integrate Realistic Apparel Fitting",
"seodescription": "Integrate the Wiro Virtual Try-On API..."
}]
}
Model Browser
Browse available models interactively. Click on a model to see its details on the model page.
Run a Model
Execute any AI model with a single API call and get real-time updates.
POST /Run/{owner-slug}/{model-slug}
Starts an AI model run. The endpoint accepts model-specific parameters and returns a task ID you can use to track progress via polling, WebSocket, or webhook by providing a callbackUrl parameter — Wiro will POST the result to your URL when the task completes.
Content Types
JSON (application/json)
Use JSON for text-based inputs — prompts, configuration, numeric parameters. This is the default and most common format.
Multipart (multipart/form-data)
Use multipart when the model requires file inputs (images, audio, documents). Include files as form fields and other parameters as text fields.
Request Parameters
Parameters vary by model. Use the /Tool/Detail endpoint to discover which parameters a model accepts. The following optional parameters apply to all runs:
Common Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
callbackUrl | string | No | URL to receive a POST webhook when the task completes |
projectid | string | No | Override the default project for billing (if you have multiple projects) |
Response
A successful run returns a task ID and a WebSocket access token:
{
"result": true,
"errors": [],
"taskid": "2221",
"socketaccesstoken": "eDcCm5yyUfIvMFspTwww49OUfgXkQt"
}
Full Flow
The typical workflow after calling the Run endpoint:
- Run — call
POST /Run/{owner-slug}/{model-slug}and receive a task ID - Track — connect via WebSocket or poll
POST /Task/Detail - Receive — get outputs as the model produces them (streaming or final)
- Complete — task reaches
endstatus with full results
For real-time streaming, use the WebSocket connection with the
socketaccesstoken returned in the run response. For simpler integrations, poll the Task Detail endpoint every few seconds.
Model Parameters
Understand parameter types, content types, and how to send inputs to any model.
Discovering Parameters
Every model has its own set of input parameters. Use the /Tool/Detail endpoint to retrieve a model's parameter definitions. The response includes a parameters array where each item describes a parameter group with its items:
{
"parameters": [
{
"title": "Input",
"items": [
{
"id": "prompt",
"type": "textarea",
"label": "Prompt",
"required": true,
"placeholder": "Describe what you want...",
"note": "Text description of the desired output"
},
{
"id": "inputImage",
"type": "fileinput",
"label": "Input Image",
"required": true,
"note": "Upload an image or provide a URL"
}
]
}
]
}
Parameter Types
| Type | Description | Example Parameters |
|---|---|---|
text | Single-line text input | URLs, names, short strings |
textarea | Multi-line text input | prompt, negative_prompt, descriptions |
select | Dropdown with predefined options | outputType, language, style |
range | Numeric value (slider) | width, height, scale, strength |
fileinput | Single file upload (1 file or 1 URL) | inputImage, inputAudio |
multifileinput | Multiple files (up to N files/URLs) | inputDocumentMultiple |
combinefileinput | Up to N entries (files, URLs, or mixed) | inputImageClothes |
JSON vs Multipart
The content type of your request depends on whether the model requires file inputs:
| Condition | Content-Type | When to Use |
|---|---|---|
| No file parameters | application/json | Text-only models (LLMs, image generation from prompt) |
| Has file parameters | multipart/form-data | Models that accept image, audio, video, or document uploads |
Tip: For fileinput and multifileinput parameters, use the {id}Url suffix to send URLs (e.g., inputImageUrl). For combinefileinput, pass URLs directly in the original parameter — no suffix needed. You can also pass a URL directly to any file parameter (e.g., inputImage) if the {id}Url field doesn't exist.
File Upload Patterns
Single File (fileinput)
For parameters like inputImage, send either a file or a URL. When using multipart, always include both the {id} and {id}Url fields — leave one empty:
# Option 1: Upload file — send file in {id}, empty {id}Url
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "inputImage=@/path/to/photo.jpg" \
-F "inputImageUrl="
# Option 2: Send URL via {id}Url — send empty {id}, URL in {id}Url
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "inputImage=" \
-F "inputImageUrl=https://example.com/photo.jpg"
# Option 3: Pass URL directly in {id} (no {id}Url needed)
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "inputImage=https://example.com/photo.jpg"
Note: Option 3 is the simplest when you only have a URL. If the {id}Url field doesn't exist for a parameter, always use this approach.
Multiple Files (multifileinput)
For parameters like inputDocumentMultiple, upload up to N files, send comma-separated URLs, or mix both:
# Option 1: Upload multiple files — add empty {id}Url
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "[email protected]" \
-F "[email protected]" \
-F "inputDocumentMultipleUrl="
# Option 2: Send URLs (comma-separated in {id}Url) — add empty {id}
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "inputDocumentMultiple=" \
-F "inputDocumentMultipleUrl=https://example.com/doc1.pdf,https://example.com/doc2.pdf"
# Option 3: Mixed — files in {id}, URLs in {id}Url
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "[email protected]" \
-F "inputDocumentMultipleUrl=https://example.com/doc2.pdf,https://example.com/doc3.pdf"
Combined (combinefileinput)
For parameters like inputImageClothes, files and URLs go directly in the same {id} field — no {id}Url suffix:
# Option 1: Upload files — each as a separate {id} entry
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "[email protected]" \
-F "[email protected]"
# Option 2: Send URLs — each directly in {id}
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "inputImageClothes=https://example.com/shirt.jpg" \
-F "inputImageClothes=https://example.com/pants.jpg"
# Option 3: Mixed — files and URLs in the same {id} field
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "[email protected]" \
-F "inputImageClothes=https://example.com/pants.jpg"
Common Model Patterns
Image Generation (text-to-image)
Models like Stable Diffusion, Flux — JSON body, no file uploads:
{
"prompt": "A futuristic city at sunset",
"negative_prompt": "blurry, low quality",
"width": 1024,
"height": 1024
}
Image-to-Image (upscaler, style transfer)
Models that take an input image — multipart with file upload:
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "[email protected]" \
-F "scale=4"
Virtual Try-On
Multiple image inputs — multipart with multiple files:
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "[email protected]" \
-F "[email protected]"
LLM / Document Processing
Text prompt with optional document uploads:
curl -X POST "https://api.wiro.ai/v1/Run/{owner-slug}/{model-slug}" \
-H "x-api-key: YOUR_API_KEY" \
-F "[email protected]" \
-F "prompt=Extract the candidate name and skills" \
-F "outputType=json" \
-F "language=en"
Note: LLM responses are delivered via debugoutput in the task result, not in the outputs file array. See Tasks for details.
Realtime Voice Conversation
Realtime voice models accept configuration parameters (voice, system instructions, audio format, etc.) as JSON. Parameters vary per model — use /Tool/Detail to discover them. The actual audio interaction happens over Realtime Voice WebSocket after the task starts:
// Example: OpenAI GPT Realtime
{
"voice": "marin",
"system_instructions": "You are a helpful voice assistant.",
"input_audio_format": "audio/pcm",
"output_audio_format": "audio/pcm",
"input_audio_rate": "24000",
"output_audio_rate": "24000"
}
Webhook Callback
All models support an optional callbackUrl parameter. When provided, Wiro will POST the task result to your URL when the task completes — no polling required:
{
"prompt": "A sunset over mountains",
"callbackUrl": "https://your-server.com/webhook/wiro"
}
Tasks
Track, monitor, and control your AI model runs.
Task Lifecycle
Every model run creates a task that progresses through a defined set of stages:
Task Statuses
| Status | Description |
|---|---|
task_queue |
The task is queued and waiting to be picked up by an available worker. Emitted once when the task enters the queue. |
task_accept |
A worker has accepted the task. The task is no longer in the general queue and is being prepared for execution. |
task_preprocess_start |
Optional preprocessing has started. This includes operations like downloading input files from URLs, converting file types, and validating/formatting parameters before the model runs. Not all models require preprocessing. |
task_preprocess_end |
Preprocessing completed. All inputs are ready for GPU assignment. |
task_assign |
The task has been assigned to a specific GPU. The model is being loaded into memory. This may take a few seconds depending on the model size. |
task_start |
The model command has started executing. Inference is now running on the GPU. |
task_output |
The model is producing output. This event is emitted multiple times — each time the model writes to stdout, a new task_output message is sent via WebSocket. For LLM models, each token/chunk arrives as a separate task_output event, enabling real-time streaming. |
task_error |
The model wrote to stderr. This is an interim log event, not a final failure — many models write warnings or debug info to stderr during normal operation. The task may still complete successfully. Always wait for task_postprocess_end to determine the actual result. |
task_output_full |
The complete accumulated stdout log, sent once after the model process finishes. Contains the full output history in a single message. |
task_error_full |
The complete accumulated stderr log, sent once after the model process finishes. |
task_end |
The model process has exited. Emitted once. This fires before post-processing — do not use this event to determine success. Wait for task_postprocess_end instead. |
task_postprocess_start |
Post-processing has started. The system is preparing the output files — encoding, uploading to CDN, and generating access URLs. |
task_postprocess_end |
Post-processing completed. Check pexit to determine success: "0" = success, any other value = error. The outputs array contains the final files with CDN URLs, content types, and sizes. This is the event you should listen for to get the final results. |
task_cancel |
The task was cancelled (if queued) or killed (if running) by the user. |
Realtime Conversation Only
The following statuses are exclusive to realtime conversation models (e.g. voice AI). They are not emitted for standard model runs.
| Status | Description |
|---|---|
task_stream_ready |
Realtime model is ready to receive audio/text input — you can start sending data |
task_stream_end |
Realtime session has ended — the model finished speaking or the session was closed |
task_cost |
Real-time cost update emitted during execution — shows the running cost of the task |
Determining Success or Failure
Both successful and failed tasks reach task_postprocess_end. The status alone does not tell you whether the task succeeded. Wait for task_postprocess_end and then check pexit or outputs (or both) to determine the actual result:
pexit— the process exit code."0"means success, any other value means the model encountered an error. This is the most reliable indicator.outputs— the output files array. For non-LLM models, a successful run populates this with CDN URLs. If it's empty or missing, the task likely failed.
Note: For LLM models, outputs will be empty even on success — the response text is delivered via debugoutput instead. Always use pexit as the primary success check.
// Success (image/audio model): pexit "0", outputs present
{
"pexit": "0",
"outputs": [{ "name": "0.png", "url": "https://cdn1.wiro.ai/..." }]
}
// Success (LLM model): pexit "0", outputs empty, response in debugoutput
{
"pexit": "0",
"outputs": [],
"debugoutput": "Hello! How can I help you today?"
}
// Failure: pexit non-zero
{
"pexit": "1",
"outputs": []
}
Important: task_error events during execution are interim log messages, not final failures. A task can emit error logs and still complete successfully. Always wait for task_postprocess_end and check pexit.
Billing & Cost
The totalcost field in the Task Detail response shows the actual cost charged for the run. Only successful tasks are billed — if pexit is non-zero (failure), the task is not charged and totalcost will be "0".
// Successful run — billed
{
"status": "task_postprocess_end",
"pexit": "0",
"totalcost": "0.003510000000",
"elapsedseconds": "6.0000"
}
// Failed run — not billed
{
"status": "task_postprocess_end",
"pexit": "1",
"totalcost": "0",
"elapsedseconds": "4.0000"
}
Use the totalcost field to track spending per task. For more details on how costs are calculated, see Pricing.
LLM Models
For LLM (Large Language Model) requests, the model's response is written to debugoutput rather than the outputs file array. When polling with Task Detail, read the debugoutput field to get the LLM's text response.
For real-time streaming of LLM responses, use WebSocket instead of polling. Each task_output event delivers a chunk of the response as it's generated, giving your users an instant, token-by-token experience.
POST /Task/Detail
Retrieves the current status and output of a task. You can query by either tasktoken or taskid.
| Parameter | Type | Required | Description |
|---|---|---|---|
tasktoken |
string | No | The task token returned from the Run endpoint |
taskid |
string | No | The task ID (alternative to tasktoken) |
Response
{
"result": true,
"errors": [],
"total": "1",
"tasklist": [{
"id": "534574",
"socketaccesstoken": "eDcCm5yyUfIvMFspTwww49OUfgXkQt",
"parameters": { "prompt": "Hello, world!" },
"status": "task_postprocess_end",
"pexit": "0",
"debugoutput": "",
"starttime": "1734513809",
"endtime": "1734513813",
"elapsedseconds": "6.0000",
"totalcost": "0.003510000000",
"outputs": [{
"name": "0.png",
"contenttype": "image/png",
"size": "202472",
"url": "https://cdn1.wiro.ai/.../0.png"
}]
}]
}
POST /Task/Cancel
Cancels a task that is still in the queue stage. Tasks that have already been assigned to a worker cannot be cancelled — use Kill instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
tasktoken |
string | Yes | The task token to cancel |
POST /Task/Kill
Terminates a task that is currently running (any status after
assign). The worker will stop processing and the task will move to cancel status.
| Parameter | Type | Required | Description |
|---|---|---|---|
tasktoken |
string | Yes | The task token to kill |
LLM & Chat Streaming
Stream LLM responses in real time with thinking/answer separation, session history, and multi-turn conversations.
Overview
LLM (Large Language Model) requests on Wiro work differently from standard model runs:
- Responses are delivered via
debugoutput, not theoutputsfile array - Streaming
task_outputmessages contain structuredthinkingandanswerarrays — not plain strings - Multi-turn conversations are supported via
session_idanduser_idparameters pexitis the primary success indicator (outputs will be empty)
Available LLM models include:
- openai/gpt-5-2 — GPT-5-2
- openai/gpt-oss-20b — GPT OSS 20B
- qwen/qwen3-5-27b — Qwen 3.5 27B
Session & Chat History
Wiro maintains conversation history per session. By sending a session_id, the model remembers previous messages and can build on the context of the conversation. Combined with user_id, this enables fully stateful chat experiences:
| Parameter | Type | Required | Description |
|---|---|---|---|
session_id | string | No | UUID identifying the conversation session. The server stores chat history per session — reuse the same ID for follow-up messages to maintain full context. |
user_id | string | No | UUID identifying the user. Allows the model to distinguish between different users sharing the same session or to personalize responses. |
prompt | string | Yes | The user's message or question. |
// First message — start a new session
{
"prompt": "What is quantum computing?",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
// Follow-up — reuse the same session_id
{
"prompt": "Can you explain qubits in more detail?",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
Tip: Generate a new UUID for session_id when starting a fresh conversation. Reuse it for all follow-up messages — the server automatically stores and retrieves the full chat history for that session. To start a new conversation with no prior context, simply generate a new UUID.
Thinking & Answer Phases
Many LLM models separate their output into two phases:
- Thinking — the model's internal reasoning process (chain-of-thought)
- Answer — the final response to the user
When streaming via WebSocket, task_output messages for LLM models contain a structured object (not a plain string):
// LLM task_output message format
{
"type": "task_output",
"id": "534574",
"tasktoken": "eDcCm5yy...",
"message": {
"thinking": ["Let me analyze this step by step...", "The key factors are..."],
"answer": ["Quantum computing uses qubits that can exist in superposition..."]
}
}
Both thinking and answer are arrays of strings. Each element represents a chunk — they grow as the model streams its response. A model may alternate between thinking and answering multiple times.
| Field | Type | Description |
|---|---|---|
message.thinking | string[] | Array of reasoning/chain-of-thought chunks. May be empty for models without thinking. |
message.answer | string[] | Array of response chunks. This is the content to show the user. |
message.raw | string | Optional raw output before thinking/answer separation. |
Note: Standard (non-LLM) models send message as a plain string. LLM models send it as a { thinking, answer } object. Check the type before parsing.
Streaming Flow
The complete flow for streaming an LLM response:
- Run the model with
prompt,session_id, anduser_id - Connect to WebSocket and send
task_info - Receive
task_outputmessages — each contains the growingthinkingandanswerarrays - Display the latest
answerarray content to the user (optionally showthinkingin a collapsible section) - Complete — on
task_postprocess_end, checkpexitfor success
Each task_output event contains the full accumulated thinking and answer arrays up to that point — not just the new chunk. Simply replace your displayed content with the latest arrays.
Polling Alternative
If you don't need real-time streaming, you can poll POST /Task/Detail instead. The final response will be in the debugoutput field as a merged plain text string (thinking + answer combined):
{
"result": true,
"tasklist": [{
"status": "task_postprocess_end",
"pexit": "0",
"debugoutput": "Quantum computing uses qubits that can exist in superposition...",
"outputs": []
}]
}
Note: When polling, debugoutput contains the merged text. To access separate thinking and answer arrays, use WebSocket streaming instead.
WebSocket
Receive real-time task updates via a persistent WebSocket connection.
Connection URL
wss://socket.wiro.ai/v1
Connect to this URL after calling the Run endpoint. Use the
socketaccesstoken from the run response to register your session.
Connection Flow
- Connect — open a WebSocket connection to
wss://socket.wiro.ai/v1 - Register — send a
task_infomessage with yourtasktoken - Receive — listen for messages as the task progresses through its lifecycle
- Close — disconnect after the
task_postprocess_endevent (this is the final event with results)
Registration message format:
{
"type": "task_info",
"tasktoken": "your-socket-access-token"
}
Message Types
| Message Type | Description |
|---|---|
task_queue |
The task is queued and waiting to be picked up by an available worker. |
task_accept |
A worker has accepted the task and is preparing for execution. |
task_preprocess_start |
Optional preprocessing has started (downloading input files from URLs, converting file types, validating parameters). |
task_preprocess_end |
Preprocessing completed. All inputs are ready for GPU assignment. |
task_assign |
The task has been assigned to a specific GPU. The model is being loaded into memory. |
task_start |
The model command has started executing. Inference is now running on the GPU. |
task_output |
The model is producing output. Emitted multiple times — each stdout write sends a new message. For LLMs, each token/chunk arrives as a separate event for real-time streaming. |
task_error |
The model wrote to stderr. This is an interim log event, not a final failure — many models write warnings to stderr during normal operation. The task may still succeed. |
task_output_full |
The complete accumulated stdout log, sent once after the model process finishes. |
task_error_full |
The complete accumulated stderr log, sent once after the model process finishes. |
task_end |
The model process has exited. Fires before post-processing — do not use this to determine success. Wait for task_postprocess_end instead. |
task_postprocess_start |
Post-processing has started. The system is preparing output files — encoding, uploading to CDN, generating access URLs. |
task_postprocess_end |
Post-processing completed. Check pexit to determine success ("0" = success). The outputs array contains the final files. This is the event to listen for. |
task_cancel |
The task was cancelled (if queued) or killed (if running) by the user. |
Binary Frames
For realtime voice models, the WebSocket may send binary frames containing raw audio data. Check if the received message is a Blob (browser) or Buffer (Node.js) before parsing as JSON.
Ending a Session
For realtime/streaming models that maintain a persistent session, send a task_session_end message to gracefully terminate:
{
"type": "task_session_end",
"tasktoken": "your-socket-access-token"
}
After sending this, wait for the task_end event before closing the connection.
Realtime Voice
Build interactive voice conversation apps with realtime AI models.
Overview
Realtime voice models enable two-way audio conversations with AI. Unlike standard model runs that process a single input and return a result, realtime sessions maintain a persistent WebSocket connection where you stream microphone audio and receive AI speech in real time.
The flow is:
- Run the realtime model via POST /Run to get a
socketaccesstoken - Connect to the WebSocket and send
task_infowith your token - Wait for
task_stream_ready— the model is ready to receive audio - Stream microphone audio as binary frames
- Receive AI audio as binary frames and play them
- End the session with
task_session_end
Run Parameters
Each realtime model has its own set of parameters. Use POST /Tool/Detail to discover the exact parameters for a specific model. See Model Parameters for details on parameter types.
Available realtime conversation models include:
- openai/gpt-realtime-mini — GPT Mini Realtime Voice Assistant
- openai/gpt-realtime — GPT Realtime Voice Assistant
- elevenlabs/realtime-conversational-ai — ElevenLabs Conversational AI
Common parameters across realtime models typically include voice selection, system instructions, and audio format settings. Example run for an OpenAI realtime model:
curl -X POST "https://api.wiro.ai/v1/Run/openai/gpt-realtime-mini" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"voice": "marin",
"system_instructions": "You are a friendly assistant.",
"input_audio_format": "audio/pcm",
"output_audio_format": "audio/pcm",
"input_audio_rate": "24000",
"output_audio_rate": "24000"
}'
Important: Parameters vary per model. Always check the model's detail page or use /Tool/Detail to get the exact parameter list before integrating.
Connection & Registration
After running the task, connect to the WebSocket and register with task_info:
var ws = new WebSocket("wss://socket.wiro.ai/v1");
ws.onopen = function() {
ws.send(JSON.stringify({
type: "task_info",
tasktoken: "YOUR_SOCKET_ACCESS_TOKEN"
}));
};
Note: Both standard and realtime models use type: "task_info" with tasktoken to register on the WebSocket.
Realtime Events
During a realtime session, you'll receive these WebSocket events:
| Event | Description |
|---|---|
task_stream_ready | Session is ready — start sending microphone audio |
task_stream_end | AI finished speaking for this turn — you can speak again |
task_cost | Cost update per turn — includes turnCost, cumulativeCost, and usage (raw cost breakdown from the model provider) |
task_output | Transcript messages prefixed with TRANSCRIPT_USER: or TRANSCRIPT_AI: |
task_end | Session fully ended — close the connection |
Audio Format
Both directions (microphone → server, server → client) use the same format:
| Property | Value |
|---|---|
| Format | PCM (raw, uncompressed) |
| Bit depth | 16-bit signed integer (Int16) |
| Sample rate | 24,000 Hz (24 kHz) |
| Channels | Mono (1 channel) |
| Byte order | Little-endian |
| Chunk size | 4,800 samples (200 ms) = 9,600 bytes |
Binary Frame Format
Every binary WebSocket frame (in both directions) is structured as:
[tasktoken]|[PCM audio data]
The pipe character | (0x7C) separates the token from the raw audio bytes.
Sending Microphone Audio
Capture microphone at 24 kHz using the Web Audio API with an AudioWorklet. Convert Float32 samples to Int16, prepend your task token, and send as a binary frame.
Key steps:
- Request microphone with
getUserMedia(enable echo cancellation and noise suppression) - Create an
AudioContextat 24,000 Hz sample rate - Use an AudioWorklet to buffer and convert samples to Int16
- Send each chunk as
tasktoken|pcm_databinary frame
Receiving AI Audio
AI responses arrive as binary WebSocket frames in the same PCM Int16 24 kHz format. To play them:
- Check if the message is a
Blob(binary) before parsing as JSON - Find the pipe
|separator and extract audio data after it - Convert Int16 → Float32 and create an
AudioBuffer - Schedule gapless playback using
AudioBufferSourceNode
Transcripts
Both user and AI speech are transcribed automatically. Transcripts arrive as task_output messages with a string prefix:
TRANSCRIPT_USER:— what the user saidTRANSCRIPT_AI:— what the AI said
// Example task_output message
{
"type": "task_output",
"message": "TRANSCRIPT_USER:What's the weather like today?"
}
{
"type": "task_output",
"message": "TRANSCRIPT_AI:I'd be happy to help, but I don't have access to real-time weather data."
}
Ending a Session
To gracefully end a realtime session, send task_session_end:
{
"type": "task_session_end",
"tasktoken": "YOUR_SOCKET_ACCESS_TOKEN"
}
After sending this, the server will process any remaining audio, send final cost/transcript events, and then emit task_end. Wait for task_end before closing the WebSocket.
Safety: If the client disconnects without sending task_session_end, the server automatically terminates the session to prevent the pipeline from running indefinitely (and the provider from continuing to charge). Always send task_session_end explicitly for a clean shutdown.
Insufficient balance: If the wallet runs out of balance during a realtime session, the server automatically stops the session. You will still receive the final task_cost and task_end events.
Files
Manage folders and upload files for use with AI models.
Overview
The Files API lets you organize and upload data that can be referenced in model runs. Common use cases include:
- Training data — upload datasets for fine-tuning models
- File inputs — provide images, audio, or documents as model inputs
- Batch processing — store files for repeated use across multiple runs
POST /File/FolderCreate
Creates a new folder to organize your uploaded files.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Folder name |
parentId |
string | No | Parent folder ID for nested structure (null for root) |
Response
{
"result": true,
"errors": [],
"data": {
"id": "folder-abc123",
"name": "training-data",
"parentId": null,
"createdAt": "2025-01-15T10:00:00Z"
}
}
POST /File/Upload
Uploads a file using multipart/form-data. You can optionally assign it to a folder.
| Parameter | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | The file to upload (multipart form field) |
folderid |
string | No | Target folder ID (uploads to user's default folder if omitted) |
File size limit: 100 MB per file.
Supported file types: Images (jpg, png, gif, jpeg, webp, heic), video (mp4, webm, mov), audio (mp3, wav, m4a), documents (pdf, csv, docx, xlsx, pptx, txt, md, epub), and ZIP archives (automatically extracted).
Response
{
"result": true,
"errors": [],
"list": [{
"id": "file-id",
"name": "dataset.csv",
"contenttype": "text/csv",
"size": "1048576",
"parentid": "folder-id",
"url": "https://cdn1.wiro.ai/...",
"addedtime": "1716276727",
"accesskey": "..."
}]
}
Using Files in Runs
Once uploaded, reference a file by its URL or ID in your model run parameters. For example, an image upscaler model might accept a
imageUrl parameter — pass the URL returned from the upload response.
{
"imageUrl": "https://files.wiro.ai/...",
"scale": 4
}
Pricing
Understand how billing works for AI model runs on Wiro.
Overview
When you run an AI model through Wiro, you are billed based on the type of work performed. Each model has its own pricing, visible on the model's page in the marketplace and on the pricing page. You pay only for successful runs — server errors are never billed.
Wiro uses a prepaid credit model. You add credits to your account and they are drawn down as you use models. Credits also determine your concurrency limit.
Billing Methods
Every model on Wiro uses one of the following billing methods. The method is set per model and determines how the cost is calculated.
Fixed-Rate Methods
| Billing Method | Code | How it works |
|---|---|---|
| Per Request | cpr | Fixed cost per run, regardless of output. Most common for image generation, image editing, and simple models. |
| Per Second | cps | Cost per second of processing time. When no dynamic pricing is set, this is the default — cost = elapsed seconds × cps rate. |
| Per Output | cpo | Cost per output item generated. Multiple files = pay per file. No output = base price charged once. |
| Per Token | cpt | Cost per token used. Total tokens (input + output) extracted from model's usage metadata. Used for LLM models. |
Usage-Based Methods
| Billing Method | Code | How it works |
|---|---|---|
| Per Pixel | cp-pixel | Cost based on output resolution. Each 1,048,576 pixels (1024×1024) = one tier. Can include per-input-image costs (priceInput). |
| Per Audio Second | cp-audiosecondslength | Cost per second of input audio duration. Duration measured via ffprobe. |
| Per Character | cp-promptlength | Cost per character in the input prompt. Total = prompt length × price. |
| Per Video Second | cp-outputVideoLength | Cost per second of generated output video. Duration measured via ffprobe. |
Special Methods
| Billing Method | Code | How it works |
|---|---|---|
| Per Realtime Turn | cp-realtimeturn | For realtime voice models. Billing per conversation turn, deducted in real time during the session. |
| Model-Reported | cp-readoutput | The model reports its own cost in stdout/stderr JSON output. |
Dynamic Pricing
Many models have dynamic pricing — the cost varies based on the input parameters you choose. For example, a video generation model might charge different rates depending on the resolution and duration you select.
The pricing is returned in the dynamicprice field of the Tool/List and Tool/Detail API responses as a JSON array:
[
{
"inputs": { "resolution": "720p", "duration": "5" },
"price": 0.13,
"priceMethod": "cpr"
},
{
"inputs": { "resolution": "1080p", "duration": "5" },
"price": 0.29,
"priceMethod": "cpr"
}
]
How Dynamic Pricing Works
Each entry in the dynamicprice array represents a pricing tier:
| Field | Type | Description |
|---|---|---|
inputs | object | The input parameter combination this price applies to. Empty {} means the price applies to all configurations. |
price | number | The cost in USD for this configuration. |
priceMethod | string | The billing method code (see tables above). |
priceExtra | number (optional) | Extra cost per additional tier. Used by cp-pixel — each additional 1MP tier costs this amount. |
priceInput | number (optional) | Per-input cost. Used by cp-pixel — each input image incurs this cost per 1MP tier. |
When inputs contains specific parameter values (e.g. "resolution": "720p"), that price only applies when you run the model with those exact parameters. When inputs is empty ({}), it's a flat rate that applies regardless of input parameters.
Input matching also supports QUANTITY values (e.g. "QUANTITY:1", "QUANTITY:3") for models where the number of input files affects pricing.
Example: Video Generation Pricing
A video model might have pricing tiers based on resolution and duration:
| Resolution | Duration | Price |
|---|---|---|
| 480p | 5 seconds | $0.06 |
| 720p | 5 seconds | $0.13 |
| 1080p | 5 seconds | $0.29 |
| 480p | 10 seconds | $0.12 |
| 720p | 10 seconds | $0.26 |
| 1080p | 10 seconds | $0.58 |
Example: Simple Flat Pricing
An image generation model with a flat rate:
[{ "inputs": {}, "price": 0.03, "priceMethod": "cpr" }]
This means every run costs $0.03, regardless of parameters.
Fallback Pricing (Per-Second)
When a model does not have dynamicprice set, billing falls back to per-second pricing:
totalcost = elapsed_seconds × cps
Where cps (cost per second) is either the model's own rate or the queue group's default rate. The API also returns an approximatelycost field — an estimate based on the model's average run time:
approximatelycost = average_elapsed_seconds × cps
This gives you a rough idea of the expected cost before running the model.
Checking Prices
Via the API
Pricing information is included in both the Tool/List and Tool/Detail responses in the dynamicprice field. Use POST /Tool/Detail with the model's slugowner and slugproject to get full pricing details.
Via MCP
When using the MCP server, both search_models and get_model_schema tools return pricing information in their responses. Your AI assistant can check the cost before running a model.
Pricing Page
Browse and compare model prices interactively on the pricing page. Select a budget to see how many runs each model can perform.
What You Pay For
You are billed for successfully completed model runs. A run is successful when the task reaches task_postprocess_end status with pexit of "0". The actual cost is recorded in the task's totalcost field, which you can retrieve via Task/Detail.
What You Are Not Charged For
- Server errors — if a run fails due to a server-side error, no charge is incurred.
- Queue time — time spent waiting in the queue before processing starts is free.
- Cancelled tasks — tasks cancelled before processing completes are not billed.
Monitoring Your Spending
- Check the
totalcostfield in Task/Detail responses to see the cost of individual runs. - View your overall balance, usage history, and billing details in the Dashboard.
- When using MCP, the
get_tasktool returns the cost of completed runs.
Concurrency Limits
Understand and manage how many requests you can run simultaneously on Wiro.
Overview
Concurrency limits control how many tasks your account can process at the same time. When you reach your limit, the API returns an error response with code 96. You should wait for a running task to complete before submitting a new one, or add funds to increase your limit.
How It Works
Your concurrency limit is determined by your current account balance:
- When your balance is $250 or below, you can run concurrent tasks equal to 10% of your current USD balance (minimum 1).
- When your balance is above $250, there is no concurrency limit.
Examples
| Account Balance | Concurrent Task Limit |
|---|---|
| $10 | 1 concurrent task (minimum) |
| $50 | 5 concurrent tasks |
| $100 | 10 concurrent tasks |
| $150 | 15 concurrent tasks |
| $250 | 25 concurrent tasks |
| $251+ | Unlimited (no limit applied) |
The formula: max(1, floor(balance_usd * 0.10)). Once your balance exceeds $250, all limits are removed.
What Counts as Active
Only tasks that are actively being processed count toward your concurrency limit. A task is considered active from task_queue until it reaches a terminal status:
task_postprocess_end— task completed (success or failure)task_cancel— task was cancelled or killed
Once a task reaches either of these statuses, it no longer counts toward your limit.
API Response
When you hit the concurrency limit, the POST /Run endpoint returns an error with code 96:
{
"result": false,
"errors": [
{
"code": 96,
"message": "You have reached your concurrent task limit. With your current balance of $50.00, you can run up to 5 tasks at the same time. Add funds to increase your limit."
}
]
}
The error message includes your current balance and the calculated limit, so you know exactly how many concurrent tasks you can run.
Error Codes
| Code | Meaning | Action |
|---|---|---|
96 | Concurrent task limit reached | Wait for a running task to finish, or add funds |
97 | Insufficient balance | Add funds to your account |
Increasing Your Limit
To increase your concurrency limit, simply add credits to your account. Your limit is recalculated automatically based on your current balance at the time of each run request.
For enterprise needs or custom concurrency arrangements, contact support.
Best Practices
- Check error code
96— if you get this error, wait for a running task to complete before submitting new ones. - Use WebSocket for monitoring — instead of polling
/Task/Detailrepeatedly, connect via WebSocket to get real-time updates without extra API calls. - Use
wait=falsein MCP — for long-running models (video, 3D), submit withwait=falseand check withget_taskto avoid holding connections. - Implement exponential backoff — if polling task status, start at 3 seconds and increase the interval for longer tasks.
Error Reference
Understand API error responses, error codes, and how to handle them.
Response Format
When an API request fails, the response includes result: false and an errors array:
{
"result": false,
"errors": [
{
"code": 97,
"message": "Insufficient balance"
}
]
}
All API responses return HTTP 200 — use the result field and error code to determine success or failure.
Error Codes
Error codes indicate the category of the problem. Use these for conditional logic in your application.
| Code | Category | Description |
|---|---|---|
0 | General | Server-side errors, validation failures, missing parameters |
1 | Not Found / Client | Resource not found or not accessible |
96 | Concurrency Limit | Too many concurrent tasks for your balance. See Concurrency Limits |
97 | Insufficient Balance | Not enough funds to run the model |
98 | Authentication Required | Sign in required to access this model |
99 | Token Invalid | Bearer token missing, invalid, or expired |
Authentication Errors
Returned when API key or bearer token authentication fails. All return HTTP 401.
| Error | Code | Message |
|---|---|---|
| API key not found | 0 | Project authorization is not founded. |
| Signature required | 0 | Project requires signature authentication. x-signature and x-nonce headers are required. |
| Invalid signature | 0 | Project authorization is not valid. |
| IP not allowed | 0 | Requested ip {ip} is not allowed. |
| Bearer token missing | 99 | Authorization bearer token is not founded in headers. |
| Bearer token invalid | 99 | Authorization bearer token is invalid. |
| Bearer token expired | 99 | Authorization bearer token expired. |
| Endpoint not found | 0 | Error parsing url. (HTTP 404) |
Run Errors
Returned by POST /Run/{owner}/{model}.
Balance & Limits
| Error | Code | Message | Action |
|---|---|---|---|
| Insufficient balance | 97 | Insufficient balance | Add funds — minimum $0.50 required ($10 for training) |
| Concurrent task limit | 96 | You have reached your concurrent task limit. With your current balance of ${balance}, you can run up to {maxConcurrent} tasks at the same time. | Wait for a task to finish, or add funds. See Concurrency Limits |
| Sign in required | 98 | sign in to run this model | Model requires a registered account |
Validation Errors
| Error | Code | Message |
|---|---|---|
| Missing parameter | 0 | Request parameter [{name}] required |
| Invalid number | 0 | Request parameter [{name}] must be integer or float |
| Out of range | 0 | Request parameter [{name}] must be between {min} and {max} |
| File required | 0 | Request files [{name}] required |
| Invalid request body | 0 | Request parameters are invalid. |
Model Access Errors
| Error | Code | Message |
|---|---|---|
| Model not accessible | 1 | tool-not-accessible |
| Model not found | 1 | slug-owner-project-not-exist |
| Account suspended | 0 | Your account has been suspended. Please contact support. |
| Permission denied | 0 | You don't have any permission for this action. |
Task Errors
Returned by POST /Task/Detail, POST /Task/Cancel, and POST /Task/Kill.
| Error | Code | Message | Endpoint |
|---|---|---|---|
| Task not found | 1 | There is no task yet. | Detail, Cancel, Kill |
| Missing identifier | 0 | taskid or socketaccesstoken is required | Detail |
| Not cancellable | 1 | Task is not in a cancellable state. | Cancel |
| Kill failed | 1 | Task could not be killed: {reason} | Kill |
| Permission denied | 0 | You don't have any permission for this action. | All |
Error Handling
Always check result first, then inspect the code in the errors array:
const data = await response.json();
if (!data.result) {
const error = data.errors[0];
switch (error.code) {
case 96:
console.log('Concurrent limit — wait for a task to finish');
break;
case 97:
console.log('Insufficient balance — add funds');
break;
case 98:
console.log('Sign in required');
break;
default:
console.log('Error:', error.message);
}
}
Retry Strategy
| Code | Retryable | Strategy |
|---|---|---|
0 (validation) | No | Fix the request parameters |
0 (server) | Yes | Retry with exponential backoff |
1 | No | Check model slug or task token |
96 | Yes | Wait for a running task to complete |
97 | No | Add funds, then retry |
98 | No | Sign in or use authenticated credentials |
99 | No | Check your API key or bearer token |
FAQ
Common questions about using the Wiro API. If you can't find the answer here, contact support.
Sign up at wiro.ai, then create a project at wiro.ai/panel/project. Your API key (and secret, if signature-based) are displayed once — copy and store them securely.
Signature-Based is recommended — it uses HMAC-SHA256 so your API secret never leaves your environment. API Key Only is simpler and fine for server-side applications where you control the environment. See Authentication for details.
Yes. Every account has a concurrency limit that controls how many tasks can run at the same time. The limit scales automatically based on your account balance. See Concurrency Limits for the full table.
No. If a task fails (non-zero pexit), you are not charged. Only successfully completed tasks are billed.
Use the POST /Tool/Detail endpoint — the response includes the model's pricing information. If you're using the MCP server, the search_models and get_model_schema tools also return pricing.
Output files are stored on Wiro's CDN and available for a limited time. Download and store any files you need to keep long-term. See Files for details on file management.
Yes. Output URLs returned by Wiro are publicly accessible. Anyone with the URL can access the file. If you need private storage, download the files to your own infrastructure.
Connect to the WebSocket at wss://socket.wiro.ai/v1 and register with the socketaccesstoken from your run response. You'll receive events as the task progresses. For simpler integrations, you can poll the Task Detail endpoint.
LLM models return their response in debugoutput, not in the outputs file array. For streaming, each token arrives as a separate task_output WebSocket event. See LLM & Chat Streaming for details.
Yes. For fileinput and multifileinput parameters, use the {id}Url suffix (e.g., inputImageUrl). For combinefileinput, pass URLs directly in the original parameter. You can also pass a URL directly to any file parameter if the {id}Url field doesn't exist. See Model Parameters.
pexit is the process exit code — "0" means success, any other value means failure. It's the most reliable way to determine if a task succeeded. Always check pexit in the task_postprocess_end event or Task Detail response. See Tasks.
No. The Wiro MCP server is free. You only pay for the model runs you trigger, at standard pricing.
Yes. Install the Wiro AI community node in your n8n instance to access all Wiro models as drag-and-drop nodes in your workflows.
Yes. All models support an optional callbackUrl parameter. When provided, Wiro will POST the task result to your URL when the task completes. See Webhook Callback in Model Parameters.
Code Examples
Complete end-to-end examples in all 9 supported languages.
Overview
Each example below demonstrates the full Wiro workflow: authenticate, run a model, poll for task completion, and retrieve the result. Choose your preferred language from the tabs.
- curl — Shell scripting with bash
- Python — Using the
requestslibrary - Node.js — Using
axios - PHP — Using cURL functions
- C# — Using
HttpClient(.NET 6+) - Swift — Using async/await
URLSession - Dart — Using the
httppackage - Kotlin — Using
java.net.http - Go — Using the standard library
net/http
Full Examples
Select a language tab to see the complete example. All examples perform the same steps:
- Set up authentication headers
- Run a model (
POST /Run/{owner-slug}/{model-slug}) - Poll the task status (
POST /Task/Detail) - Print the final output
Wiro MCP Server
Connect AI coding assistants to Wiro's AI models via the Model Context Protocol.
What is MCP?
Model Context Protocol (MCP) is an open standard that lets AI assistants use external tools directly. With the Wiro MCP server, your AI assistant can search models, run inference, track tasks, and upload files — all without leaving your editor.
The hosted MCP server is available at mcp.wiro.ai/v1 and works with any MCP-compatible client, including Cursor, Claude Code, Claude Desktop, and Windsurf. Every request uses your own API key — nothing is stored on the server.
You need a Wiro API key to use the MCP server. If you don't have one yet, create a project here.
Links
- Model Context Protocol (MCP) — open standard specification
- GitHub: wiroai/Wiro-MCP — source code & self-hosting instructions
- npm: @wiro-ai/wiro-mcp — npm package
- Wiro Model Catalog — browse all available models
- Create API Key — get started in seconds
Setup
Connect your AI assistant to Wiro's MCP server. Pick your client:
Cursor
- Open MCP Settings — Use
Cmd+Shift+P(Ctrl+Shift+Pon Windows) and search for "Open MCP settings". - Add the Wiro server — Add the following to your
mcp.jsonfile:Signature Auth (if your project uses signature-based authentication):
{ "mcpServers": { "wiro": { "url": "https://mcp.wiro.ai/v1", "headers": { "Authorization": "Bearer YOUR_API_KEY:YOUR_API_SECRET" } } } }API Key Only Auth (if your project uses API Key Only authentication):
{ "mcpServers": { "wiro": { "url": "https://mcp.wiro.ai/v1", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } - Restart Cursor — Save the file and restart Cursor to activate the connection.
Claude Code
Run this command in your terminal:
claude mcp add --transport http wiro \
https://mcp.wiro.ai/v1 \
--header "Authorization: Bearer YOUR_API_KEY:YOUR_API_SECRET"
That's it. Claude Code will now have access to all Wiro tools.
Claude Desktop
Add the following to your claude_desktop_config.json:
{
"mcpServers": {
"wiro": {
"url": "https://mcp.wiro.ai/v1",
"headers": {
"Authorization": "Bearer YOUR_API_KEY:YOUR_API_SECRET"
}
}
}
}
Windsurf
Open Settings → MCP and add a new server:
{
"mcpServers": {
"wiro": {
"serverUrl": "https://mcp.wiro.ai/v1",
"headers": {
"Authorization": "Bearer YOUR_API_KEY:YOUR_API_SECRET"
}
}
}
}
Other MCP Clients
The Wiro MCP server uses the Streamable HTTP transport at:
https://mcp.wiro.ai/v1
Authentication is via the Authorization header:
Authorization: Bearer YOUR_API_KEY:YOUR_API_SECRET
Or for API Key Only auth: Bearer YOUR_API_KEY
Credentials are sent as plain text — no base64 encoding needed. Any MCP client that supports Streamable HTTP transport can connect.
Authentication
The MCP server supports both Wiro authentication types. Your project's auth type determines what credentials you provide.
Signature-Based (Recommended)
More secure. Requires both API key and API secret. Pass them as plain text separated by a colon:
Authorization: Bearer YOUR_API_KEY:YOUR_API_SECRET
API Key Only
Simpler. Only requires the API key:
Authorization: Bearer YOUR_API_KEY
No base64 encoding needed. Credentials are sent per-request and are never stored. The server is fully stateless.
Available Tools
The MCP server exposes 11 tools organized in four categories. Your AI assistant picks the right tool automatically.
Model slugs: Use the clean/lowercase format owner/model (e.g. openai/sora-2, wiro/virtual-try-on). These correspond to the cleanslugowner/cleanslugproject values returned by search_models.
Discovery
| Tool | Description |
|---|---|
search_models | Search Wiro's model catalog by keyword, category, or owner |
get_model_schema | Get the full parameter schema and pricing for any model |
recommend_model | Describe what you want to build and get model recommendations ranked by relevance |
explore | Browse curated AI models organized by category — featured, recently added, popular |
Execution
| Tool | Description |
|---|---|
run_model | Run any model and wait for the result, or submit and get a task token |
Task Management
| Tool | Description |
|---|---|
get_task | Check task status, outputs, cost, and elapsed time |
get_task_price | Get the cost of a completed task — shows whether it was billed and the total charge |
cancel_task | Cancel a task still in the queue |
kill_task | Kill a task that is currently running |
Utility
| Tool | Description |
|---|---|
upload_file | Upload a file from a URL to Wiro for use as model input |
search_docs | Search the Wiro documentation for guides, API references, and examples |
Examples
Generate an image
"Generate a photorealistic image of a mountain lake at golden hour"
The assistant will use search_models → get_model_schema → run_model and return the image URL.
Generate a video
"Create a 5-second cinematic video of a drone shot over mountains using Kling V3"
The assistant will use get_model_schema → run_model (wait=false) → get_task to poll for the result.
Find models
"What models are available for text-to-video?"
The assistant will call search_models with categories filter.
How It Works
The MCP server is stateless. Each request is fully isolated:
- Your AI assistant sends a request to
mcp.wiro.ai/v1with your credentials - The server calls the Wiro API on your behalf
- Results are returned to your assistant
All tools are dynamic — they fetch model data at runtime. New models are instantly available via MCP.
Tool Reference
search_models
Search Wiro's model catalog by keyword, category, owner, or any combination. Calls POST /Tool/List on the Wiro API.
| Parameter | Type | Description |
|---|---|---|
search | string (optional) | Free-text search, e.g. "flux", "video generation" |
categories | string[] (optional) | Filter by category: text-to-image, text-to-video, image-to-video, llm, text-to-speech, image-editing, etc. |
slugowner | string (optional) | Filter by model owner slug, e.g. "openai", "stability-ai", "klingai" |
start | number (optional) | Pagination offset (default 0) |
limit | number (optional) | Max results (default 20, max 100) |
Returns a list of models with their cleanslugowner/cleanslugproject (the slug you pass to other tools), title, description, categories, and pricing.
get_model_schema
Get the full parameter schema for a specific model. Calls POST /Tool/Detail on the Wiro API.
| Parameter | Type | Description |
|---|---|---|
model | string | Model slug using clean/lowercase format: "owner/model". Use cleanslugowner/cleanslugproject from search_models. Examples: "openai/sora-2", "black-forest-labs/flux-2-pro", "wiro/virtual-try-on" |
Returns the model's parameter groups, each containing items with id, type (text, textarea, select, range, fileinput, etc.), label, required, options, default, and note. Also includes pricing information. Use these to construct the params object for run_model.
recommend_model
Describe what you want to create and get model recommendations ranked by relevance. Calls POST /Tool/List on the Wiro API with relevance sorting.
| Parameter | Type | Description |
|---|---|---|
task | string | What you want to do, e.g. "generate a photorealistic portrait", "upscale an image to 4K", "transcribe audio to text" |
Returns a list of recommended models with slugs, descriptions, categories, and pricing — sorted by relevance to your task.
explore
Browse curated AI models on Wiro, organized by category. Calls POST /Tool/Explore on the Wiro API. No parameters required.
Returns models grouped into curated sections like "Recently Added", "Image Generation", "Video", etc. Each model includes its slug, description, categories, and rating. Use this to discover what's available without searching.
run_model
Run any AI model on Wiro. Calls POST /Run/{owner}/{model} on the Wiro API.
| Parameter | Type | Description |
|---|---|---|
model | string | Model slug in clean/lowercase format. Same as get_model_schema. Examples: "openai/sora-2", "klingai/kling-v3" |
params | object | Model-specific parameters as key-value pairs. Use get_model_schema to discover accepted fields. Common: prompt, negativePrompt, width, height, aspectRatio |
wait | boolean (optional) | If true (default), polls POST /Task/Detail until the task completes and returns the result. If false, returns the task token immediately for async monitoring via get_task. |
timeout_seconds | number (optional) | Max seconds to wait (default 120, max 600). Only applies when wait=true. |
When wait=true, returns the final task result including pexit (exit code, "0" = success), outputs (CDN URLs for generated files), and debugoutput (LLM text responses).
When wait=false, returns taskid and tasktoken — use get_task to check progress.
get_task
Check task status and get results. Calls POST /Task/Detail on the Wiro API.
| Parameter | Type | Description |
|---|---|---|
tasktoken | string (optional) | The task token returned from run_model |
taskid | string (optional) | The task ID (alternative to tasktoken) |
Returns the task's current status, pexit (process exit code), outputs (file URLs), debugoutput (LLM responses), elapsedseconds, and totalcost.
Determining success: Check pexit — "0" means success, any other value means failure. For LLM models, the response text is in debugoutput, not outputs. See Tasks for the full task lifecycle.
get_task_price
Get the cost of a completed task. Calls POST /Task/Detail on the Wiro API and returns billing information.
| Parameter | Type | Description |
|---|---|---|
tasktoken | string (optional) | The task token returned from run_model |
taskid | string (optional) | The task ID (alternative to tasktoken) |
Returns the task's billing status, total cost, and duration. Only successful tasks (pexit: "0") are billed — failed tasks show $0 with a clear explanation that they were not charged.
cancel_task
Cancel a task that is still queued (before worker assignment). Calls POST /Task/Cancel on the Wiro API.
| Parameter | Type | Description |
|---|---|---|
tasktoken | string | The task token to cancel |
Tasks that have already been assigned to a worker cannot be cancelled — use kill_task instead.
kill_task
Kill a task that is currently running (after worker assignment). Calls POST /Task/Kill on the Wiro API.
| Parameter | Type | Description |
|---|---|---|
tasktoken | string | The task token to kill |
The worker will stop processing and the task will move to task_cancel status.
upload_file
Upload a file from a URL to Wiro for use as model input. Downloads the file and uploads it via POST /File/Upload.
| Parameter | Type | Description |
|---|---|---|
url | string | URL of the file to upload (image, audio, video, document) |
file_name | string (optional) | Custom filename. If not provided, derived from the URL. |
Returns the uploaded file's Wiro URL, which can be passed to any model parameter that accepts a file.
search_docs
Search the Wiro documentation for guides, API references, and code examples.
| Parameter | Type | Description |
|---|---|---|
query | string | What you're looking for, e.g. "how to upload a file", "websocket", "authentication", "LLM streaming" |
Returns relevant documentation sections matching your query.
FAQ
What models can I use?
All models in the Wiro catalog.
Is my API key stored?
No. The server is fully stateless. Credentials are sent per-request and never stored.
Does it cost extra?
No. The MCP server is free. You pay for model runs at standard pricing.
Can I self-host?
Yes. See Self-Hosted MCP for instructions.
Self-Hosted MCP
Run the Wiro MCP server locally on your own machine using npx.
Quick Start
Add to your AI assistant's MCP config:
{
"mcpServers": {
"wiro": {
"command": "npx",
"args": ["-y", "@wiro-ai/wiro-mcp"],
"env": {
"WIRO_API_KEY": "your-api-key",
"WIRO_API_SECRET": "your-api-secret"
}
}
}
}
That's it. Your assistant now has access to all 70+ Wiro AI models.
Setup
Add the self-hosted MCP server to your AI assistant:
Cursor
Open MCP settings (Cmd+Shift+P → "Open MCP settings") and add the config above.
Claude Code
claude mcp add wiro -- npx -y @wiro-ai/wiro-mcp
Then set WIRO_API_KEY and WIRO_API_SECRET environment variables.
Claude Desktop
Add to claude_desktop_config.json using the Quick Start config above.
Windsurf
Add to your MCP settings using the Quick Start config above.
Other Clients
Any MCP client that supports the stdio transport can run the self-hosted server. Use the Quick Start config with your client's MCP configuration format. The command is npx -y @wiro-ai/wiro-mcp with WIRO_API_KEY and WIRO_API_SECRET as environment variables.
Authentication
Signature-Based (Recommended)
Provide both API key and secret:
WIRO_API_KEY=your-api-key
WIRO_API_SECRET=your-api-secret
API Key Only
Omit WIRO_API_SECRET:
WIRO_API_KEY=your-api-key
Available Tools
The self-hosted server provides the same 11 tools as the hosted MCP server. Your AI assistant picks the right tool automatically.
Model slugs: Use the clean/lowercase format owner/model (e.g. openai/sora-2, wiro/virtual-try-on). These correspond to cleanslugowner/cleanslugproject values returned by search_models.
Discovery
| Tool | API Endpoint | What it does |
|---|---|---|
search_models | POST /Tool/List | Search and browse AI models by keyword, category, or owner. Returns model slugs, titles, descriptions, categories, and pricing. |
get_model_schema | POST /Tool/Detail | Get full parameter schema and pricing for any model — parameter names, types, options, defaults, and required fields. |
recommend_model | POST /Tool/List | Describe what you want to build and get model recommendations ranked by relevance. |
explore | POST /Tool/Explore | Browse curated AI models organized by category. No parameters needed. |
Execution
| Tool | API Endpoint | What it does |
|---|---|---|
run_model | POST /Run/{owner}/{model} | Run any model with parameters. With wait=true (default), polls until complete and returns outputs. With wait=false, returns task token for async monitoring. |
Task Management
| Tool | API Endpoint | What it does |
|---|---|---|
get_task | POST /Task/Detail | Check task status, pexit (exit code), outputs (CDN URLs), debugoutput (LLM responses), elapsed time, and cost. pexit="0" means success. |
get_task_price | POST /Task/Detail | Get the cost of a completed task. Shows whether it was billed and the total charge. Only successful tasks (pexit: "0") are billed. |
cancel_task | POST /Task/Cancel | Cancel a task still in queue (before worker assignment). |
kill_task | POST /Task/Kill | Kill a running task (after worker assignment). Task moves to task_cancel status. |
Utility
| Tool | API Endpoint | What it does |
|---|---|---|
upload_file | POST /File/Upload | Upload a file from a URL to Wiro for use as model input. |
search_docs | Wiro Docs | Search the Wiro documentation for guides, API references, and examples. |
See the Wiro MCP Server page for detailed parameter tables and examples.
Environment Variables
| Variable | Required | Description |
|---|---|---|
WIRO_API_KEY | Yes | Your Wiro project API key |
WIRO_API_SECRET | No | API secret (for signature auth) |
WIRO_API_BASE_URL | No | Override API URL (default: https://api.wiro.ai/v1) |
GitHub & npm
- GitHub: github.com/wiroai/Wiro-MCP
- npm: @wiro-ai/wiro-mcp
Using as a Library
import { createMcpServer, WiroClient } from '@wiro-ai/wiro-mcp';
const client = new WiroClient('your-api-key', 'your-api-secret');
const server = createMcpServer(client);
Self-Hosting on Your Server
git clone https://github.com/wiroai/Wiro-MCP.git
cd Wiro-MCP
npm install
npm run build
export WIRO_API_KEY="your-api-key"
export WIRO_API_SECRET="your-api-secret"
node dist/index.js
Requires Node.js 18 or later. Create a project to get API keys.
n8n Wiro Integration
Use all Wiro AI models directly in your n8n workflows — video, image, audio, LLM, 3D, and more.
Overview
n8n is a powerful workflow automation platform. The Wiro AI community node gives you access to all Wiro AI models as individual nodes you can drag and drop into any workflow.
Each model is a separate node — so you get dedicated parameters, descriptions, and output handling for every model without any configuration hassle.
Links
- npm: @wiro-ai/n8n-nodes-wiroai
- GitHub: wiroai/n8n-nodes-wiroai
- n8n Community Nodes Installation Guide
- Wiro Model Catalog — browse all available models
Available Model Categories
| Category | Models | Examples |
|---|---|---|
| Video Generation | Text-to-video, image-to-video | Sora 2, Veo 3, Kling V3, Seedance, Hailuo, PixVerse, Runway |
| Image Generation | Text-to-image, style transfer | Imagen V4, Flux 2 Pro, Seedream, Nano Banana, SDXL |
| Image Editing | Try-on, face swap, background removal | Virtual Try-On, Face Swap, Inpainting, Style Transfer |
| Audio & Speech | TTS, STT, voice clone, music | ElevenLabs TTS, Gemini TTS, Whisper STT, Voice Clone |
| LLM Chat | Chat completion, RAG | GPT-5, Gemini 3, Qwen 3.5, RAG Chat |
| 3D Generation | Image/text to 3D | Trellis 2, Hunyuan3D 2.1 |
| Translation | Multi-language with image support | Gemma-based (4B, 12B, 27B) |
| E-Commerce | Product photos, ads, templates | Product Photoshoot, Shopify Templates, UGC Creator |
| HR Tools | CV analysis, job descriptions | CV Evaluator, Resume Parser, Culture Fit |
Installation
Install the community node package in your n8n instance:
Via n8n UI (Recommended)
- Open your n8n instance Navigate to your self-hosted or cloud n8n dashboard
- Go to Settings Open Settings → Community Nodes
- Install the node Click Install a community node and enter:
@wiro-ai/n8n-nodes-wiroai - Confirm Click Install and wait for completion
Via Command Line
npm install @wiro-ai/n8n-nodes-wiroai
Restart n8n after installation.
Authentication
The node supports both Wiro authentication methods:
- Add credentials Go to Credentials → Add new → Wiro API in n8n
- Select auth method Signature-Based — enter API key + secret (recommended) | API Key Only — enter API key only
- Save Click Save to store your credentials
Get your credentials at wiro.ai/panel/project.
Usage
Each Wiro model appears as a separate node in the n8n node picker. Search for "Wiro" or the model name to find it.
Example: Generate a Video with Sora 2
- Add the Wiro - Sora 2 Pro node to your workflow
- Connect your Wiro credentials
- Set the parameters:
- Prompt:
A cat astronaut floating in space - Seconds:
8 - Resolution:
1080p
- Prompt:
- Run the workflow
The node returns the task result with output URLs:
{
"taskid": "abc123",
"status": "completed",
"url": "https://cdn1.wiro.ai/xyz/0.mp4"
}
Example: Transcribe Audio with Whisper
- Add the Wiro - Whisper Large 3 node
- Connect an audio file from a previous node or provide a URL
- Select language and output format
- Run — get the transcribed text
Example: LLM Chat with GPT-5
- Add the Wiro - GPT-5 node
- Set your prompt and system instructions
- Run — get the AI response
Compatibility
| Requirement | Version |
|---|---|
| n8n | v1.0+ |
| Node.js | v18+ |
| Package | @wiro-ai/n8n-nodes-wiroai@latest |