> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trymaitai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflows

> Execute complex AI workflows through a single API call.

Maitai Workflows let you execute multi-step AI pipelines through a single API call. A workflow is a Python script you write; Maitai runs it in a managed worker pool and handles execution, scaling, monitoring, and caching. Orchestrate models, agents, HTTP services, and custom logic — then invoke the whole thing the way you'd call any model.

## What a workflow is

A workflow is **one Python file** with an `execute(ctx)` function. Maitai calls `execute`, passes it a [context object](/build/workflows/context), and returns whatever you return.

```python theme={null}
from maitai_workflow import WorkflowContext

def execute(ctx: WorkflowContext):
    resp = ctx.chat.completions.create(
        messages=[{"role": "user", "content": ctx.input["query"]}],
        application="Support", intent="answer",
    )
    return {"answer": resp["choices"][0]["message"]["content"]}
```

The `ctx` object is the workflow's connection to everything: its input, Maitai model and agent calls, arbitrary HTTP, queryable datastores, streaming, and logging.

### Building blocks

<CardGroup cols={2}>
  <Card title="Structure" icon="file-code" href="/build/workflows/structure">
    The script layout — the `execute` entrypoint, input/output schemas, and helpers.
  </Card>

  <Card title="Context (ctx)" icon="plug" href="/build/workflows/context">
    The runtime API — models, agents, HTTP, streaming, logging, and more.
  </Card>

  <Card title="Datastores & Accessories" icon="database" href="/build/workflows/data">
    Attach reference data — bundled files or large, queryable record stores.
  </Card>

  <Card title="Invoke" icon="play" href="/build/workflows/overview#invoke-a-workflow">
    Call a workflow from the SDK or raw API, including streaming.
  </Card>
</CardGroup>

### Lifecycle

