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

# Create an Agent

> Register a Custom Agent with its prompts and IO schema

Register a [Custom Agent](/tasks/agent/custom-agent) in the marketplace under your organization.

The agent is authored at the organization level, so it is not bound to a workspace and is not runnable until it is connected into one. Returns the agent `id`, which you pass to [Update an Agent](/api-reference/v1-agent/update-agent).

Requires the **Agent: Full** permission at organization level.

<Note>
  Every agent created here is a Custom Agent. There is no `agentClass` field to set — the class is pinned, and `blueprint` must carry the prompts the agent runs with.
</Note>

<Note>
  There is no provider call to make first. Your organization's agent provider is created on demand on your first agent, and `providerId` is not accepted in the body.
</Note>

## Headers

<ParamField header="x-service-key" type="string" required>
  Your API authentication key
</ParamField>

## Body Parameters

The agent is nested under an `agent` key.

<ParamField body="agent" type="object" required>
  The agent to create. Fields below.
</ParamField>

<ParamField body="agent.name" type="string" required>
  Agent display name. Maximum 255 characters.
</ParamField>

<ParamField body="agent.icon" type="string" required>
  Agent icon as a base64-encoded SVG (or PNG) — a plain string, with no `data:` prefix. Maximum 65535 characters.
</ParamField>

