Polling status
Endpoint
GET /agent/v1/run/:id/status
curl "https://api.case.dev/agent/v1/run/$RUN_ID/status" \
-H "Authorization: Bearer $CASEDEV_API_KEY"
casedev agent:v1:run get-status --id $RUN_ID
const status = await client.agents.runs.getStatus(run.id)
// { id, status, startedAt, completedAt, durationMs }
status = client.agent.v1.run.get_status(run.id)
# { id, status, started_at, completed_at, duration_ms }
status, _ := client.Agent.V1.Run.GetStatus(ctx, run.ID)
fmt.Printf("Status: %s, Duration: %dms\n", status.Status, status.DurationMs)
Response
{
"id": "run_abc123",
"status": "completed",
"startedAt": "2025-01-15T10:30:05Z",
"completedAt": "2025-01-15T10:35:22Z",
"durationMs": 317000
}
| Field | Description |
|---|---|
status | queued, running, completed, failed, or cancelled |
durationMs | Elapsed time. For running tasks, this updates on each poll. |
Poll every 2-5 seconds. Status is a lightweight read — no rate limit concerns at this
interval.
Getting full results
Endpoint
GET /agent/v1/run/:id/details
curl "https://api.case.dev/agent/v1/run/$RUN_ID/details" \
-H "Authorization: Bearer $CASEDEV_API_KEY"
casedev agent:v1:run get-details --id $RUN_ID
const details = await client.agents.runs.getDetails(run.id)
console.log(details.result.output) // Final text output
console.log(details.steps.length) // Number of steps taken
details = client.agent.v1.run.get_details(run.id)
print(details.result.output)
print(len(details.steps))
details, _ := client.Agent.V1.Run.GetDetails(ctx, run.ID)
fmt.Println(details.Result.Output)
fmt.Printf("Steps: %d\n", len(details.Steps))
Response
{
"id": "run_abc123",
"agentId": "agent_xyz",
"status": "completed",
"prompt": "Research employment law...",
"result": {
"output": "## Research Report\n\nBased on my analysis of the vault documents...",
"logs": {
"server": "agent server listening on http://0.0.0.0:4096...",
"runner": "[runner] Sending prompt to agent..."
}
},
"usage": {
"model": "anthropic/claude-sonnet-4.6",
"inputTokens": 45000,
"outputTokens": 3200,
"toolCalls": 12,
"durationMs": 317000
},
"steps": [
{
"id": "step_001",
"type": "tool_call",
"toolName": "bash",
"toolInput": { "command": "casedev vault list" },
"toolOutput": "{ \"vaults\": [...] }",
"durationMs": 1200,
"timestamp": "2025-01-15T10:30:10Z"
},
{
"id": "step_002",
"type": "output",
"content": "I found 3 vaults. Let me search the most relevant one...",
"timestamp": "2025-01-15T10:30:12Z"
}
],
"createdAt": "2025-01-15T10:30:00Z",
"startedAt": "2025-01-15T10:30:05Z",
"completedAt": "2025-01-15T10:35:22Z"
}
Response fields
| Field | Type | Description |
|---|---|---|
result.output | string | The agent’s final text response |
result.logs | object | Sandbox execution logs (agent server + runner script) |
usage.inputTokens | number | Total input tokens consumed |
usage.outputTokens | number | Total output tokens generated |
usage.toolCalls | number | Number of tool invocations |
steps | array | Ordered list of every action the agent took |
Step types
| Type | Description |
|---|---|
output | Text generated by the agent |
thinking | Internal reasoning (if model supports it) |
tool_call | Tool invocation with input |
tool_result | Tool output/response |
Watching runs
Register an HTTPS callback URL to get notified when a run completes. Useful for production workflows where you don’t want to poll.Endpoint
POST /agent/v1/run/:id/watch
# Register watcher
curl -X POST "https://api.case.dev/agent/v1/run/$RUN_ID/watch" \
-H "Authorization: Bearer $CASEDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"callbackUrl": "https://your-app.com/webhooks/agent-run"}'
# Then execute
curl -X POST "https://api.case.dev/agent/v1/run/$RUN_ID/exec" \
-H "Authorization: Bearer $CASEDEV_API_KEY"
# Register watcher and execute
casedev agent:v1:run create \
--agent-id $AGENT_ID \
--prompt "Analyze the contract..."
casedev agent:v1:run watch --id $RUN_ID \
--callback-url "https://your-app.com/webhooks/agent-run"
casedev agent:v1:run exec --id $RUN_ID
// Register before executing
const run = await client.agents.runs.create({
agentId: agent.id,
prompt: 'Analyze the contract...',
})
await client.agents.runs.watch(run.id, {
callbackUrl: 'https://your-app.com/webhooks/agent-run',
})
await client.agents.runs.exec(run.id)
run = client.agent.v1.run.create(
agent_id=agent.id,
prompt="Analyze the contract...",
)
client.agent.v1.run.watch(run.id,
callback_url="https://your-app.com/webhooks/agent-run",
)
client.agent.v1.run.exec(run.id)
run, _ := client.Agent.V1.Run.New(ctx, casedev.AgentV1RunNewParams{
AgentID: casedev.F(agent.ID),
Prompt: casedev.F("Analyze the contract..."),
})
client.Agent.V1.Run.Watch(ctx, run.ID, casedev.AgentV1RunWatchParams{
CallbackURL: casedev.F("https://your-app.com/webhooks/agent-run"),
})
client.Agent.V1.Run.Exec(ctx, run.ID)
Callback requirements
| Requirement | Details |
|---|---|
| Protocol | HTTPS only — HTTP is rejected |
| Network | Must be publicly reachable — private IPs, localhost, and .local domains are blocked |
| Multiple watchers | You can register multiple callback URLs for the same run |
| Retries | 3 retries with exponential backoff |
The callback is best-effort. Always treat
/run/:id/details as the source of truth. Use
watchers to trigger your workflow, then fetch the full details.Production polling pattern
For production use, wrap polling in a timeout:# Poll until status is "completed"
while true; do
STATUS=$(curl -s "https://api.case.dev/agent/v1/run/$RUN_ID/status" \
-H "Authorization: Bearer $CASEDEV_API_KEY" | jq -r '.status')
case "$STATUS" in
completed) break ;;
failed|cancelled) echo "Run $STATUS"; exit 1 ;;
*) sleep 3 ;;
esac
done
# Get full results
curl -s "https://api.case.dev/agent/v1/run/$RUN_ID/details" \
-H "Authorization: Bearer $CASEDEV_API_KEY"
# Poll in a loop until completed
while true; do
STATUS=$(casedev agent:v1:run get-status --id $RUN_ID --format json | jq -r '.status')
case "$STATUS" in
completed) break ;;
failed|cancelled) echo "Run $STATUS"; exit 1 ;;
*) sleep 3 ;;
esac
done
casedev agent:v1:run get-details --id $RUN_ID
async function waitForRun(
client: Casedev,
runId: string,
timeoutMs = 600_000
): Promise<RunDetails> {
const deadline = Date.now() + timeoutMs
while (Date.now() < deadline) {
const status = await client.agents.runs.getStatus(runId)
switch (status.status) {
case 'completed':
return client.agents.runs.getDetails(runId)
case 'failed':
throw new Error(`Run failed after ${status.durationMs}ms`)
case 'cancelled':
throw new Error('Run was cancelled')
default:
await new Promise((r) => setTimeout(r, 3000))
}
}
throw new Error(`Run timed out after ${timeoutMs}ms`)
}
const details = await waitForRun(client, run.id)
console.log(details.result.output)
import time
def wait_for_run(client, run_id, timeout_ms=600_000):
import time as _time
deadline = _time.time() * 1000 + timeout_ms
while _time.time() * 1000 < deadline:
status = client.agent.v1.run.get_status(run_id)
if status.status == "completed":
return client.agent.v1.run.get_details(run_id)
elif status.status == "failed":
raise Exception(f"Run failed after {status.duration_ms}ms")
elif status.status == "cancelled":
raise Exception("Run was cancelled")
time.sleep(3)
raise Exception(f"Run timed out after {timeout_ms}ms")
details = wait_for_run(client, run.id)
print(details.result.output)
func waitForRun(ctx context.Context, client *casedev.Client, runID string, timeout time.Duration) (*casedev.AgentRunDetails, error) {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
status, _ := client.Agent.V1.Run.GetStatus(ctx, runID)
switch status.Status {
case "completed":
return client.Agent.V1.Run.GetDetails(ctx, runID)
case "failed":
return nil, fmt.Errorf("run failed after %dms", status.DurationMs)
case "cancelled":
return nil, fmt.Errorf("run was cancelled")
}
time.Sleep(3 * time.Second)
}
return nil, fmt.Errorf("run timed out after %s", timeout)
}

