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

# Agent API

> Call a Suppio agent from your backend and receive an answer, action signals, and caller-defined tool calls.

The Suppio Agent API lets your backend send a support message with optional image attachments and function tools to one Suppio agent and receive a generated response.

Use the API when you want to build Suppio into your own support workflow, route messages through your own app, or decide what to do when Suppio suggests escalation, resolution, or a title.

<Warning>
  API keys are secrets. Use them only from trusted backend code. Do not put Suppio API keys in browsers, mobile apps, frontend bundles, or public repositories.
</Warning>

## What it is

The Agent API is a server-to-server endpoint:

```http theme={null}
POST /v1/agents/{agentId}/responses
```

You send a `message` and can include base64-encoded images. Suppio answers using the selected agent's context and returns:

* `outcome`: whether the request succeeded or was blocked
* `response`: the text answer
* `actions`: structured signals your app can act on
* `tool_calls`: caller-defined action signals your app can execute
* `usage`: credits and tokens consumed

## When to use it

Use the API when:

* you already have a custom chat UI
* you want to create tickets in your own system after Suppio escalates
* you want to label or route conversations using `suggest_title`
* you need a backend integration instead of the Discord bot, chat widget, or support page

## Set it up

<Steps>
  <Step title="Create or open an agent">
    Open the Suppio dashboard, choose your workspace, and open the agent.
  </Step>

  <Step title="Add context">
    Add at least one context source. API keys cannot be created until the agent has context.
  </Step>

  <Step title="Open Deploy">
    Open **Deploy** and choose **API**.
  </Step>

  <Step title="Generate an API key">
    Click **Generate API Key**. Copy the key immediately. Suppio shows the full key only once.
  </Step>

  <Step title="Copy the agent ID">
    Copy the agent ID from the agent page URL. In `/dashboard/workspace/{workspaceId}/agent/{agentId}`, the `{agentId}` value is the ID you use in API requests.
  </Step>

  <Step title="Call the endpoint from your backend">
    Send the key as a bearer token to the documented endpoint and include a JSON body with `message`, optional image `attachments`, and optional function `tools`.
  </Step>

  <Step title="Return from the sidebar">
    After the first key is created, **API** remains in the agent sidebar for key management.
  </Step>
</Steps>

## Authentication

Send the API key in the `Authorization` header.

```http theme={null}
Authorization: Bearer suppio_live_your_key
Content-Type: application/json
```

API keys:

* start with `suppio_live_`
* are scoped to exactly one agent
* stop working immediately after deletion
* are shown in full only when created
* are stored by Suppio as a hash, a display prefix, and the last four characters

## Endpoint

```http theme={null}
POST https://api.suppio.ai/v1/agents/{agentId}/responses
```

Build the URL with the production API host and your agent ID:

```text theme={null}
https://api.suppio.ai/v1/agents/{agentId}/responses
```

For example, if your agent ID is `agent_123`, use:

```text theme={null}
https://api.suppio.ai/v1/agents/agent_123/responses
```

<Tip>
  You can find the agent ID in the dashboard URL when the agent is open. It is the value after `/agent/`.
</Tip>

### Path parameters

| Field     | Required | Description                                                                        |
| --------- | -------- | ---------------------------------------------------------------------------------- |
| `agentId` | Yes      | The ID of the Suppio agent to call. The API key must be scoped to this same agent. |

### Headers

| Header          | Required | Description                   |
| --------------- | -------- | ----------------------------- |
| `Authorization` | Yes      | `Bearer suppio_live_your_key` |
| `Content-Type`  | Yes      | Must be `application/json`    |

### Request body

```json theme={null}
{
  "message": "What does this error screenshot mean?",
  "attachments": [
    {
      "type": "image",
      "name": "error.png",
      "media_type": "image/png",
      "data": "iVBORw0KGgoAAAANSUhEUgAA..."
    }
  ],
  "tools": [
    {
      "type": "function",
      "name": "create_ticket",
      "description": "Create a support ticket.",
      "instructions": "Call when the user needs human follow-up.",
      "strict": true,
      "parameters": {
        "type": "object",
        "properties": {
          "priority": {
            "type": "string",
            "enum": ["low", "high"]
          }
        },
        "required": ["priority"],
        "additionalProperties": false
      }
    }
  ]
}
```

