Defining profiles
A profile captures the full agent setup needed to execute a run: worker type, model, agent version, MCP servers, skills, and (where the worker supports them) VS Code extensions. Profiles let you re-use the same setup across many runs and guarantee that a past run can be reproduced exactly.
When to use profiles
Section titled “When to use profiles”Use a profile when you want to:
- Compare the same task across different agent setups — create one profile per setup, submit a request per profile against the same task prompt and criteria, compare reports.
- Share a tested configuration with teammates — they pick the profile by name instead of re-deriving the same fields.
- Pin a run for audit / reproducibility — runs record the exact profile version they used.
You don't need a profile to submit a run — every field can be set inline at submission — but inline runs are harder to compare and reproduce later.
Profile identity vs. profile version
Section titled “Profile identity vs. profile version”A profile has two layers:
- Identity (mutable) —
name,description. You can rename or re-describe a profile any time. - Version (immutable) — the actual configuration: worker, model, agent version, MCP servers, skills, extensions. Once a version is saved, it never changes.
When you change a profile's configuration, Scope creates a new version (v2, v3, …) instead of mutating the existing one. The latest version is what's used by default when you select the profile in a new run, but every prior version is kept and can be referenced explicitly.
This is the property that makes runs reproducible: a run record points
at a specific profileVersionId that is guaranteed not to drift.
Anatomy of a profile
Section titled “Anatomy of a profile”| Field | Layer | Description |
| --- | --- | --- |
| name | identity | Human-readable, 1–128 characters. |
| description | identity | Optional, up to 512 characters. |
| workerType | version | Which agent runtime — see Choosing a coding agent. |
| model | version | Model identifier (e.g. gpt-4o, claude-3.5-sonnet). |
| agentVersion | version | Optional; pins a specific agent build. Latest active build is used if omitted. |
| mcpServers | version | Optional list of MCP server slugs. |
| skillRevisions | version | Optional list of Copilot agent skills, pinned to commit. |
| extensions | version | Optional list of VS Code extensions. VS Code Copilot only. |
For full field details and types, see the Profile schema reference.
Creating a profile
Section titled “Creating a profile”From the Portal
Section titled “From the Portal”- Open Profiles in the navigation, then click New profile.
- Fill in
nameand (optionally)description. - Pick a worker and model. These are required.
- Optionally add an
agentVersion, MCP servers, skills, and extensions. - Click Create. The profile is created at version 1.
From the REST API
Section titled “From the REST API”curl --request POST \ --url https://your-scope.example.com/api/v1/profiles \ --header 'Content-Type: application/json' \ --data '{ "name": "Copilot + Azure Skills", "description": "Latest Copilot with the Azure context skill", "workerType": "coder-acp-copilot", "model": "gpt-4o", "mcpServers": ["filesystem", "github"], "skillRevisions": ["github/vercel-labs/agent-skills/azure"]}'const url = 'https://your-scope.example.com/api/v1/profiles';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"name":"Copilot + Azure Skills","description":"Latest Copilot with the Azure context skill","workerType":"coder-acp-copilot","model":"gpt-4o","mcpServers":["filesystem","github"],"skillRevisions":["github/vercel-labs/agent-skills/azure"]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}import requests
url = "https://your-scope.example.com/api/v1/profiles"
payload = { "name": "Copilot + Azure Skills", "description": "Latest Copilot with the Azure context skill", "workerType": "coder-acp-copilot", "model": "gpt-4o", "mcpServers": ["filesystem", "github"], "skillRevisions": ["github/vercel-labs/agent-skills/azure"]}headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.json())package main
import ( "fmt" "strings" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/profiles"
payload := strings.NewReader("{\n \"name\": \"Copilot + Azure Skills\",\n \"description\": \"Latest Copilot with the Azure context skill\",\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\",\n \"mcpServers\": [\"filesystem\", \"github\"],\n \"skillRevisions\": [\"github/vercel-labs/agent-skills/azure\"]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");RequestBody body = RequestBody.create(mediaType, "{\n \"name\": \"Copilot + Azure Skills\",\n \"description\": \"Latest Copilot with the Azure context skill\",\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\",\n \"mcpServers\": [\"filesystem\", \"github\"],\n \"skillRevisions\": [\"github/vercel-labs/agent-skills/azure\"]\n}");Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/profiles") .post(body) .addHeader("Content-Type", "application/json") .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri("https://your-scope.example.com/api/v1/profiles"), Content = new StringContent("{\n \"name\": \"Copilot + Azure Skills\",\n \"description\": \"Latest Copilot with the Azure context skill\",\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\",\n \"mcpServers\": [\"filesystem\", \"github\"],\n \"skillRevisions\": [\"github/vercel-labs/agent-skills/azure\"]\n}") { Headers = { ContentType = new MediaTypeHeaderValue("application/json") } }};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}The response includes the new profile and its v1 version.
Creating a new version
Section titled “Creating a new version”Configurations evolve. When you want to capture a change (e.g. swap the model, add a skill), create a new version instead of mutating the existing one.
From the Portal
Section titled “From the Portal”On the profile detail page, click New version. The form is pre-populated with the current latest version's fields — edit what you need and save.
From the REST API
Section titled “From the REST API”curl --request POST \ --url https://your-scope.example.com/api/v1/profiles/%7BprofileId%7D \ --header 'Content-Type: application/json' \ --data '{ "workerType": "coder-acp-copilot", "model": "gpt-4o", "agentVersion": "copilot-0.0.418", "mcpServers": ["filesystem", "github"], "skillRevisions": ["github/vercel-labs/agent-skills/azure"]}'const url = 'https://your-scope.example.com/api/v1/profiles/%7BprofileId%7D';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"workerType":"coder-acp-copilot","model":"gpt-4o","agentVersion":"copilot-0.0.418","mcpServers":["filesystem","github"],"skillRevisions":["github/vercel-labs/agent-skills/azure"]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}import requests
url = "https://your-scope.example.com/api/v1/profiles/%7BprofileId%7D"
payload = { "workerType": "coder-acp-copilot", "model": "gpt-4o", "agentVersion": "copilot-0.0.418", "mcpServers": ["filesystem", "github"], "skillRevisions": ["github/vercel-labs/agent-skills/azure"]}headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.json())package main
import ( "fmt" "strings" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/profiles/%7BprofileId%7D"
payload := strings.NewReader("{\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\",\n \"agentVersion\": \"copilot-0.0.418\",\n \"mcpServers\": [\"filesystem\", \"github\"],\n \"skillRevisions\": [\"github/vercel-labs/agent-skills/azure\"]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");RequestBody body = RequestBody.create(mediaType, "{\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\",\n \"agentVersion\": \"copilot-0.0.418\",\n \"mcpServers\": [\"filesystem\", \"github\"],\n \"skillRevisions\": [\"github/vercel-labs/agent-skills/azure\"]\n}");Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/profiles/%7BprofileId%7D") .post(body) .addHeader("Content-Type", "application/json") .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri("https://your-scope.example.com/api/v1/profiles/%7BprofileId%7D"), Content = new StringContent("{\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\",\n \"agentVersion\": \"copilot-0.0.418\",\n \"mcpServers\": [\"filesystem\", \"github\"],\n \"skillRevisions\": [\"github/vercel-labs/agent-skills/azure\"]\n}") { Headers = { ContentType = new MediaTypeHeaderValue("application/json") } }};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}The new version becomes the profile's latestVersion.
Using a profile when submitting a run
Section titled “Using a profile when submitting a run”Portal
Section titled “Portal”In the request submit form, pick the profile from the dropdown. The configuration fields lock to that profile's latest version. Expand the version selector to choose an older version explicitly.
See Submitting requests (Portal).
REST API
Section titled “REST API”Pass profileId (resolves to latest version) or profileVersionId
(pins a specific version):
{ "scenario": { "task": "Create a Hello World Node.js / Express REST API.", "criteria": ["c-hello-world-express"] }, "profileVersionId": "p-550e8400-e29b-41d4-a716-446655440001@2"}For production pipelines, prefer profileVersionId — it shields
your pipeline from later edits to the profile.
See Submitting requests (REST API).
Save-as-profile from a run
Section titled “Save-as-profile from a run”The Portal's submit form has a Save as profile button. After configuring a run inline, click it to capture the current fields as a new profile (v1) without leaving the submission flow.
Pinning behavior
Section titled “Pinning behavior”Scope pins versionable references inside a profile so that re-running the profile later produces the same code paths:
- Skills — when you provide a skill slug like
github/vercel-labs/agent-skills/azure, Scope resolves it to a commit hash and stores…/azure@<commit>in the version. If you pre-pin (…/azure@abc1234), it's accepted as-is. - Extensions — extension IDs like
ms-python.pythonresolve to the latest stable version (ms-python.python@2024.8.1). Pre-pinned values pass through.
Pinning happens at version-creation time. The pinned values are part of the immutable version record.
Soft-delete
Section titled “Soft-delete”Deleting a profile is a soft-delete:
- The profile disappears from list views and is no longer selectable in the submit form.
GET /api/v1/profiles/:idreturns 404.- Past runs that referenced this profile still resolve correctly — the historical configuration is preserved.
There's no hard-delete from the user-facing surface.
Worker-specific constraints
Section titled “Worker-specific constraints”The extensions field is only valid on the VS Code Copilot
coding agent. The GitHub Copilot CLI and Claude Code CLI agents
do not support VS Code extensions. Trying to attach extensions
to those agents returns:
HTTP 400 — "Worker type 'coder-acp-copilot' does not support VS Code extensions"See Choosing a coding agent for the full capability matrix.
Naming conventions
Section titled “Naming conventions”There's no enforcement, but in practice these names age well:
- Lead with the agent and model:
"Copilot + GPT-4o","Claude 3.5 Sonnet". - Add a tools hint when relevant:
"Copilot + Azure Skills". - Don't put dates in the name — the version's
createdAtalready records when it was made.
Common mistakes
Section titled “Common mistakes”- Editing a profile expecting versions to mutate. Versions are immutable. Editing creates a new version; older versions still exist and are still referenced by past runs.
- Attaching extensions to a CLI-based agent. Returns 400. Use VS Code Copilot, or remove the extensions.
- Submitting with
profileIdin production. Works, but uses whatever the latest version is at submission time. UseprofileVersionIdfor stable pipelines.