# Save a new agent at revision 1.

`POST /o/{org}/agents`

Requires an `Idempotency-Key`. A replay of the same key with the same body returns the
original agent (201) before any catalog or connection check runs; the same key with a
different body is `409 IDEMPOTENCY_CONFLICT`. Validation failures are `422` (not `400`)
with a `code` and per-field `fields`; the first failing check wins, except catalog support,
which reports every unsupported field at once under `UNSUPPORTED_PROFILE`.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `org` | path | string | yes | The org's slug or id. A caller with no role in the org gets `404`. |
| `Idempotency-Key` | header | string | yes | 1–128 characters after trimming. |

## Request body

- `name` (string, required): Trimmed.
- `handle` (string, required): Trimmed and lowercased before matching; unique per caller.
- `description` (string, optional)
- `instructions` (string, optional): At most 16 KiB of UTF-8, no NUL.
- `harness` (string, required, one of `claude`, `codex`, `opencode`): Launch-boundary managed harnesses.
- `catalog_revision` (string, required): Must equal the current `catalog_revision` from `GET /o/{org}/agents/available`.
- `runtime` (object | object, optional)
- `defaults` (object, optional): Omitted `model`/`effort` take the harness's `workhorse` suggested setup; `speed` defaults to `standard` (`fast` is refused today).
  - `model` (SavedAgentModel, optional)
    - `selection` (string, required)
    - `id` (string, required): A model id the harness lists in `GET /o/{org}/agents/available`.
  - `effort` (string | null, optional)
  - `speed` (string, optional, one of `standard`, `fast`)
- `connection_refs` (array of SavedAgentConnectionRef, optional): Each must be one of the caller's own usable connections.
  - `kind` (string, required, one of `oauth`, `mcp`)
  - `id` (string, required)
- `capabilities` (object, optional): Missing keys are false; values are coerced to boolean.
  - `browser` (boolean, optional)
  - `computer` (boolean, optional)
- `tools` (object | object, optional)
- `network` (object | object, optional)
- `plugins` (array of object, optional): Only `catalog_id` is read; the version is locked from the catalog at save.
  - `catalog_id` (string, required)
- `source` (object | object, optional)

## Example request

```bash
curl -X POST "https://api.prix.dev/o/$RUSH_ORG/agents" \
  -H "Authorization: Bearer $RUSH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string",
    "handle": "string",
    "harness": "claude",
    "catalog_revision": "string"
  }'
```

```typescript
const org = process.env.RUSH_ORG;

const res = await fetch(`https://api.prix.dev/o/${org}/agents`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RUSH_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "string",
    handle: "string",
    harness: "claude",
    catalog_revision: "string",
  }),
});

console.log(res.status, await res.json());
```

```python
import os

import requests

org = os.environ["RUSH_ORG"]

res = requests.post(
    f"https://api.prix.dev/o/{org}/agents",
    headers={"Authorization": f"Bearer {os.environ['RUSH_TOKEN']}"},
    json={
        "name": "string",
        "handle": "string",
        "harness": "claude",
        "catalog_revision": "string",
    },
)