| Field                      | Required | Description                                                                                                                                                                       |
| -------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message`                  | Yes      | The support message to send to the agent. Leading and trailing whitespace is trimmed. Empty messages are rejected. The model input is capped at 12,000 characters after trimming. |
| `attachments`              | No       | Up to five base64-encoded image attachment objects.                                                                                                                               |
| `attachments[].type`       | Yes      | Must be `image`.                                                                                                                                                                  |
| `attachments[].name`       | No       | A display name of up to 120 characters. Suppio generates a name when omitted.                                                                                                     |
| `attachments[].media_type` | Yes      | `image/png`, `image/jpeg`, `image/webp`, or `image/gif`.                                                                                                                          |
| `attachments[].data`       | Yes      | The raw base64 string only. Do not include a `data:image/...;base64,` prefix.                                                                                                     |
| `tools`                    | No       | Up to 10 caller-defined function tools for this request. Definitions are not saved to the agent.                                                                                  |
| `tools[].type`             | Yes      | Must be `function`.                                                                                                                                                               |
| `tools[].name`             | Yes      | A unique name of up to 64 characters. Start with a letter and use only letters, numbers, underscores, or hyphens. Suppio-owned names are reserved.                                |
| `tools[].description`      | Yes      | A concise purpose of up to 1,024 characters.                                                                                                                                      |
| `tools[].instructions`     | Yes      | When and how the agent should call the tool, up to 4,000 characters.                                                                                                              |
| `tools[].strict`           | No       | May be omitted or set to `true`. `false` is rejected.                                                                                                                             |
| `tools[].parameters`       | Yes      | A strict object JSON Schema describing the arguments. Each schema can be up to 16 KB.                                                                                             |

Each decoded image can be up to 5 MB, with a 10 MB decoded total per request. The JSON body limit is 16 MB. Base64 encoding increases the transferred size, and Suppio processes the images directly from the request without storing them.

Image inputs count toward model input tokens. Those tokens are included in `usage.tokens`, converted to Suppio credits using the normal model rate, and deducted from the workspace just like text input tokens.

### Caller-defined tools

Caller-defined tools are action signals. Suppio adds each tool's `instructions` to the agent instructions for that request and exposes its `name`, `description`, and `parameters` as a function tool.

When the agent calls one, Suppio records the name and arguments, then continues generating the text response. Suppio does not call your services, run a webhook, or confirm that the external action succeeded. Your backend receives the calls in `tool_calls` and decides whether to execute them.

Use caller-defined tools for actions such as creating a ticket, adding a routing tag, or requesting a callback. They are not synchronous data-retrieval tools because your backend cannot return a tool result during the same request.

Tool schemas must use strict mode:

* The root must be an object schema.
* Every object, including nested objects, must set `additionalProperties` to `false`.
* Every property must appear in that object's `required` array.
* Represent an optional value with a nullable type, such as `"type": ["string", "null"]`, while keeping the field in `required`.

The complete `tools` array can be up to 64 KB. Tool definitions count toward model input usage. Duplicate names, reserved names, unsupported fields, invalid schemas, and definitions over the limits return `invalid_tools` before the agent runs.

The agent can call multiple tools or the same tool more than once. Suppio preserves the calls in invocation order.

### Current API limitations

* Only image attachments are supported. PDFs and other document attachments are not accepted.
* Caller-defined tools cannot return data to the agent during the same request.
* Suppio does not mutate your system through the API. Your app decides what to do with returned actions and tool calls.

## Example requests

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://api.suppio.ai/v1/agents/agent_123/responses" \
    -H "Authorization: Bearer suppio_live_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "message": "How do I reset my password?"
    }'
  ```

  ```javascript Node.js theme={null}
  import fs from "node:fs";

  const endpoint = "https://api.suppio.ai/v1/agents/agent_123/responses";

  const tools = [
    {
      type: "function",
      name: "create_ticket",
      description: "Create a support ticket.",
      instructions: "Call when the user needs human follow-up.",
      strict: true,
      parameters: {
        type: "object",
        properties: {
          priority: { type: "string", enum: ["low", "high"] },
        },
        required: ["priority"],
        additionalProperties: false,
      },
    },
  ];

  async function askSuppio(message, imagePath) {
    const attachments = imagePath
      ? [
          {
            type: "image",
            name: "screenshot.png",
            media_type: "image/png",
            data: fs.readFileSync(imagePath, "base64"),
          },
        ]
      : [];

    const response = await fetch(endpoint, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SUPPIO_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ message, attachments, tools }),
    });

    const payload = await response.json().catch(() => null);

    if (!response.ok) {
      const code = payload?.error?.code ?? "unknown_error";
      const detail = payload?.error?.message ?? `Request failed (${response.status})`;
      throw new Error(`${code}: ${detail}`);
    }

    return payload;
  }

  askSuppio("What does this error mean?", "./screenshot.png")
    .then((payload) => {
      console.log(payload.response);

      for (const action of payload.actions ?? []) {
        if (action.type === "escalate_to_human") {
          console.log("Escalate:", action.reason);
        }
      }

      for (const toolCall of payload.tool_calls ?? []) {
        console.log("Tool call:", toolCall.name, toolCall.arguments);
      }
    })
    .catch((error) => {
      console.error("Suppio API error:", error.message);
    });
  ```
</CodeGroup>

## Example success response

```json theme={null}
{
  "outcome": "success",
  "response": "I’ll pass this to the support team.",
  "actions": [
    {
      "type": "escalate_to_human",
      "reason": "The user requested human follow-up."
    }
  ],
  "tool_calls": [
    {
      "name": "create_ticket",
      "arguments": {
        "priority": "high"
      }
    }
  ],
  "usage": {
    "credits": 12,
    "tokens": 1234
  }
}
```

## Example blocked response

