Skip to content
Select themeSelect language

`POST /api/agent-profiles` — create an agent profile (workspace-admin).

POST
/api/agent-profiles
curl --request POST \
--url https://example.com/api/agent-profiles \
--header 'Content-Type: application/json' \
--cookie supacloud_session=<supacloud_session> \
--data '{ "avatar_kind": "example", "avatar_value": "example", "capability_allowlist": [ "example" ], "color": "example", "default_agent_type": "example", "description": "example", "effort_default": "example", "enabled": true, "execution_mode": "example", "fixed_model": "example", "interactive_approval": true, "max_subagents": 1, "max_turns": 1, "mcp_memory_create": true, "mcp_ops_per_hour_cap": 1, "mcp_ops_per_task_cap": 1, "memory_policy": "example", "model_policy": "example", "name": "example", "persona_prompt": "example", "post_flight_checks": "example", "pr_template": "example", "pre_flight_checks": "example", "resource_class": "normal", "retry_policy": "example", "review_required": true, "script_allowlist": [ "example" ], "slug": "example", "tags": [ "example" ], "tool_approval_deny_minutes": 1 }'
Media typeapplication/json
object
avatar_kind
string | null
avatar_value
string | null
capability_allowlist
Array<string> | null
color
string | null
default_agent_type
string | null
description
string | null
effort_default

ADR 0045 §3.6 — default reasoning effort (minimal | low | medium | high | max). Optional; preserves the current value on update, leaves None (inherit the runner default) on create.

string | null
enabled
boolean | null
execution_mode

Issue #364 / ADR 0039 (D7); #708 who-3 — normal | workflow (ultracode RETIRED to the effort axis). Optional; preserves the current value on update, defaults to normal on create.

string | null
fixed_model
string | null
interactive_approval

ADR 0045 §3.2 — opt this profile into the interactive tool-approval gate (resolve_interactive_approvals_for_run). Optional; preserves the current value on update, defaults to false on create. #708 W5 wired this WRITE path (it was read-only before — the create/update never persisted it).

boolean | null
max_subagents

#838 — per-profile DEFAULT for the subagent concurrency ceiling (migration 358; 1..=64). The middle tier of task.config.max_subagents ?? profile ?? workspace ?? (no env). DOUBLE-Option with the same semantics as [Self::max_turns]: absent preserves the current value on update / leaves NULL on create; an explicit null clears it; a value sets it. “Absent = unchanged” is load-bearing — the simple profiles tab sends a partial payload and must never null a stored default (the #858 W4a ops-cap pattern).

integer | null format: int32
max_turns

#1018 — per-profile DEFAULT for the harness turn limit (migration 340, positive). DOUBLE-Option: absent preserves the current value on update / leaves NULL (no default — the runner’s 500 applies) on create; an explicit null clears it; a value sets it. “Absent = unchanged” is load-bearing — the simple profiles tab sends a partial payload, and it must never null a stored default (the #858 W4a ops-cap pattern).

integer | null format: int32
mcp_memory_create

