Schedules
Run a command every so often, or at a time of day, and have a stopped sandbox started for it and stopped again after.
For your agent
agental.io/docs/schedules.mdcurl -X POST https://agental.io/v1/sandboxes/{id}/schedules \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"scrape","command":"python scrape.py","every":"1h"}'commandis one line, run bysh -c, with the variables and files any command of the sandbox has.everyis a number andm,hord, from5mto31d, counted from when the schedule is made: what has to run more often is better left running in a sandbox that stays up. Or givecron, five fields: minute, hour, day of the month, month and day of the week, such as"0 7 * * 1-5", with no two runs less than 5 minutes apart, read intimeZone, such as"Europe/Sofia", UTC unless it says. Or giveat, a date and time up to a year away, and it runs once: a check to come back to, such as a deploy at 09:00. Afterwards itsnextRunAtis null, and it is kept, with its run, until it is deleted.- The answer is
201with the schedule, itsnextRunAt, andlastRun, null until it has run.nameis lower-case letters, digits and dashes, unique on the sandbox: left out, it isschedule-1,schedule-2and so on. A sandbox has 20 at most.
When the sandbox is stopped
A run that finds the sandbox stopped starts it, runs the command, and stops it again when the command ends, keeping its disk. The sandbox is billed by the second for the minutes it runs, and at the stopped price between runs: an hourly job of two minutes comes to about a tenth of what the same sandbox costs left running. A run is made within about 15 seconds of its time, and a stopped sandbox takes some seconds more to start. A cron's or a one-off's time is also given the schedule's own delay of up to a minute, the same each time and in its nextRunAt, so that the schedules due at 07:00 do not all start their sandboxes in the same moment.
| Field | What it does |
|---|---|
ifStopped | start, the default: start it for the run, then stop it again. skip: a run that finds it stopped is skipped, so the schedule runs only while the sandbox runs. |
keepMemory | true stops it with its memory kept, at the stopped price with memory, so the next start puts back its processes as they were. A sandbox that was stopped with its memory is stopped so again, whatever this says. |
A run leaves the sandbox running when someone else began to use it meanwhile: it was started again, its stop was moved, or a command was sent into it by /exec, /exec/stream or /commands. An SSH session or the desktop is not seen: move the stop, as with PUT /v1/sandboxes/{id}/auto-stop, to keep it up. When two runs go in one sandbox, the last one to end stops it.
A schedule that starts its sandbox takes a key that may start and stop it, sandbox.resume and sandbox.stop, as well as exec; one with ifStopped: "skip" takes exec alone. Another key is answered 403 key_action_forbidden.
Delete the sandbox after
curl -X POST https://agental.io/v1/sandboxes/{id}/schedules \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"last-run","command":"./finish.sh","at":"2026-11-04T09:00:00Z","deleteAfter":true}'- A schedule that runs once, with
at, may delete its sandbox after its run:deleteAfter: true. Once the command exits 0, the sandbox is deleted asDELETE /v1/sandboxes/{id}deletes it, whether the run started it or it was running already, and the run'sdeletedSandboxistrue. Itssandbox.deletedevent hasreason: "schedule". - A run that fails, times out, is skipped, is replaced, could not start or is cut short by a restart of the API leaves the sandbox, stopped again if the run started it, so that you can look at what went wrong. Its
reasonsays the sandbox was not deleted. deleteAfteroneveryorcronis refused with400 invalid_request, and so is a change that makes a one-off with it run again and again: givedeleteAfter: falsewith it. Making, changing or running now a schedule with it takes a key that may delete the sandbox,sandbox.delete, orsandbox.delete-ownfor one the key made, as the delete itself does. Those rights are checked then, not when the run ends: a schedule acts for the account, so it still deletes the sandbox after the key that made it is revoked or the member who made it leaves. Delete the schedule, or give itdeleteAfter: false, to stop that.- A scheduled run still going in the sandbox when it is deleted ends as
failed. The schedule and its runs can be read until the sandbox's history is cleared. From a terminal,agental schedule add <sandbox-id> --at <date-time> --delete-after -- ./finish.sh.
When a run is still going
For a schedule that runs again and again: a one-off's run never finds one of its own still going.
| overlap | The run that comes due |
|---|---|
skip, the default | is skipped, and the one going goes on. |
replace | stops the one going and takes its place. |
parallel | runs beside it, five of one schedule at most. |
- A run gets
timeoutMs,600000(10 minutes) unless it says, up to21600000(6 hours). One that runs out is given up with exit code124. - A run that could not be made within two minutes of its time, as while the sandbox was being moved to another server, is told as
missedand never made up later. One under way when the API restarts isinterrupted, and a sandbox it started is stopped. - After 10 runs one after another failed or timed out, the schedule pauses itself, so that a broken command does not start and bill its sandbox on and on.
pausedReasonsays why, and the eventsandbox.schedule_pausedis sent. A resume,{"enabled":true}, starts it again and clears the reason. Skipped and missed runs, and runs that could not start the sandbox, which cost nothing, count neither way; nor do the failures before a pause or a resume. Anatnames its zone, asZor+02:00.
Its runs
Its runs, the latest first, 100 unless limit says, at most 500. Each has dueAt, startedAt and finishedAt, outcome, exitCode, startedSandbox, stoppedSandbox (disk, memory or null), deletedSandbox and commandId, whose output GET /v1/sandboxes/{id}/commands/{commandId} gives. A run's reason says, in words, why it ended as it did where the outcome does not.
| outcome | What it means |
|---|---|
running | It is going. |
succeeded, failed, timed_out | Its command exited 0, exited with another code, or ran out of time. |
skipped | The sandbox was stopped and the schedule skips then, or the last run was still going. |
replaced | A later run of the same schedule took its place. |
missed | It could not be made on time. |
could_not_start | The sandbox could not be started: its reason says why, such as the account's plan. |
interrupted | The API restarted while it ran. |
Each finished run is also the event sandbox.scheduled_run, sent to the webhooks that take it, with the run's outcome and exit code, and a schedule that pauses itself is sandbox.schedule_paused.
Run now, pause, change, delete
POST /v1/sandboxes/{id}/schedules/{scheduleId}/runmakes a run now, by the schedule's own rules, paused or not, and answers202as it begins. Its next run does not move.PATCH /v1/sandboxes/{id}/schedules/{scheduleId}changes what it is given and keeps the rest.{"enabled":false}pauses it; a change of when it runs, or a resume, sets its next run from now. A run under way goes on as it began, save that turningdeleteAfteroff while it runs keeps its sandbox; turning it on does not delete the sandbox after a run already under way.DELETE /v1/sandboxes/{id}/schedules/{scheduleId}deletes it with its runs.GET /v1/sandboxes/{id}/scheduleslists a sandbox's, each with its last run.- From a terminal,
agental schedule add <sandbox-id> --every 1h -- python scrape.py, andagental schedules <sandbox-id>lists them. In the console, a sandbox's Schedules view.
The full machine-readable reference is OpenAPI 3.1. Every page as Markdown, for an agent: llms.txt. Questions: [email protected]