Self-hosted agents
Jobs and results
Queue work for your agents, find out which device runs it, and get the results back.
Create a job
Organizations queue jobs from AI clients and scripts with the MCP tool agent_job_create, using a key with agents:run. exec jobs also need agents:write on the key. The platform's apps use the agents API.
| Field | |
|---|---|
type | http.batch, browser.batch or exec. |
input | The job's input, as described in Job types. Up to 300,000 characters of JSON. |
hosts | The hosts the job will contact, such as ["www.example.com"] (up to 50; *.example.com wildcards allowed). Used to pick a device; see below. |
label | A name shown in Cloud (up to 120 characters). |
callbackUrl | An https URL to notify when the job finishes. |
callbackSecret | Sent back in the callback's x-si-callback-secret header (MCP only). |
browser.batch input is validated when the job is created, and a mistake fails the request with the field at fault. The result is the job's id, such as { "jobId": "job_…" }.
Which device runs it
Jobs wait in a queue. Each time a device polls, it claims the oldest queued job it's allowed to run:
- The device has the job type's capability (HTTP requests, Browser pages or Shell commands).
- For
exec: the device has full network access. - For the others: the device has full network access, or every host in
hostsis on its allowlist.
List every host the job needs in hosts. A device on an allowlist claims a job whose hosts are all allowed; if the job then contacts a host that isn't, that part fails with network_denied.
A poll looks at the organization's 25 oldest queued jobs. A job that no device claims within 7 days is dropped. Idle devices poll every 15 seconds, so a job usually starts within 15 seconds once an eligible device is free.
Cancelling a job and seeing why a job is still queued are planned. Coming soon
Statuses
| Status | |
|---|---|
queued | Waiting for an eligible device. |
claimed | A device is running it. |
done | The device ran it and uploaded the results. |
failed | The job couldn't run, for example because of invalid input or a missing capability. error says why (up to 2,000 characters). |
done means the job ran, not that everything in it succeeded: requests and pages that failed are reported in the results, each with its own error.
Cloud → Agents shows recent jobs with their status, device and error.
Results
The agent uploads the results as JSON. They're kept for 7 days.
Read a job with agent_job_get (or the API); once it's done, resultsUrl is a link to download the results, valid for an hour. Read the job again for a fresh link. Long jobs are fine: an agent running a job for more than 45 minutes gets a new upload link before it finishes.
Callbacks
With a callbackUrl, the platform posts to it when the job is done or has failed:
POST /your/callback HTTP/1.1
content-type: application/json
x-si-callback-secret: <callbackSecret>
{ "jobId": "job_…", "status": "done", "resultsUrl": "https://…" }A failed job sends "status": "failed" and "error" instead of resultsUrl. The header is sent only when the job has a callbackSecret; check it before trusting the request.
- Answer with a 2xx status within 60 seconds.
- A 5xx response, a timeout or a connection failure is retried, about 90 seconds apart, for up to 5 attempts in all.
- A 4xx response isn't retried.
callbackUrlmust be anhttpsURL with a domain name.
The resultsUrl in a callback is valid for an hour, like any other.