1. **Write** the [script](/build/workflows/structure) and (optionally) attach [datastores or accessories](/build/workflows/data).
2. **Upload** it — see [Uploading a workflow](/build/workflows/data#uploading-a-workflow). Maitai registers it under a `workflow_ref_name`.
3. **Invoke** it as `model="workflow:<ref_name>"` (below). Maitai runs it, monitors every model call, and returns the result.

The Maitai team can also configure workflows for your account during onboarding.

## Invoke a workflow

Set `model` to `workflow:<your-workflow-name>`. You can pass either `messages` (chat-style) or `input` (arbitrary JSON payload), or both.

### With messages (chat-style)

<RequestExample>
  ```python Python theme={null}
  from maitai import Maitai

  maitai = Maitai()

  response = maitai.workflow.completions.create(
      model="workflow:my-workflow",
      messages=[
          {"role": "user", "content": "Summarize this quarter's earnings report."},
      ],
  )
  print(response.choices[0].message.content)
  ```
</RequestExample>

<RequestExample>
  ```javascript Node theme={null}
  import Maitai from "maitai";

  const maitai = new Maitai();

  const response = await maitai.workflow.completions.create({
    model: "workflow:my-workflow",
    messages: [
      { role: "user", content: "Summarize this quarter's earnings report." },
    ],
  });

  console.log(response.choices[0].message.content);
  ```
</RequestExample>

### With input (structured payload)

For workflows that expect structured data rather than chat messages, use `input`:

```python theme={null}
response = maitai.workflow.completions.create(
    model="workflow:my-etl-pipe",
    input={"document_url": "https://...", "output_format": "markdown"},
)
```

```javascript theme={null}
const response = await maitai.workflow.completions.create({
  model: "workflow:my-etl-pipe",
  input: { document_url: "https://...", output_format: "markdown" },
});
```

### Raw API (cURL)

```bash theme={null}
curl -X POST https://api.trymaitai.ai/workflow/chat/completions \
  -H "Content-Type: application/json" \
  -H "x-api-key: $MAITAI_API_KEY" \
  -d '{
    "model": "workflow:my-workflow",
    "input": {"document_url": "https://...", "output_format": "markdown"}
  }'
```

### With session\_id (for correlation)

Pass `session_id` to correlate all model calls within a workflow run in Maitai analytics:

```python theme={null}
response = maitai.workflow.completions.create(
    model="workflow:my-workflow",
    messages=[{"role": "user", "content": "..."}],
    session_id="my-session-123",  # Optional; overrides auto-generated
)
```

### Streaming responses

To receive workflow output in real-time as the script executes, set `stream=True`. The workflow will yield intermediate chunks as they are emitted (via `ctx.emit()`), followed by the final completion.

```python theme={null}
stream = maitai.workflow.completions.create(
    model="workflow:my-workflow",
    input={"document_url": "https://...", "output_format": "markdown"},
    stream=True,
)

for chunk in stream:
    if hasattr(chunk, "type"):
        # This is an intermediate workflow chunk (from ctx.emit)
        print(f"[{chunk.type}] {chunk.content}")
    elif hasattr(chunk, "choices"):
        # This is the final ChatCompletion object
        print("Final result:", chunk.choices[0].message.content)
```

```javascript theme={null}
const stream = await maitai.workflow.completions.create({
  model: "workflow:my-workflow",
  input: { document_url: "https://...", output_format: "markdown" },
  stream: true,
});

for await (const chunk of stream) {
  if (chunk.type) {
    // Intermediate chunk
    console.log(`[${chunk.type}]`, chunk.content);
  } else if (chunk.choices) {
    // Final completion
    console.log("Final result:", chunk.choices[0].message.content);
  }
}
```

## Passing secrets

If your workflow requires runtime credentials (e.g. third-party API keys), pass them via `secrets`. These are never stored — they're only available for the duration of the request.

```python theme={null}
response = maitai.workflow.completions.create(
    model="workflow:my-workflow",
    messages=[{"role": "user", "content": "Fetch and summarize the latest data."}],
    secrets={"EXTERNAL_API_KEY": "tok-..."},
)
```

```javascript theme={null}
const response = await maitai.workflow.completions.create({
  model: "workflow:my-workflow",
  messages: [{ role: "user", content: "Fetch and summarize the latest data." }],
  secrets: { EXTERNAL_API_KEY: "tok-..." },
});
```

## Request format

<ParamField path="model" type="string" required>
  The workflow to invoke, in the format `workflow:<ref_name>`.
</ParamField>

<ParamField path="messages" type="array">
  An array of chat messages (OpenAI format). Required unless `input` is provided.

  ```json theme={null}
  [
    {"role": "system", "content": "You are an analyst."},
    {"role": "user", "content": "Summarize the report."}
  ]
  ```
</ParamField>

<ParamField path="input" type="object | string | array">
  Arbitrary JSON payload passed to the workflow. Required unless `messages` is provided. Use this for Lambda-style invocations where chat messages don't apply.

  ```json theme={null}
  {"document_url": "https://...", "output_format": "markdown"}
  ```
</ParamField>

<ParamField path="session_id" type="string">
  Optional session ID for the workflow run. If provided, overrides the auto-generated session ID. All model calls within the workflow use this session, enabling correlation across steps in Maitai analytics.
</ParamField>

<ParamField path="stream" type="boolean">
  Optional. When `true`, returns a stream of intermediate workflow chunks (emitted via `ctx.emit()`) followed by a final `chat.completion` object, allowing you to display real-time progress. Default `false`.
</ParamField>

<ParamField path="secrets" type="object">
  Optional key-value pairs available to the workflow at runtime (e.g. API keys the workflow needs to call external services). Not stored.
</ParamField>

<ParamField path="metadata" type="object">
  Optional metadata passed to the workflow.
</ParamField>

## Response format

Workflows return a standard OpenAI-compatible `chat.completion` response:

```json theme={null}
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "created": 1770848541,
  "model": "workflow:my-workflow",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Here is the earnings summary..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 0,
    "completion_tokens": 0,
    "total_tokens": 0
  },
  "request_id": "d4f7e2a1-..."
}
```

<ResponseField name="id" type="string">
  Unique identifier for the completion.
</ResponseField>

<ResponseField name="model" type="string">
  The workflow that was invoked.
</ResponseField>

<ResponseField name="choices" type="array">
  Array containing the workflow output. `choices[0].message.content` holds the result.
</ResponseField>

<ResponseField name="request_id" type="string">
  Maitai request identifier, useful for debugging.
</ResponseField>

## Authentication

Workflows use the same API key authentication as all Maitai endpoints. See [Authentication](/get_started/authentication).

## Error handling

| Status | Meaning                                                                   |
| ------ | ------------------------------------------------------------------------- |
| `400`  | Invalid request (missing both `input` and `messages`, bad `model` format) |
| `401`  | Invalid or missing API key                                                |
| `404`  | Workflow not found for your account                                       |
| `408`  | Workflow exceeded its timeout                                             |
| `500`  | Workflow execution error (check `error.message` for details)              |

Error responses follow the standard format:

```json theme={null}
{
  "error": {
    "message": "Workflow not found: my-workflow",
    "type": "invalid_request_error"
  }
}
```

## Next

* Write your first workflow: [Workflow Structure](/build/workflows/structure)
* The full runtime API: [Workflow Context (`ctx`)](/build/workflows/context)
* Attach reference data and upload: [Datastores & Accessories](/build/workflows/data)
