Scheduled jobs
A schedule calls a path on your app whenever its cron expression says, in UTC. Use one for a nightly report, a sync every few minutes or a weekly cleanup. Each run is a request to your app through our sign-in, so your app checks who sent it the same way it checks anyone. Schedules are on every plan.
Add a schedule
Section titled “Add a schedule”jiayang schedule add acme/hello "0 6 * * 1-5" /jobs/reportAdded 0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21: POST /jobs/report at "0 6 * * 1-5" (UTC). It runs next at 2026-10-05 06:00 UTC.That one runs at 06:00 UTC on weekdays. A schedule sends POST unless you add --method get. It sends no body.
The cron expression has five fields: minute, hour, day of the month, month and day of the week. Each is *, a number, a range like 1-5, a step like */15 or 9-17/2, or a list of those joined by commas. Sunday is 0 or 7. When both the day of the month and the day of the week are set, a day matching either one runs, as in every cron. Names like MON and shortcuts like @daily aren’t read.
| Expression | Runs |
|---|---|
*/5 * * * * |
Every 5 minutes |
0 * * * * |
On the hour |
30 2 * * * |
At 02:30 UTC every day |
0 9 * * 1 |
At 09:00 UTC on Mondays |
0 0 1 * * |
At midnight UTC on the first of each month |
The path is a path on your app, like /jobs/report, with no query string. Paths under /.jiayang are ours. An app can have up to 10 schedules.
Check it’s the scheduler
Section titled “Check it’s the scheduler”A run reaches your app with the same signed identity token as any request. Its kind is service, its sub starts with scheduler: and is followed by the schedule’s id, and its role is viewer. Check both before doing the job, since anyone your app is shared with can send a request to the same path:
import { getUser, jiayang } from "@jiayang-cloud/sdk/hono";
app.use("*", jiayang());app.post("/jobs/report", async (c) => { const caller = getUser(c); if (caller.kind !== "service" || !caller.sub.startsWith("scheduler:")) return c.text("forbidden", 403); await sendReport(); return c.text("done");});Each SDK gives you sub the same way. Nothing a client sends can make a request look like the scheduler: the token is signed by us, and we remove any X-Jiayang-* header a client sends before your app sees it.
See how it went
Section titled “See how it went”jiayang schedule list acme/hello0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21 0 6 * * 1-5 POST /jobs/report next 2026-10-05 06:00 UTC, last 2026-10-02 06:00 UTC ok (200)The last run is ok when your app answered with a 2xx, failed with the status when it answered with anything else, refused with the reason when it never reached your app, and error when your app didn’t answer within 30 seconds, or we never heard how it went (no_result). Every run is also in the audit log, as the scheduler.
Your app has 30 seconds to answer. For a longer job, answer first and carry on in the background, with ctx.waitUntil in a Worker.
A schedule that’s turned off says off, and one we turned off says why, like off (maker_cannot_edit).
A schedule that ran late says so, with how many runs the late one stood in for. It shows the last time that happened:
0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21 * * * * * POST /cron next 2026-10-05 13:41 UTC, last 2026-10-05 13:40 UTC ok (200), missed 1 run: the run due 2026-10-05 13:30 UTC went at 2026-10-05 13:31 UTCSee When a run doesn’t happen for why one can.
The app’s Schedules tab in the dashboard shows the same list. You can add, turn off, run and remove schedules there.
Turn one off or on
Section titled “Turn one off or on”jiayang schedule off acme/hello 0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21jiayang schedule on acme/hello 0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21Turned 0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21 off. It doesn't run until it's turned on again.Turned 0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21 on. It runs next at 2026-10-05 06:00 UTC.A schedule turned back on runs next at the first time its expression names from then, not for the runs it missed.
One we turned off can’t be turned on, here or in the dashboard, since the reason we turned it off still holds. Add it again instead.
A schedule that’s already off, or already on, is left as it is, and the CLI says so:
0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21 is already off: whoever added it can no longer edit the app. It runs again only once it's added again.Run one now
Section titled “Run one now”jiayang schedule run acme/hello 0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21It runs within a minute, the way it always does. A schedule that’s turned off can’t be run: turn it on first. One we turned off because its maker can no longer edit the app, or for single sign-on, has to be added again.
Remove one
Section titled “Remove one”jiayang schedule remove acme/hello 0c9e8b7a-1f2d-4c3b-9a8e-7d6c5b4a3f21When a run doesn’t happen
Section titled “When a run doesn’t happen”- A schedule runs at most once at a time. If a run is still going when the next one is due, the next one waits, and runs at the first minute after the one before it ends. So an every-minute job whose runs take 75 seconds runs every two minutes.
- If we miss a run, the job runs once when we can, not once for every run it missed. The same goes for runs that came due while one was still going.
- A run that’s turned off, or removed, after it was due and before it reached your app doesn’t happen. One that has already reached your app goes on to the end, and is written down as it went.
- Each workspace’s jobs get up to 20 runs a minute. More than that due at once run in the next minute, in order.
- A run is refused when the app’s workspace is over its plan’s limits, is suspended, or when nothing is live.
- A schedule whose maker can no longer edit the app is turned off, and says
maker_cannot_edit. Ask an editor to add it again. - While its app or workspace is deleted, a schedule doesn’t run. Once it’s restored, each job runs when it’s next due. The runs it missed while deleted aren’t made up, and aren’t counted as missed.
- If the workspace was restored and used SCIM, the jobs of someone your identity provider added wait, still on, until the provider confirms them. Then each runs when it’s next due. Until then, owners and admins see them listed as waiting, and Run now is refused.
- A schedule made on a session the workspace’s single sign-on doesn’t accept, once that’s required of its maker, is turned off, and says
sso_required. Add it again after signing in through your provider.
What it costs
Section titled “What it costs”Each run is a request to your app. It counts toward your plan’s requests and CPU like any other, and it stops when your plan’s limits stop other requests. See Limits.
From an agent
Section titled “From an agent”The list_schedules, add_schedule and remove_schedule tools do the same for an agent.
Permissions
Section titled “Permissions”Adding, changing, running and removing schedules take an editor or owner of the app, or a workspace admin or owner. Each change is recorded in the audit log.