each::labs API Documentation
This interactive API reference is deprecated. Use
docs.eachlabs.ai
for canonical Eachlabs API documentation. Machine-readable specs remain
available below.
Machine-readable API specifications:
Models API (OpenAPI 3.0.3)
openapi: 3.0.3
info:
title: each::labs Official API
description: |
Official each::labs API. Existing resources remain under `/v1`; cursor-based
execution history is additive under `/v2`.
This API provides access to our comprehensive AI model catalog and other resources.
## Public contract notes
- Public API routes handled by request logging include `X-Request-Id` for
support and log correlation. Clients may send `X-Request-Id`; otherwise
api-service generates one. Root, health, and metrics endpoints are excluded.
- REST error bodies keep the backward-compatible JSON shape used by existing
clients. Most errors include at least `error`; some shared error paths also
include `status` and `details`. Use the HTTP status code as the primary
programmatic signal unless an endpoint documents a richer envelope.
- Rate-limit and retry headers are endpoint-dependent. If a response includes
`Retry-After`, honor it before retrying; otherwise use bounded exponential
backoff for retryable 429/5xx responses.
- api-service exposes single workflow trigger through
`/v1/workflows/trigger/{workflowID}/{versionID}` and bulk workflow trigger
(up to 10 executions per call) through
`/v1/workflows/bulk-trigger/{workflowID}/{versionID}`. Both pin the version
in the path.
version: 1.0.0
contact:
name: API Support
email: support@eachlabs.ai
x-logo:
url: './logo-white.svg'
altText: 'each::labs'
servers:
- url: https://api.eachlabs.ai
description: Production server
tags:
- name: AI Models
description: Endpoints for listing and retrieving AI models and other resources.
- name: AI Models Prediction
description: Endpoints for managing model predictions
- name: Uploads
description: Endpoints for uploading files that can be used as model inputs.
- name: Executions
description: Endpoints for listing execution history.
- name: Billing
description: API-key-authenticated organization billing reads.
- name: Workflows
description: Endpoints for triggering workflow executions through api-service.
- name: Public Workflows
description: Anonymous endpoints for reading and triggering publicly shared workflow versions.
- name: OpenAI Compatibility
description: OpenAI-compatible endpoints for LLM Router requests.
- name: Webhooks
description: |
Endpoints for retrieving webhook information and delivery history.
**Note:** Currently, webhooks are only supported for Workflows V2. We are actively expanding webhook support to other services and will update this documentation as new integrations become available.
- name: MCP OAuth
description: |
OAuth 2.0 authorization-server endpoints for the MCP product. These are
served on the `https://mcp.eachlabs.ai` host (not api.eachlabs.ai) and
are public PKCE endpoints — no API key or bearer token. crm-service is
the authorization authority; api-service is the protocol edge.
paths:
/v1/billing/balance:
get:
tags:
- Billing
summary: Get organization balance
description: |
Returns the USD balance for the organization bound to the authenticated
API key. The request cannot select another organization.
Balance reads are eventually consistent with top-ups because the
existing organization projection is cached and ledger synchronization
may add delay. No fixed refresh time is guaranteed. Retry with bounded
backoff while staying within the endpoint limit.
The limit is 3 requests per second with burst 3 for each authenticated
organization in each api-service process. It is process-local, not a
fleet-wide organizational quota. A rejected request does not read the
balance and returns `Retry-After: 1`.
operationId: getBillingBalance
security:
- BearerAuth: []
responses:
'200':
description: Organization balance retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/BillingBalance'
example:
balance_usd: 53.475
'401':
$ref: '#/components/responses/Unauthorized'
'429':
description: Process-local organization request limit exceeded
headers:
Retry-After:
description: Seconds to wait before retrying.
required: true
schema:
type: string
enum: ['1']
content:
application/json:
schema:
$ref: '#/components/schemas/BillingBalanceError'
example:
status: 429
error: too many requests
'503':
description: Balance data is temporarily unavailable; no zero balance is fabricated
content:
application/json:
schema:
$ref: '#/components/schemas/BillingBalanceError'
example:
status: 503
error: service unavailable
/v1/models:
get:
tags:
- AI Models
summary: List AI models
description: Retrieve a paginated list of AI models with optional filters
operationId: listModels
parameters:
- name: name
in: query
schema:
type: string
description: Filter by model name (case-insensitive search)
example: "nano-banana-pro-edit"
- name: limit
in: query
schema:
type: integer
default: 50
minimum: 1
maximum: 500
description: Maximum number of results to return
example: 20
- name: offset
in: query
schema:
type: integer
default: 0
minimum: 0
description: Number of results to skip for pagination
example: 0
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Model'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/models/{slug}:
get:
tags:
- AI Models
summary: Get model details
description: |
Retrieve detailed information about a specific model by slug.
The nullable `cost` field contains compact estimated price guidance;
final settled cost is available as `metrics.cost` after execution.
operationId: getModelByPath
parameters:
- name: slug
in: path
required: true
schema:
type: string
description: Model slug identifier
example: "nano-banana-pro-edit"
responses:
'200':
description: Model details retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ModelDetail'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/models/{slug}/schemas/openapi:
get:
tags:
- AI Models
summary: Get model OpenAPI schema
description: |
Returns an OpenAPI 3.0 document describing the model's input
contract (derived from its request_schema) and the prediction
lifecycle endpoints. Intended for the model API tab and for
clients that consume a per-model machine-readable spec.
operationId: getModelOpenAPI
parameters:
- name: slug
in: path
required: true
schema:
type: string
description: Model slug identifier
example: "nano-banana-pro-edit"
responses:
'200':
description: OpenAPI 3.0 document for the model
content:
application/json:
schema:
type: object
description: An OpenAPI 3.0 document.
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/model:
get:
tags:
- AI Models
summary: Get model details (deprecated)
description: |
Deprecated: use `GET /v1/models/{slug}` instead. Retained for
backwards compatibility; returns the same payload, including the
nullable compact `cost` estimate.
deprecated: true
operationId: getModel
parameters:
- name: slug
in: query
required: true
schema:
type: string
description: Model slug identifier
example: "nano-banana-pro-edit"
responses:
'200':
description: Model details retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ModelDetail'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/llm-router/model-catalog:
get:
tags:
- AI Models
summary: Get eachlabs-llm-router model catalog
description: Retrieve the curated visible model list and generated model enum schema for eachlabs-llm-router.
operationId: getLLMRouterModelCatalog
responses:
'200':
description: LLM router model catalog retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/LLMRouterModelCatalog'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/prediction:
post:
tags:
- AI Models Prediction
summary: Create Model Prediction
description: |
Create a new prediction for a specific model.
operationId: createPrediction
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePredictionRequest'
example:
model: "flux-1-1-pro"
input:
prompt: "A beautiful sunset over the ocean with vibrant colors"
aspect_ratio: "16:9"
webhook_url: "https://your-app.com/webhook"
webhook_secret: "your-secret-key"
responses:
'200':
description: Prediction created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePredictionResponse'
example:
status: "success"
message: "Prediction created successfully"
predictionID: "abc123-def456-ghi789"
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/prediction/run:
post:
tags:
- AI Models Prediction
summary: Run Model Prediction Synchronously
description: |
Create a prediction and wait for the result. This compatibility endpoint is only supported
for models with synchronous execution enabled; unsupported models return a bad request error.
operationId: runPrediction
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePredictionRequest'
example:
model: "eachlabs-llm-router"
input:
model: "openai/gpt-4o-mini"
messages:
- role: "user"
content: "Write a one sentence caption."
responses:
'200':
description: Prediction completed or failed synchronously
content:
application/json:
schema:
$ref: '#/components/schemas/RunPredictionResponse'
example:
status: "success"
message: "Prediction completed successfully"
predictionID: "abc123-def456-ghi789"
output:
text: "A cinematic sunset over the ocean."
error: ""
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'422':
$ref: '#/components/responses/UnprocessableEntity'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/prediction/{id}:
get:
tags:
- AI Models Prediction
summary: Get Model Prediction
description: |
Retrieve the status and results of a model prediction by its ID.
Prediction statuses are `created`, `starting`, or `processing` while work is in flight.
Terminal statuses are `success`, `error`, and `cancelled`.
operationId: getPrediction
parameters:
- name: id
in: path
required: true
schema:
type: string
description: Prediction ID
example: "abc123-def456-ghi789"
security:
- BearerAuth: []
responses:
'200':
description: Prediction retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Prediction'
example:
id: "abc123-def456-ghi789"
input:
prompt: "A beautiful sunset over the ocean with vibrant colors"
aspect_ratio: "16:9"
status: "success"
output: "https://storage.example.com/predictions/abc123/image.jpg"
logs: null
metrics:
predict_time: 12.5
cost: 0.05
urls:
cancel: "https://api.eachlabs.ai/v1/prediction/abc123-def456-ghi789/cancel"
get: "https://api.eachlabs.ai/v1/prediction/abc123-def456-ghi789"
'404':
description: Prediction not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Prediction not found"
'500':
$ref: '#/components/responses/InternalServerError'
/v1/prediction/{id}/cancel:
post:
tags:
- AI Models Prediction
summary: Cancel Model Prediction
description: |
Cancel a running prediction by ID. Cancellation is record-only: the Eachlabs
execution record is set to `cancelled` for every provider and no upstream
cancellation is attempted — in-flight provider work runs to completion
upstream and its result is discarded.
operationId: cancelPrediction
security:
- BearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
description: Prediction ID
example: "abc123-def456-ghi789"
responses:
'200':
description: Prediction cancellation accepted
content:
application/json:
schema:
$ref: '#/components/schemas/CancelPredictionResponse'
example:
status: "success"
message: "Prediction canceled successfully"
predictionID: "abc123-def456-ghi789"
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/upload/presign:
post:
tags:
- Uploads
summary: Create Presigned Upload URL
description: |
Create a short-lived presigned URL for uploading media files. Upload the raw file bytes
to `presigned_url` using every header returned in `required_headers`, then pass
`public_url` as a model input.
operationId: presignUpload
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PresignUploadRequest'
example:
content_type: "image/png"
file_type: "image"
responses:
'200':
description: Presigned upload URL created
content:
application/json:
schema:
$ref: '#/components/schemas/PresignUploadResponse'
example:
id: "0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0"
presigned_url: "https://eachlabs-storage.s3.amazonaws.com/uploads/...?X-Amz-Signature=..."
public_url: "https://cdn-us.eachlabs.ai/uploads/0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0.png"
expires_at: "2026-04-27T18:15:00Z"
required_headers:
x-amz-meta-file-id: "0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0"
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/executions:
get:
tags:
- Executions
summary: List executions (deprecated)
deprecated: true
description: |
Deprecated: use `GET /v2/executions` for cursor-based traversal.
V1 remains available for shallow compatibility.
Retrieve execution history for the authenticated API key's organization
with optional filters.
Model prediction terminal statuses include `success`, `error`, and `cancelled`;
workflow executions can surface `running`, `completed`, `failed`, or `cancelled`
depending on the originating backend. The `status` filter accepts the
four tokens listed on the parameter; unrecognized tokens return 400.
Results cover every API key in the organization that owns the
authenticated key.
Pagination: `offset` is retained for shallow compatibility but is
deprecated for history traversal. Use `GET /v2/executions` and its
opaque cursor to traverse matching history without an implicit total.
Computing an exact total over a large execution history is expensive,
so `total_count` can be capped and is then only a lower bound.
When `from` is omitted, the window defaults to the 90 days ending at
`to` (or now).
operationId: listExecutions
security:
- BearerAuth: []
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
description: Maximum number of executions to return
example: 20
- name: offset
in: query
deprecated: true
schema:
type: integer
minimum: 0
default: 0
description: >-
Deprecated shallow-page offset; use GET /v2/executions for traversal.
The maximum requested prefix is 1,000 rows, evaluated after limit
normalization as offset <= 1000 - appliedLimit. Requests beyond
that boundary return {"error":"V1 execution pagination is limited
to 1000 rows; use GET /v2/executions with cursor pagination"}.
example: 0
- name: model
in: query
schema:
type: string
description: Filter executions by model slug
example: "flux-1-1-pro"
- name: status
in: query
schema:
type: string
description: |
Comma-separated status filter. Each token must be one of `created`,
`starting`, `success`, or `error` (lowercase); any other token
returns 400 naming the allowed set. An empty or absent parameter
applies no status filter.
example: "success,error"
- name: workflow_id
in: query
schema:
type: string
description: Filter by workflow ID
example: "wf_abc123"
- name: workflow_execution_id
in: query
schema:
type: string
description: Filter by workflow execution ID
example: "wf_exec_abc123"
- name: error_classification
in: query
schema:
type: string
enum:
- content_moderation
- execution_timeout
- invalid_user_input
- invalid_model_config
- provider_auth
- provider_error
- provider_rate_limit
- provider_unavailable
- internal_error
- unknown
description: |
Comma-separated canonical error classification filter. Restricts
results to terminal error executions whose classification is in the
set; an unrecognized value returns 400. Each value must be one of
the listed enum members.
example: "provider_rate_limit,provider_unavailable"
- name: from
in: query
schema:
type: string
format: date-time
description: |
Start of creation time window, as RFC 3339. When omitted, defaults
to 90 days before `to` (or before now when `to` is also omitted).
example: "2026-01-01T00:00:00Z"
- name: to
in: query
schema:
type: string
format: date-time
description: End of creation time window, as RFC 3339
example: "2026-01-31T23:59:59Z"
responses:
'200':
description: List of executions
headers:
Deprecation:
$ref: '#/components/headers/Deprecation'
Link:
$ref: '#/components/headers/DeprecationLink'
content:
application/json:
schema:
$ref: '#/components/schemas/ListExecutionsResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'408':
$ref: '#/components/responses/RequestTimeout'
'500':
$ref: '#/components/responses/InternalServerError'
'502':
$ref: '#/components/responses/BadGateway'
/v2/executions:
get:
tags:
- Executions
summary: List executions with cursor pagination
description: |
Returns a total-free execution page for the authenticated API key's
organization, ordered by `created_at` descending. Continue with
`next_cursor` while `has_more` is true. Cursors are opaque and bound to
the authenticated scope, normalized filters, order, and applied limit.
Changing any of those inputs requires restarting without a cursor.
When `from` is omitted, the window defaults to the 90 days ending at
`to` (or now).
operationId: listExecutionsV2
security:
- BearerAuth: []
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
description: Maximum number of executions to return
- name: cursor
in: query
schema:
type: string
description: Opaque cursor returned as `next_cursor` by the previous page
- name: model
in: query
schema:
type: string
description: Filter executions by public model slug
- name: status
in: query
schema:
type: string
description: |
Comma-separated status filter. Each token must be one of `created`,
`starting`, `success`, or `error`; any other token returns 400.
example: "success,error"
- name: workflow_id
in: query
schema:
type: string
description: Filter by workflow ID
- name: workflow_execution_id
in: query
schema:
type: string
description: Filter by workflow execution ID
- name: error_classification
in: query
style: form
explode: false
schema:
type: array
items:
type: string
enum:
- content_moderation
- execution_timeout
- invalid_user_input
- invalid_model_config
- provider_auth
- provider_error
- provider_rate_limit
- provider_unavailable
- internal_error
- unknown
description: Comma-separated canonical error classifications
example:
- content_moderation
- provider_error
- name: from
in: query
schema:
type: string
format: date-time
description: Start of creation time window, as RFC 3339
- name: to
in: query
schema:
type: string
format: date-time
description: End of creation time window, as RFC 3339
responses:
'200':
description: Total-free cursor page
content:
application/json:
schema:
$ref: '#/components/schemas/ListExecutionsV2Response'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'408':
$ref: '#/components/responses/RequestTimeout'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/workflows/executions/{executionID}:
get:
tags:
- Executions
summary: Get workflow execution
description: |
Retrieve the detail of a single workflow execution: status, inputs,
per-step outputs, final output, error, and `total_cost` (the summed
cost of the run's model executions). Scoped to the authenticated
caller's organization; returns 404 when the execution is missing or
owned by another organization.
operationId: getWorkflowExecution
security:
- BearerAuth: []
parameters:
- name: executionID
in: path
required: true
schema:
type: string
description: The workflow execution id.
example: "b3b1f1a0-0000-0000-0000-000000000000"
responses:
'200':
description: Workflow execution detail
content:
application/json:
schema:
type: object
properties:
execution_id:
type: string
workflow_id:
type: string
version_id:
type: string
description: Workflow version the execution ran against.
bulk_id:
type: string
status:
type: string
example: completed
started_at:
type: string
format: date-time
description: RFC3339 timestamp; omitted until the run starts.
completed_at:
type: string
format: date-time
description: RFC3339 timestamp; omitted until the run finishes.
inputs:
type: object
additionalProperties: true
step_outputs:
type: object
additionalProperties: true
output:
description: Free-form final output (object, array, or scalar).
workflow_output:
type: object
additionalProperties: true
error:
type: string
error_cause:
type: string
total_cost:
type: number
format: double
nullable: true
description: Summed cost of the run's model executions. Present only once the execution is terminal (completed/failed/cancelled); omitted while running or if unavailable.
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/chat/completions:
post:
tags:
- OpenAI Compatibility
summary: Create Chat Completion
description: |
OpenAI-compatible chat completions endpoint backed by Eachlabs LLM Router. Use
`Authorization: Bearer <API_KEY>` and point an OpenAI-compatible client at
`https://api.eachlabs.ai/v1`.
This endpoint uses the OpenAI-compatible error envelope rather than the REST
error body described in the global contract notes.
operationId: createChatCompletion
security:
- BearerAuth: []
parameters:
- name: X-Eachlabs-Webhook-Url
in: header
required: false
schema:
type: string
format: uri
description: Optional webhook URL for asynchronous result delivery.
example: "https://your-app.com/webhook"
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionRequest'
example:
model: "openai/gpt-4o-mini"
messages:
- role: "user"
content: "Write a one sentence caption."
responses:
'200':
description: "OpenAI-compatible chat completion. Requests with `stream: true` return Server-Sent Events."
content:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionResponse'
text/event-stream:
schema:
type: string
description: |
Server-Sent Events stream for requests with `stream: true`.
Events use OpenAI-compatible `chat.completion.chunk` payloads
and the stream terminates with `data: [DONE]`.
example: |
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"A cinematic"},"finish_reason":null}]}
data: [DONE]
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'401':
description: Authentication failed
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'402':
description: Insufficient balance
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
/v1/responses:
post:
tags:
- OpenAI Compatibility
summary: Create a response
description: |
OpenAI-compatible Responses API backed by Eachlabs LLM Router. This
independently feature-gated endpoint is disabled by default during rollout.
Model availability comes only from the qualified model catalog after
qualification; this contract does not imply current support for any
particular model.
Requests are stateless: `store` and `background` may be omitted or false;
`previous_response_id` is unsupported. Only custom function tools with strict
JSON schemas are accepted. Built-in tools, provider-selection extensions,
webhooks, and stateful response chaining are rejected before provider
dispatch. A buffered success is the canonical Responses object. With
`stream: true`, the response is `text/event-stream` and relays canonical
Responses events without converting them into Chat Completions `choices`.
Authentication, balance, request validation, and stream establishment errors
return the documented JSON error envelope before SSE headers are sent.
operationId: createResponse
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ResponsesRequest'
example:
model: "qualified-model-id"
input: "Write a one sentence caption."
store: false
stream: false
responses:
'200':
description: Canonical buffered Responses object or Responses SSE stream
content:
application/json:
schema:
$ref: '#/components/schemas/ResponseObject'
text/event-stream:
schema:
type: string
description: |
Canonical Responses lifecycle events for `stream: true`, preserving
event names and sequence order. Events may include response.created,
response.in_progress, output/content text, refusal, reasoning, and
function-call argument events, followed by response.completed,
response.incomplete, response.failed, or error. A provider `[DONE]`
sentinel may appear only after a canonical terminal event.
example: |
event: response.created
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_example","object":"response","created_at":1777777777,"status":"in_progress","model":"qualified-model-id","output":[]}}
event: response.completed
data: {"type":"response.completed","sequence_number":1,"response":{"id":"resp_example","object":"response","created_at":1777777777,"status":"completed","model":"qualified-model-id","output":[],"usage":{"input_tokens":1,"output_tokens":1,"total_tokens":2,"input_tokens_details":{"cached_tokens":0},"output_tokens_details":{"reasoning_tokens":0}}}}
data: [DONE]
'400':
description: Invalid or unsupported request
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'401':
description: Authentication failed
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'402':
description: Insufficient balance
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'429':
description: Provider or router rate limit
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'503':
description: AI model service temporarily unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'504':
description: Request timed out before a response or stream was established
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
/v1/audio/transcriptions:
post:
tags:
- OpenAI Compatibility
summary: Transcribe an audio file
description: |
OpenAI-compatible bounded file transcription. Use `Authorization: Bearer
<API_KEY>` and `multipart/form-data`. The initial profile supports files up
to 25 MB and `json` or `verbose_json` responses. Unsupported OpenAI fields
are rejected explicitly.
The accepted model set is a curated per-environment allowlist that a model
joins only after passing contract qualification; `openai/whisper-large-v3`
is the launch model. Requesting a model outside the allowlist returns a 400
without dispatching audio.
This endpoint is independently feature-gated during preview rollout. A
disabled deployment returns an OpenAI-shaped 404 without dispatching audio.
It is synchronous and does not use prediction polling or streaming.
operationId: createAudioTranscription
security:
- BearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/AudioTranscriptionRequest'
responses:
'200':
description: OpenAI-compatible JSON transcription response
headers:
X-Generation-Id:
description: Upstream generation identifier when available.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/AudioTranscriptionResponse'
'400':
description: Invalid multipart input or unsupported field/model/format
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'401':
description: Authentication failed
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'402':
description: Insufficient balance
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'404':
description: Capability disabled for this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'413':
description: Audio file exceeds 25 MB
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'429':
description: Provider rate limit reached
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'503':
description: Authentication or transcription service temporarily unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'504':
description: Transcription request timed out
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
/v1/audio/speech:
post:
tags:
- OpenAI Compatibility
summary: Stream synthesized speech
description: |
OpenAI-compatible text-to-speech. Use `Authorization: Bearer <API_KEY>`
and a JSON body. The response is a raw audio byte stream forwarded
progressively (no prediction polling); the negotiated content type is
returned verbatim (`audio/mpeg` for mp3, `audio/pcm;rate=…;channels=…`
for pcm). An omitted `response_format` defaults to `mp3`.
The accepted model/voice/format set is a curated per-environment
catalog that a model joins only after passing contract qualification;
`deepgram/aura-2` is the launch model. An unsupported model, voice, or
format, or a `speed`/`instructions` value for a model that does not
support it, returns a 400 without dispatching a synthesis.
This endpoint is independently feature-gated during preview rollout. A
disabled deployment returns an OpenAI-shaped 404 without dispatching.
operationId: createAudioSpeech
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AudioSpeechRequest'
responses:
'200':
description: |
Raw audio byte stream. Bytes are forwarded progressively as the
provider synthesizes them; the content type is the negotiated
format (`audio/mpeg` or `audio/pcm;rate=…;channels=…`).
headers:
X-Generation-Id:
description: Upstream generation identifier for out-of-band billing reconciliation.
schema:
type: string
X-Execution-ID:
description: Eachlabs execution identifier for the synthesis.
schema:
type: string
content:
audio/mpeg:
schema:
type: string
format: binary
audio/pcm:
schema:
type: string
format: binary
'400':
description: Unsupported model/voice/format/param or invalid input
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'401':
description: Authentication failed
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'402':
description: Insufficient balance
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'404':
description: Capability disabled for this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'413':
description: Request body exceeds the maximum size
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'429':
description: Provider rate limit reached
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'503':
description: Authentication or speech service temporarily unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'504':
description: Speech request timed out
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
/v1/realtime:
get:
tags:
- OpenAI Compatibility
summary: Stream realtime audio transcription
description: |
Preview WebSocket endpoint for realtime speech-to-text. Authenticate with
`Authorization: Bearer <API_KEY>` and request exactly one allowed `model`
query parameter. API keys in query parameters and browser requests carrying
an `Origin` header are rejected before upgrade.
After the `101` response, send text JSON events only. The launch profile
accepts 24 kHz mono PCM16 audio encoded as base64 in
`input_audio_buffer.append` events, followed by an explicit
`input_audio_buffer.commit`; automatic voice activity detection is not
enabled. Transcription delta and completion events are returned as text
JSON frames.
The endpoint is independently feature-gated during preview rollout. A
disabled deployment returns an OpenAI-shaped 404 without opening an
upstream session. Established sessions have bounded message size,
inactivity, duration, and concurrency limits and are not transparently
reconnected or replayed.
operationId: createRealtimeTranscription
security:
- BearerAuth: []
parameters:
- name: model
in: query
required: true
schema:
type: string
example: openai/gpt-realtime-whisper
description: Realtime transcription model from the environment allowlist.
responses:
'101':
description: WebSocket upgrade accepted; subsequent messages are text JSON frames.
headers:
X-Execution-ID:
description: Eachlabs execution identifier for billing and support correlation.
schema:
type: string
'400':
description: Missing, repeated, or unsupported model/query parameter
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'401':
description: Authentication failed
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'402':
description: Insufficient balance
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'403':
description: Browser Origin requests are not supported
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'404':
description: Capability disabled for this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'429':
description: Upstream session capacity or rate limit reached
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'503':
description: Authentication, realtime, or session capacity temporarily unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
'504':
description: Realtime session establishment timed out
content:
application/json:
schema:
$ref: '#/components/schemas/OpenAIErrorResponse'
/v1/workflows:
get:
tags:
- Workflows
summary: List workflows
description: |
List the workflows visible to the caller's organization, newest first.
`offset` in the response is the **next** page's start, not an echo of the one you
sent. It is present whenever the page came back full, so page until a page returns
fewer than `limit` rows.
operationId: listWorkflows
security:
- BearerAuth: []
parameters:
- name: limit
in: query
required: false
schema:
type: integer
default: 50
maximum: 100
description: Page size. Defaults to 50, capped at 100.
- name: offset
in: query
required: false
schema:
type: integer
default: 0
description: Page start.
- name: category
in: query
required: false
schema:
type: string
description: Keep only workflows carrying this category.
- name: keyword
in: query
required: false
schema:
type: string
description: Free-text match on the workflow name.
- name: sort_key
in: query
required: false
schema:
type: string
enum: [name, created_at, updated_at, trigger_count, status]
description: Sort column. Ignored unless `sort_direction` is also given.
- name: sort_direction
in: query
required: false
schema:
type: string
enum: [asc, desc]
description: Sort direction. Ignored unless `sort_key` is also given.
- name: node_based
in: query
required: false
schema:
type: boolean
description: Keep only workflows whose `node_based` flag matches. Omit for all.
responses:
'200':
description: A page of workflows
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowListResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'408':
$ref: '#/components/responses/RequestTimeout'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
post:
tags:
- Workflows
summary: Create workflow
description: |
Create a new workflow together with its initial version. This route forwards to
workflows-engine, which generates the slug, persists the workflow + version, and
provisions the execution state machine.
Only `name` is required. `definition` is the workflow DSL as an arbitrary JSON
object (steps, input_schema, metadata). Categories, tags, and visibility are not
accepted on this route — workflows-engine applies its defaults (categories
`["general"]`, visibility `private`).
operationId: createWorkflow
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowCreateRequest'
example:
name: "My Workflow"
definition:
version: "1.0"
steps: []
responses:
'201':
description: Workflow created
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowDetailResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'408':
$ref: '#/components/responses/RequestTimeout'
'409':
$ref: '#/components/responses/Conflict'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/workflows/{workflowID}:
get:
tags:
- Workflows
summary: Get workflow details
description: |
Fetch a workflow (summary fields + all its versions) by UUID or slug. The request
is routed to whichever engine owns the workflow, so callers never need to know
where it is stored.
operationId: getWorkflow
security:
- BearerAuth: []
parameters:
- name: workflowID
in: path
required: true
schema:
type: string
description: Workflow UUID or slug.
example: "wf_abc123"
responses:
'200':
description: The workflow detail
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowDetailResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'408':
$ref: '#/components/responses/RequestTimeout'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- Workflows
summary: Update workflow
description: |
Rename a workflow. `name` is the only updatable field on this route — a real
rename also regenerates the slug. Definition and versions are managed through
the version endpoints; unknown body fields are ignored.
operationId: updateWorkflow
security:
- BearerAuth: []
parameters:
- name: workflowID
in: path
required: true
schema:
type: string
description: Workflow UUID or slug.
example: "wf_abc123"
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowUpdateRequest'
example:
name: "My Renamed Workflow"
responses:
'200':
description: The refreshed workflow detail
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowDetailResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'408':
$ref: '#/components/responses/RequestTimeout'
'409':
$ref: '#/components/responses/Conflict'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/workflows/{workflowID}/versions/{versionID}:
put:
tags:
- Workflows
summary: Create or update workflow version
description: |
Create or patch a workflow version through api-service. The request is routed to
whichever engine owns the workflow, so callers never need to know where it is stored.
All body fields follow patch semantics on an existing version: an omitted field
leaves the stored value untouched. `output_mapping` maps custom output keys to
step-output references — a value is either a reference string (starting with `$.`
or `$[`) or a nested object of them for grouped keys — without resending the
definition: omitted = unchanged, `{}` = cleared, non-empty = replaced as a whole,
and it overrides the definition's own `metadata.output_mapping` when both are sent.
Creating a new version requires `definition`. A locked version rejects updates
unless the payload explicitly sets `locked: false`.
operationId: upsertWorkflowVersion
security:
- BearerAuth: []
parameters:
- name: workflowID
in: path
required: true
schema:
type: string
description: Workflow ID that owns the version.
example: "wf_abc123"
- name: versionID
in: path
required: true
schema:
type: string
description: Version ID to create or update. A `version_id` in the body takes precedence.
example: "v1"
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowVersionUpsertRequest'
example:
output_mapping:
primary: "$.step1.primary"
images:
image_1: "$.step1.output[0]"
image_2: "$.step1.output[1]"
responses:
'200':
description: The upserted version summary
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowVersionSummaryResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'408':
$ref: '#/components/responses/RequestTimeout'
'409':
$ref: '#/components/responses/Conflict'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/workflows/trigger/{workflowID}/{versionID}:
post:
tags:
- Workflows
summary: Trigger workflow execution
description: |
Start an asynchronous workflow execution through api-service. This route forwards to
workflows-engine and requires callers to pin the workflow version in the path.
The immediate response only confirms the workflow was queued. Downstream workflow
lifecycle statuses are `running`, `completed`, `failed`, and `cancelled`.
To queue several executions of the same version in one call, use the bulk trigger route.
operationId: triggerWorkflowExecution
security:
- BearerAuth: []
parameters:
- name: workflowID
in: path
required: true
schema:
type: string
description: Workflow ID or slug to execute.
example: "wf_abc123"
- name: versionID
in: path
required: true
schema:
type: string
description: Workflow version ID to execute.
example: "v1"
- name: webhook_url
in: query
required: false
schema:
type: string
format: uri
description: Optional compatibility query parameter for execution completion webhooks. The request body field takes precedence when both are present.
example: "https://your-app.com/webhook"
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowTriggerRequest'
example:
inputs:
prompt: "A cinematic establishing shot of a mountain lake"
webhook_url: "https://your-app.com/webhook"
webhook_secret: "your-secret-key"
responses:
'202':
description: Workflow execution queued
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowTriggerResponse'
example:
execution_id: "exec_xyz789"
status: "queued"
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'408':
$ref: '#/components/responses/RequestTimeout'
'409':
$ref: '#/components/responses/Conflict'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/workflows/bulk-trigger/{workflowID}/{versionID}:
post:
tags:
- Workflows
summary: Bulk trigger workflow executions
description: |
Queue several asynchronous executions of one workflow version in a single call
(maximum 10 inputs per request). Each entry in `inputs` becomes its own execution;
all executions share a single `bulk_id` for grouped tracking and consolidated
webhook delivery. This route forwards to workflows-engine and requires callers to
pin the workflow version in the path.
The immediate response only confirms the executions were queued. A per-input failure
is reported inline on its `executions` entry (`status: failed` + `message`) — the
bulk call still returns 202 so partial failures don't lose the queued executions.
operationId: bulkTriggerWorkflowExecutions
security:
- BearerAuth: []
parameters:
- name: workflowID
in: path
required: true
schema:
type: string
description: Workflow ID or slug to execute.
example: "wf_abc123"
- name: versionID
in: path
required: true
schema:
type: string
description: Workflow version ID to execute.
example: "v1"
- name: webhook_url
in: query
required: false
schema:
type: string
format: uri
description: Optional compatibility query parameter for execution completion webhooks. The request body field takes precedence when both are present.
example: "https://your-app.com/webhook"
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowBulkTriggerRequest'
example:
inputs:
- prompt: "A cinematic establishing shot of a mountain lake"
- prompt: "A close-up of dew on a spider web at sunrise"
webhook_url: "https://your-app.com/webhook"
webhook_secret: "your-secret-key"
responses:
'202':
description: Workflow executions queued
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowBulkTriggerResponse'
example:
bulk_id: "bulk_abc123"
executions:
- execution_id: "exec_xyz789"
status: "queued"
- status: "failed"
message: "invalid input value"
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'408':
$ref: '#/components/responses/RequestTimeout'
'409':
$ref: '#/components/responses/Conflict'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/public/@{nickname}/workflows/{slug}/versions/{versionID}:
get:
tags:
- Public Workflows
summary: Get public workflow version
description: |
Fetch a publicly shared workflow version by its owner's @nickname, workflow slug,
and version ID. No authentication required. Empty optional fields (categories,
thumbnail, definition, example_output) are omitted from the response.
operationId: getPublicWorkflowVersion
parameters:
- name: nickname
in: path
required: true
schema:
type: string
description: The owning organization's nickname (the path literal keeps the leading @).
example: "acme-corp"
- name: slug
in: path
required: true
schema:
type: string
description: Workflow slug.
example: "my-generator"
- name: versionID
in: path
required: true
schema:
type: string
description: Workflow version ID.
example: "v1"
responses:
'200':
description: The public workflow version
content:
application/json:
schema:
$ref: '#/components/schemas/PublicWorkflowVersionResponse'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'408':
$ref: '#/components/responses/RequestTimeout'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/public/@{nickname}/workflows/{slug}/versions/{versionID}/trigger:
post:
tags:
- Public Workflows
summary: Trigger public workflow version
description: |
Queue an execution of a publicly shared workflow version. The route itself is
anonymous: pass your API key in the request body `api_key` field (or an
`X-API-Key` header). The key's organization — the caller, not the workflow
owner — is billed for the execution.
operationId: triggerPublicWorkflowVersion
parameters:
- name: nickname
in: path
required: true
schema:
type: string
description: The owning organization's nickname (the path literal keeps the leading @).
example: "acme-corp"
- name: slug
in: path
required: true
schema:
type: string
description: Workflow slug.
example: "my-generator"
- name: versionID
in: path
required: true
schema:
type: string
description: Workflow version ID to execute.
example: "v1"
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/PublicWorkflowTriggerRequest'
example:
api_key: "your-api-key"
inputs:
prompt: "A watercolor fox"
webhook_url: "https://your-app.com/webhook"
responses:
'202':
description: Workflow execution queued
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowTriggerResponse'
example:
execution_id: "exec_xyz789"
status: "queued"
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'408':
$ref: '#/components/responses/RequestTimeout'
'502':
$ref: '#/components/responses/BadGateway'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/files/{id}:
delete:
tags:
- Uploads
summary: Delete uploaded file
description: |
Delete a previously uploaded file by its ID. Returns 204 on success.
Proxies the delete request to storage-service and passes through its
status codes: 404 when the file does not exist, 409 when deletion is
not permitted for the file's current state.
operationId: deleteFile
security:
- BearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
description: File ID returned by the presign upload endpoint
example: "0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0"
responses:
'204':
description: File deleted successfully
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/webhooks:
get:
tags:
- Webhooks
summary: List webhooks
description: Retrieve a paginated list of webhooks for the authenticated organization
operationId: listWebhooks
security:
- BearerAuth: []
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 50
description: Maximum number of webhooks to return
example: 50
- name: offset
in: query
schema:
type: integer
minimum: 0
default: 0
description: Number of webhooks to skip for pagination
example: 0
responses:
'200':
description: List of webhooks
content:
application/json:
schema:
$ref: '#/components/schemas/ListWebhooksResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/webhooks/{execution_id}:
get:
tags:
- Webhooks
summary: Get webhook by execution ID
description: Retrieve details of a specific webhook including delivery attempts by execution ID
operationId: getWebhook
security:
- BearerAuth: []
parameters:
- name: execution_id
in: path
required: true
schema:
type: string
description: Execution ID for tracking the webhook
example: "abc123-def456-ghi789"
responses:
'200':
description: Webhook details
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookWithAttempts'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/.well-known/oauth-authorization-server:
get:
tags:
- MCP OAuth
summary: OAuth authorization server metadata
description: |
RFC 8414 authorization-server metadata for the MCP issuer
(`https://mcp.eachlabs.ai`). Static document; the `authorization_endpoint`
points at the consent page on www.eachlabs.ai.
operationId: getOAuthAuthorizationServerMetadata
responses:
'200':
description: Authorization server metadata
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthServerMetadata'
/oauth/token:
post:
tags:
- MCP OAuth
summary: OAuth token endpoint
description: |
RFC 6749 token endpoint for the MCP authorization server. Supports the
`authorization_code`, `refresh_token`, and RFC 8693
`urn:ietf:params:oauth:grant-type:token-exchange` grants. Accepts the
OAuth-normative `application/x-www-form-urlencoded` body or JSON.
Errors are HTTP 400 with the RFC error body; exchange-gate failures
are 401. Served on `https://mcp.eachlabs.ai`.
operationId: oauthToken
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/OAuthTokenRequest'
application/json:
schema:
$ref: '#/components/schemas/OAuthTokenRequest'
responses:
'200':
description: Minted token
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthTokenResponse'
'400':
description: RFC 6749 §5.2 error body
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
'401':
description: Gate failure (RFC 6749 §5.2 error body)
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
/oauth/register:
post:
tags:
- MCP OAuth
summary: Dynamic client registration
description: |
RFC 7591 dynamic client registration for public PKCE clients of the
MCP authorization server. Served on `https://mcp.eachlabs.ai`.
operationId: oauthRegister
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthClientRegistrationRequest'
responses:
'201':
description: Registered client metadata
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthClientRegistrationResponse'
'400':
description: RFC 7591 §3.2.2 error body
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
/oauth/jwks.json:
get:
tags:
- MCP OAuth
summary: JSON Web Key Set
description: |
RFC 7517 JWKS for the MCP issuer's ES256 signing ring. Cached at the
edge (5 minutes, stale-served during crm outages). Served on
`https://mcp.eachlabs.ai`.
operationId: getOAuthJWKS
responses:
'200':
description: JSON Web Key Set
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthJWKS'
'503':
description: Signing keys unavailable (no cached document)
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
components:
headers:
RequestId:
description: Request correlation ID. Echoes the incoming `X-Request-Id` header or a generated ID.
schema:
type: string
example: "123e4567-e89b-12d3-a456-426614174000"
RetryAfter:
description: Optional retry delay returned by rate-limit or upstream retry responses when available.
schema:
oneOf:
- type: integer
minimum: 0
- type: string
example: 30
Deprecation:
description: RFC 9745 date indicating that GET /v1/executions is deprecated.
schema:
type: string
example: "@1788480000"
DeprecationLink:
description: Link to execution-list migration guidance.
schema:
type: string
example: '<https://docs.eachlabs.ai/changelog>; rel="deprecation"; type="text/html"'
securitySchemes:
BearerAuth:
type: http
scheme: bearer
description: 'API key passed as `Authorization: Bearer <api_key>`'
schemas:
BillingBalance:
type: object
additionalProperties: false
required:
- balance_usd
properties:
balance_usd:
type: number
format: double
description: |
Balance in USD. Zero and negative values are valid. Fractional-cent
source precision is preserved rather than rounded to two decimal places.
example: 53.475
BillingBalanceError:
type: object
additionalProperties: false
required:
- status
- error
properties:
status:
type: integer
enum: [401, 429, 503]
error:
type: string
enum: [unauthorized, too many requests, service unavailable]
Model:
type: object
properties:
title:
type: string
example: "Nano Banana Pro Edit"
slug:
type: string
example: "nano-banana-pro-edit"
version:
type: string
deprecated: true
description: "Deprecated. Always returns \"0.0.1\". Will be removed in a future version."
example: "0.0.1"
output_type:
type: string
example: "array"
request_schema:
type: object
description: |
JSON Schema for API requests to this model.
Defines the structure, types, and validation rules for making requests.
example:
type: object
required: ["prompt"]
properties:
prompt:
type: string
minLength: 10
maxLength: 500
num_images:
type: integer
minimum: 1
maximum: 4
default: 1
ModelDetail:
type: object
required:
- cost
properties:
title:
type: string
example: "Nano Banana Pro Edit"
slug:
type: string
example: "nano-banana-pro-edit"
version:
type: string
deprecated: true
description: "Deprecated. Always returns \"0.0.1\". Will be removed in a future version."
example: "0.0.1"
output_type:
type: string
example: "array"
request_schema:
type: object
description: |
JSON Schema for API requests to this model.
Defines the structure, types, and validation rules for making requests.
example:
type: object
required: ["prompt"]
properties:
prompt:
type: string
minLength: 10
maxLength: 500
openapi_schema:
type: object
description: |
OpenAPI 3.0 document for this model's prediction contract.
Includes the model-specific input schema and prediction lifecycle endpoints.
example:
openapi: "3.0.4"
info:
title: "Nano Banana Pro Edit"
version: "0.0.1"
x-eachlabs-model-slug: "nano-banana-pro-edit"
x-eachlabs-output-type: "array"
paths:
/v1/prediction:
post:
summary: "Create a prediction"
cost:
type: object
nullable: true
required:
- currency
- type
- amount
- unit
- estimate
- description
description: |
Compact estimated pricing for the model's active pricing document.
Returns `null` when no pricing document exists. During the
mixed-version rollout, the legacy fallback can also return `null`
for unsupported pricing. Final settled cost is returned as
`metrics.cost` after execution.
properties:
currency:
type: string
enum: [USD]
example: "USD"
type:
type: string
enum: [fixed, usage_based]
description: Whether the estimate is fixed per execution or usage-based.
example: "fixed"
amount:
type: number
format: double
nullable: true
description: Estimated USD amount for fixed or free pricing; otherwise `null`.
example: 0.04
unit:
type: string
nullable: true
enum: [execution, second, processing_second, minute, hour, image, megapixel, million_tokens, character, step, generation]
description: Billing unit for fixed and usage-based pricing; `null` for unlisted pricing.
example: "second"
estimate:
type: string
description: |
Presentation-ready price guidance. Usage-based pricing may
include a safely recognized unit rate, range, or billing
driver; otherwise it falls back to `Usage-based pricing`.
example: "$0.068–$0.28 per second, depending on resolution"
description:
type: string
enum: [Pricing is an estimate and may change as rates are updated.]
example: "Pricing is an estimate and may change as rates are updated."
min:
type: number
format: double
nullable: true
description: Minimum USD price per unit for usage-based pricing; otherwise `null`.
example: 0.068
max:
type: number
format: double
nullable: true
description: Maximum USD price per unit for usage-based pricing; otherwise `null`.
example: 0.28
depends_on:
type: array
description: Normalized input names that determine a usage-based range.
items:
type: string
example: [resolution]
rows:
type: array
description: Optional public pricing breakdown in USD per unit.
items:
type: object
required: [label, value]
properties:
label:
type: string
example: "480p"
value:
type: number
format: double
example: 0.068
LLMRouterModelCatalog:
type: object
properties:
slug:
type: string
example: "eachlabs-llm-router"
source:
type: string
enum: ["catalog", "odoo_fallback"]
version:
type: integer
example: 1
updated_at:
type: string
example: "2026-05-24"
model_enum:
type: array
items:
type: string
example: ["openai/gpt-5.2", "anthropic/claude-sonnet-4-6"]
request_schema:
type: object
description: JSON Schema fragment containing the generated model enum.
models:
type: array
items:
$ref: '#/components/schemas/LLMRouterCatalogModel'
LLMRouterCatalogModel:
type: object
properties:
id:
type: string
example: "openai/gpt-5.2"
status:
type: string
example: "visible"
family:
type: string
example: "openai"
canonical_provider:
type: string
example: "openrouter"
canonical_target_model:
type: string
example: "openai/gpt-5.2"
routing_policy:
type: string
example: "explicit_route"
legacy_requesty:
type: boolean
title:
type: string
example: "GPT-5.2"
description:
type: string
faqs:
type: array
items:
$ref: '#/components/schemas/LLMRouterCatalogFAQ'
readme_md:
type: string
LLMRouterCatalogFAQ:
type: object
properties:
question:
type: string
answer:
type: string
WorkflowTriggerRequest:
type: object
properties:
inputs:
type: object
additionalProperties: true
description: Input values defined by the workflow version. May be omitted for workflows with no inputs.
example:
prompt: "A cinematic establishing shot of a mountain lake"
webhook_url:
type: string
format: uri
description: Optional webhook URL for workflow execution completion.
example: "https://your-app.com/webhook"
webhook_secret:
type: string
description: Optional secret used to sign workflow webhook requests.
example: "your-secret-key"
WorkflowTriggerResponse:
type: object
properties:
execution_id:
type: string
description: Queued workflow execution identifier.
example: "exec_xyz789"
status:
type: string
enum:
- queued
description: Execution queue status returned immediately after triggering.
example: "queued"
WorkflowBulkTriggerRequest:
type: object
required:
- inputs
properties:
inputs:
type: array
minItems: 1
maxItems: 10
description: >-
One input object per execution (1–10), each validated against the
workflow version's input schema. Every entry must be a JSON object;
a non-object entry (array, string, number) rejects the whole request
with 400 rather than failing only that item.
items:
type: object
additionalProperties: true
example:
- prompt: "A cinematic establishing shot of a mountain lake"
- prompt: "A close-up of dew on a spider web at sunrise"
webhook_url:
type: string
format: uri
description: Optional webhook URL applied to every execution in the batch.
example: "https://your-app.com/webhook"
webhook_secret:
type: string
description: Optional secret used to sign workflow webhook requests.
example: "your-secret-key"
WorkflowBulkTriggerResponse:
type: object
properties:
bulk_id:
type: string
description: Identifier grouping every execution queued by this request.
example: "bulk_abc123"
executions:
type: array
description: One acknowledgment per input, in request order.
items:
type: object
properties:
execution_id:
type: string
description: Queued execution identifier. Absent on a per-input failure.
example: "exec_xyz789"
status:
type: string
description: Per-execution queue status (`queued`) or `failed` for an input that could not be queued.
example: "queued"
message:
type: string
description: Failure reason, present only when status is `failed`.
example: "invalid input value"
WorkflowCreateRequest:
type: object
required:
- name
properties:
name:
type: string
description: Human-readable workflow name. Required.
example: "My Workflow"
definition:
type: object
additionalProperties: true
description: Workflow DSL (steps, input_schema, metadata) as a JSON object.
example:
version: "1.0"
steps: []
WorkflowDetailResponse:
type: object
description: The workflow detail (summary fields + its versions), matching the engine's JSON.
properties:
workflow_id:
type: string
example: "wf_abc123"
slug:
type: string
example: "my-workflow"
name:
type: string
example: "My Workflow"
categories:
type: array
items:
type: string
example: ["video"]
tags:
type: array
items:
type: string
example: ["beta"]
status:
type: string
example: "active"
trigger_count:
type: integer
example: 0
clone_count:
type: integer
example: 0
is_public:
type: boolean
example: false
production:
type: boolean
example: false
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
versions:
type: array
items:
$ref: '#/components/schemas/WorkflowVersionSummaryResponse'
WorkflowSummaryResponse:
type: object
description: The list view of a workflow — summary fields only, without its versions.
properties:
workflow_id:
type: string
example: "wf_abc123"
slug:
type: string
example: "my-workflow"
name:
type: string
example: "My Workflow"
categories:
type: array
items:
type: string
example: ["video"]
tags:
type: array
items:
type: string
example: ["beta"]
status:
type: string
example: "active"
trigger_count:
type: integer
example: 12
clone_count:
type: integer
example: 0
is_public:
type: boolean
description: True when the workflow has at least one public published version.
example: false
production:
type: boolean
description: True when the workflow has at least one active production version.
example: false
node_based:
type: boolean
description: True when the workflow's latest active version runs as a node graph.
example: false
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
WorkflowListResponse:
type: object
description: A page of workflows.
properties:
workflows:
type: array
items:
$ref: '#/components/schemas/WorkflowSummaryResponse'
offset:
type: integer
description: Start of the next page; present while pages come back full.
example: 50
total_count:
type: integer
description: Full result-set size for the applied filters; a lower bound on very large sets.
example: 231
WorkflowUpdateRequest:
type: object
description: Rename payload. `name` is the only honored field; unknown keys are ignored.
properties:
name:
type: string
description: New human-readable workflow name. A real rename regenerates the slug.
example: "My Renamed Workflow"
PublicWorkflowVersionResponse:
type: object
description: A publicly shared workflow version. Empty optional fields are omitted.
properties:
workflow_id:
type: string
example: "wf_abc123"
version_id:
type: string
example: "v1"
slug:
type: string
example: "my-generator"
name:
type: string
example: "My Generator"
description:
type: string
thumbnail:
type: string
nullable: true
categories:
type: array
items:
type: string
example: ["video"]
definition:
type: object
additionalProperties: true
description: The version's workflow DSL.
status:
type: string
example: "active"
example_output:
type: string
nullable: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
PublicWorkflowTriggerRequest:
type: object
description: |
Public trigger payload. The API key rides in the body (`api_key`) or the
X-API-Key header — this anonymous route uses no Authorization header.
properties:
api_key:
type: string
description: Your eachlabs API key. The key's organization is billed for the execution.
inputs:
type: object
additionalProperties: true
description: Workflow input values. Optional for workflows without inputs.
webhook_url:
type: string
format: uri
description: Optional endpoint notified when the execution completes.
webhook_secret:
type: string
description: Optional secret used to sign webhook deliveries.
WorkflowVersionUpsertRequest:
type: object
description: |
Patch payload for a workflow version. Every field is optional on an existing
version; creating a new version requires `definition`.
properties:
version_id:
type: string
description: Overrides the path version ID when present.
example: "v1"
definition:
type: object
additionalProperties: true
description: Workflow DSL (steps, input_schema, metadata) as a JSON object. Omit to keep the stored definition.
output_mapping:
type: object
additionalProperties:
oneOf:
- type: string
- type: object
additionalProperties: true
description: |
Custom output keys mapped to step-output references. A value is either a
reference string (starting with `$.` or `$[`) or a nested object of them
for grouped keys. Omitted = unchanged, empty object = cleared, non-empty =
replaced as a whole.
example:
image_1: "$.step1.output[0]"
images:
image_2: "$.step1.output[1]"
published:
type: boolean
locked:
type: boolean
production:
type: boolean
allowed_to_share:
type: boolean
WorkflowVersionSummaryResponse:
type: object
description: The upserted version summary, matching the engine's version JSON.
properties:
version_id:
type: string
example: "v1"
slug:
type: string
example: "my-workflow"
published:
type: boolean
locked:
type: boolean
production:
type: boolean
allowed_to_share:
type: boolean
trigger_count:
type: integer
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
definition:
type: object
additionalProperties: true
description: The version's workflow DSL, including `metadata.output_mapping`.
status:
type: string
example: "active"
visibility:
type: string
example: "private"
is_popular:
type: boolean
updated_by:
type: string
public_description:
type: string
nullable: true
public_thumbnail:
type: string
nullable: true
public_popular_sort_order:
type: integer
nullable: true
public_example_output:
type: string
nullable: true
Error:
type: object
description: |
Backward-compatible REST error body. Endpoint-specific handlers may return only
`error`; shared error paths may also include `status` and `details`.
properties:
status:
type: integer
description: HTTP status code, when returned by the shared error handler.
example: 400
error:
type: string
description: Safe user-facing error message.
example: "error message"
details:
nullable: true
description: Optional validation or request details for client errors.
required:
- error
OAuthServerMetadata:
type: object
description: RFC 8414 authorization-server metadata for the MCP issuer.
properties:
issuer:
type: string
example: "https://mcp.eachlabs.ai"
authorization_endpoint:
type: string
example: "https://www.eachlabs.ai/mcp/oauth/authorize"
token_endpoint:
type: string
example: "https://mcp.eachlabs.ai/oauth/token"
registration_endpoint:
type: string
example: "https://mcp.eachlabs.ai/oauth/register"
jwks_uri:
type: string
example: "https://mcp.eachlabs.ai/oauth/jwks.json"
response_types_supported:
type: array
items:
type: string
example: ["code"]
grant_types_supported:
type: array
items:
type: string
example: ["authorization_code", "refresh_token"]
code_challenge_methods_supported:
type: array
items:
type: string
example: ["S256"]
scopes_supported:
type: array
items:
type: string
example: ["predictions:create", "predictions:read"]
token_endpoint_auth_methods_supported:
type: array
items:
type: string
example: ["none"]
client_id_metadata_document_supported:
type: boolean
example: true
authorization_response_iss_parameter_supported:
type: boolean
example: true
required:
- issuer
- authorization_endpoint
- token_endpoint
- jwks_uri
OAuthTokenRequest:
type: object
description: Union of the authorization_code, refresh_token, and token-exchange grant shapes; `grant_type` selects which fields are read.
properties:
grant_type:
type: string
enum: ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"]
client_id:
type: string
code:
type: string
description: authorization_code grant.
redirect_uri:
type: string
description: authorization_code grant.
code_verifier:
type: string
description: authorization_code grant (RFC 7636).
refresh_token:
type: string
description: refresh_token grant.
subject_token:
type: string
description: token-exchange grant (RFC 8693).
subject_token_type:
type: string
description: token-exchange grant (RFC 8693).
resource:
type: string
description: RFC 8707 resource indicator.
scope:
type: string
required:
- grant_type
OAuthTokenResponse:
type: object
properties:
access_token:
type: string
token_type:
type: string
example: "Bearer"
expires_in:
type: integer
format: int64
refresh_token:
type: string
scope:
type: string
issued_token_type:
type: string
description: Present on token-exchange responses only.
required:
- access_token
- token_type
OAuthClientRegistrationRequest:
type: object
description: RFC 7591 §2 client metadata for public PKCE clients.
properties:
redirect_uris:
type: array
items:
type: string
client_name:
type: string
client_uri:
type: string
logo_uri:
type: string
scope:
type: string
grant_types:
type: array
items:
type: string
response_types:
type: array
items:
type: string
token_endpoint_auth_method:
type: string
example: "none"
required:
- redirect_uris
OAuthClientRegistrationResponse:
allOf:
- $ref: '#/components/schemas/OAuthClientRegistrationRequest'
- type: object
properties:
client_id:
type: string
client_id_issued_at:
type: integer
format: int64
description: Unix seconds.
required:
- client_id
OAuthJWKS:
type: object
description: RFC 7517 JSON Web Key Set (ES256, P-256).
properties:
keys:
type: array
items:
type: object
properties:
kty:
type: string
example: "EC"
use:
type: string
example: "sig"
alg:
type: string
example: "ES256"
kid:
type: string
crv:
type: string
example: "P-256"
x:
type: string
description: base64url-encoded affine x coordinate
y:
type: string
description: base64url-encoded affine y coordinate
required:
- keys
OAuthError:
type: object
description: RFC 6749 §5.2 error body.
properties:
error:
type: string
example: "invalid_grant"
error_description:
type: string
required:
- error
CreatePredictionRequest:
type: object
required:
- model
- input
properties:
model:
type: string
description: Model slug or identifier
example: "flux-1-1-pro"
version:
type: string
deprecated: true
description: "Deprecated. This field is ignored. Kept for backwards compatibility."
example: "0.0.1"
input:
type: object
description: Input parameters for the prediction
additionalProperties: true
example:
prompt: "A beautiful sunset over the ocean with vibrant colors"
aspect_ratio: "16:9"
webhook_url:
type: string
format: uri
description: Optional webhook URL to receive prediction result asynchronously
example: "https://your-app.com/webhook"
webhook_secret:
type: string
description: Optional secret used to sign webhook requests
example: "your-secret-key"
source:
type: string
deprecated: true
description: |
Ignored. The recorded execution source is derived server-side from the
authenticated credential (API key requests record `api`); a request-supplied
value never overrides it. The reserved values `replay` and `mcp` are rejected
with `400`.
CreatePredictionResponse:
type: object
properties:
status:
type: string
description: Status of the prediction creation request
example: "success"
message:
type: string
description: Human-readable message about the prediction creation
example: "Prediction created successfully"
predictionID:
type: string
description: Unique prediction identifier
example: "abc123-def456-ghi789"
RunPredictionResponse:
type: object
properties:
status:
type: string
enum:
- success
- error
- cancelled
description: Final terminal status of the synchronous prediction
example: "success"
message:
type: string
description: Human-readable message
example: "Prediction completed successfully"
predictionID:
type: string
description: Unique prediction identifier
example: "abc123-def456-ghi789"
output:
description: Prediction output, when available
nullable: true
oneOf:
- type: string
- type: array
- type: object
error:
type: string
description: User-facing error message when the prediction failed
example: ""
CancelPredictionResponse:
type: object
properties:
status:
type: string
enum:
- success
description: Cancellation status returned when the cancellation request succeeds
example: "success"
message:
type: string
description: Human-readable cancellation message
example: "Prediction canceled successfully"
predictionID:
type: string
description: Unique prediction identifier
example: "abc123-def456-ghi789"
Prediction:
type: object
properties:
id:
type: string
description: Unique prediction identifier
example: "abc123-def456-ghi789"
input:
type: object
description: Input parameters used for the prediction
additionalProperties: true
example:
prompt: "A beautiful sunset over the ocean with vibrant colors"
aspect_ratio: "16:9"
status:
type: string
description: |
Current status of the prediction. `created`, `starting`, and
`processing` are non-terminal. `success`, `error`, and `cancelled`
are terminal.
enum:
- created
- starting
- processing
- success
- error
- cancelled
example: "success"
output:
description: Prediction output (type varies based on model)
oneOf:
- type: string
- type: array
- type: object
example: "https://storage.example.com/predictions/abc123/image.jpg"
logs:
type: string
nullable: true
description: Execution logs from the prediction
example: null
metrics:
type: object
description: Performance and cost metrics
properties:
predict_time:
type: number
description: Time taken for prediction in seconds
example: 12.5
cost:
type: number
description: Cost of the prediction in USD
example: 0.05
tier:
type: string
description: |
Compute tier the execution was billed on. Present only for
metered engines (e.g. Video API); omitted for other models.
example: "gpu"
billed_seconds:
type: integer
description: |
Billed compute seconds for the execution. Present only for
metered engines; omitted for other models.
example: 42
output_gb:
type: number
description: |
Size of the uploaded output artifacts in GB. Present only for
metered engines; omitted for other models.
example: 1.5
urls:
type: object
description: Related API endpoints
properties:
cancel:
type: string
format: uri
description: URL to cancel the prediction
example: "https://api.eachlabs.ai/v1/prediction/abc123-def456-ghi789/cancel"
get:
type: string
format: uri
description: URL to get the prediction
example: "https://api.eachlabs.ai/v1/prediction/abc123-def456-ghi789"
PresignUploadRequest:
type: object
required:
- content_type
properties:
content_type:
type: string
description: MIME type of the file to upload
example: "image/png"
file_type:
type: string
description: High-level file category
enum:
- image
- video
- audio
- other
example: "image"
ttl_days:
type: integer
nullable: true
description: Optional file TTL tier in days, when enabled for the caller
example: 30
PresignUploadResponse:
type: object
properties:
id:
type: string
description: Upload identifier
example: "0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0"
presigned_url:
type: string
format: uri
description: Short-lived URL for uploading raw file bytes
example: "https://eachlabs-storage.s3.amazonaws.com/uploads/...?X-Amz-Signature=..."
public_url:
type: string
format: uri
description: Public URL to pass as a model input after upload succeeds
example: "https://cdn-us.eachlabs.ai/uploads/0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0.png"
expires_at:
type: string
format: date-time
description: Presigned URL expiration time
example: "2026-04-27T18:15:00Z"
required_headers:
type: object
description: Headers that must be sent verbatim with the upload PUT request
additionalProperties:
type: string
example:
x-amz-meta-file-id: "0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0"
ExecutionSummary:
type: object
properties:
id:
type: string
description: Execution identifier
example: "exec_abc123"
model:
type: string
description: Model slug
example: "flux-1-1-pro"
requested_model:
type: string
nullable: true
description: The field is always present in the JSON response. Its value is the original LLM model requested through `eachlabs-llm-router`, or null for other executions
example: "openai/gpt-4.1-mini"
status:
type: string
description: |
Execution status. Model prediction terminal statuses include `success`,
`error`, and `cancelled`; workflow executions can surface `running`,
`completed`, `failed`, or `cancelled` depending on the originating backend.
example: "success"
workflow_id:
type: string
nullable: true
description: Workflow ID, when execution was triggered by a workflow
example: "wf_abc123"
workflow_execution_id:
type: string
nullable: true
description: Workflow execution ID, when execution was triggered by a workflow
example: "wf_exec_abc123"
execution_cost:
type: number
description: Execution cost in USD
example: 0.05
run_time:
type: number
description: Runtime in seconds
example: 12.5
created_at:
type: string
format: date-time
description: Creation timestamp
example: "2026-01-01T00:00:00Z"
started_at:
type: string
format: date-time
nullable: true
description: Start timestamp
example: "2026-01-01T00:00:02Z"
completed_at:
type: string
format: date-time
nullable: true
description: Completion timestamp
example: "2026-01-01T00:00:15Z"
output:
type: string
nullable: true
description: Primary execution output, when available
example: "https://storage.example.com/predictions/abc123/image.jpg"
ListExecutionsResponse:
type: object
properties:
executions:
type: array
items:
$ref: '#/components/schemas/ExecutionSummary'
total_count:
type: integer
description: |
Number of matching executions. When `total_capped` is `true` this is
a lower bound, not the exact total. Use `GET /v2/executions` and its
opaque cursor for history traversal rather than relying on this
count as a pagination stop condition.
example: 123
total_capped:
type: boolean
description: |
`true` when the server did not compute the exact count (the count is
capped, or its time budget elapsed) and `total_count` is a lower
bound — more matching executions may exist beyond it.
example: false
offset:
type: integer
description: Applied offset
example: 0
limit:
type: integer
description: Applied limit
example: 20
required:
- executions
- total_count
- total_capped
- offset
- limit
ListExecutionsV2Response:
type: object
required:
- executions
- limit
- has_more
- next_cursor
properties:
executions:
type: array
items:
$ref: '#/components/schemas/ExecutionSummary'
limit:
type: integer
minimum: 1
maximum: 100
description: Applied page limit
example: 20
has_more:
type: boolean
description: Whether another page is available
example: true
next_cursor:
type: string
nullable: true
description: Opaque cursor for the next page; null at exhaustion
ChatCompletionRequest:
type: object
description: OpenAI-compatible chat completions request body
required:
- model
- messages
additionalProperties: true
properties:
model:
type: string
description: LLM model identifier
example: "openai/gpt-4o-mini"
messages:
type: array
items:
type: object
additionalProperties: true
description: OpenAI-compatible chat messages
example:
- role: "user"
content: "Write a one sentence caption."
stream:
type: boolean
description: Set to `true` to receive OpenAI-compatible SSE chunks (`text/event-stream`) instead of a buffered JSON response.
example: false
stream_options:
type: object
nullable: true
additionalProperties: true
description: Optional OpenAI-compatible streaming options. When `include_usage` is true, the stream may include a final usage chunk before `[DONE]`.
properties:
include_usage:
type: boolean
description: Include token usage in the streamed response when provider usage data is available.
example: true
ChatCompletionResponse:
type: object
description: OpenAI-compatible chat completions response
properties:
id:
type: string
example: "chatcmpl-abc123"
object:
type: string
example: "chat.completion"
created:
type: integer
format: int64
example: 1773651600
model:
type: string
example: "eachlabs-llm-router"
choices:
type: array
items:
type: object
properties:
index:
type: integer
example: 0
message:
type: object
properties:
role:
type: string
example: "assistant"
content:
type: string
example: "A cinematic sunset over the ocean."
finish_reason:
type: string
example: "stop"
usage:
type: object
nullable: true
properties:
prompt_tokens:
type: integer
example: 12
completion_tokens:
type: integer
example: 8
total_tokens:
type: integer
example: 20
ResponsesRequest:
type: object
description: |
Stateless buffered or streaming Responses request. Model availability comes
only from the qualified model catalog after qualification.
additionalProperties: false
required:
- model
- input
properties:
model:
type: string
minLength: 1
example: "qualified-model-id"
input:
oneOf:
- type: string
minLength: 1
- type: array
minItems: 1
items:
$ref: '#/components/schemas/ResponsesInputItem'
instructions:
type: string
nullable: true
max_output_tokens:
type: integer
minimum: 1
nullable: true
temperature:
type: number
minimum: 0
maximum: 2
nullable: true
top_p:
type: number
minimum: 0
maximum: 1
nullable: true
prompt_cache_key:
type: string
nullable: true
parallel_tool_calls:
type: boolean
nullable: true
reasoning:
type: object
nullable: true
additionalProperties: false
properties:
effort:
type: string
nullable: true
enum: [none, minimal, low, medium, high, xhigh]
summary:
type: string
nullable: true
enum: [auto, concise, detailed]
tools:
type: array
nullable: true
items:
$ref: '#/components/schemas/ResponsesFunctionTool'
tool_choice:
nullable: true
oneOf:
- type: string
enum: [auto, none, required]
- $ref: '#/components/schemas/ResponsesFunctionToolChoice'
store:
type: boolean
nullable: true
enum: [false]
description: Phase one accepts only false or null.
stream:
type: boolean
nullable: true
description: Set true to receive canonical Responses Server-Sent Events.
background:
type: boolean
nullable: true
enum: [false]
description: Phase one accepts only false or null.
ResponsesInputItem:
description: |
Typed phase-one input item. `previous_response_id` is intentionally absent
from ResponsesRequest; any presence is rejected.
oneOf:
- $ref: '#/components/schemas/ResponsesInputMessage'
- $ref: '#/components/schemas/ResponsesInputFunctionCall'
- $ref: '#/components/schemas/ResponsesInputFunctionCallOutput'
- $ref: '#/components/schemas/ResponsesInputReasoning'
ResponsesInputMessage:
type: object
additionalProperties: false
required:
- role
- content
properties:
type:
type: string
enum: [message]
id:
type: string
minLength: 1
nullable: true
status:
$ref: '#/components/schemas/ResponsesItemStatus'
role:
type: string
enum: [user, assistant, developer, system]
content:
oneOf:
- type: string
minLength: 1
- type: array
minItems: 1
items:
$ref: '#/components/schemas/ResponsesInputContentPart'
ResponsesInputContentPart:
oneOf:
- $ref: '#/components/schemas/ResponsesInputTextPart'
- $ref: '#/components/schemas/ResponsesInputImagePart'
- $ref: '#/components/schemas/ResponsesInputFilePart'
ResponsesInputTextPart:
type: object
additionalProperties: false
required: [type, text]
properties:
type:
type: string
enum: [input_text, output_text]
text:
type: string
minLength: 1
ResponsesInputImagePart:
type: object
additionalProperties: false
required: [type]
properties:
type:
type: string
enum: [input_image]
image_url:
type: string
minLength: 1
file_id:
type: string
minLength: 1
detail:
type: string
nullable: true
enum: [auto, low, high]
description: Exactly one of image_url or file_id is required.
ResponsesInputFilePart:
type: object
additionalProperties: false
required: [type]
properties:
type:
type: string
enum: [input_file]
file_id:
type: string
minLength: 1
file_url:
type: string
minLength: 1
file_data:
type: string
minLength: 1
filename:
type: string
minLength: 1
nullable: true
description: Exactly one of file_id, file_url, or file_data is required.
ResponsesInputFunctionCall:
type: object
additionalProperties: false
required: [type, name, call_id, arguments]
properties:
type:
type: string
enum: [function_call]
id:
type: string
minLength: 1
nullable: true
status:
$ref: '#/components/schemas/ResponsesItemStatus'
name:
type: string
minLength: 1
maxLength: 64
pattern: '^[A-Za-z0-9_-]+$'
call_id:
type: string
minLength: 1
arguments:
type: string
description: JSON encoded as a string.
ResponsesInputFunctionCallOutput:
type: object
additionalProperties: false
required: [type, call_id, output]
properties:
type:
type: string
enum: [function_call_output]
call_id:
type: string
minLength: 1
output:
type: string
minLength: 1
ResponsesInputReasoning:
type: object
additionalProperties: false
required: [type]
properties:
type:
type: string
enum: [reasoning]
id:
type: string
minLength: 1
nullable: true
status:
$ref: '#/components/schemas/ResponsesItemStatus'
encrypted_content:
type: string
minLength: 1
nullable: true
summary:
type: array
items:
$ref: '#/components/schemas/ResponsesSummaryPart'
content:
type: array
items:
$ref: '#/components/schemas/ResponsesReasoningPart'
ResponsesItemStatus:
type: string
nullable: true
enum: [completed, incomplete, in_progress]
ResponsesSummaryPart:
type: object
additionalProperties: false
required: [type, text]
properties:
type:
type: string
enum: [summary_text]
text:
type: string
ResponsesReasoningPart:
type: object
additionalProperties: false
required: [type, text]
properties:
type:
type: string
enum: [reasoning_text]
text:
type: string
ResponsesFunctionTool:
type: object
additionalProperties: false
required:
- type
- name
- parameters
- strict
properties:
type:
type: string
enum: [function]
name:
type: string
minLength: 1
maxLength: 64
pattern: '^[A-Za-z0-9_-]+$'
description:
type: string
nullable: true
parameters:
type: object
description: Strict JSON Schema with object root, every property required, and additionalProperties false.
additionalProperties: true
strict:
type: boolean
enum: [true]
ResponsesFunctionToolChoice:
type: object
additionalProperties: false
required:
- type
- name
properties:
type:
type: string
enum: [function]
name:
type: string
minLength: 1
maxLength: 64
pattern: '^[A-Za-z0-9_-]+$'
ResponseObject:
type: object
description: |
Canonical OpenAI-compatible Responses object. Provider credentials,
routing metadata, internal cost evidence, and execution metadata are never
included. The schema lists the complete phase-one output allowlist.
required:
- id
- object
- model
- status
- output
- created_at
- usage
additionalProperties: false
properties:
id:
type: string
example: "resp_abc123"
object:
type: string
enum: [response]
created_at:
type: integer
format: int64
model:
type: string
example: "qualified-model-id"
status:
type: string
enum: [completed, incomplete]
output:
type: array
items:
$ref: '#/components/schemas/ResponsesOutputItem'
example:
- type: message
role: assistant
content:
- type: output_text
text: A cinematic sunset over the ocean.
usage:
$ref: '#/components/schemas/ResponsesUsage'
completed_at:
type: integer
format: int64
minimum: 0
output_text:
type: string
incomplete_details:
type: object
additionalProperties: false
required: [reason]
properties:
reason:
type: string
minLength: 1
ResponsesOutputItem:
oneOf:
- $ref: '#/components/schemas/ResponsesOutputMessage'
- $ref: '#/components/schemas/ResponsesOutputFunctionCall'
- $ref: '#/components/schemas/ResponsesOutputReasoning'
ResponsesOutputMessage:
type: object
additionalProperties: false
required: [id, type, role, content]
properties:
id:
type: string
type:
type: string
enum: [message]
status:
$ref: '#/components/schemas/ResponsesItemStatus'
role:
type: string
enum: [assistant]
content:
type: array
items:
$ref: '#/components/schemas/ResponsesOutputContentPart'
ResponsesOutputContentPart:
oneOf:
- $ref: '#/components/schemas/ResponsesOutputTextPart'
- $ref: '#/components/schemas/ResponsesRefusalPart'
ResponsesOutputTextPart:
type: object
additionalProperties: false
required: [type, text]
properties:
type:
type: string
enum: [output_text]
text:
type: string
annotations:
type: array
items:
$ref: '#/components/schemas/ResponsesAnnotation'
ResponsesRefusalPart:
type: object
additionalProperties: false
required: [type, refusal]
properties:
type:
type: string
enum: [refusal]
refusal:
type: string
ResponsesAnnotation:
oneOf:
- $ref: '#/components/schemas/ResponsesFileCitation'
- $ref: '#/components/schemas/ResponsesURLCitation'
- $ref: '#/components/schemas/ResponsesContainerFileCitation'
- $ref: '#/components/schemas/ResponsesFilePath'
ResponsesFileCitation:
type: object
additionalProperties: false
required: [type, file_id, filename, index]
properties:
type:
type: string
enum: [file_citation]
file_id:
type: string
filename:
type: string
index:
type: integer
minimum: 0
ResponsesURLCitation:
type: object
additionalProperties: false
required: [type, url, title, start_index, end_index]
properties:
type:
type: string
enum: [url_citation]
url:
type: string
title:
type: string
start_index:
type: integer
minimum: 0
end_index:
type: integer
minimum: 0
ResponsesContainerFileCitation:
type: object
additionalProperties: false
required: [type, container_id, file_id, filename, start_index, end_index]
properties:
type:
type: string
enum: [container_file_citation]
container_id:
type: string
file_id:
type: string
filename:
type: string
start_index:
type: integer
minimum: 0
end_index:
type: integer
minimum: 0
ResponsesFilePath:
type: object
additionalProperties: false
required: [type, file_id, index]
properties:
type:
type: string
enum: [file_path]
file_id:
type: string
index:
type: integer
minimum: 0
ResponsesOutputFunctionCall:
type: object
additionalProperties: false
required: [id, type, call_id, name, arguments]
properties:
id:
type: string
type:
type: string
enum: [function_call]
status:
$ref: '#/components/schemas/ResponsesItemStatus'
call_id:
type: string
name:
type: string
arguments:
type: string
description: JSON encoded as a string.
ResponsesOutputReasoning:
type: object
additionalProperties: false
required: [id, type]
properties:
id:
type: string
type:
type: string
enum: [reasoning]
status:
$ref: '#/components/schemas/ResponsesItemStatus'
encrypted_content:
type: string
nullable: true
summary:
type: array
items:
$ref: '#/components/schemas/ResponsesSummaryPart'
content:
type: array
items:
$ref: '#/components/schemas/ResponsesReasoningPart'
ResponsesUsage:
type: object
additionalProperties: false
required: [input_tokens, output_tokens, total_tokens]
properties:
input_tokens:
type: integer
minimum: 0
input_tokens_details:
type: object
additionalProperties: false
properties:
cached_tokens:
type: integer
minimum: 0
output_tokens:
type: integer
minimum: 0
output_tokens_details:
type: object
additionalProperties: false
properties:
reasoning_tokens:
type: integer
minimum: 0
total_tokens:
type: integer
minimum: 0
AudioTranscriptionRequest:
type: object
required:
- file
- model
properties:
file:
type: string
format: binary
description: FLAC, MP3/MPEG/MPGA, MP4/M4A, OGG, WAV, or WebM audio up to 25 MB.
model:
type: string
example: openai/whisper-large-v3
description: >-
Must be in the environment's transcription allowlist. Requesting a
model outside it returns 400 without dispatching audio.
language:
type: string
maxLength: 32
description: Optional ISO language code.
response_format:
type: string
default: json
enum:
- json
- verbose_json
temperature:
type: number
minimum: 0
maximum: 1
timestamp_granularities[]:
type: array
description: Available only with `verbose_json`; repeat the multipart field for multiple values.
items:
type: string
enum:
- word
- segment
AudioSpeechRequest:
type: object
required:
- model
- input
- voice
properties:
model:
type: string
example: deepgram/aura-2
description: >-
Must be in the environment's speech catalog. An unlisted model
returns 400 without dispatching a synthesis.
input:
type: string
description: Text to synthesize. Bounded by the model's per-character limit (counted in Unicode runes).
voice:
type: string
example: aura-2-thalia-en
description: Model-specific, namespaced voice id. Must be allowlisted for the model.
response_format:
type: string
default: mp3
description: Audio format. Omitted defaults to `mp3`. Allowed values are per-model (e.g. `mp3`, `pcm`).
example: mp3
speed:
type: number
minimum: 0.25
maximum: 4.0
description: Playback speed. Accepted only for models that declare support; otherwise 400.
instructions:
type: string
description: Steering instructions. Accepted only for models that declare support; otherwise 400.
stream_format:
type: string
enum:
- audio
description: Only raw audio streaming is supported. `sse` is rejected with 400.
AudioTranscriptionResponse:
type: object
required:
- text
properties:
text:
type: string
language:
type: string
duration:
type: number
segments:
type: array
items:
type: object
additionalProperties: true
words:
type: array
items:
type: object
additionalProperties: true
usage:
type: object
additionalProperties: true
OpenAIErrorResponse:
type: object
description: OpenAI-compatible error envelope used by Responses, chat, audio, and realtime compatibility endpoints.
properties:
error:
type: object
properties:
message:
type: string
example: "Invalid API key"
type:
type: string
example: "authentication_error"
param:
type: string
example: "model"
code:
type: string
example: "invalid_api_key"
Webhook:
type: object
description: Webhook information without attempts
properties:
execution_id:
type: string
nullable: true
description: Execution identifier for tracking
example: "abc123-def456-ghi789"
url:
type: string
format: uri
description: Target URL for webhook delivery
example: "https://api.example.com/webhook"
request:
type: string
description: Original request payload
example: "{\"event\":\"prediction.completed\"}"
headers:
type: object
description: Headers included in the webhook request
additionalProperties:
type: string
example:
"Content-Type": "application/json"
source:
type: string
description: Service that triggered the webhook
example: "api-gateway"
created_at:
type: string
format: date-time
description: When the webhook was created
example: "2025-12-14T10:30:00Z"
required:
- url
- request
- source
- created_at
WebhookWithAttempts:
allOf:
- $ref: '#/components/schemas/Webhook'
- type: object
properties:
attempts:
type: array
description: Delivery attempts for this webhook
items:
$ref: '#/components/schemas/WebhookAttempt'
WebhookAttempt:
type: object
description: Individual webhook delivery attempt
properties:
status:
type: string
enum:
- PENDING
- SUCCESS
- FAILED
description: Status of the delivery attempt
example: "SUCCESS"
status_code:
type: integer
nullable: true
description: HTTP status code from target URL
example: 200
response:
type: string
nullable: true
description: Response body from target URL
example: "{\"received\":true}"
error_message:
type: string
nullable: true
description: Error message if delivery failed
example: "connection timeout"
created_at:
type: string
format: date-time
description: When the attempt was made
example: "2025-12-14T10:30:05Z"
required:
- status
- created_at
ListWebhooksResponse:
type: object
properties:
webhooks:
type: array
items:
$ref: '#/components/schemas/Webhook'
limit:
type: integer
description: Applied limit
example: 50
offset:
type: integer
description: Applied offset
example: 0
total_count:
type: integer
description: Total number of matching webhooks
example: 123
required:
- webhooks
- limit
- offset
- total_count
responses:
BadRequest:
description: Bad request
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "slug parameter is required"
NotFound:
description: Resource not found
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Failed to fetch model: model not found"
Unauthorized:
description: Authentication required or invalid credentials
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Invalid or missing API key"
PaymentRequired:
description: Insufficient balance
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "insufficient balance"
Forbidden:
description: Forbidden
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "forbidden"
Conflict:
description: Conflict with current resource state
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "conflict"
RequestTimeout:
description: Upstream request timed out
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Request timeout"
UnprocessableEntity:
description: Request body could not be processed
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "invalid request body"
BadGateway:
description: Upstream service unavailable
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "workflows-engine unavailable"
ServiceUnavailable:
description: Service temporarily unavailable
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "execution store unavailable"
InternalServerError:
description: Internal server error
headers:
X-Request-Id:
$ref: '#/components/headers/RequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Failed to fetch models: internal error"
Workflows API (OpenAPI 3.0.3)
openapi: 3.0.3
info:
title: Workflows Engine API
description: |
# Workflows Engine API
Execute and monitor AI workflows with a simple, consistent API.
## Quick Start
1. **Trigger a workflow** - POST to `https://api.eachlabs.ai/v1/workflows/trigger/{workflowID}/{versionID}` with your inputs
2. **Monitor execution** - GET `/executions/{executionID}` to check status
3. **Optional**: Provide a `webhook_url` to receive automatic notifications when complete
---
## Webhook Notifications
When you provide a `webhook_url` when triggering a workflow, the Workflows Engine will automatically
**POST** to your endpoint with the execution results when the workflow completes (successfully or with an error).
### Key Features
- ✅ **Consistent Payload**: The webhook payload has the same structure as the response from `GET /executions/{executionID}`
- ✅ **Automatic Retries**: Failed webhook deliveries are automatically retried with exponential backoff
- ✅ **Simple Integration**: No authentication required - your endpoint just needs to accept POST requests
### Webhook Request
The engine will POST to your `webhook_url` with:
- **Method**: `POST`
- **Content-Type**: `application/json`
- **Body**: Execution details (see payload structure below)
### Example Webhook Payload
```json
{
"execution_id": "69ae8c7b-7500-4a45-b7c0-348b8cc2665b",
"workflow_id": "50741f40-8621-4d46-8a91-dff4d873be98",
"status": "completed",
"started_at": "2025-12-04T11:48:10Z",
"inputs": {
"story": "tell an epic story of a rat graduating from law"
},
"step_outputs": {
"step1": {
"step_id": "step1",
"status": "completed",
"output": "Once upon a time in Ratropolis...",
"primary": "Once upon a time in Ratropolis...",
"metadata": {
"model": "openai-chatgpt-5",
"version": "0.0.1",
"params": {
"system_prompt": "You are a helpful assistant",
"max_output_tokens": 512
}
}
},
"step2": {
"step_id": "step2",
"status": "completed",
"output": [
"https://storage.googleapis.com/uploads/image1.png",
"https://storage.googleapis.com/uploads/image2.png"
],
"primary": "https://storage.googleapis.com/uploads/image1.png",
"metadata": {
"model": "nano-banana-pro",
"params": {
"num_images": 2,
"aspect_ratio": "16:9"
}
}
}
},
"output": [
"https://storage.googleapis.com/uploads/image1.png",
"https://storage.googleapis.com/uploads/image2.png"
]
}
```
### Example Webhook Handler
```python
@app.post("/webhooks/workflow-completed")
async def handle_workflow_webhook(request: Request):
execution = await request.json()
if execution["status"] == "completed":
# Process successful execution
final_output = execution["output"]
step_details = execution["step_outputs"]
print(f"Workflow completed: {final_output}")
elif execution["status"] == "failed":
# Handle failure
error = execution.get("error_cause", "Unknown error")
print(f"Workflow failed: {error}")
return {"received": True}
```
Your webhook endpoint should return a `200 OK` status to acknowledge receipt.
---
## Fallback Configuration
Model steps support an optional **fallback configuration** that automatically retries with an alternative
model if the primary model invocation fails. This provides resilience against temporary model outages
or rate limits.
### How Fallback Works
When a model step has fallback configured, the workflow engine will:
1. **Execute the primary model** with its configured parameters
2. **If the primary attempt fails**, automatically execute the fallback model configuration
3. **Mark the step as failed only if both primary and fallback attempts fail**
### Fallback Configuration
Add a `fallback` object to any model step in your workflow definition:
```json
{
"step_id": "generate_image",
"type": "model",
"model": "flux-dev",
"params": {
"prompt": "{{inputs.prompt}}",
"num_images": 1
},
"fallback": {
"enabled": true,
"model": "flux-1-1-pro",
"params": {
"prompt": "{{inputs.prompt}}",
"guidance_scale": 7.5
}
}
}
```
### Fallback Properties
| Property | Type | Description |
|----------|------|-------------|
| `enabled` | boolean | Enable or disable fallback for this step (default: `true`) |
| `model` | string | Alternative AI model identifier to use on failure |
| `version` | string | Fallback model version (defaults to primary version when omitted) |
| `params` | object | Model-specific parameters for the fallback (supports template variables) |
### Detecting Fallback Usage
When a fallback is used, the step's metadata in execution results will include:
- `fallback_used: true` - Indicates the step completed using the fallback configuration
- `primary_error` - Human-readable description of why the primary attempt failed
### Example Execution Response with Fallback
```json
{
"step_id": "generate_image",
"status": "completed",
"output": "https://storage.googleapis.com/uploads/image.png",
"metadata": {
"model": "flux-1-1-pro",
"fallback_used": true,
"primary_error": "Primary model flux-dev failed: rate limit exceeded"
}
}
```
---
## Unlisted Workflows
Versions can be "unlisted" - accessible via direct link but hidden from public listings.
**Visibility states:**
- **Private** (default): Organization only
- **Unlisted**: Accessible via link, hidden from public listings
- **Public**: Visible in listings and accessible to everyone
**Setting unlisted:** Use `allowed_to_share: true` when creating or updating a version.
```bash
# Create unlisted version
PUT /workflows/{workflowID}/versions/v1 { "allowed_to_share": true, ... }
```
**Behavior:**
- `allowed_to_share: true` → Creates/updates as unlisted
- `allowed_to_share: false` → Creates/updates as private
- No effect on public versions (never downgraded)
version: 1.0.0
x-logo:
url: './logo-white.svg'
altText: 'Eachlabs Logo'
servers:
- url: https://workflows.eachlabs.run/api/v1
description: Production server
security:
- BearerAuth: []
tags:
- name: Categories
description: Workflow categories for organization
- name: Workflows
description: Workflow and version management
- name: Executions
description: Workflow execution and monitoring
- name: Public Workflows
description: Access public and unlisted workflows by organization nickname
paths:
/categories:
get:
tags:
- Categories
summary: List categories
description: |
Retrieve all available workflow categories.
Categories help organize workflows into logical collections.
Use the category slugs when creating or updating workflows.
operationId: listCategories
responses:
'200':
description: Categories retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/CategoriesResponse'
/workflows:
post:
tags:
- Workflows
summary: Create workflow
description: |
Create a new workflow with an initial version and generate its state machine.
This endpoint creates both the workflow metadata and its first version in a single operation.
The workflow definition must include at least one step.
operationId: createWorkflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateWorkflowRequest'
responses:
'201':
description: Workflow created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowDetail'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/workflows/{workflowID}:
parameters:
- $ref: '#/components/parameters/WorkflowID'
get:
tags:
- Workflows
summary: Get workflow details
description: |
Retrieve detailed information about a workflow including all its versions.
You can specify either the workflow UUID or slug as the workflowID parameter.
operationId: getWorkflow
responses:
'200':
description: Workflow details retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowDetail'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
put:
tags:
- Workflows
summary: Update workflow
description: |
Update workflow metadata such as name, description, categories, and visibility.
Note: To update the workflow definition (steps), use the version endpoints instead.
Locked workflows cannot be modified unless you're unlocking them.
operationId: updateWorkflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateWorkflowRequest'
responses:
'200':
description: Workflow updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowDetail'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/workflows/{workflowID}/versions/{versionID}:
parameters:
- $ref: '#/components/parameters/WorkflowID'
- name: versionID
in: path
required: true
description: Version identifier (e.g., "v1", "v2")
schema:
type: string
example: v1
put:
tags:
- Workflows
summary: Create or update workflow version
description: |
Create a new workflow version or update an existing one.
This operation will:
- Create/update the version in the database
- Generate or update the AWS Step Functions state machine
- Set this version as the latest version for the workflow
On updates, `definition` may be omitted to keep the stored definition —
useful for flag-only changes or updating `output_mapping` on its own.
**Important**: Once a version is locked, it cannot be modified.
operationId: upsertWorkflowVersion
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpsertVersionRequest'
responses:
'200':
description: Workflow version created or updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowVersionSummary'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/{workflowID}/trigger:
parameters:
- name: workflowID
in: path
required: true
description: Unique workflow identifier
schema:
type: string
example: wf_abc123
post:
tags:
- Executions
summary: Trigger workflow (deprecated)
deprecated: true
description: |
> **⚠️ Deprecated.** This version-less endpoint is deprecated and will be
> removed on **1 August 2026**. Single workflow trigger has moved to
> api-service; use `POST https://api.eachlabs.ai/v1/workflows/trigger/{workflowID}/{versionID}`
> instead so the triggered version is explicit. Deprecated responses carry
> `Deprecation`, `Sunset`, `Link` (`rel="successor-version"`), and `Warning` headers.
Start an execution of the workflow.
By default, this triggers the latest version of the workflow.
You can optionally specify a version_id in the request body to trigger a specific version.
The workflow is executed asynchronously - you'll receive an execution ID immediately,
then poll `/executions/{executionID}` to check status and retrieve results.
operationId: triggerWorkflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TriggerWorkflowRequest'
responses:
'202':
description: Workflow execution started
content:
application/json:
schema:
$ref: '#/components/schemas/ExecutionResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/{workflowID}/bulk-trigger:
parameters:
- name: workflowID
in: path
required: true
description: Unique workflow identifier
schema:
type: string
example: wf_abc123
post:
tags:
- Executions
summary: Bulk trigger workflow (deprecated)
deprecated: true
description: |
> **⚠️ Deprecated.** This version-less endpoint is deprecated and will be
> removed on **1 August 2026**. Use the versioned successor
> `POST /workflows/{workflowID}/versions/{versionID}/bulk-trigger` instead so
> the triggered version is explicit. Deprecated responses carry `Deprecation`,
> `Sunset`, `Link` (`rel="successor-version"`), and `Warning` headers.
Start multiple executions of the same workflow with different inputs in parallel.
This endpoint is useful for batch processing scenarios where you need to run the same
workflow multiple times with different input parameters.
**Key Features:**
- Maximum 10 executions per request
- All executions share the same `bulk_id` for tracking
- Executions run in parallel
- Partial failures are allowed - some executions may succeed while others fail
- Optional webhook notification when all executions complete
**Bulk ID:**
All executions from a bulk trigger share a unique `bulk_id` that can be used to:
- Query all executions from a single bulk operation
- Track batch progress
- Correlate webhook notifications
**Webhook Behavior:**
When a `webhook_url` is provided, the notification payload includes the `bulk_id`
field to help you identify and correlate executions from the same bulk operation.
operationId: bulkTriggerWorkflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BulkTriggerWorkflowRequest'
responses:
'202':
description: Bulk workflow executions started
content:
application/json:
schema:
$ref: '#/components/schemas/BulkTriggerWorkflowResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/workflows/{workflowID}/versions/{versionID}/bulk-trigger:
parameters:
- $ref: '#/components/parameters/WorkflowID'
- name: versionID
in: path
required: true
description: Version identifier (e.g., "v1", "v2")
schema:
type: string
example: v1
post:
tags:
- Executions
summary: Bulk trigger workflow version
description: |
Start multiple executions of a specific workflow version with different inputs
in parallel. This is the versioned successor to the deprecated
`POST /{workflowID}/bulk-trigger` endpoint — the version to run is taken from
the URL path rather than the request body.
**Key Features:**
- Maximum 10 executions per request
- All executions share the same `bulk_id` for tracking
- Executions run in parallel
- Partial failures are allowed - some executions may succeed while others fail
- Optional webhook notification when all executions complete
operationId: bulkTriggerWorkflowVersion
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BulkTriggerWorkflowRequest'
responses:
'202':
description: Bulk workflow executions started
content:
application/json:
schema:
$ref: '#/components/schemas/BulkTriggerWorkflowResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/workflows/{workflowID}/executions:
parameters:
- $ref: '#/components/parameters/WorkflowID'
get:
tags:
- Executions
summary: List workflow executions
description: |
Retrieve a paginated list of executions for a specific workflow.
**Filtering by Bulk ID:**
When a `bulk_id` query parameter is provided, the endpoint returns only executions
from that specific bulk operation. This allows you to:
- Track progress of a batch of executions
- Retrieve all executions triggered via the bulk-trigger endpoint
- Correlate webhook notifications from the same bulk operation
**Usage Example:**
```bash
# 1. Trigger a bulk operation
POST /workflows/wf-123/bulk-trigger
{
"inputs": [
{"prompt": "sunset"},
{"prompt": "mountains"},
{"prompt": "ocean"}
]
}
# Returns: { "bulk_id": "550e8400-e29b-41d4-a716-446655440000", ... }
# 2. List all executions from that bulk operation
GET /workflows/wf-123/executions?bulk_id=550e8400-e29b-41d4-a716-446655440000
```
**Note:** When filtering by `bulk_id`, pagination still applies using the same
`offset` and `limit` parameters.
operationId: listWorkflowExecutions
parameters:
- name: bulk_id
in: query
description: |
**Optional**: Filter executions by bulk operation ID.
When provided, only returns executions from the specified bulk trigger operation.
The `bulk_id` is returned when you trigger a workflow via the bulk-trigger endpoint.
schema:
type: string
format: uuid
example: "550e8400-e29b-41d4-a716-446655440000"
- name: offset
in: query
description: |
Pagination offset - number of items to skip.
The response includes a `offset` field pointing to the next page if more results exist.
schema:
type: integer
format: int32
default: 0
minimum: 0
example: 0
- name: limit
in: query
description: |
Maximum number of executions to return per page.
Default: 50
Maximum: 100
schema:
type: integer
format: int32
default: 50
minimum: 1
maximum: 100
example: 50
responses:
'200':
description: List of workflow executions
content:
application/json:
schema:
$ref: '#/components/schemas/ListExecutionsResponse'
examples:
normal:
summary: Normal workflow execution list
value:
executions:
- execution_id: "exec-1"
flow_id: "wf-123"
flow_name: "Text to Image Generator"
version_id: "v1"
status: "completed"
started_at: "2025-12-20T10:00:00Z"
ended_at: "2025-12-20T10:05:00Z"
created_at: "2025-12-20T10:00:00Z"
updated_at: "2025-12-20T10:05:00Z"
- execution_id: "exec-2"
flow_id: "wf-123"
flow_name: "Text to Image Generator"
version_id: "v1"
status: "running"
started_at: "2025-12-20T10:10:00Z"
created_at: "2025-12-20T10:10:00Z"
updated_at: "2025-12-20T10:10:00Z"
offset: 2
total_count: 142
bulk_filtered:
summary: Executions filtered by bulk_id
value:
executions:
- execution_id: "exec-1"
flow_id: "wf-123"
flow_name: "Batch Image Generator"
version_id: "v1"
status: "completed"
started_at: "2025-12-20T10:00:00Z"
ended_at: "2025-12-20T10:05:00Z"
created_at: "2025-12-20T10:00:00Z"
updated_at: "2025-12-20T10:05:00Z"
- execution_id: "exec-2"
flow_id: "wf-123"
flow_name: "Batch Image Generator"
version_id: "v1"
status: "completed"
started_at: "2025-12-20T10:00:01Z"
ended_at: "2025-12-20T10:05:15Z"
created_at: "2025-12-20T10:00:01Z"
updated_at: "2025-12-20T10:05:15Z"
- execution_id: "exec-3"
flow_id: "wf-123"
flow_name: "Batch Image Generator"
version_id: "v1"
status: "completed"
started_at: "2025-12-20T10:00:02Z"
ended_at: "2025-12-20T10:04:58Z"
created_at: "2025-12-20T10:00:02Z"
updated_at: "2025-12-20T10:04:58Z"
total_count: 3
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/executions/{executionID}:
parameters:
- $ref: '#/components/parameters/ExecutionID'
get:
tags:
- Executions
summary: Get execution details
description: Get the current status and output of a workflow execution
operationId: getExecution
responses:
'200':
description: Execution details
content:
application/json:
schema:
$ref: '#/components/schemas/ExecutionDetails'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/public/@{nickname}/workflows/{slug}/versions/{versionID}:
parameters:
- $ref: '#/components/parameters/Nickname'
- $ref: '#/components/parameters/Slug'
- $ref: '#/components/parameters/VersionID'
get:
tags:
- Public Workflows
summary: Get public workflow version
description: |
Retrieve a specific version of a public or unlisted workflow by organization nickname.
**No authentication required** for unlisted workflows.
Example:
```
GET /api/v1/public/@acme-corp/workflows/my-generator/versions/v1
```
operationId: getPublicWorkflowVersionByNickname
responses:
'200':
description: Workflow version details
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowVersionSummary'
'404':
$ref: '#/components/responses/NotFound'
/public/@{nickname}/workflows/{slug}/versions/{versionID}/trigger:
parameters:
- $ref: '#/components/parameters/Nickname'
- $ref: '#/components/parameters/Slug'
- $ref: '#/components/parameters/VersionID'
post:
tags:
- Public Workflows
summary: Trigger public workflow version
description: |
Trigger a public or unlisted workflow using the organization nickname, workflow slug, and version.
**Authentication required** - provide API key via `Authorization: Bearer <api_key>` header or `api_key` in request body.
Example:
```
POST /api/v1/public/@acme-corp/workflows/my-generator/versions/v1/trigger
Headers: Authorization: Bearer your-api-key
Body: { "inputs": { "prompt": "..." } }
```
Or with API key in body:
```
POST /api/v1/public/@acme-corp/workflows/my-generator/versions/v1/trigger
Body: { "api_key": "your-api-key", "inputs": { "prompt": "..." } }
```
operationId: triggerPublicWorkflowVersionByNickname
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TriggerWorkflowRequest'
responses:
'202':
description: Workflow execution started
content:
application/json:
schema:
$ref: '#/components/schemas/ExecutionResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
description: 'API key passed as `Authorization: Bearer <api_key>`'
parameters:
WorkflowID:
name: workflowID
in: path
required: true
description: Unique workflow identifier
schema:
type: string
example: wf_abc123
ExecutionID:
name: executionID
in: path
required: true
description: Execution identifier
schema:
type: string
example: exec_xyz789
Nickname:
name: nickname
in: path
required: true
description: Organization nickname (without @ prefix)
schema:
type: string
example: acme-corp
Slug:
name: slug
in: path
required: true
description: Workflow slug
schema:
type: string
example: my-image-generator
VersionID:
name: versionID
in: path
required: true
description: Version identifier
schema:
type: string
example: v1
schemas:
CategoriesResponse:
type: object
required:
- categories
properties:
categories:
type: array
items:
$ref: '#/components/schemas/Category'
description: List of available workflow categories
example:
categories:
- slug: "image-generation"
label: "Image Generation"
description: "Workflows for generating images"
is_default: true
created_at: "2025-12-01T10:00:00Z"
updated_at: "2025-12-01T10:00:00Z"
- slug: "text-processing"
label: "Text Processing"
description: "Workflows for processing text"
is_default: true
created_at: "2025-12-01T10:00:00Z"
updated_at: "2025-12-01T10:00:00Z"
Category:
type: object
required:
- slug
- label
properties:
slug:
type: string
description: Unique category identifier (used in API requests)
example: "image-generation"
label:
type: string
description: Human-readable category name
example: "Image Generation"
description:
type: string
description: Category description
example: "Workflows for generating images"
is_default:
type: boolean
description: Whether this is a default system category
example: true
created_at:
type: string
format: date-time
description: RFC3339 timestamp when category was created
example: "2025-12-01T10:00:00Z"
updated_at:
type: string
format: date-time
description: RFC3339 timestamp when category was last updated
example: "2025-12-01T10:00:00Z"
CreateWorkflowRequest:
type: object
required:
- name
- description
- definition
properties:
name:
type: string
description: Human-readable workflow name
example: "Text to Image Generator"
description:
type: string
description: Detailed description of what the workflow does
example: "Generates images from text prompts using AI models"
categories:
type: array
items:
type: string
description: List of category slugs to organize the workflow
example: ["image-generation", "ai"]
locked:
type: boolean
description: Whether the workflow is locked from modifications
default: false
production:
type: boolean
description: Whether this is a production workflow
default: false
definition:
type: object
description: |
The workflow definition containing steps, input schema, and metadata.
This follows the workflow DSL format.
properties:
version:
type: string
description: Version identifier for this workflow
example: "v1"
steps:
type: array
description: Array of step definitions
items:
type: object
example:
- step_id: "step1"
type: "model"
model: "openai-chatgpt-5"
params:
system_prompt: "You are a helpful assistant"
user_prompt: "{{inputs.prompt}}"
fallback:
enabled: true
model: "anthropic-claude-4"
params:
system_prompt: "You are a helpful assistant"
user_prompt: "{{inputs.prompt}}"
input_schema:
type: object
description: JSON Schema for validating workflow inputs
example:
type: "object"
properties:
prompt:
type: "string"
description: "Text prompt for generation"
required: ["prompt"]
metadata:
type: object
description: |
Additional workflow metadata.
`output_mapping` shapes the `workflow_output` field delivered in the
webhook payload and `GET /executions/{executionID}` response. Keys are
free-form (name them whatever your consumer expects); values are
`$.`-prefixed references into step outputs and support array indexing:
- `$.<step_id>.output` — the step's raw output (array/object/string)
- `$.<step_id>.primary` — always the single primary asset URL
- `$.<step_id>.output[0]` — element of an array output
A reference that cannot be resolved keeps its key and yields
`{"status": "unknown", "error": "output_resolution_failed"}`.
example:
output_mapping:
output_json: "$.step1.output"
image_1: "$.step2.output[0]"
image_2: "$.step2.output[1]"
UpdateWorkflowRequest:
type: object
description: |
Update workflow metadata. All fields are optional - only provided fields will be updated.
Note: To update the workflow definition (steps), use the version endpoints instead.
properties:
name:
type: string
description: Human-readable workflow name
example: "Text to Image Generator v2"
description:
type: string
description: Detailed description of what the workflow does
example: "Enhanced image generation with multiple models"
categories:
type: array
items:
type: string
description: List of category slugs
example: ["image-generation", "ai", "production"]
locked:
type: boolean
description: Lock/unlock the workflow from modifications
production:
type: boolean
description: Mark as production workflow
UpsertVersionRequest:
type: object
required:
- version_id
properties:
version_id:
type: string
description: Version identifier (e.g., "v1", "v2", "v1.1")
example: "v2"
definition:
type: object
description: |
The complete workflow definition for this version.
This follows the workflow DSL format and must include all steps.
Required when creating a new version. When omitted on an update,
the stored definition is kept unchanged (flag-only or
`output_mapping`-only patches).
properties:
version:
type: string
description: Version identifier (should match version_id)
example: "v2"
steps:
type: array
description: Array of step definitions
items:
type: object
example:
- step_id: "generate_text"
type: "model"
model: "openai-chatgpt-5"
params:
system_prompt: "You are a helpful assistant"
user_prompt: "{{inputs.prompt}}"
- step_id: "generate_image"
type: "model"
model: "flux-dev"
params:
prompt: "{{generate_text.output}}"
num_images: 2
fallback:
enabled: true
model: "flux-1-1-pro"
params:
prompt: "{{generate_text.output}}"
guidance_scale: 7.5
input_schema:
type: object
description: JSON Schema for validating workflow inputs
metadata:
type: object
description: Additional workflow metadata including output mappings
output_mapping:
type: object
additionalProperties:
type: string
description: |
Sets `metadata.output_mapping` on the effective definition without
sending the full definition. Keys are free-form; values must be
`$.`-prefixed step-output references (array indexing supported,
e.g. `$.step.output[0]`).
Omit to leave the stored mapping unchanged; send `{}` to clear it.
When sent together with `definition`, this field wins over the
definition's own `metadata.output_mapping`.
example:
image_1: "$.generate_image.output[0]"
image_2: "$.generate_image.output[1]"
locked:
type: boolean
description: Whether to lock this version from further modifications
default: false
production:
type: boolean
description: Whether this is a production version
default: false
allowed_to_share:
type: boolean
description: |
Toggle unlisted sharing for this version.
- `true` → Makes version "unlisted" (accessible via link, hidden from public listings)
- `false` → Makes version "private" (organization only)
- No effect on public versions
- New versions default to private when omitted
default: false
WorkflowDetail:
type: object
description: Complete workflow information including all versions
required:
- workflow_id
- name
- description
properties:
workflow_id:
type: string
format: uuid
description: Unique workflow identifier
example: "50741f40-8621-4d46-8a91-dff4d873be98"
slug:
type: string
description: URL-friendly workflow identifier
example: "text-to-image-generator"
name:
type: string
description: Human-readable workflow name
example: "Text to Image Generator"
description:
type: string
description: Workflow description
example: "Generates images from text prompts"
categories:
type: array
items:
type: string
description: Category slugs
example: ["image-generation", "ai"]
locked:
type: boolean
description: Whether workflow is locked
example: false
production:
type: boolean
description: Whether this is a production workflow
example: false
latest_version_id:
type: string
description: Most recently updated version ID
example: "v2"
status:
type: string
enum: [active, archived, deleted]
description: Workflow lifecycle status
example: active
trigger_count:
type: integer
description: Total number of times this workflow has been triggered
example: 42
created_at:
type: string
format: date-time
description: RFC3339 timestamp when workflow was created
example: "2025-12-01T10:00:00Z"
updated_at:
type: string
format: date-time
description: RFC3339 timestamp when workflow was last updated
example: "2025-12-07T15:30:00Z"
versions:
type: array
description: List of all versions for this workflow
items:
$ref: '#/components/schemas/WorkflowVersionSummary'
WorkflowVersionSummary:
type: object
description: Summary information about a workflow version
required:
- version_id
- slug
properties:
version_id:
type: string
description: Version identifier
example: "v1"
slug:
type: string
description: Workflow slug (inherited from parent workflow)
example: "text-to-image-generator"
locked:
type: boolean
description: Whether this version is locked from modifications
example: false
production:
type: boolean
description: Whether this is a production version
example: true
allowed_to_share:
type: boolean
description: True if version is unlisted (accessible via direct link but hidden from public listings).
example: false
trigger_count:
type: integer
description: Number of times this specific version has been triggered
example: 15
created_at:
type: string
format: date-time
description: RFC3339 timestamp when version was created
example: "2025-12-01T10:00:00Z"
updated_at:
type: string
format: date-time
description: RFC3339 timestamp when version was last updated
example: "2025-12-05T14:20:00Z"
status:
type: string
enum: [active, archived, deleted]
description: Version lifecycle status
example: active
definition:
type: object
description: |
The complete workflow definition (only included in detail responses).
Contains steps, input schema, and metadata.
TriggerWorkflowRequest:
type: object
properties:
version_id:
type: string
description: |
**Optional**: Specific version to trigger.
If not provided, triggers the latest version of the workflow.
This allows you to execute specific versions for testing or comparison.
example: "v1"
inputs:
type: object
description: Input parameters defined in the workflow definition for the workflow
example:
text: "tell me a story"
webhook_url:
type: string
format: uri
description: |
**Optional webhook URL for execution completion notifications.**
When provided, the engine will POST an `ExecutionDetails` payload to this URL
when the workflow finishes (either successfully or with an error).
See the **Webhooks** section for detailed information about webhook behavior and payload structure.
example: https://your-api.com/webhooks/workflow-completed
BulkTriggerWorkflowRequest:
type: object
required:
- inputs
properties:
version_id:
type: string
description: |
**Optional**: Specific version to trigger for all executions.
If not provided, triggers the latest version of the workflow.
example: "v1"
inputs:
type: array
minItems: 1
maxItems: 10
description: |
Array of input parameter objects. Each item in the array will result in a separate workflow execution.
**Constraints:**
- Minimum 1 input required
- Maximum 10 inputs per request
All inputs must conform to the workflow's input schema.
items:
type: object
additionalProperties: true
example:
- prompt: "Generate image of a sunset"
style: "photorealistic"
- prompt: "Generate image of mountains"
style: "artistic"
- prompt: "Generate image of ocean"
style: "minimalist"
webhook_url:
type: string
format: uri
description: |
**Optional webhook URL for execution completion notifications.**
When provided, webhook notifications for all executions in this bulk operation
will include a `bulk_id` field to help you correlate them.
Each execution sends its own webhook notification when it completes.
example: https://your-api.com/webhooks/workflow-completed
BulkTriggerWorkflowResponse:
type: object
required:
- bulk_id
- executions
properties:
bulk_id:
type: string
format: uuid
description: |
Unique identifier for this bulk operation. All executions from this bulk trigger
share this ID, which can be used to query and track them together.
example: "550e8400-e29b-41d4-a716-446655440000"
executions:
type: array
description: |
Array of execution responses, one per input in the request.
The order matches the order of inputs in the request.
**Partial Failures:**
If some inputs fail validation, those entries will have `status: "failed"`
with an error message, while successful triggers will have `status: "queued"`.
items:
$ref: '#/components/schemas/ExecutionResponse'
example:
- execution_id: "exec-1"
status: "queued"
started_at: "2025-12-20T10:00:00Z"
- execution_id: "exec-2"
status: "queued"
started_at: "2025-12-20T10:00:01Z"
- status: "failed"
message: "invalid input: missing required field 'prompt'"
ExecutionResponse:
type: object
description: Response from triggering a workflow execution
properties:
execution_id:
type: string
description: Unique execution identifier (only present for successful triggers)
example: exec_xyz789
status:
type: string
enum: [queued, failed]
description: |
Execution status immediately after triggering.
- `queued`: Workflow execution has been queued and will start shortly
- `failed`: Trigger failed due to validation or system error
example: queued
started_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution was queued (only present for successful triggers)
message:
type: string
description: Error message (only present when status is "failed")
example: "invalid input: missing required field"
example:
execution_id: "e2dba2bb-bc1d-4651-b6bf-fbbbebdee104"
status: "queued"
started_at: "2025-12-03T10:21:09Z"
ListExecutionsResponse:
type: object
description: Paginated list of workflow executions
required:
- executions
- total_count
properties:
executions:
type: array
description: Array of execution summaries
items:
$ref: '#/components/schemas/ExecutionSummary'
offset:
type: integer
format: int32
description: |
Offset for the next page of results.
If null, there are no more results to fetch.
example: 50
total_count:
type: integer
format: int32
description: Total number of executions matching the query
example: 142
example:
executions:
- execution_id: "exec-1"
flow_id: "wf-123"
flow_name: "Text to Image Generator"
version_id: "v1"
status: "completed"
started_at: "2025-12-20T10:00:00Z"
ended_at: "2025-12-20T10:05:00Z"
created_at: "2025-12-20T10:00:00Z"
updated_at: "2025-12-20T10:05:00Z"
- execution_id: "exec-2"
flow_id: "wf-123"
flow_name: "Text to Image Generator"
version_id: "v1"
status: "running"
started_at: "2025-12-20T10:10:00Z"
created_at: "2025-12-20T10:10:00Z"
updated_at: "2025-12-20T10:10:00Z"
offset: 2
total_count: 142
ExecutionSummary:
type: object
description: |
Summary information about a workflow execution, used in list responses.
Contains less detail than `ExecutionDetails` to optimize list queries.
required:
- execution_id
- flow_id
- flow_name
- version_id
- status
- created_at
properties:
execution_id:
type: string
description: Unique execution identifier
example: "exec-1"
flow_id:
type: string
description: Workflow identifier (UUID)
example: "wf-123"
flow_name:
type: string
description: Human-readable workflow name
example: "Text to Image Generator"
version_id:
type: string
description: Version identifier for this execution
example: "v1"
api_key:
type: string
description: User/API key identifier that triggered this execution
example: "user-456"
status:
type: string
enum: [running, completed, failed, cancelled]
description: Current execution status
example: "completed"
started_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution started (omitted if not started yet)
example: "2025-12-20T10:00:00Z"
ended_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution completed (omitted for running executions)
example: "2025-12-20T10:05:00Z"
created_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution was created
example: "2025-12-20T10:00:00Z"
updated_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution was last updated
example: "2025-12-20T10:05:00Z"
deleted_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution was soft-deleted (if applicable)
example: null
inputs:
type: object
description: Input parameters provided when triggering the workflow
additionalProperties: true
example:
prompt: "Generate an image of a sunset"
step_results:
description: |
Brief summary of step execution results.
Structure varies - may be an object with step IDs as keys, or an array.
oneOf:
- type: object
- type: array
items: {}
output:
type: string
description: |
Simple string output from the workflow (if available).
For complex outputs, use `output_json` instead.
example: "https://storage.googleapis.com/uploads/image.png"
output_json:
type: object
description: |
Structured JSON output from the workflow (if available).
Contains the full workflow output when output mapping is configured.
additionalProperties: true
example:
image_url: "https://storage.googleapis.com/uploads/image.png"
generation_time: 2.45
cost:
description: |
Cost information for this execution (if available).
Structure varies by provider and workflow configuration.
oneOf:
- type: object
- type: number
ExecutionDetails:
type: object
description: |
Execution details returned by both `GET /executions/{id}` and webhook notifications.
### Webhook Notifications
When you provide a `webhook_url` when triggering a workflow, this same structure
will be POSTed to your webhook URL when the execution completes.
required:
- execution_id
- workflow_id
- status
properties:
execution_id:
type: string
description: Unique execution identifier (UUID format)
example: "69ae8c7b-7500-4a45-b7c0-348b8cc2665b"
workflow_id:
type: string
description: Workflow identifier (UUID format)
example: "50741f40-8621-4d46-8a91-dff4d873be98"
bulk_id:
type: string
format: uuid
description: |
Bulk operation identifier (only present for executions triggered via bulk-trigger endpoint).
All executions from the same bulk operation share this ID, allowing you to:
- Query all executions from a bulk operation together
- Track batch progress
- Correlate webhook notifications from related executions
example: "550e8400-e29b-41d4-a716-446655440000"
status:
type: string
enum: [running, completed, failed, cancelled]
description: |
Current execution status:
- `running` - Workflow is currently executing
- `completed` - Workflow finished successfully
- `failed` - Workflow encountered an error
- `cancelled` - Workflow was cancelled by user
example: completed
started_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution started
example: "2025-12-04T11:48:10Z"
completed_at:
type: string
format: date-time
description: RFC3339 timestamp when the execution completed (omitted for running executions)
example: "2025-12-04T11:50:53Z"
inputs:
type: object
description: Input parameters provided when triggering the workflow
additionalProperties: true
example:
story: "tell an epic story of a rat graduating from law"
step_outputs:
type: object
description: |
Complete outputs from all workflow steps. Each key is a step ID (e.g., "step1", "step2"),
and the value is a `StepOutput` object containing detailed information about that step's execution.
additionalProperties:
$ref: '#/components/schemas/StepOutput'
example:
step1:
step_id: "step1"
status: "completed"
started_at: "2025-12-04T11:48:10Z"
completed_at: "2025-12-04T11:48:39Z"
output: "Once upon a time..."
primary: "Once upon a time..."
metadata:
model: "openai-chatgpt-5"
version: "0.0.1"
params:
system_prompt: "You are a helpful assistant"
user_prompt: "tell an epic story of a rat graduating from law"
max_output_tokens: 512
step2:
step_id: "step2"
status: "completed"
started_at: "2025-12-04T11:48:39Z"
completed_at: "2025-12-04T11:50:53Z"
output:
- "https://storage.googleapis.com/uploads/image1.png"
- "https://storage.googleapis.com/uploads/image2.png"
primary: "https://storage.googleapis.com/uploads/image1.png"
metadata:
model: "nano-banana-pro"
version: "0.0.1"
params:
prompt: "Generate images based on the story"
num_images: 2
aspect_ratio: "16:9"
resolution: "1K"
output_format: "png"
output:
description: |
Output from the last completed step in the workflow.
This provides quick access to the final result without navigating step_outputs.
**Important**: This field is only populated when the workflow status is `completed`.
- For `running` workflows: null (even if some steps have completed)
- For `failed` workflows: null
- For `cancelled` workflows: null
- For `completed` workflows: contains output from the chronologically last step
The last step is determined by the latest `completed_at` timestamp.
The value is extracted from the step's `output` field, or `primary` field as fallback.
The type varies based on the workflow's final step:
- Text generation steps return a string
- Image generation steps return an array of URLs
- Structured data steps return an object
oneOf:
- type: string
- type: array
items: {}
- type: object
- type: 'null'
example:
- "https://storage.googleapis.com/uploads/image1.png"
- "https://storage.googleapis.com/uploads/image2.png"
workflow_output:
type: object
description: |
Custom-shaped output resolved from the version's
`metadata.output_mapping`. Only present when the definition declares
a mapping and the execution completed. Keys are exactly the mapping
keys; an unresolvable reference keeps its key with
`{"status": "unknown", "error": "output_resolution_failed"}`.
The same field is included in the webhook payload.
additionalProperties: true
example:
image_1: "https://storage.googleapis.com/uploads/image1.png"
image_2: "https://storage.googleapis.com/uploads/image2.png"
error:
type: string
description: High-level error message (only present if status is "failed")
example: "ExecutionFailed"
error_cause:
type: string
description: Detailed error cause with context (only present if status is "failed")
example: "Step 'generate_image' failed: Model timeout after 30s"
StepOutput:
type: object
description: |
Detailed information about a single step's execution within a workflow.
Each step in the workflow produces one of these objects.
required:
- step_id
- status
properties:
step_id:
type: string
description: Unique identifier for this step within the workflow
example: "step1"
status:
type: string
enum: [queued, running, completed, failed, cancelled]
description: |
Current status of this step:
- `queued` - Step is waiting to execute
- `running` - Step is currently executing
- `completed` - Step finished successfully
- `failed` - Step encountered an error
- `cancelled` - Step was skipped due to an earlier failure in the workflow
example: "completed"
started_at:
type: string
format: date-time
description: RFC3339 timestamp when the step started executing (omitted for queued steps)
example: "2025-12-04T11:48:10Z"
completed_at:
type: string
format: date-time
description: RFC3339 timestamp when the step finished (only present if completed or failed)
example: "2025-12-04T11:48:39Z"
output:
description: |
The step's output data. The structure depends on the step type:
- Text generation: string
- Image generation: array of URLs
- Structured data: object
- Multi-output steps: array or object with multiple values
oneOf:
- type: string
- type: array
items: {}
- type: object
- type: number
- type: boolean
example: "Once upon a time in the city of Ratropolis..."
primary:
description: |
The primary output value from this step. For steps that return multiple values
(e.g., generating multiple images), this field contains the first/main result
for quick access.
oneOf:
- type: string
- type: number
- type: boolean
- type: object
example: "https://storage.googleapis.com/uploads/image1.png"
input:
type: object
description: |
The complete resolved input payload sent to this step handler.
This includes the full step configuration with all template values resolved:
- `step_id` - The step identifier
- `type` or `model` - The step type or model name
- `version` - API version (if applicable)
- `params` or `inputs` - All parameters with values from previous steps resolved
Values like `{{step1.output}}` are replaced with actual data from prior steps.
additionalProperties: true
example:
step_id: "generate_image"
model: "flux-dev"
version: "1.0.0"
params:
prompt: "Generate an image of a rat graduating from law school"
num_images: 2
metadata:
type: object
description: |
Step configuration and runtime information. Fields vary by step type:
**Model steps** include:
- `model` - The AI model identifier (e.g., "openai-chatgpt-5")
- `version` - API version for the step handler (e.g., "0.0.1")
- `params` - All parameters sent to the model
- `prediction_id` - External prediction ID (when completed)
- `processed_at` - Processing timestamp (when completed)
- `fallback_used` - Present and `true` when the primary attempt failed and the fallback configuration was used
- `primary_error` - Human-readable error message from the primary attempt when fallback was used
**HTTP steps** include:
- `type` - Always "http"
- `method` - HTTP method (e.g., "GET", "POST")
- `url` - Request URL
- `params` - Request parameters
- `elapsed_seconds` - Request duration (when completed)
**Python steps** include:
- `type` - Always "python"
- `params` - Input parameters
- `execution_time` - Execution duration in seconds (when completed)
- `processed_at` - Processing timestamp (when completed)
properties:
model:
type: string
description: The AI model used (model steps only)
example: "openai-chatgpt-5"
version:
type: string
description: API version (model steps only)
example: "0.0.1"
type:
type: string
description: Step type (http or python steps)
enum: [http, python]
example: "http"
method:
type: string
description: HTTP method (http steps only)
example: "POST"
url:
type: string
description: Request URL (http steps only)
example: "https://api.example.com/endpoint"
params:
type: object
description: Complete parameters sent to the step, including resolved values
additionalProperties: true
example:
system_prompt: "You are a helpful assistant"
user_prompt: "tell an epic story of a rat graduating from law"
max_output_tokens: 512
prediction_id:
type: string
description: External prediction ID (model steps, when completed)
example: "6yxkzw4th9rme0ct1re8q42xer"
processed_at:
type: string
format: date-time
description: Processing timestamp (when available)
example: "2025-12-04T11:48:39Z"
fallback_used:
type: boolean
description: |
Indicates that the step completed successfully using a configured fallback after the primary attempt failed (model steps only).
example: true
primary_error:
type: string
description: |
Original error message from the primary attempt when a fallback configuration was used (model steps only).
example: "primary model failed due to timeout"
elapsed_seconds:
type: number
description: Execution duration in seconds (when completed)
example: 2.45
execution_time:
type: number
description: Python execution time in seconds (python steps, when completed)
example: 0.123
error:
type: string
description: Error message if the step failed
example: "ModelTimeout"
error_cause:
type: string
description: Detailed error information if the step failed
example: "Model request timed out after 30 seconds"
FallbackConfig:
type: object
description: |
Fallback configuration for model steps. When configured, the workflow engine will
automatically retry with the fallback model if the primary model invocation fails.
properties:
enabled:
type: boolean
default: true
description: Enable or disable fallback for this step
example: true
model:
type: string
description: |
Fallback AI model identifier to use if the primary model fails.
Should be compatible with the expected output format.
example: "flux-1-1-pro"
version:
type: string
description: |
Fallback model version. When omitted, defaults to the primary model's version.
example: "1.0.0"
params:
type: object
description: |
Fallback-specific model parameters. Supports template variables like `{{inputs.prompt}}`
and `{{step_id.output}}` for referencing workflow inputs and previous step outputs.
additionalProperties: true
example:
prompt: "{{inputs.prompt}}"
guidance_scale: 7.5
example:
enabled: true
model: "flux-1-1-pro"
params:
prompt: "{{inputs.prompt}}"
guidance_scale: 7.5
Error:
type: object
required:
- error
properties:
error:
type: string
description: Error message
responses:
BadRequest:
description: Bad request - Invalid input
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized - Invalid or missing API key
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Forbidden - Operation not allowed (e.g., workflow is locked)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Not found - Resource does not exist
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Models API YAML |
Workflows API YAML |
llms.txt