> ## 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.

# Agent Call

> Send a request to an Agent you built in the Portal.

Use this when you've built an **Agent** in the Portal and want to invoke it from your application code.

## Prerequisites

* Create an Agent in the Portal: [Create Agent](/build/agents/create_agent)
* Get the **Agent name** and **Application name** from the Portal

## Execute an Agent

<RequestExample>
  ```python Python (async) theme={null}
  import maitai
  from maitai.models.agent.extended import AgentRequest
  from maitai.models.chat import ChatCompletionParams

  client = maitai.MaitaiAsync()

  request = AgentRequest(
      agent="YOUR_AGENT_NAME",
      application="YOUR_APPLICATION",
      session_id="YOUR_SESSION_ID",
      params=ChatCompletionParams(
          messages=[
              {"role": "user", "content": "Help me triage this customer ticket and draft a reply."},
          ],
      ),
  )

  response = await client.agent.completions.create(request=request)
  print(response.choices[0].message.content)
  ```

  ```python Python (sync) theme={null}
  import maitai
  from maitai.models.agent.extended import AgentRequest
  from maitai.models.chat import ChatCompletionParams

  client = maitai.Maitai()

  request = AgentRequest(
      agent="YOUR_AGENT_NAME",
      application="YOUR_APPLICATION",
      session_id="YOUR_SESSION_ID",
      params=ChatCompletionParams(
          messages=[
              {"role": "user", "content": "Help me triage this customer ticket and draft a reply."},
          ],
      ),
  )

  response = client.agent.completions.create(request=request)
  print(response.choices[0].message.content)
  ```

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

  const client = new Maitai();

  const request = {
      agent: "YOUR_AGENT_NAME",
      application: "YOUR_APPLICATION",
      session_id: "YOUR_SESSION_ID",
      params: {
          messages: [
              { role: "user", content: "Help me triage this customer ticket and draft a reply." },
          ],
      },
  };

  const response = await client.agent.completions.create({ request });
  print(response.choices[0].message.content);
  ```
</RequestExample>

## OpenAI SDK usage (base\_url)

If you're already using the OpenAI SDK, you can call Maitai directly by setting `model` to your agent name and passing `application` via `extra_body`.

To use the Agents API, set the OpenAI SDK base URL to:

* `https://api.trymaitai.ai/agent/`

<CodeGroup>
  ```python Python theme={null}
  import os
  import openai

  client = openai.OpenAI(
      base_url="https://api.trymaitai.ai/agent/",
      api_key=os.getenv("MAITAI_API_KEY"),
  )

  response = client.chat.completions.create(
      model="YOUR_AGENT_NAME",
      messages=[
          {"role": "user", "content": "Help me handle a refund for order 12345."},
      ],
      extra_body={
          "application": "YOUR_APPLICATION",
          "session_id": "YOUR_SESSION_ID",
          "high_performance": True,   # Optional: flatten sub-agents, run in-process
      },
  )
  ```

  ```javascript Node theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    baseURL: "https://api.trymaitai.ai/agent/",
    apiKey: process.env.MAITAI_API_KEY,
  });

  const response = await client.chat.completions.create({
    model: "YOUR_AGENT_NAME",
    messages: [{ role: "user", content: "Help me handle a refund for order 12345." }],
    extra_body: {
      application: "YOUR_APPLICATION",
      session_id: "YOUR_SESSION_ID",
      high_performance: true,   // Optional: flatten sub-agents, run in-process
    },
  });
  ```
</CodeGroup>

## Per-request overlays (`agent.get`)

Sometimes you want to tweak how an agent behaves for a **single request**: seed some context, turn off a [flow step](/build/agents/agent_flow), or skip a sub-agent, without changing the published agent definition. Use `client.agent.get(...)` to resolve the agent, adjust it locally, then send it.

The published version stays the source of truth and the security ceiling: overlays are **subtractive** (you can disable capabilities but never enable ones the version disabled), config changes are limited to whitelisted flow-step toggles, and state is validated against the schema defined on the agent's [State tab](/build/agents/agent_state).

