Submitting requests (REST API)
The Scope REST API exposes everything the Portal does. Use it to automate request submission, integrate Scope into your tools, or build custom dashboards.
For interactive use, see Submitting requests (Portal).
Base URL
Section titled “Base URL”All endpoints live under /api/v1 on the Scope host:
https://your-scope.example.com/api/v1A live OpenAPI / Swagger explorer is available at:
https://your-scope.example.com/api-docsAn auto-generated reference of every endpoint, built from the same OpenAPI spec, also lives in this site at REST API reference.
This page covers the common workflows.
Access
Section titled “Access”Use the authentication method configured for your Scope deployment. Contact your deployment administrator if you need access details.
See Access for details.
Requests vs runs
Section titled “Requests vs runs”You submit a request. Scope creates one run per execution attempt — the first attempt automatically, plus one new run per retry. Logs and reports are produced per run.
There is one endpoint group, /api/v1/requests, that covers both
submission and inspection. There's no separate /runs resource at the
API level; run state lives on the request.
Submit a request
Section titled “Submit a request”Minimum payload — a task prompt (as text), a list of criteria IDs, and an inline runtime configuration:
curl --request POST \ --url https://your-scope.example.com/api/v1/requests \ --header 'Content-Type: application/json' \ --data '{ "scenario": { "task": "Create a Hello World Node.js / Express REST API.", "criteria": ["c-hello-world-express"] }, "workerType": "coder-acp-copilot", "model": "gpt-4o"}'const url = 'https://your-scope.example.com/api/v1/requests';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"scenario":{"task":"Create a Hello World Node.js / Express REST API.","criteria":["c-hello-world-express"]},"workerType":"coder-acp-copilot","model":"gpt-4o"}'};
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/requests"
payload = { "scenario": { "task": "Create a Hello World Node.js / Express REST API.", "criteria": ["c-hello-world-express"] }, "workerType": "coder-acp-copilot", "model": "gpt-4o"}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/requests"
payload := strings.NewReader("{\n \"scenario\": {\n \"task\": \"Create a Hello World Node.js / Express REST API.\",\n \"criteria\": [\"c-hello-world-express\"]\n },\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\"\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 \"scenario\": {\n \"task\": \"Create a Hello World Node.js / Express REST API.\",\n \"criteria\": [\"c-hello-world-express\"]\n },\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\"\n}");Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/requests") .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/requests"), Content = new StringContent("{\n \"scenario\": {\n \"task\": \"Create a Hello World Node.js / Express REST API.\",\n \"criteria\": [\"c-hello-world-express\"]\n },\n \"workerType\": \"coder-acp-copilot\",\n \"model\": \"gpt-4o\"\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);}scenario.task is the prompt text itself — you don't pass a task
prompt ID. Scope de-duplicates by text server-side: the first time
it sees a given task prompt it creates a TaskPrompt record; later
requests with the same text link to the same record. That's how
prompt features and the Tasks page in the Portal work across
requests.
scenario.criteria is a list of criteria-set IDs — the criteria
themselves live in /api/v1/criteria. See
Defining evaluation criteria.
Or submit using a saved profile (recommended for reproducibility):
curl --request POST \ --url https://your-scope.example.com/api/v1/requests \ --header 'Content-Type: application/json' \ --data '{ "scenario": { "task": "Create a Hello World Node.js / Express REST API.", "criteria": ["c-hello-world-express"] }, "profileId": "p-550e8400-e29b-41d4-a716-446655440001"}'const url = 'https://your-scope.example.com/api/v1/requests';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"scenario":{"task":"Create a Hello World Node.js / Express REST API.","criteria":["c-hello-world-express"]},"profileId":"p-550e8400-e29b-41d4-a716-446655440001"}'};
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/requests"
payload = { "scenario": { "task": "Create a Hello World Node.js / Express REST API.", "criteria": ["c-hello-world-express"] }, "profileId": "p-550e8400-e29b-41d4-a716-446655440001"}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/requests"
payload := strings.NewReader("{\n \"scenario\": {\n \"task\": \"Create a Hello World Node.js / Express REST API.\",\n \"criteria\": [\"c-hello-world-express\"]\n },\n \"profileId\": \"p-550e8400-e29b-41d4-a716-446655440001\"\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 \"scenario\": {\n \"task\": \"Create a Hello World Node.js / Express REST API.\",\n \"criteria\": [\"c-hello-world-express\"]\n },\n \"profileId\": \"p-550e8400-e29b-41d4-a716-446655440001\"\n}");Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/requests") .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/requests"), Content = new StringContent("{\n \"scenario\": {\n \"task\": \"Create a Hello World Node.js / Express REST API.\",\n \"criteria\": [\"c-hello-world-express\"]\n },\n \"profileId\": \"p-550e8400-e29b-41d4-a716-446655440001\"\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);}When you reference a profile by ID, Scope resolves it to the
profile's latest version and stamps both profileId and
profileVersionId on the request. To pin a specific version, pass
profileVersionId directly.
The response includes the new request's ID and initial state:
{ "id": "req-018c7d2a-7e92-7b1a-9c3a-4f4f5d6e7e8a", "submissionId": "sub-018c7d2a-…", "status": "pending", "mode": "multi-turn"}Stream logs
Section titled “Stream logs”Logs from the current run stream over Server-Sent Events:
curl --request GET \ --url https://your-scope.example.com/api/v1/requests/%7BrequestId%7D/logsconst url = 'https://your-scope.example.com/api/v1/requests/%7BrequestId%7D/logs';const options = {method: 'GET'};
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/requests/%7BrequestId%7D/logs"
response = requests.get(url)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/requests/%7BrequestId%7D/logs"
req, _ := http.NewRequest("GET", url, nil)
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();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/requests/%7BrequestId%7D/logs") .get() .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://your-scope.example.com/api/v1/requests/%7BrequestId%7D/logs"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}The response is text/event-stream. Pass ?fromStart=true to replay
historical logs from blob storage. Consume it with any SSE-capable
client.
Fetch request state
Section titled “Fetch request state”curl --request GET \ --url https://your-scope.example.com/api/v1/requests/%7BrequestId%7Dconst url = 'https://your-scope.example.com/api/v1/requests/%7BrequestId%7D';const options = {method: 'GET'};
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/requests/%7BrequestId%7D"
response = requests.get(url)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/requests/%7BrequestId%7D"
req, _ := http.NewRequest("GET", url, nil)
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();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/requests/%7BrequestId%7D") .get() .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://your-scope.example.com/api/v1/requests/%7BrequestId%7D"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}Returns the request document, including status, the resolved
configuration, and the current run (with attemptNumber, status,
outcome, turns, …).
curl --request GET \ --url https://your-scope.example.com/api/v1/requestsconst url = 'https://your-scope.example.com/api/v1/requests';const options = {method: 'GET'};
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/requests"
response = requests.get(url)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/requests"
req, _ := http.NewRequest("GET", url, nil)
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();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/requests") .get() .build();
Response response = client.newCall(request).execute();using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://your-scope.example.com/api/v1/requests"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}Lists requests. Supports query parameters for filtering, search, and pagination (see Swagger for the full set).
To create another run on the same request:
curl --request POST \ --url https://your-scope.example.com/api/v1/requests/bulk-resubmitconst url = 'https://your-scope.example.com/api/v1/requests/bulk-resubmit';const options = {method: 'POST'};
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/requests/bulk-resubmit"
response = requests.post(url)
print(response.json())package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://your-scope.example.com/api/v1/requests/bulk-resubmit"
req, _ := http.NewRequest("POST", url, nil)
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();
Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/requests/bulk-resubmit") .post(null) .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/requests/bulk-resubmit"),};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}This endpoint takes a list of request IDs and reuses each request's configuration to create a new run.
Reports
Section titled “Reports”Reports are created on demand:
curl --request POST \ --url https://your-scope.example.com/api/v1/reports \ --header 'Content-Type: application/json' \ --data '{ "requestId": "req-018c7d2a-…" }'const url = 'https://your-scope.example.com/api/v1/reports';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"requestId":"req-018c7d2a-…"}'};
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/reports"
payload = { "requestId": "req-018c7d2a-…" }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/reports"
payload := strings.NewReader("{ \"requestId\": \"req-018c7d2a-…\" }")
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, "{ \"requestId\": \"req-018c7d2a-…\" }");Request request = new Request.Builder() .url("https://your-scope.example.com/api/v1/reports") .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/reports"), Content = new StringContent("{ \"requestId\": \"req-018c7d2a-…\" }") { 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 report's ID and status: "pending".
Fetch it later with GET /api/v1/reports/{id}.
Manage task prompts, criteria, profiles, prompt features
Section titled “Manage task prompts, criteria, profiles, prompt features”Each of these has its own CRUD endpoint group under /api/v1/:
/api/v1/task-prompts— see Managing task prompts./api/v1/criteria— see Defining evaluation criteria./api/v1/profiles— see Defining profiles./api/v1/prompt-features— see Working with prompt features.
A consolidated reference is at REST API.
Errors
Section titled “Errors”Errors follow a consistent shape:
{ "error": { "code": "INVALID_REQUEST", "message": "Worker type 'coder-acp-copilot' does not support VS Code extensions" }}HTTP status codes follow standard conventions: 400 for malformed
input, 404 for missing resources, 409 for conflicts, 503 when a
downstream service (e.g. an LLM provider) is unavailable.
Idempotency
Section titled “Idempotency”Request submission is not idempotent — every POST /api/v1/requests
creates a new request, even if you send the same payload twice. Build
idempotency on top by caching the returned id if you need it.
- Pin everything for production: pass
profileVersionId(notprofileId) so a later edit to the profile doesn't change what your pipeline runs. - Poll sparingly: prefer the SSE log stream to status polling.
- Use the reference: the REST API reference
is the source of truth for exact request and response shapes. Your
deployment may also provide Swagger UI at
/api-docs.