ADR 0049 (#547 / #548) — opt-in gate for the agent memory.create MCP tool. Optional; preserves the current value on update, defaults to false (fail-closed) on create. A workspace admin sets this on the profile editor to open the unreviewed direct-write path.

boolean | null
mcp_ops_per_hour_cap

#858 W4a — per-profile override of the profile-hourly MCP Ops budget (migration 320; range 1–1000). Same DOUBLE-Option semantics as mcp_ops_per_task_cap. The workspace-daily Ops ceiling stays fixed.

integer | null format: int32
mcp_ops_per_task_cap

#858 W4a — per-profile override of the per-task/per-tool MCP Ops budget (migration 320; range 1–500). DOUBLE-Option: absent preserves the current value on update / leaves NULL (the ADR 0028 default, 10) on create; an explicit null clears the override back to the default; a value sets it. Admin-gated — the profile PUT already requires workspace admin.

integer | null format: int32
memory_policy
model_policy
string | null
name
required
string
persona_prompt
string | null
post_flight_checks
pr_template
string | null
pre_flight_checks
resource_class
One of:
null
retry_policy
review_required
boolean | null
script_allowlist

The script-run allowlist. Optional; absent preserves the current value on update / leaves None (OPEN — every script allowed) on create; a value (incl. [], fail-closed) sets it. Mirrors capability_allowlist. #708 W9 wired this so a workspace manifest round-trips it — the manifest apply supplies a fail-closed Some([]) default (never the open None) when the manifest omits it.

Array<string> | null
slug
string | null
tags
Array<string> | null
tool_approval_deny_minutes

#1175 (migration 363) — the profile’s TOOL-GATE deny budget in minutes, the middle tier of task.config.tool_approval_deny_minutes ?? profile ?? workspace ?? 1440.

It governs ONLY the deny timeout policy, which a caller must name explicitly. hold — the default for every runner-raised gate since #1164 — carries no deadline, and no value here can give it one.

Range [5, 43200], enforced by the migration-363 CHECK and refused BY NAME at write time rather than clamped, so a stored value is always the value that applies. DOUBLE-Option, same semantics as [Self::max_turns].

integer | null format: int32
Media typeapplication/json
object
autonomy_level

ADR 0045 §3.1 Stage 4 — OPTIONAL profile autonomy override (mig 214, nullable SMALLINT). None = inherit the project/workspace level (sits between project and workspace in the §3.1 precedence chain). #[serde(default)] keeps old AgentProfileVersion JSON snapshots (pre-214) deserializable — they default to None (a no-op).

integer | null format: int32
autonomy_overrides

ADR 0045 §3.1 — per-axis Advanced override vector (mig 214, JSONB NOT NULL DEFAULT '{}'). Empty {} = no per-axis override; the editor reads an optional level key. #[serde(default)] keeps old snapshots deserializable. Runtime stays serde_json::Value; the spec types it as an open map.

object
key
additional properties
avatar_kind
required

Avatar rendering kind (color | initial | icon | image). Runtime String; the spec narrows it to the closed [AvatarKind] the editor offers.

string
Allowed values: color initial icon image
avatar_value
string | null
capability_allowlist
required
Array<string>
color
required
string
created_at
required
string format: date-time
default_agent_type
required
string
description
string | null
effort_default
One of:
null
enabled
required
boolean
execution_mode

Issue #364 / ADR 0039 (D7) — Claude execution mode lever (normal | workflow; ultracode was RETIRED from this dimension by #708 who-3, migration 285 — it survives only as the top EFFORT stage). #[serde(default = ...)] keeps old AgentProfileVersion JSON snapshots (written before this column existed) deserializable; they default to "normal" (a no-op). Runtime String; the spec narrows it to the closed [ExecutionMode].

string
Allowed values: normal workflow
fixed_model
string | null
id
required
string format: uuid
interactive_approval

ADR 0045 §3.2 Stage 3 — when true, a task running under this profile arms the interactive-approval gate (the runner’s canUseTool / pre-exec hook pauses on a dangerous tool call, an open question, or a visual before/after and waits for a human Approve/Reject; migration 213). false is today’s behaviour (no gate). #[serde(default)] keeps old AgentProfileVersion JSON snapshots (written before this column existed) deserializable — they default to false (a no-op).

boolean
is_workspace_default

Issue #242 — when true, this profile’s capability_allowlist is the workspace fallback MCP tool set for profile-less / empty-allowlist tasks. At most one profile per team carries this flag (enforced by the partial unique index uq_agent_profiles_workspace_default in migration 119).

boolean
max_subagents

#838 — per-profile DEFAULT for the subagent concurrency ceiling (migration 358, nullable; 1..=64). The env build folds task.config.max_subagents ?? profile.max_subagents ?? workspace_settings.default_max_subagents ?? (no env), so this is the middle tier: the launch override still wins, and with every tier absent no SUBAGENT_MAX_PARALLEL is emitted and the harness keeps its own default. None = today’s behaviour.

The ceiling is only as strong as the harness allows — codex takes a real per-session cap, the rest receive it as an instruction — which is why the UI labels it from model_capabilities.subagent_cap_level instead of implying a guarantee. #[serde(default)] keeps pre-358 AgentProfileVersion snapshots deserializable.

integer | null format: int32
max_turns

#1018 — per-profile DEFAULT for the harness turn limit (migration 340, nullable; > 0). The env build folds task.config.max_turns ?? profile.max_turns ?? (no env) — the task config stays the per-launch override, and with both absent no MAX_TURNS env is emitted so the runner default (500, DEFAULT_MAX_TURNS) applies. None = today’s behaviour. #[serde(default)] keeps pre-340 AgentProfileVersion snapshots deserializable.

integer | null format: int32
mcp_memory_create

ADR 0049 (#547 / #548) — opt-in gate for the agent memory.create MCP tool (migration 249, BOOLEAN NOT NULL DEFAULT false). When false (the default for every profile) a task running under this profile CANNOT directly create a memory — memory.create fails closed; only a workspace admin enabling this on the profile editor opens the unreviewed direct-write path. Distinct from memory_policy.allow_propose_create (the default-true review-first gate). #[serde(default)] keeps old AgentProfileVersion JSON snapshots (written before this column existed) deserializable — they default to false (a no-op).

boolean
mcp_ops_per_hour_cap

#858 W4a — per-profile override of the rolling profile-hourly MCP Ops budget (migration 320, nullable; range 1–1000). None = the ADR 0028 default (30). The workspace-daily Ops ceiling (100/day) is NOT overridable. #[serde(default)] keeps pre-320 snapshots deserializable.

integer | null format: int32
mcp_ops_per_task_cap

#858 W4a — per-profile override of the per-task/per-tool MCP Ops budget (migration 320, nullable; range 1–500). None = the ADR 0028 default (10). Admin-gated via the profile PUT; a long-lived coordinator session raises it deliberately instead of the global default weakening. #[serde(default)] keeps pre-320 AgentProfileVersion snapshots deserializable.

integer | null format: int32
memory_policy
required

The fixed memory-policy knobs the editor reads. Runtime stays serde_json::Value; the spec types it as [AgentMemoryPolicy].

object
allow_personal
required
boolean
auto_inject_kinds
required
Array<string>
auto_inject_scopes
required
Array<string>
max_auto_memories
required
integer format: int64
mcp_read_kinds
required
Array<string>
mcp_read_scopes
required
Array<string>
model_policy
required

Model-selection policy (manual | fixed | auto). Runtime String; the spec narrows it to the closed [ModelPolicy].

string
Allowed values: manual fixed auto
name
required
string
persona_prompt
required
string
post_flight_checks
required

Forward-compatible list of post-flight check entries (e.g. the kind:"visual_verify" gate). Runtime stays serde_json::Value; spec-typed as an array of open maps.

Array<object>
object
key
additional properties
pr_template
string | null
pre_flight_checks
required

Forward-compatible list of pre-flight check entries (each an open object, e.g. a kind-discriminated gate). Runtime stays serde_json::Value; the spec types it as an array of open maps so the value is not stripped.

Array<object>
object
key
additional properties
required_worker_group_ids

ADR 0046 RUNG 1b — OPTIONAL profile worker-group affinity override (mig 227, nullable UUID[]). None = inherit (the head of the first-non-null profile->project->task chain — NULL contributes nothing); Some(vec![]) = an explicit “no affinity from this source”. Resolved into the task’s required_labels by services::runners::groups. #[serde(default)] keeps old AgentProfileVersion JSON snapshots (pre-227) deserializable.

NB: this binding is intentionally EXCLUDED from agent-profile version snapshot/restore — it is dispatch-routing scope (a live operational binding), not part of the profile’s behavioural definition, so a version restore does not carry or revert it (ADR 0046 RUNG 1b review).

Array<string> | null
resource_class
One of:
null
retry_policy
required

Open retry-policy bag. Runtime stays serde_json::Value; spec-typed as an open map.

object
key
additional properties
review_required
required
boolean
script_allowlist

Issue #217 / ADR 0028 — optional slug whitelist for the script.run MCP tool. NULL means any script in the workspace is callable; a non-NULL array (including empty) is an explicit whitelist.

Array<string> | null
slug
required
string
tags
required
Array<string>
tool_approval_deny_minutes

#1175 (migration 363, nullable) — the profile’s TOOL-GATE deny budget in minutes. The middle tier of task.config.tool_approval_deny_minutes ?? profile ?? workspace_settings.default_tool_approval_deny_minutes ?? 1440.

It sits on the profile for the same reason the resource class does: a profile already decides WHO runs and how far it may go unattended, and how long that runbook’s gate may wait before an explicit deny policy expires is the same kind of fact. None = today’s behaviour.

It NEVER affects a hold gate, which is the default for every runner-raised gate and carries no deadline at all (#1164).

integer | null format: int32
updated_at
required
string format: date-time
version
required
integer format: int32
workspace_id
required
string format: uuid
Example
{
"avatar_kind": "color",
"effort_default": "minimal",
"execution_mode": "normal",
"model_policy": "manual",
"resource_class": "normal"
}