<CodeGroup>
  ```python Python (sync) theme={null}
  import maitai

  client = maitai.Maitai()

  # Resolve the published agent (pin a version number or named release).
  agent = client.agent.get("YOUR_AGENT_NAME", version="prod")

  # Turn off the "progress" flow step for this request only.
  agent.config.system_steps.progress = False

  # Subtractively disable a capability (cannot re-enable a version-disabled one).
  agent.disable_subagent("refunds")

  # Seed typed state: field-level, absent keys untouched, explicit None clears.
  agent.state["user_facts"] = "VIP customer since 2021"

  request = agent.to_request(
      messages=[{"role": "user", "content": "Help me triage this customer ticket."}],
      session_id="YOUR_SESSION_ID",
  )
  response = client.agent.completions.create(request=request)
  print(response.choices[0].message.content)
  ```

  ```python Python (async) theme={null}
  import maitai

  client = maitai.MaitaiAsync()

  agent = await client.agent.get("YOUR_AGENT_NAME", version="prod")
  agent.config.system_steps.progress = False
  agent.disable_subagent("refunds")
  agent.state["user_facts"] = "VIP customer since 2021"

  request = agent.to_request(
      messages=[{"role": "user", "content": "Help me triage this customer ticket."}],
      session_id="YOUR_SESSION_ID",
  )
  response = await client.agent.completions.create(request=request)
  print(response.choices[0].message.content)
  ```

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

  const client = new Maitai();

  const agent = await client.agent.get("YOUR_AGENT_NAME", { version: "prod" });

  agent.setSystemStepEnabled("progress", false);
  agent.disableSubagent("refunds");
  agent.state.user_facts = "VIP customer since 2021";

  const request = agent.toRequest(
      [{ role: "user", content: "Help me triage this customer ticket." }],
      { sessionId: "YOUR_SESSION_ID" },
  );
  const response = await client.agent.completions.create({ request });
  console.log(response.choices[0].message.content);
  ```
</CodeGroup>

<ParamField path="agent.get(agent, ...)" type="AgentHandle">
  Resolves the runtime view of an agent and returns a handle you can mutate.

  <Expandable title="Arguments and handle members">
    <ParamField path="version" type="string">
      Pin a specific version number. Omit to resolve the current draft.
    </ParamField>

    <ParamField path="release" type="string">
      Pin a named release (e.g. a release labeled `prod`). Alternative to `version`.
    </ParamField>

    <ParamField path="session_id" type="string">
      Optional. Include the session's current state in the resolved view (`sessionId` in Node).
    </ParamField>

    <ParamField path="agent.config" type="object">
      Mutable view of the agent config. Set [flow-step](/build/agents/agent_flow) toggles via `agent.config.system_steps.<step>` (Python) or `agent.setSystemStepEnabled("<step>", enabled)` (Node), where `<step>` is one of `orchestration`, `processing`, `progress`, `respond`.
    </ParamField>

    <ParamField path="agent.actions" type="array">
      The resolved capability tree (actions + sub-agents). Disable nodes for this request with `agent.disable_action(name)` / `agent.disable_subagent(name)` (`disableAction` / `disableSubagent` in Node).
    </ParamField>

    <ParamField path="agent.state" type="object">
      Mutable dict of [state](/build/agents/agent_state) values to seed. Set `agent.state["field"] = value`.
    </ParamField>

    <ParamField path="agent.secrets" type="object">
      Mutable dict of per-request credentials. Set `agent.secrets["name"] = value` to resolve `{{secrets.name}}` placeholders in a capability's auth/headers. Values are never stored or shown to the LLM. You can also pass them as `to_request(..., secrets={...})`.
    </ParamField>

    <ParamField path="agent.to_request(messages, ...)" type="AgentRequest">
      Builds an `AgentRequest` carrying only your changes (`toRequest(messages, options)` in Node). Pass it to `client.agent.completions.create(request=...)`.
    </ParamField>
  </Expandable>
</ParamField>

The resolved tree and config that actually ran (including the applied overlay) are persisted on the stored agent response under `meta.effective_execution` for auditability, inspect a run in [Request Overview](/observe/request_overview).

## Per-request secrets

When an agent capability makes an authenticated API call, you can supply the credential at request time instead of hard-coding it in the agent. Reference the secret by name in the action's **auth** or **headers** config using a `{{secrets.NAME}}` placeholder (set this up in the Portal when you build the action), then pass the value in `secrets` on the request.

```python Python theme={null}
import maitai

client = maitai.Maitai()

agent = client.agent.get("YOUR_AGENT_NAME", version="prod")

request = agent.to_request(
    messages=[{"role": "user", "content": "Charge the customer for order 12345."}],
    session_id="YOUR_SESSION_ID",
    secrets={"stripe_key": "sk_live_..."},  # resolves {{secrets.stripe_key}} in the action's auth/headers
)
response = client.agent.completions.create(request=request)
```

```javascript Node theme={null}
const agent = await client.agent.get("YOUR_AGENT_NAME", { version: "prod" });

