Run Droid with the Execute API

Start a headless Droid turn with one API call, on a fresh computer from an Environment, an existing computer, or an existing session.

The Droid Execute API runs a Droid turn with a single request. You send a prompt and a target, and Factory finds or provisions the computer, starts or continues a session, and sends your prompt. You don't need to create a computer or session first.

Use it to trigger Droid from CI pipelines, internal tools, webhooks, or scripts. The Execute a Droid turn reference lists every request field, response field, and error status. This page explains how to use it.

Authentication

Every request needs a Factory API key in the Authorization: Bearer header. Generate one in the Factory API keys settings and export it as FACTORY_API_KEY.

Service account API keys work too. The session, and any computer created for it, is owned by the service account.

Execute a Droid turn

POST https://api.factory.ai/api/v0/droid/execute

The request returns 202 Accepted once the turn has started. Droid keeps working in the background, so poll the session to follow progress and read the results.

The response includes sessionId, computerId, and messageId, the ID of the user message created from your prompt.

model and reasoningEffort apply only when the request starts a new session. Sending either one with a session target returns 400.

Targets

target is an object with a kind field and exactly one matching ID field.

kindID fieldBehavior
environmentenvironmentIdProvisions a fresh computer from the Environment's active build, then starts a new session on it.
computercomputerIdStarts a new session on an existing computer you own.
sessionsessionIdSends the prompt as a follow-up message in an existing session you own. The session keeps its computer, model, and history.
Note

An environment target waits for the new computer to finish provisioning before it returns, so expect that request to take longer than the other targets. The computer is not listed in your Droid Computers and is cleaned up after it goes idle. To keep working on it, send follow-ups with a session target and the returned sessionId.

Look up IDs with the rest of the API: GET /api/v0/environments lists Environments, and GET /api/v0/computers lists computers.

Examples

Start a session on a fresh computer from an Environment:

Bash
curl -X POST "https://api.factory.ai/api/v0/droid/execute" \
  -H "Authorization: Bearer $FACTORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Fix the failing unit tests in packages/api and open a pull request.",
    "target": { "kind": "environment", "environmentId": "<environment-id>" },
    "model": "claude-opus-5-5",
    "reasoningEffort": "medium"
  }'

Start a session on a computer you already have:

Bash
curl -X POST "https://api.factory.ai/api/v0/droid/execute" \
  -H "Authorization: Bearer $FACTORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Summarize the open TODOs in this repository.",
    "target": { "kind": "computer", "computerId": "<computer-id>" }
  }'

Send a follow-up to an existing session:

Bash
curl -X POST "https://api.factory.ai/api/v0/droid/execute" \
  -H "Authorization: Bearer $FACTORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Also add a regression test for the bug you fixed.",
    "target": { "kind": "session", "sessionId": "<session-id>" }
  }'

Poll for results

The execute call only starts the turn. Use the Droid Sessions API with the returned IDs to follow it:

  1. 1
    Poll the session status

    Call GET /api/v0/sessions/{sessionId}?computerId={computerId}. The status field is pending or running while Droid works and idle when it stops.

  2. 2
    Read the transcript

Right after the call, the session can still report idle from the previous turn, especially when you send a follow-up to an existing session. Treat the turn as complete only when the session is idle and the transcript has an assistant message after the user message whose id is the returned messageId.

A newly created session can briefly return 404 before it is visible. Retry until it appears.

execute-and-poll.mjs
const headers = {
  Authorization: `Bearer ${process.env.FACTORY_API_KEY}`,
  "Content-Type": "application/json",
};
const base = "https://api.factory.ai/api/v0";
 
const started = await fetch(`${base}/droid/execute`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    prompt: "Upgrade lodash to the latest version and run the tests.",
    target: { kind: "environment", environmentId: "<environment-id>" },
  }),
});
if (started.status !== 202) throw new Error(await started.text());
const { sessionId, computerId, messageId } = await started.json();
 
const query = `computerId=${encodeURIComponent(computerId)}`;
let replies = [];
for (;;) {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  const session = await fetch(`${base}/sessions/${sessionId}?${query}`, {
    headers,
  });
  if (!session.ok) continue;
  const { status } = await session.json();
  if (status !== "idle") continue;
 
  const { messages } = await fetch(
    `${base}/sessions/${sessionId}/messages?${query}&limit=100`,
    { headers },
  ).then((response) => response.json());
  const promptIndex = messages.findIndex((message) => message.id === messageId);
  replies =
    promptIndex === -1
      ? []
      : messages
          .slice(promptIndex + 1)
          .filter((message) => message.role === "assistant");
  if (replies.length > 0) break;
}
console.log(replies);