<ParamField body="agent.blueprint" type="object" required>
  The agent implementation: the prompts the agent runs with, plus the model that runs them. See [Blueprint](#blueprint). Maximum 256KB JSON-encoded.
</ParamField>

<ParamField body="agent.categories" type="string[]" required>
  Categories the agent is filed under in the marketplace. Maximum 5 entries. See [Categories](#categories).
</ParamField>

<ParamField body="agent.description" type="string">
  What the agent does, shown under its name. Maximum 2048 characters.
</ParamField>

<ParamField body="agent.inputs" type="object">
  The agent's input schema, keyed by each variable's `variableName`. Maximum 100 entries and 512KB JSON-encoded. See [Variable object](#variable-object).
</ParamField>

<ParamField body="agent.outputs" type="object">
  The agent's output schema, keyed by each variable's `variableName`. Same shape and limits as `inputs`.
</ParamField>

<ParamField body="agent.status" type="string" default="active">
  Agent status — `active`, `inactive`, `soon` or `deprecated`.
</ParamField>

<ParamField body="agent.isNew" type="boolean" default="true">
  Show the "new" badge on the agent tile.
</ParamField>

<ParamField body="agent.isPopular" type="boolean" default="false">
  Show the "popular" badge on the agent tile.
</ParamField>

<ParamField body="agent.isBeta" type="boolean" default="false">
  Show the "beta" badge on the agent tile. Accepted on create but not echoed back in the response.
</ParamField>

<Note>
  Your organization is always taken from the API key — it cannot be set in the body. Nothing here is workspace-scoped.
</Note>

## Blueprint

The prompts the agent runs with.

<ParamField body="systemPrompt" type="string" required>
  System prompt the agent runs with. Must be non-empty.
</ParamField>

<ParamField body="userPrompt" type="string" required>
  User prompt template. Must be non-empty. Reference the agent's inputs with `{{variableName}}`.
</ParamField>

<ParamField body="model" type="string">
  Model the agent runs on, for example `gpt-4o`. Must be one the platform routes to — see [Models and providers](#models-and-providers).
</ParamField>

<ParamField body="provider" type="string">
  Provider of that model, for example `openai`. Must agree with the model when both are given.
</ParamField>

<ParamField body="backupModel" type="string">
  Model to fall back to. Validated the same way as `model`.
</ParamField>

<ParamField body="backupProvider" type="string">
  Provider of the fallback model. Validated the same way as `provider`.
</ParamField>

<Note>
  `systemPrompt` and `userPrompt` may be sent in either camelCase or snake\_case (`system_prompt`, `user_prompt`). A blueprint missing either one, or carrying an empty or whitespace-only value for it, is rejected with `400`.
</Note>

<Warning>
  Apart from the prompts and these two pairs, `blueprint` is forwarded untouched and stored verbatim — nothing else in it is validated structurally beyond its encoded size. The fields above are a base, not a ceiling: extra fields are kept, and unrecognised keys are forwarded exactly as they arrived. The documented camelCase keys are renamed to the agent service's snake\_case at the boundary.
</Warning>

### Models and providers

`model` and `provider` are checked before the agent is created, and the same rules apply to `backupModel` and `backupProvider`. Both pairs are accepted in either casing (`backup_model`, `backup_provider`).

A model is accepted when the platform's model registry knows it, or when its name begins with a prefix belonging to a provider the platform routes to — `gpt-`, `o` followed by a digit, and `chatgpt` for `openai`, `claude` for `anthropic`, `gemini` for `google`. The prefix fallback means a model released before the registry catches up is still accepted, so `claude-opus-9-future` passes while `llama-3-70b-instruct` does not.

When both a model and a provider are given, the provider must be the one that model actually runs on — `{ "model": "gpt-4o", "provider": "anthropic" }` is rejected.

Each pair is checked only when it names a model or a provider. A blueprint that names neither is accepted as it stands.

#### Providers

A provider named on its own, with no model beside it, must be one of these. Anything else — `mistral`, say — is rejected.

| Provider | Routes to |
| - | - |
| `openai` | GPT models, called directly |
| `anthropic` | Claude models, called directly |
| `google` | Gemini models |
| `bedrock` | Claude and GPT models through AWS Bedrock, ids prefixed `bedrock/` |
| `compass` | GPT models through Compass, ids prefixed `compass/` |
| `custom_openai` | Custom OpenAI-compatible endpoints. Not offered in the app's model picker |
| `custom_anthropic` | Custom Anthropic-compatible endpoints. Not offered in the app's model picker |

#### Commonly used models

A sample of the current lineup, not the whole of it — the registry moves with the platform, and this page will lag it. The model picker in the Opus app is the live list.

| `model` | `provider` | Shown in the app as |
| - | - | - |
| `gpt-5.6-terra` | `openai` | GPT 5.6 Terra |
| `gpt-5.2` | `openai` | GPT 5.2 |
| `gpt-4o` | `openai` | GPT 4o |
| `claude-opus-5` | `anthropic` | Claude Opus 5 |
| `claude-sonnet-5` | `anthropic` | Claude Sonnet 5 |
| `claude-haiku-4-5-20251001` | `anthropic` | Claude Haiku 4.5 |
| `gemini-3.5-flash` | `google` | Gemini 3.5 Flash |
| `gemini-2.5-pro` | `google` | Gemini 2.5 Pro |
| `bedrock/claude-opus-5` | `bedrock` | Claude Opus 5 (Bedrock) |
| `compass/gpt-5.1` | `compass` | GPT 5.1 (Compass) |

<Warning>
  Models reached through Bedrock or Compass carry the provider in the id itself, and the two halves must agree. `{ "model": "bedrock/claude-sonnet-5", "provider": "bedrock" }` is accepted; the same model named bare, as `{ "model": "claude-sonnet-5", "provider": "bedrock" }`, resolves to `anthropic` and is rejected.
</Warning>

## Variable object

Each value inside `inputs` and `outputs`. The map key must equal the variable's `variableName`.

<ParamField body="id" type="string" required>
  Stable identifier for the variable, unique within the schema. Maximum 255 characters.
</ParamField>

<ParamField body="variableName" type="string" required>
  Programmatic key. Must equal the key this entry sits under. Maximum 255 characters.
</ParamField>

<ParamField body="allowedTypes" type="object[]" required>
  Value types this variable accepts. Between 1 and 11 entries. Each has a `type` — one of `int`, `float`, `bool`, `str`, `object`, `file`, `array`, `date`, `binary`, `json_string`, `node` — plus optional `typeDefinition` and `typeDefinitionEnforced`.
</ParamField>

<ParamField body="displayName" type="string">
  Human-readable label rendered in the workflow builder. Maximum 255 characters. When omitted, the agent service applies its own default.
</ParamField>

<ParamField body="description" type="string">
  Description shown next to the variable in the builder. Maximum 2048 characters.
</ParamField>

<ParamField body="default" type="any" default="null">
  Pre-filled default value. Its type must match one of `allowedTypes`.
</ParamField>

<ParamField body="isNullable" type="boolean" default="false">
  When true, the workflow may leave this variable unset.
</ParamField>

<ParamField body="tags" type="object[]">
  Annotations the builder uses for grouping and filtering, each `{ variableName, value, description }`. Maximum 20.
</ParamField>

<ParamField body="options" type="any[]">
  Enum-style choice list. When non-empty the builder renders a dropdown. Each entry is free-form JSON. Maximum 20.
</ParamField>

<ParamField body="currentType" type="string">
  Which of `allowedTypes` is currently active. Must be one of them — consumers fall back to the first entry when unset.
</ParamField>

<ParamField body="nonEditableFields" type="string[]">
  Fields the builder locks, preventing the workflow author from changing them — any of `variable_name`, `display_name`, `description`, `is_nullable`, `tags`, `options`, `allowed_types`. Maximum 20.
</ParamField>

<ParamField body="canDelete" type="boolean" default="true">
  When false, the builder hides the "remove variable" affordance.
</ParamField>

<Note>
  For composite types, `typeDefinition` describes the shape inside: a nested variable map for `object`, a single nested variable for `array`, or a date format string for `date`. Omit it for scalars. Set `typeDefinitionEnforced` to hold the workflow author to it exactly.
</Note>

## Categories

One to five of: `parsing_and_serialization`, `validation_and_normalization`, `transformation_and_mapping`, `aggregation_and_analysis`, `numerical_computation`, `statistical_computation`, `optimization_and_search`, `text_processing`, `document_and_ocr_processing`, `media_processing`, `control_flow_and_orchestration`, `state_and_caching`, `io_and_storage`, `networking_and_apis`, `security_and_identity`.

## Response

<ResponseField name="id" type="string" required>
  The agent id. Pass it as `{agentId}` to [Update an Agent](/api-reference/v1-agent/update-agent).
</ResponseField>

<ResponseField name="name" type="string" required>
  Agent display name.
</ResponseField>

<ResponseField name="description" type="string">
  Agent description.
</ResponseField>

<ResponseField name="icon" type="string">
  Agent icon.
</ResponseField>

<ResponseField name="categories" type="string[]">
  Categories the agent is filed under.
</ResponseField>

<ResponseField name="status" type="string">
  Agent status.
</ResponseField>

<ResponseField name="isNew" type="boolean">
  Whether the "new" badge is shown.
</ResponseField>

<ResponseField name="isPopular" type="boolean">
  Whether the "popular" badge is shown.
</ResponseField>

<ResponseField name="versionId" type="string">
  Id of the version this call created.
</ResponseField>

<ResponseField name="groupId" type="string">
  Owning workspace, or `null` when the agent is visible across the marketplace.
</ResponseField>

## Errors

<ResponseField name="400" type="Bad Request">
  Invalid body — a missing required field, a value over its limit, more than 5 categories, a `blueprint` without a non-empty `systemPrompt` and `userPrompt`, a `blueprint` naming a model or provider the platform cannot route to, or an `inputs`/`outputs` entry that is not a valid variable.
</ResponseField>

<ResponseField name="401" type="Unauthorized">
  Missing, invalid, or expired API key.
</ResponseField>

<ResponseField name="403" type="Forbidden">
  The API key is not org-scoped, or the key owner lacks the **Agent: Full** permission in the organization.
</ResponseField>

<ResponseField name="422" type="Unprocessable Entity">
  Rejected downstream after passing validation here — most often an organization that is not verified to publish to the marketplace.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://operator.opus.com/api/v1/agent \
    --header 'Content-Type: application/json' \
    --header 'x-service-key: {YOUR_SERVICE_KEY}' \
    --data '{
      "agent": {
        "name": "Adder",
        "description": "Adds two numbers and returns the total",
        "icon": "{BASE64_ICON}",
        "categories": ["numerical_computation"],
        "blueprint": {
          "systemPrompt": "You are a careful arithmetic assistant.",
          "userPrompt": "Add {{firstNumber}} and {{secondNumber}} and return the total.",
          "model": "gpt-4o",
          "provider": "openai"
        },
        "inputs": {
          "firstNumber": {
            "id": "1",
            "variableName": "firstNumber",
            "displayName": "First Number",
            "allowedTypes": [{ "type": "float" }]
          },
          "secondNumber": {
            "id": "2",
            "variableName": "secondNumber",
            "displayName": "Second Number",
            "allowedTypes": [{ "type": "float" }]
          }
        },
        "outputs": {
          "sumResult": {
            "id": "1",
            "variableName": "sumResult",
            "displayName": "Sum Result",
            "allowedTypes": [{ "type": "float" }]
          }
        }
      }
    }'
  ```

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

  url = "https://operator.opus.com/api/v1/agent"
  headers = {
      "Content-Type": "application/json",
      "x-service-key": "{YOUR_SERVICE_KEY}"
  }
  payload = {
      "agent": {
          "name": "Adder",
          "description": "Adds two numbers and returns the total",
          "icon": "{BASE64_ICON}",
          "categories": ["numerical_computation"],
          "blueprint": {
              "systemPrompt": "You are a careful arithmetic assistant.",
              "userPrompt": "Add {{firstNumber}} and {{secondNumber}} and return the total.",
              "model": "gpt-4o",
              "provider": "openai",
          },
          "inputs": {
              "firstNumber": {
                  "id": "1",
                  "variableName": "firstNumber",
                  "displayName": "First Number",
                  "allowedTypes": [{"type": "float"}],
              },
              "secondNumber": {
                  "id": "2",
                  "variableName": "secondNumber",
                  "displayName": "Second Number",
                  "allowedTypes": [{"type": "float"}],
              },
          },
          "outputs": {
              "sumResult": {
                  "id": "1",
                  "variableName": "sumResult",
                  "displayName": "Sum Result",
                  "allowedTypes": [{"type": "float"}],
              }
          },
      }
  }

  response = requests.post(url, json=payload, headers=headers)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://operator.opus.com/api/v1/agent", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-service-key": "{YOUR_SERVICE_KEY}",
    },
    body: JSON.stringify({
      agent: {
        name: "Adder",
        description: "Adds two numbers and returns the total",
        icon: "{BASE64_ICON}",
        categories: ["numerical_computation"],
        blueprint: {
          systemPrompt: "You are a careful arithmetic assistant.",
          userPrompt: "Add {{firstNumber}} and {{secondNumber}} and return the total.",
          model: "gpt-4o",
          provider: "openai",
        },
        inputs: {
          firstNumber: {
            id: "1",
            variableName: "firstNumber",
            displayName: "First Number",
            allowedTypes: [{ type: "float" }],
          },
          secondNumber: {
            id: "2",
            variableName: "secondNumber",
            displayName: "Second Number",
            allowedTypes: [{ type: "float" }],
          },
        },
        outputs: {
          sumResult: {
            id: "1",
            variableName: "sumResult",
            displayName: "Sum Result",
            allowedTypes: [{ type: "float" }],
          },
        },
      },
    }),
  });

  const data = await response.json();
  console.log(data);
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Response theme={null}
  {
    "id": "{AGENT_ID}",
    "name": "Adder",
    "description": "Adds two numbers and returns the total",
    "icon": "{BASE64_ICON}",
    "categories": ["numerical_computation"],
    "status": "active",
    "isNew": true,
    "isPopular": false,
    "versionId": "{AGENT_VERSION_ID}",
    "groupId": null
  }
  ```
</ResponseExample>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.