Blocked responses still use HTTP 200 because the request was processed successfully.

```json theme={null}
{
  "outcome": "blocked",
  "response": "I can't help with that request.",
  "actions": [],
  "tool_calls": [],
  "usage": {
    "credits": 2,
    "tokens": 120
  }
}
```

## Action signals

Suppio returns action signals so your application can decide what to do next.

<AccordionGroup>
  <Accordion title="escalate_to_human">
    The user asked for a person or the agent cannot confidently continue.

    ```json theme={null}
    {
      "type": "escalate_to_human",
      "reason": "The user asked to talk to support."
    }
    ```
  </Accordion>

  <Accordion title="mark_resolved">
    The request was fully answered or the user indicated the issue is resolved.

    ```json theme={null}
    {
      "type": "mark_resolved",
      "reason": "The request was fully answered."
    }
    ```
  </Accordion>

  <Accordion title="suggest_title">
    The agent identified a short support title that can help your app label or route the conversation.

    ```json theme={null}
    {
      "type": "suggest_title",
      "reason": "The user has a clear billing issue.",
      "title": "Billing Error"
    }
    ```
  </Accordion>
</AccordionGroup>

## Handling caller-defined tool calls

Inspect `tool_calls` and allowlist the names your backend supports before executing anything. Arguments are JSON objects that follow the schema sent with the request.

```javascript custom-tool-handler.js theme={null}
for (const toolCall of payload.tool_calls ?? []) {
  if (toolCall.name === "create_ticket") {
    await createTicket({
      priority: toolCall.arguments.priority,
      firstMessage: payload.response,
      source: "suppio_api",
    });
  }
}
```

Blocked responses always return an empty `tool_calls` array.

## Handling escalations

The API does not automatically notify staff or create tickets. Your backend should inspect `actions` and run your own workflow.

```javascript escalation-handler.js theme={null}
const payload = await askSuppio("I need to talk to a human.");

if (payload.actions?.some((action) => action.type === "escalate_to_human")) {
  await createTicket({
    subject:
      payload.actions.find((action) => action.type === "suggest_title")?.title ??
      "Support request",
    firstMessage: payload.response,
    source: "suppio_api",
  });
}
```

## Errors

API errors use this shape:

```json theme={null}
{
  "error": {
    "code": "out_of_credits",
    "message": "This workspace is out of credits for the current billing period."
  }
}
```

| Code                      | HTTP status | What it means                                                                                      |
| ------------------------- | ----------: | -------------------------------------------------------------------------------------------------- |
| `authentication_required` |         401 | The authorization header is missing, malformed, too long, or does not use a `suppio_live_` key.    |
| `invalid_api_key`         |         401 | The key is malformed, deleted, revoked, unknown, or does not match the stored hash.                |
| `wrong_agent`             |         403 | The key is valid but is scoped to a different agent.                                               |
| `agent_disabled`          |         403 | The agent is disabled in Sandbox. Enable it before sending API requests.                           |
| `agent_not_found`         |         404 | The requested agent could not be found for this key.                                               |
| `invalid_request`         |         400 | The JSON body did not include a usable `message`.                                                  |
| `invalid_attachment`      |         400 | An attachment was malformed, unsupported, too large, or did not match its declared media type.     |
| `invalid_tools`           |         400 | Caller-defined tools were malformed, duplicated, reserved, non-strict, or over a documented limit. |
| `invalid_json`            |         400 | The request body was not valid JSON.                                                               |
| `payload_too_large`       |         413 | The JSON body exceeded 16 MB.                                                                      |
| `context_required`        |         409 | The agent has no active context. Add context before sending API requests.                          |
| `out_of_credits`          |         402 | The workspace has no credits left for the current billing period.                                  |
| `rate_limited`            |         429 | The API key exceeded its requests-per-minute limit.                                                |
| `daily_rate_limited`      |         429 | The agent exceeded its daily request limit for the current UTC day.                                |
| `internal_error`          |         500 | Suppio could not generate the response.                                                            |

## Rate limits

Rate limits are enforced per API key with a one-minute window. The effective limit is the lower of the key's stored limit and the current workspace plan limit.

| Plan       | Requests per minute | Requests per day |
| ---------- | ------------------: | ---------------: |
| Free       |                  20 |            7,200 |
| Plus       |                  60 |           21,600 |
| Pro        |                 180 |           64,800 |
| Enterprise |                 600 |          216,000 |

Authenticated API responses include:

```http theme={null}
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1780343190
```

Rate-limited responses also include:

```http theme={null}
Retry-After: 12
```

Daily dashboard counters are tracked per UTC day across all API keys for an agent. The daily limit is the plan's requests-per-minute limit multiplied by 360 minutes.

## Key management

* You can have up to 5 active API keys per agent.
* You can create up to 5 API keys per day.
* Rotate keys by creating a new key, updating your backend, then deleting the old key.
* Deleted keys stop working immediately.
* The dashboard shows key prefix, last four characters, created time, last used time, total requests, requests today, and active key count.

## Related reference

The generated endpoint reference is available in the **API Reference** tab.