print(res.status_code, res.json())
```

## Responses

### 201

Saved (or replayed) agent.

- `id` (string, required)
- `name` (string, required)
- `handle` (string, required)
- `description` (string, required)
- `harness` (string, required, one of `claude`, `codex`, `opencode`): Launch-boundary managed harnesses.
- `revision` (integer, required)
- `archived` (boolean, required)
- `source` (object | object | null, required)
- `config` (SavedAgentConfig, required): The desired configuration stored with one revision.
  - `harness` (string, required, one of `claude`, `codex`, `opencode`): Launch-boundary managed harnesses.
  - `catalog_revision` (string, required)
  - `runtime` (object | object, required)
  - `defaults` (object, required)
    - `model` (SavedAgentModel, required)
      - `selection` (string, required)
      - `id` (string, required): A model id the harness lists in `GET /o/{org}/agents/available`.
    - `effort` (string | null, required)
    - `speed` (string, required, one of `standard`, `fast`)
  - `instructions` (string, required)
  - `connection_refs` (array of SavedAgentConnectionRef, required)
    - `kind` (string, required, one of `oauth`, `mcp`)
    - `id` (string, required)
  - `capabilities` (object, required)
    - `browser` (boolean, required)
    - `computer` (boolean, required)
  - `tools` (object | object, required)
  - `network` (object | object, required)
  - `plugins` (array of object, required)
    - `catalog_id` (string, required)
- `resolved_at_save` (SavedAgentResolved, required): What the config resolved to against the catalog when this revision was saved.
  - `model` (string, required)
  - `effort` (string | null, required)
  - `speed` (string, required, one of `standard`, `fast`)
  - `harness_version` (string | null, required)
  - `tool_ids` (array of string | null, required)
  - `plugins` (array of object, required)
    - `catalog_id` (string, required)
    - `version` (string, required)
    - `digest` (string, required)
  - `runtime_id` (string | null, required)
  - `catalog_revision` (string, required)
  - `pin_guarantee` (boolean, required)
- `created_at` (string, required)
- `updated_at` (string, required)

### 400

Missing or over-long `Idempotency-Key` (`IDEMPOTENCY_KEY_REQUIRED`).

### 401

No bearer, an unknown, revoked or expired one, or an API key on a route keys cannot call.

### 404

Org not found, or `source.profile_id`/`source.revision` is not one of the caller's agents (`SOURCE_NOT_FOUND`).

### 409

`IDEMPOTENCY_CONFLICT` (key reused with a different body) or `HANDLE_TAKEN` (the caller already has an agent with this handle).

### 422

Body refused. `code` is one of `INVALID_BODY`, `INVALID_NAME`, `INVALID_HANDLE`,
`INVALID_DESCRIPTION`, `INVALID_HARNESS`, `INVALID_CATALOG`, `CATALOG_REFRESH_REQUIRED`
(`catalog_revision` is not the current one), `INVALID_INSTRUCTIONS`, `INVALID_RUNTIME`,
`INVALID_TOOLS`, `INVALID_NETWORK`, `INVALID_CONNECTION_REFS`, `INVALID_SOURCE`,
`INVALID_MODEL`, `INVALID_EFFORT`, `INVALID_SPEED`, `INVALID_PLUGINS`,
`UNSUPPORTED_PROFILE` (field codes such as `UNKNOWN_MODEL`, `SPEED_NOT_QUALIFIED`,
`NETWORK_ENFORCEMENT_NOT_QUALIFIED`, `UNKNOWN_PLUGIN`), or `CONNECTION_NOT_OWNED`.

### 500

`INTERNAL_ERROR`.

## Example response (201)

```json
{
  "id": "3f6c1e2a-8b4d-4c1e-9a7f-2d5b6e8c0a14",
  "name": "string",
  "handle": "string",
  "description": "string",
  "harness": "claude",
  "revision": 1,
  "archived": true,
  "source": {
    "profile_id": "string",
    "revision": 1
  },
  "config": {
    "harness": "claude",
    "catalog_revision": "string",
    "runtime": {
      "selection": "latest_verified"
    },
    "defaults": {
      "model": {
        "selection": "pinned",
        "id": "string"
      },
      "effort": "none",
      "speed": "standard"
    },
    "instructions": "string",
    "connection_refs": [
      {
        "kind": "oauth",
        "id": "string"
      }
    ],
    "capabilities": {
      "browser": true,
      "computer": true
    },
    "tools": {
      "mode": "default"
    },
    "network": {
      "mode": "public"
    },
    "plugins": [
      {
        "catalog_id": "string"
      }
    ]
  },
  "resolved_at_save": {
    "model": "string",
    "effort": "none",
    "speed": "standard",
    "harness_version": "string",
    "tool_ids": [
      "string"
    ],
    "plugins": [
      {
        "catalog_id": "string",
        "version": "string",
        "digest": "string"
      }
    ],
    "runtime_id": "string",
    "catalog_revision": "string",
    "pin_guarantee": true
  },
  "created_at": "2026-10-06T12:00:00Z",
  "updated_at": "2026-10-06T12:00:00Z"
}
```
