Crons
Work is filed as a task by Sensei. Open Tasks above Files in the workspace sidebar: Todo, In progress and Done (last 7 days). Cards open the task modal; Open chat opens the execution. The New session button and ⌘J / ⌘⇧J shortcuts are gone.
File with kosuke tasks create <orgId> --hint-project <projectId> --title "<title>" --description "<work>". Check duplicates with kosuke tasks list <orgId> --status todo,in_progress; Sensei can use kosuke tasks list --status todo,in_progress. Only members and Sensei may omit the hint. Sandbox and Cron callers need a hint and --reason. Start with kosuke tasks start <orgId> <taskId> --project-id <projectId> --skill ship. A draft starts in its original repository. MCP ship needs a session target; a task without a session must use tasks start.
See File now, start later and Tasks endpoints. Legacy tasks create <orgId> --hint-project <projectId> --title … --description … --reason … remains supported.
Task lists are paginated. Follow meta.pagination.nextCursor with --cursor until null, and read each full description with kosuke tasks get <workspaceId> <taskId> before judging overlap; lists omit draft prompts.
A cron sends a saved prompt to one or more of a workspace's projects on a schedule. Each time it fires, Sensei starts one Cron Run covering the selected projects in its own disposable sandbox. A run opens no chat session of its own. It is its own record, with its own transcript, Artifacts and Reply. Every run spends the workspace's Credentials, never a member's Subscription. Crons explains the idea and the page in the app, and Schedule recurring work walks through a first cron.
A cron belongs to its workspace, so its endpoints sit under /api/organizations/{workspaceId}/crons. kosuke workspaces list gives the workspace id. They used to sit under a project, before a cron could cover several; those endpoints are removed. Five paths answer 410 to GET, POST, PUT, PATCH and DELETE: /api/projects/{projectId}/crons, and under it /{cronId}, /{cronId}/run, /{cronId}/runs and /{cronId}/runs/{runId}. The message says to upgrade @kosuke-ai/kosuke, which is what a kosuke older than 5.0.0 gets on every crons command. Any other path under /api/projects/{projectId}/crons answers 404.
A Personal Cron belongs to you and to no workspace, so its endpoints sit under /api/user/crons. In the CLI they are kosuke crons list personal, get, create, update, run, runs list and runs get. Without repositories, each fire sends its prompt as your message to its own Sensei Thread. With Personal Repositories or Apps, it runs in its own sandbox. Both use your Subscriptions, or Sensei free when you hold none. Its crontab is read in its own timezone, not the Workspace Timezone. Your API key and your Sensei reach these endpoints. A chat session's sandbox token, a Cron Run's token and a Connected App's Connector Token answer 403. Deleting a Personal Cron, a run's transcript and Stop stay in the web app.
| Method | Path | Description |
|---|---|---|
| GET | /api/organizations/{workspaceId}/crons?projectId=&mine=&search=&page=&pageSize= | List the workspace's crons, newest first, with each one's projects, creator and last run status: the status of its latest run. |
| POST | /api/organizations/{workspaceId}/crons | Create a cron, enabled, with you as its creator, with zero or more of the workspace's projects: each fire is one Cron Run in its own sandbox, cloning only the selected repositories. Every run spends the workspace's Credentials, never a member's Subscription, so a workspace with no usable provider is refused. The model is an encoded id such as anthropic:opus__xhigh; null or left out uses the saved Workspace Default Model when its Provider has usable workspace Credentials, otherwise the platform's choice among usable workspace Credentials at run time. The crontab is read in the Workspace Timezone (kosuke workspaces timezone set), and may not fire more often than every 15 minutes. A body that sends timezone is refused. From an agent (Sensei, a sandbox or a Cron Run) it is refused with 409 CRON_ALREADY_EXISTS when the workspace has a Cron by that name. |
| GET | /api/organizations/{workspaceId}/crons/{cronId} | Read one cron: its prompt, schedule, model, creator, when it runs next, its projects, its last run status, and how many runs in a row failed: five disable it. |
| PATCH | /api/organizations/{workspaceId}/crons/{cronId} | Change a cron; only the fields sent change. projectIds replaces its projects. Any edit sets the cron's failure count back to zero. enabled=false pauses the cron and enabled=true resumes it; model=null goes back to the workspace default. The creator or a workspace admin only. |
| DELETE | /api/organizations/{workspaceId}/crons/{cronId} | Delete a cron with its runs, their transcripts and their Artifacts. The sessions its runs filed stay. The creator or a workspace admin only. |
| GET | /api/organizations/{workspaceId}/crons/{cronId}/artifacts?projectId=&kind=&page=&pageSize= | List the Artifacts a cron's runs published, across all of its runs, most recently changed first: each one's title, kind and version, with the run and the project it came from, and the total the filters keep. One row per Artifact: the same slug published by two runs is two rows. Rows only: a body or a file url comes from crons runs artifacts get, with the row's runId. The project and kind filters each take several values and keep an Artifact matching any of them. |
| POST | /api/organizations/{workspaceId}/crons/{cronId}/run | Fire one Cron Run covering all the cron's projects or the selected projectIds, in its own sandbox; a cron with no projects runs with none. Refused for a cron stopped with No projects left until an edit picks a project. Answers with runs: [{ runId, status, projectIds, projectId }]; projectId is deprecated and names the first project by name, or null. The creator or a workspace admin only. |
| GET | /api/organizations/{workspaceId}/crons/{cronId}/runs?projectId=&status=&page=&pageSize= | List a cron's runs, newest first: each one's projects, status, the first line of its reply or its error, with the total and failed counts of the runs the filters keep. The project and status filters each take several values and keep a run matching any of them; a project filter keeps every run that covered the project. |
| GET | /api/organizations/{workspaceId}/crons/{cronId}/runs/{runId} | Read one run of a cron: the projects it covered, its status, the model it ran on, the Cron Agent's reply, and the sessions it filed with each one's status and whether it waits for an answer. |
| GET | /api/organizations/{workspaceId}/crons/{cronId}/runs/{runId}/artifacts | List the Artifacts a run of a cron published, most recently changed first. Rows only: a body or a file url comes from reading one. |
| GET | /api/organizations/{workspaceId}/crons/{cronId}/runs/{runId}/artifacts/{artifactId} | Read one Artifact of a run of a cron, by its id or its slug: a markdown or html one carries its body, an image or a video a presigned url, its media type and its size. |
| POST | /api/organizations/{workspaceId}/crons/{cronId}/runs/{runId}/cancel | Stop a run of a cron that is still going, the way Stop on its page does. The run ends cancelled; the Artifacts it stored and the sessions it filed stay, and its sandbox is removed. The creator or a workspace admin only. |
| GET | /api/organizations/{workspaceId}/crons/{cronId}/runs/{runId}/messages | Read the Cron Agent's transcript of one run of a cron: the prompt it was given, then everything it did, with the run's status. |
| GET | /api/user/crons?search=&page=&pageSize= | List your Personal Crons, newest first, with each one's thread and last run status. A Personal Cron belongs to you and no workspace: with Personal Repositories or Apps it runs in its own sandbox; without, in its Sensei Thread. Both use your Subscriptions or Sensei free when you hold none. Among agents, only your Sensei reaches them. |
| POST | /api/user/crons | Create a Personal Cron, enabled, with you as its owner. A Personal Cron belongs to you and no workspace: with Personal Repositories or Apps it runs in its own sandbox; without, in its Sensei Thread. Both use your Subscriptions or Sensei free when you hold none. The crontab is read in the cron's own timezone, and may not fire more often than every 15 minutes. The model is an encoded id such as anthropic:opus__xhigh; repositories require an explicit Model from any released Provider on one of your Subscriptions, or kosuke-free when you hold none. Changing to Free while you hold a Subscription, or to a personal Model without one, is refused. Without repositories, null or left out runs on the thread’s model. From your Sensei it is refused with 409 CRON_ALREADY_EXISTS when you have a Personal Cron by that name: update that one instead. From Sensei, create or change one only when the member asks. To move a workspace Cron here, read it and recreate it with its timezone (its model only when the member's own Subscription runs it); the member deletes the workspace one in the app. |
| GET | /api/user/crons/{cronId} | Read one of your Personal Crons: its prompt, schedule, timezone, model, when it runs next, its thread, its last run status, and how many runs in a row failed: five disable it. A Personal Cron belongs to you and no workspace: with Personal Repositories or Apps it runs in its own sandbox; without, in its Sensei Thread. Both use your Subscriptions or Sensei free when you hold none. |
| PATCH | /api/user/crons/{cronId} | Change one of your Personal Crons; only the fields sent change. Any edit sets the cron's failure count back to zero. enabled=false pauses the cron and enabled=true resumes it. No projects left stays until a save picks at least one repository, and Resume is refused meanwhile. Changing repositories rechecks the Model: a sandbox requires an explicit Model from any released Provider on one of your Subscriptions, or kosuke-free when you hold none. Changing to Free while you hold a Subscription, or to a personal Model without one, is refused. Re-sending the unchanged stored Model is accepted. Without repositories, model=null goes back to the thread’s model. The crontab is read in the cron's own timezone. |
| GET | /api/user/crons/{cronId}/artifacts?projectId=&kind=&page=&pageSize= | List the Artifacts a cron's runs published, across all of its runs, most recently changed first: each one's title, kind and version, with the run and the project it came from, and the total the filters keep. One row per Artifact: the same slug published by two runs is two rows. Rows only: a body or a file url comes from crons runs artifacts get, with the row's runId. The project and kind filters each take several values and keep an Artifact matching any of them. |
| POST | /api/user/crons/{cronId}/run | Fire one of your Personal Crons once now, on your own Subscription: with repositories it runs in its own sandbox; without, in its Sensei Thread. Resume a paused Cron first. Refused for No projects left until a save picks a repository. Answers with runs: [{ runId, status, projectIds, projectId }]. Status may be queued while the fire is pending. |
| GET | /api/user/crons/{cronId}/runs?status=&page=&pageSize= | List the runs of one of your Personal Crons, newest first: each one's status, the first line of its report or its error, and whether it ran in a sandbox or Sensei Thread, with the total and failed counts of the runs the filter keeps. The status filter takes several values and keeps a run matching any of them. |
| GET | /api/user/crons/{cronId}/runs/{runId} | Read one Personal Cron run: its kind, status, Model, report, repositories and filed sessions. A sandbox run has its Cron Agent transcript and Artifacts and threadId null. A thread run names the Sensei Thread holding its Turn. |
| GET | /api/user/crons/{cronId}/runs/{runId}/artifacts | List the Artifacts a run of a cron published, most recently changed first. Rows only: a body or a file url comes from reading one. |
| GET | /api/user/crons/{cronId}/runs/{runId}/artifacts/{artifactId} | Read one Artifact of a run of a cron, by its id or its slug: a markdown or html one carries its body, an image or a video a presigned url, its media type and its size. |
A Personal Cron with repositories accepts kosuke-free when you hold no Subscription. Both scheduled fires and Run now use Free while you hold none. When moving a workspace Cron to Personal, use its model only if your Subscription runs it, or kosuke-free with none.
Each Personal Cron run on Free uses one Source. Sensei moves it automatically when that Source reaches its limit, is paused or is removed. The model, Source and provider stay hidden in run pages and live updates.
What the Cron Agent does
The Cron Agent clones the selected repositories at /app/projects/<slug> in its own sandbox. It may edit the clones, run the project’s checks and commit there. It makes no GitHub writes: no push, pull request, comment, label or review. A code change becomes a ticket sent as a message, with the patch in an Artifact. The main sandbox and live checkout stay untouched. No live app runs here; checks against public addresses must change nothing. To test the running app, it files a session in a Collaboration project, labels it cron-run, starts it, follows its first turn, reads the result and archives it. Save results in Artifacts, Files or tickets sent as messages. Clone edits disappear when the sandbox is removed; the Reply, transcript, Artifacts and filed sessions stay. It never asks a Clarification. When something is unclear, it decides and states its assumptions in its Reply. It reaches the Project Skills in the checkout and the Factory Skills, and runs only the Factory Skills that read and report, such as kosuke:web-pentest. A prompt that calls any other skill is passed, word for word, into a session it files or a ticket it sends.
Work the Cron Agent hands over goes into a chat session as a ticket it sends as a message (kosuke sensei messages send, key ticket-<slug>): it reaches a member's Sensei, and the ticket is created in the Backlog only when that member approves. A cron prompt that explicitly says to file Backlog tickets directly is the exception: then the Cron Agent files them itself. It starts a session or sends into one only when the cron's prompt asks for exactly that. Before it sends or files, it reads the workspace's open tasks, so it does not hand over the same work twice, and it may fold news into a Todo task its own cron filed earlier in the project. A run may send or file any number of tickets. When the cron has Reports to (reportTo), every message goes to one of those members, and POST /api/sensei/teammate-messages refuses a run's message to anyone else with 403 RECIPIENT_NOT_IN_REPORT_TO. A run cannot change its own cron's Reports to (403 REPORT_TO_MEMBER_ONLY).
The sessions it files are Sensei's. Sensei creates them and is their Assignee. These sessions run on the workspace's Credentials, until a member starts one or reassigns it to someone. From then on it runs on that Assignee's credentials. Each runs on the model the prompt names, otherwise on the cron's. A model the workspace cannot run falls back to the cron's. In every other way they are ordinary sessions: they show on the board and may ask Clarifications. While a session is Sensei's, every member gets its attention cues, unread and waiting on you, as its Assignee would.
Create a cron
POST /api/organizations/{workspaceId}/crons creates a cron, enabled, with you as its creator.
| Field | Type | Required | What it does |
|---|---|---|---|
name | string, 1 to 100 characters | Yes | The cron's name. |
projectIds | array of project ids, at least one | Yes | The projects the cron fires in, covered by one Cron Run. Every one must belong to the workspace. The list is fixed when you save: a project created later joins only when someone edits the cron to add it. |
prompt | string | Yes | What the Cron Agent is asked to do, handed to it word for word on every fire. |
cronExpression | string | Yes | Five fields, for minute, hour, day of month, month and day of week, read in the Workspace Timezone. It may fire at most once every 15 minutes. |
model | string, or null | No | A value from GET /api/models, such as anthropic:opus__xhigh. Left out or null, each run uses the saved Workspace Default Model when its Provider has usable workspace Credentials. Otherwise Sensei chooses a model those Credentials can run. The Cron Agent runs on this model, and so does every session it files unless the prompt names another. |
curl -s -X POST "https://app.kosuke.ai/api/organizations/$WORKSPACE_ID/crons" \
-H "Authorization: Bearer $KOSUKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Weekly dependency check", "projectIds": ["'"$PROJECT_ID"'"], "prompt": "List outdated dependencies and send a ticket for the safe updates.", "cronExpression": "0 9 * * 1"}'
The answer is 201 with the cron:
{
"data": {
"id": "c4a81f3e-2b69-4d07-9e15-6f0b3c8d2a71",
"projects": [
{
"id": "9b1d4e27-5c3a-4f86-a0d2-7e4b8c6f1a35",
"name": "storefront",
"baseBranch": "main",
"lastRunStatus": null,
"running": false
}
],
"consecutiveFailures": 0,
"createdBy": "Hn2kW8xQ4pZ7cV1mB5tR9yL3sD6fG0jA",
"createdByName": "Mara Keller",
"name": "Weekly dependency check",
"prompt": "List outdated dependencies and send a ticket for the safe updates.",
"cronExpression": "0 9 * * 1",
"timezone": "Europe/Zurich",
"model": null,
"enabled": true,
"disabledReason": null,
"lastRunAt": null,
"nextRunAt": "2026-10-05T07:00:00.000Z",
"lastRunStatus": null,
"createdAt": "2026-10-01T12:30:04.512Z",
"updatedAt": "2026-10-01T12:30:04.512Z"
}
}
The same with the CLI, where --project takes one project and repeats for several:
kosuke crons create $WORKSPACE_ID --project $PROJECT_ID --project $OTHER_PROJECT_ID \
--name "Weekly dependency check" \
--prompt "List outdated dependencies and send a ticket for the safe updates." \
--cron-expression "0 9 * * 1"
A cron has no timezone of its own: every cron of a workspace is read in the Workspace Timezone, UTC until an owner or admin sets another with PUT /api/organizations/{workspaceId}/timezone (kosuke workspaces timezone set). The cron's timezone is that zone, read-only. In the example above the workspace is on Europe/Zurich, so the 09:00 Monday fire is 07:00 UTC. A create or change whose body sends timezone answers 400, pointing to kosuke workspaces timezone set.
The crontab is checked on every create and every change. A request answers 400 when the crontab does not have five fields or the schedule could fire twice within 15 minutes in the Workspace Timezone. That includes the days a clock change shifts. It also answers 400 when projectIds is empty or names a project of another workspace. A workspace with no usable Credentials can hold no cron, and a create there answers 400.
List and read crons
GET /api/organizations/{workspaceId}/crons lists the workspace's crons, newest first, as { "data": { "crons": [...], "total": 12 } }.
| Parameter | What it does |
|---|---|
projectId | Keeps the crons that include this project, alone or among others. |
mine | true keeps the crons you created. |
search | Keeps crons whose name or prompt contains the text, ignoring case. |
page, pageSize | The page to read. pageSize defaults to 25 and goes up to 100. |
Each cron lists its projects, ordered by name. lastRunStatus is the cron's latest run status. consecutiveFailures counts failed runs since its last success or reset. A project's running says whether a running run covers it.
GET /api/organizations/{workspaceId}/crons/{cronId} reads one cron, with nextRunAt. The CLI commands are kosuke crons list <workspaceId>, with --project for the project filter, and kosuke crons get <workspaceId> <cronId>.
Change a cron
PATCH /api/organizations/{workspaceId}/crons/{cronId} takes any field of the create body, plus enabled. Only the fields you send change. projectIds replaces the cron's projects and may not be empty. model: null goes back to the workspace default. Any edit resets the cron's failure count. Saving leaves a disabled cron disabled.
kosuke crons update $WORKSPACE_ID $CRON_ID --cron-expression "0 7 * * 1-5"
kosuke crons update $WORKSPACE_ID $CRON_ID --project $PROJECT_ID --project $OTHER_PROJECT_ID
A flag cannot send null, so the CLI resets the model with --body '{"model": null}'.
Pause and resume
enabled: false pauses a cron and enabled: true resumes it. On the CLI these are --no-enabled and --enabled. Either one clears disabledReason. Resuming resets the cron's failure count.
Five failed runs in a row disable the cron with a reason. A success resets the count. Cancelled and skipped runs leave it unchanged.
When a cron's last project is deleted, the cron stays, disabled, and disabledReason is No projects left. Edit it to pick other projects, or delete it.
Legacy project resume
The project resume endpoint returns 410 Gone. Resume the cron instead.
Run now
POST .../{workspaceId}/crons/{cronId}/run fires one run now, paused or not. With no body it covers all selected projects. { "projectIds": [...] } covers only those projects, each of which must belong to the cron. The run starts its own sandbox on workspace Credentials. Its response carries one run with projectIds. Read the run by its runId. The CLI command is kosuke crons run <workspaceId> <cronId>, with --project <projectId> repeated to pick projects.
Read runs
Every fire leaves one run covering its selected projects, including when no Cron Agent started. GET .../{workspaceId}/crons/{cronId}/runs lists them, newest first, as { "data": { "runs": [...], "total": 30, "failed": 2 } }. It takes page and pageSize, and two filters that each take several values as repeated keys, such as ?projectId=…&projectId=…&status=failed&status=skipped:
| Parameter | What it does |
|---|---|
projectId | Keeps each run that covered any of these projects. |
status | Keeps the runs with these statuses: running, succeeded, failed, cancelled or skipped. |
total and failed count the runs the filters keep. On the CLI, kosuke crons runs list <workspaceId> <cronId> --project <projectId> --status failed repeats either flag for several values.
Each run carries projectIds and projects, its status, a one-line summary that is the first line of the Cron Agent's Reply, and error. GET .../{workspaceId}/crons/{cronId}/runs/{runId} adds the model it ran on, the report that holds the Reply, and filedSessions, the sessions the run filed, each with its status and whether it waits for an answer.
status | What it means | Counts toward disabling the cron |
|---|---|---|
running | The Cron Agent is working. | No. It leaves the count as it was. |
succeeded | The Cron Agent finished. | No. It sets the count back to zero. |
failed | A gate refused the fire, something broke, the run passed its 5 hours, or its sandbox stopped. | Yes. |
cancelled | The cron's creator or a workspace admin stopped it. | No. It leaves the count as it was. |
skipped | The cron's previous run was still running, or a Run now met another fire of the cron that was still starting. | No. It leaves the count as it was. |
A merge that updates the checkout does not stop a run.
A run keeps what the Cron Agent did. Its page in the app shows it, and so do these commands:
- the transcript, with
kosuke crons runs messages list; - the Artifacts, with
kosuke crons runs artifacts listandkosuke crons runs artifacts get; - the Reply, the
reportfield ofkosuke crons runs get; - the sessions the run filed,
filedSessionsonkosuke crons runs get.
The sessions a run filed are ordinary sessions. They show on the board and in the session lists.
Stop a run
POST .../{workspaceId}/crons/{cronId}/runs/{runId}/cancel stops a run that is still going, the way Stop on its page does. The CLI command is kosuke crons runs cancel <workspaceId> <cronId> <runId>. The run ends cancelled. The Artifacts it already stored and the sessions it already filed stay, and the cron's runs in its other projects go on. Only the cron's creator or a workspace admin can stop a run.
Delete a cron
DELETE /api/organizations/{workspaceId}/crons/{cronId} answers 204. It removes the cron with its runs, their transcripts and their Artifacts. A run still going in any of its projects is stopped first, and its key stops working at once. The sessions its runs filed are ordinary sessions and stay. The CLI command is kosuke crons delete <workspaceId> <cronId>.
A Cron Run's own key
Each run gets a key of its own, prefixed kosuke_crn_, which the Cron Agent uses through the kosuke CLI. The key is revoked when the run ends. It acts as the workspace, in the cron's workspace only: as Sensei, an admin of that workspace and never its owner, whoever created the cron. Sensei is not a member, so it shows in no member list. A Personal Cron's key acts as its owner instead, as long as they belong to the workspace. It sends tickets as messages (sensei messages send, key ticket-<slug>), which create nothing until the routed member approves the card their Sensei draws. It files Todo tasks itself only when the cron's prompt says to file them directly (tasks create <orgId> --hint-project <projectId> --title … --description … --reason …), and then Sensei is their Assignee.
Acting as the workspace, the key reads everything in the workspace: its members, projects, sessions with their messages, jobs and artifacts, tasks, Crons and their runs, files, credits, sandbox use, models, Deploys and the Deploy Chat, settings, and skills and plugins. It writes only in the projects covered by the run, where it may file, start, label and archive sessions and tasks, and it may create and change the workspace's Crons and Files. It reads a project's settings and live app only in those projects. It is refused, with 403, on changing the workspace's AI provider, provider logins or Grok access, on disconnecting an integration, on a go-ahead and on deleting a project. It writes nothing to GitHub. A person's own things (their Memory, personal Files, Personal Crons, personal tasks, logins and Mac) and their Sensei are out of its reach, and GET /api/user/skills and GET /api/user/plugins answer an empty list. A Cron Run using GLM calls Z.ai directly with the workspace's GLM Coding Plan key, one using Kimi calls Moonshot AI directly with the workspace's Kimi Code key, and one using DeepSeek calls DeepSeek directly with the workspace's DeepSeek API key.
Who can do what
| Action | Who |
|---|---|
| List and read crons and their runs | Any member of the workspace. |
| Create a cron | Any member, over any of the workspace's projects. They become its creator, shown in createdBy and createdByName. |
| Change, pause, resume, resume a project, run now, stop a run or delete | The creator, or a workspace owner or admin. |
The agent inside a chat session acts as the session's Assignee, when the Assignee sent the turn, and can do with crons what they could. When anyone else sent the turn, it acts as the workspace, like a Cron Run's key.
Limits
- A run acts as the workspace, whoever created or fired the cron. The sessions it files are Sensei's, and Sensei is their Assignee.
- A run sends tickets as messages; no ticket exists until a member accepts one. A prompt that says to file Backlog tickets directly makes the run file them instead.
- A cron runs on the workspace's Credentials only. It never falls back to a member's Subscription, so a cron keeps firing after its creator leaves.
- A cron's projects are fixed when it is saved. A project created later joins only when someone edits the cron to add it.
- A run has 5 hours. At the limit it is stopped and ends
failed. - Runs from before the Cron Agent keep their status, summary, error and branch, but not their transcripts.
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | The body does not match the schema: a required field is missing or empty, a field is too long or of the wrong type, the model is not a supported id, or projectIds is empty. |
| 400 | BAD_REQUEST | The crontab or the 15-minute floor is not met, the body sends timezone, a project belongs to another workspace, Run now names a project that is not one of the cron's or a cron with no projects is resumed, a runs filter holds a value it does not know, or the workspace has no usable Credentials. |
| 403 | FORBIDDEN | You are not a member of the workspace, you are not the cron's creator or a workspace owner or admin, or the caller is an agent token or a Cron Run's key reaching beyond what it may. |
| 404 | NOT_FOUND | The cron, run or project does not exist in the workspace. |
| 409 | CRON_RUN_STARTING | A delete met a fire that was still starting. Try again in a moment. |
Responses and errors has the full table.
Next steps
Each fire creates one run covering the selected projects in its own sandbox. Run records expose projectIds and projects. The deprecated projectId names the first remaining project by name, or is null. Manual runs accept projectIds to run on some projects. Five failed runs in a row disable the cron. Resume or saving an edit resets the count; saving leaves it disabled. Project resume is no longer supported.