const request = agent.toRequest(
    [{ role: "user", content: "Charge the customer for order 12345." }],
    { sessionId: "YOUR_SESSION_ID", secrets: { stripe_key: "sk_live_..." } },
);
const response = await client.agent.completions.create({ request });
```

How secrets behave:

* **Reference by name.** Only `{{secrets.NAME}}` placeholders inside an action's `auth`/`headers` are resolved, secrets never appear in prompts or the LLM context.
* **Validated up front.** If a capability the request leaves enabled references a secret you didn't supply, the request fails fast with a `400` listing the missing names. Extra secrets you pass that nothing references are ignored.
* **Never stored.** Secret values are not written to agent/session state, and they are redacted (replaced with their `{{secrets.NAME}}` reference) before any request record is persisted.
* **Delegated securely.** Sub-agents invoked during the run can resolve the same secrets, so a credential supplied to the root agent reaches the descendant capability that needs it.

## Structured final response (`response_format`)

By default the agent's final answer is freeform text. You can also set a default on the agent itself: open the **Respond** step on the Configuration tab and choose **Response Format** (structured JSON schema). That default applies on every invoke. Pass an OpenAI-style `response_format` on `params` to override for a single call, either free-shape (`json_object`) or constrained to a schema (`json_schema`).

<CodeGroup>
  ```python Python theme={null}
  import json

  import maitai
  from maitai.models.agent.extended import AgentRequest
  from maitai.models.chat import ChatCompletionParams

  client = maitai.Maitai()

  request = AgentRequest(
      agent="YOUR_AGENT_NAME",
      application="YOUR_APPLICATION",
      session_id="YOUR_SESSION_ID",
      params=ChatCompletionParams(
          messages=[{"role": "user", "content": "Triage this ticket."}],
          response_format={
              "type": "json_schema",
              "json_schema": {
                  "name": "triage_result",
                  "schema": {
                      "type": "object",
                      "properties": {
                          "priority": {"type": "string"},
                          "category": {"type": "string"},
                          "summary": {"type": "string"},
                      },
                      "required": ["priority", "category", "summary"],
                  },
              },
          },
      ),
  )
  response = client.agent.completions.create(request=request)
  result = json.loads(response.choices[0].message.content)
  ```

  ```python Python (agent.get) theme={null}
  agent = client.agent.get("YOUR_AGENT_NAME", version="prod")

  request = agent.to_request(
      messages=[{"role": "user", "content": "Triage this ticket."}],
      session_id="YOUR_SESSION_ID",
      response_format={"type": "json_object"},
  )
  response = client.agent.completions.create(request=request)
  ```

  ```javascript Node theme={null}
  const agent = await client.agent.get("YOUR_AGENT_NAME", { version: "prod" });

  const request = agent.toRequest(
      [{ role: "user", content: "Triage this ticket." }],
      { sessionId: "YOUR_SESSION_ID", responseFormat: { type: "json_object" } },
  );
  const response = await client.agent.completions.create({ request });
  const result = JSON.parse(response.choices[0].message.content);
  ```
</CodeGroup>

How it behaves:

* **Respond step only.** The constraint applies to the agent's final answer. Orchestration, processing, and capability calls are unaffected, and sub-agents are never constrained.
* **Streaming works unchanged.** The JSON answer streams as ordinary content deltas; accumulate them into a JSON string.
* **The content is a string.** `choices[0].message.content` carries the JSON, parse it client-side. When a format was applied, the stored agent response also carries it under `meta.response_format`.
* **Default is freeform.** Omit `response_format` (or pass `{"type": "text"}`) for normal prose answers.

Also available on the Developer API: `POST /agents/{agent_id}/invoke` accepts a top-level `response_format` object (CLI: `maitai agents invoke --response-format '<json>'`).

## Parameters

Maitai agents support two equivalent request shapes:

* `AgentRequest` (recommended): pass a `request=...` wrapper to `client.agent.completions.create(...)`
* OpenAI SDK (base\_url): pass standard chat completion fields plus agent fields via `extra_body`

### Agent identification (required)

<ParamField path="agent" type="string">
  The agent name. When using the OpenAI SDK base\_url approach, you can alternatively set `model` to the agent name and omit this field.
</ParamField>

<ParamField path="application" type="string">
  Required when identifying an agent by name.
</ParamField>

<ParamField path="agent_id" type="integer">
  Alternative identifier. If `agent` is provided, it is preferred over `agent_id`.
</ParamField>

<ParamField path="application_id" type="integer">
  Optional. Alternative application identifier (legacy). Can be used with `agent` instead of `application`.
</ParamField>

<ParamField path="model" type="string">
  When using the OpenAI SDK base\_url approach without providing `agent` or `agent_id`, Maitai will use `model` as the agent name.
</ParamField>

### Agent execution controls (optional)

<Note>
  How the agent processes a request (the full reasoning loop or single-step routing) is part of the agent's configuration, not the request. Set it when you build the agent and change it from the agent's **Configuration** tab in the Portal. See [Execution Mode](/build/agents/execution_mode).
</Note>

<ParamField path="max_iterations" type="integer">
  Maximum number of orchestration iterations before the agent stops. Useful as a safety limit for agents in `reasoning` mode. Defaults to 100.
</ParamField>

<ParamField path="high_performance" type="boolean" default={false}>
  When `true`, the agent **flattens sub-agent actions** into the root agent and runs everything in-process. Instead of dispatching work to sub-agents over Redis queues, all capabilities (including those defined on sub-agents) execute directly within the root agent's process.

  This eliminates inter-process overhead and reduces latency, especially for agents with deep sub-agent hierarchies. The orchestrator sees a single flat list of all available capabilities.
</ParamField>

<ParamField path="session_id" type="string">
  A unique identifier you set for the session. Recommended for tracking conversation threads. The agent uses this to maintain state (forms, context, checkpoints) across multiple requests.
</ParamField>

<ParamField path="task_id" type="string">
  Optional task identifier used to group related agent requests.
</ParamField>

### `request` (AgentRequest wrapper)

<ParamField path="request" type="AgentRequest" required>
  Wrapper request for agent execution. Provider model inputs live under `request.params` and match the standard chat completion params documented at [Chat](/sdk/chat).

  <Expandable title="AgentRequest fields">
    <ParamField path="agent_id" type="integer">
      The numeric agent identifier.
    </ParamField>

    <ParamField path="agent" type="string">
      Preferred. The agent name.
    </ParamField>

    <ParamField path="session_id" type="string">
      A unique identifier you set for the session. Recommended for tracking conversation threads.
    </ParamField>

    <ParamField path="task_id" type="string">
      Optional task identifier used to group related agent requests.
    </ParamField>

    <ParamField path="max_iterations" type="integer">
      Maximum orchestration iterations for agents in `reasoning` mode. Defaults to 100.
    </ParamField>

    <ParamField path="high_performance" type="boolean" default={false}>
      Flatten sub-agent actions into the root agent and run everything in-process. Eliminates inter-process overhead.
    </ParamField>

    <ParamField path="params" type="ChatCompletionParams" required>
      Provider model parameters (messages, model, temperature, streaming, tools, etc.). See [Chat](/sdk/chat) for the full parameter set.
    </ParamField>

    <ParamField path="config" type="object">
      Optional per-request overlay of whitelisted flow-step toggles (`agent_config.meta.system_steps`). Only changes relative to the published version apply; changing a non-overridable key returns `400`. Usually set via the `agent.get(...)` handle rather than by hand (see the **Per-request overlays** section above).
    </ParamField>

    <ParamField path="actions" type="array">
      Optional subtractive capability mask: the capability tree with `enabled: false` on nodes to skip. You can only disable capabilities the version already exposes, never enable disabled ones. Usually produced by `agent.disable_action(...)` / `agent.disable_subagent(...)`.
    </ParamField>

    <ParamField path="state" type="object">
      Optional field-level patch of the agent's [state](/build/agents/agent_state), validated against the schema defined on the State tab (each value must match its declared type). Absent keys are untouched; an explicit `null` clears a field.
    </ParamField>

    <ParamField path="secrets" type="object">
      Optional per-request credentials (`{ name: value }`) referenced by capabilities via `{{secrets.NAME}}` placeholders in their auth/headers. Validated at request time (missing referenced secrets cause a `400`), never written to state or persisted records (redacted on store), and delegated to sub-agents that need them.
    </ParamField>
  </Expandable>
</ParamField>

## Observability

To debug a single agent run in the Portal, open the resulting request in **Request Overview**:

* [Request Overview](/observe/request_overview)
