# Run Conductor-style agent tasks from code

> Your Slack bot or orchestrator created Conductor Cloud workspaces through its API or CLI. Keep the code and swap the calls: a sandbox per task from the Prized Python SDK, or a run in a git worktree on your box with a signed webhook when it ends.

URL: https://prized.dev/docs/conductor-api-agent-tasks
Updated: 2026-11-05

Conductor Cloud stops taking new work on November 13, 2026 ([Moving from Conductor Cloud](https://prized.dev/docs/moving-from-conductor-cloud)). If your code drove it, such as a Slack bot that opens a workspace per request or an orchestrator that runs `conductor workspace create` for each plan item, keep that code and swap its calls.

A task becomes one of two things on Prized:

* **A sandbox per task**, like a cloud workspace: a fresh microVM clones your branch, runs the agent on a key you pass in, pushes, and is deleted. Steps 1 to 3.
* **A run on your box:** the agent works in a git worktree on a box you keep, on the sign-in already there, and a signed webhook tells your code when it ends ([Or run it on your box](#or-run-it-on-your-box)).

## 1. Export your keys [#1-export-your-keys]

Make a Prized API key on [API keys](https://prized.dev/dashboard/workspace/api-keys?via=conductor) with **Sandboxes: Write**. New to Prized? [Open Sandboxes](https://prized.dev/onboarding/sandboxes?via=conductor) signs you up with free credit and no card. Export it with your agent's key and a fine-grained GitHub token for the one repo, with **Contents** and **Pull requests** at read and write:

```bash
export PRIZED_API_KEY=dcp_…
export ANTHROPIC_API_KEY=sk-ant-…
export GH_TOKEN=github_pat_…
```

## 2. Save the script [#2-save-the-script]

```bash
python3 -m venv .venv && .venv/bin/pip install prized
```

```python title="run_task.py"
import json, os, re
import httpx  # installed with prized
from prized import Sandbox

GH_TOKEN = os.environ["GH_TOKEN"]
AGENT = {"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]}  # the agent's sign-in

def sh(sb, cmd, cwd="app", **kw):
    r = sb.exec(cmd, cwd=cwd, **kw)
    if r.exit_code != 0:
        raise RuntimeError(f"{str(cmd)[:60]}: exit {r.exit_code}\n{r.stderr[-2000:]}")
    return r.stdout

def run_task(repo, base, branch, task):
    if not all(re.fullmatch(r"[\w.][\w./-]*", s) for s in (repo, base, branch)):
        raise ValueError("repo, base and branch go into shell commands")
    url = f"https://x-access-token:$GH_TOKEN@github.com/{repo}"  # $GH_TOKEN expands in the sandbox
    gh = {"GH_TOKEN": GH_TOKEN}  # set for the clone and the push only, not for the agent's run
    with Sandbox.create(template="prized-agent-v1", env_vars=AGENT) as sb:  # deleted when the block ends
        sh(sb, f"git clone -q -b {base} {url} app", cwd=".", env=gh, timeout=600)
        sh(sb, f"git remote set-url origin https://github.com/{repo} && git switch -qc {branch}")
        sh(sb, "git config user.name 'Prized agent' && git config user.email agent@example.com")
        # Run your repository's setup script here, as Conductor did for each new workspace.
        sb.files.write("task.md", task)  # beside the repo, not in it
        out = sh(sb, "claude -p --dangerously-skip-permissions --output-format json < ../task.md", timeout=3000)
        result = json.loads(out)["result"]  # the agent's last message
        sh(sb, 'git add -A && (git diff --cached --quiet || git commit -qm "$MSG")', env={"MSG": task[:72]})
        if not sh(sb, f"git log --oneline origin/{base}..HEAD"):
            return None, result  # nothing changed, so no pull request
        sh(sb, f"git push -q {url} {branch}", env=gh, timeout=600)
    pr = httpx.post(f"https://api.github.com/repos/{repo}/pulls", headers={"Authorization": f"Bearer {GH_TOKEN}"},
                    json={"title": task[:72], "head": branch, "base": base, "body": result})
    pr.raise_for_status()
    return pr.json()["html_url"], result

if __name__ == "__main__":
    url, result = run_task("acme/app", "main", "agent/login-rate-limit", "Add rate limiting to the login endpoint, with a test. Run the tests.")
    print(url or "No changes, so no pull request", result, sep="\n\n")
```

`run_task` is the call your bot or orchestrator makes once per task:

* **A fresh machine:** `prized-agent-v1` has Claude Code and Codex, with nothing signed in; the `with` block deletes the sandbox when the task ends, error or not ([Templates](https://prized.dev/docs/sandboxes#templates)).
* **The agent runs without asking** inside the sandbox, which holds the repo and the agent's key ([Skip permissions in an isolated VM](https://prized.dev/docs/sandboxes-claude-code-skip-permissions)). The task goes in as a file, so any text works.
* **The GitHub token** is set only for the clone and the push, so it is in neither the agent's environment nor the repo's config. The agent can still catch it at push time (a git hook, say), so keep it to the one repo with a short expiry, or take the commits out with `git bundle` and push from your side.
* **The result** is the agent's last message, and it becomes the pull request's body. The script commits whatever the agent left uncommitted, and opens no pull request when nothing changed.

For Codex, pass `CODEX_API_KEY` instead, run `codex exec --dangerously-bypass-approvals-and-sandbox -o /home/prized/work/last.txt - < ../task.md`, and read the result with `sb.files.read("last.txt")`. With a Claude plan instead of an API key, pass `CLAUDE_CODE_OAUTH_TOKEN` from `claude setup-token`.

## 3. Run it [#3-run-it]

```bash
.venv/bin/python run_task.py
# https://github.com/acme/app/pull/42
#
# I added a rate limit of 5 attempts per minute per IP to the login endpoint, with a test …
```

It returns when the agent is done, so post the link and the message wherever the task came from, such as the Slack thread your bot answers in. Tasks in threads or a job queue run side by side, each in its own sandbox, up to your workspace's [sandbox limit](https://prized.dev/docs/limits#sandboxes).

On the [free tier](https://prized.dev/docs/sandboxes#free-tier) a sandbox runs 60 minutes in all, so the script stops the agent at 50 (`timeout=3000`), and work cut off there is lost; on a paid workspace, raise it.

## Or run it on your box [#or-run-it-on-your-box]

To keep the agent on a sign-in already on a [box](https://prized.dev/docs/boxes), such as your Claude or ChatGPT plan, run each task in a worktree there. The box needs your repo first ([Moving from Conductor Cloud](https://prized.dev/docs/moving-from-conductor-cloud#3-put-your-repo-on-the-box)). These two commands do what `conductor workspace create` did: add a branch in its own folder from the latest `main` (run your setup script there the same way), then start the agent in it and return.

```bash
prized exec mango -- 'cd code/myproject && git fetch -q origin && git worktree add --no-track -b login-rate-limit ../myproject-login-rate-limit origin/main'
```

```bash
prized prompt mango --provider claude --model opus --effort high --cwd code/myproject-login-rate-limit --auto --detach --notify https://hooks.example.com/prized - < task.md
```

They use your CLI sign-in; with `PRIZED_API_KEY` set, the key needs **Boxes: Write**.

* **Told when it ends:** the URL, a public https address, gets one signed `prompt.finished` POST with the run's status and links to its events, where the `result` event holds the agent's last message. The workspace owner creates the signing secret first, on [API keys](https://prized.dev/dashboard/workspace/api-keys?via=conductor); until then a prompt with `--notify` is refused and nothing starts ([Webhooks](https://prized.dev/docs/api#webhooks)).
* **Or poll:** without `--notify`, `prized events --run pr_… --follow` waits for the run and prints what the agent did, and `prized prompt ls` lists each run's state.
* **From code:** the same calls are `POST /exec` and `POST /prompts` (with `cwd` and `notify`) on the edge API ([The edge API](https://prized.dev/docs/remote-agents#the-edge-api)).
* **Several at once:** a worktree per task, up to one run per 2 GB of the box's memory, at most 16 ([Limits](https://prized.dev/docs/limits#prompting-an-agent-remotely)). The runs share the box's CPU and disk. [Run coding agents from your own tools](https://prized.dev/docs/orchestrate-coding-agents) fans a whole plan out this way, with a Python receiver for the webhooks.
* **The pull request:** ask for it in the task ("commit, push, and open a pull request"). `gh` on the box is signed in if it was on your machine at setup, and `--auto` lets the agent use it and run any command on the box ([Approvals](https://prized.dev/docs/remote-agents#approvals)).

## Conductor's calls on Prized [#conductors-calls-on-prized]

| Conductor Cloud | A sandbox per task | A run on your box |
| --- | --- | --- |
| `POST /v0/workspaces`, or `conductor workspace create` | `Sandbox.create(...)`, then `git clone -b` | `POST /exec` with `git worktree add`, then `POST /prompts` |
| `projectId` or `repositoryUrl`, and `branch` | The repo and branch you clone | The repo on the box, and the branch the worktree starts from |
| `agent`, `model`, `effort` | The agent's own command line, such as `claude -p --model opus --effort high` | `provider`, `model` (the agent's own ids), `reasoningEffort` (`low`, `medium` or `high`) |
| `message`, or `conductor workspace create --message-file task.md` | The task file you pass the agent | `prompt`; `prized prompt … - < task.md` reads a file |
| `env` | `env_vars` on create, or `env` on one command | The box's variables ([Environments](https://prized.dev/docs/environments#per-box-vars)) |
| The repo's setup script | Run it after the clone | Run it in the new worktree with `POST /exec` |
| Polling `GET /v0/sessions/{id}/status` | `run_task` returns when the agent exits | The `notify` webhook, or `GET /prompts/{runId}` |
| `GET /v0/sessions/{id}/messages` | `claude -p --output-format stream-json --verbose` prints every step; `json` only the last message | `GET /prompts/{runId}/events` |
| `POST /v0/sessions/{id}/messages`, a follow-up | Create the sandbox without `with` and end with `sb.pause()`; later, `Sandbox.connect(id)`, write the follow-up to `task.md`, and run `claude -p --continue < ../task.md` in `app` | `POST /prompts` with the run's `conversationId` |
| `POST /v0/sessions/{id}/cancel` | Delete the sandbox | `POST /prompts/{runId}/interrupt` |
| `POST /v0/workspaces/{id}/archive` | The `with` block deletes the sandbox | `git worktree remove` through `POST /exec` |

## What's different [#whats-different]

* **No Slack app of ours:** keep your bot and point its calls at Prized.
* **No multiplayer app:** no Conductor chat and diff views, and no shared chats with presence. Members of a Prized workspace can open any box, and **Activity** lists each box's last prompt ([Team activity](https://prized.dev/docs/teams#team-activity)).
* **No routines:** nothing on Prized starts a saved prompt from an incoming URL, so the endpoint you already run makes the call.
* **No transcript SQL:** read one run at a time (`GET /prompts/{runId}/events`), or keep what your code saved.
* **Agents and efforts:** the prompt API runs Claude Code, Codex, OpenCode and pi, with efforts from `low` to `high`. Conductor's `cursor` and `acp` agents have no match there, though the [Cursor CLI](https://prized.dev/docs/run-cursor-cli-in-the-cloud) runs on a box.

## Getting help moving [#getting-help-moving]

Email the support address shown on https://prized.dev/docs/troubleshooting with what your code called, and we will help you map it.
