Schedules
No CI setup, no GitHub Actions config. Declare a schedule in your tessl.json, push to your default branch, and Tessl runs it for you.
What is a schedule?
A schedule runs a plugin on a cron. You pick a plugin, a repo, and a time, and Tessl runs the plugin against that repo on that schedule. Each time the schedule fires, Tessl starts a cloud run and reports the result back so you can see what happened.
Tessl skips a tick if the previous run from the same schedule is still active when the next one would start. There is no queue: a skipped tick does not run later.
Think of it as two parts working together. The plugin describes the task: it tells the agent what to do. The schedule describes the timing: it tells Tessl when to do it.
What you need first
A Tessl account with a workspace.
GitHub connected to your workspace. See Connect Tessl to GitHub.
A plugin that describes the task. See Create a plugin.
An environment. A scheduled run cannot mint its own GitHub token, so it needs a
GITHUB_TOKENfrom its environment to check out your repo and open PRs. The environment also holds any other secrets the plugin needs. See Environments.
Define a schedule in tessl.json
The primary way to create a schedule is to declare it in your project's tessl.json. Add a schedules block, push it to your default branch, and Tessl reads the file and creates the schedule for you. The schedule lives next to the code it runs against, so it is reviewed and version-controlled like any other change.
The name field helps Tessl resolve the manifest to a project. Use the workspace/project format, for example "name": "my-org/my-app". Before schedules sync, an active project must exist in that workspace, be connected to the same GitHub repository, and use the manifest's directory as its repository subpath. The GitHub App installation must currently have access to the workspace. When Tessl resolves that binding, schedules from the manifest appear under the project's Schedules tab in the web app. Schedule sync does not create or reassign projects.
Tessl discovers and syncs each tessl.json in a repository independently. In a monorepo, a manifest such as apps/backend/tessl.json manages only its own schedules, without affecting the root or sibling manifests. Deleting that subdirectory manifest removes only the schedules managed by that manifest in workspaces that the GitHub App installation can still access. Schedules in workspaces it can no longer access are not reconciled by that deletion.
Let the schedule-setup skill do it for you
If you'd rather describe the schedule in plain language than write the JSON yourself, install tessl/schedule-setup.
When invoking the skill, tell your agent what you want to run and when ("run the docs audit every Monday at 9am London time using the production environment"). The skill asks clarifying questions one at a time, picks the most readable schedule format, writes and validates the entry, and explains what happens on push. It can also author a new repo-local skill for your schedule to use if you don't have one yet.
The schedules block
schedules is a map of schedule key to schedule entry. The key is the schedule's stable identity: a lowercase kebab-case slug, not a display label. Renaming a key removes the old schedule from the file and adds the new one. Sync validates the complete change before deleting the old schedule and creating the new one. If the new entry fails validation, neither operation is applied.
Each entry needs skill, environment, and one timing field. Use either cron or its alias, schedule, but not both.
skill
Yes
The plugin to run: a registry ref workspace/plugin[@version][#skill], or a repo-local file:path.
cron
One of two
A five-field cron expression, supported shorthand, or structured schedule object. The minimum interval between runs is 5 minutes.
schedule
One of two
Alias for cron. Use one timing field, not both.
environment
Yes
The workspace environment the run executes in, referenced by name. It supplies the GitHub token and any other secrets the plugin needs.
timezone
No
IANA timezone the cron is interpreted in (for example America/New_York). Defaults to UTC.
baseBranch
No
The branch each run checks out and opens PRs against. When omitted, each run uses the project's Loop default branch or the repository default.
workdir
No
A subdirectory of the repo the plugin runs in.
agent
No
Which agent runs the plugin (claude-code or claude (default), cursor, codex, gemini, tessl or tessl-agent).
model
No
Which model the agent uses (e.g. claude-sonnet-4-6 for claude, auto for cursor, gpt-5.4 for codex).
inputs
No
Skill input values, as a map of string keys to string values.
instructions
No
Free-text directions appended to the plugin prompt for this schedule.
description
No
A human-readable description of the schedule.
Raw cron expressions have five fields: minute, hour, day of month, month, and day of week. 0 9 * * 5 means "at 9:00 on every Friday". 0 8 * * * means "at 8:00 every day".
You can also use these shorthand strings:
hourlydaily at HH:MMweekdays at HH:MMweekly on DAY at HH:MMmonthly on Nth at HH:MMevery N minutesorevery N hours, whenNdivides evenly into 60 or 24every N minutes between HH:MM-HH:MM; the start is inclusive and the end is exclusive, and multi-hour windows use whole-hour endpointsevery N minutes on weekdays
For schedules that don't fit those patterns, use an object with minute, hour, day, month, and weekday fields. Omitted fields mean every value. Numeric fields accept a number, a list of numbers, { "every": N }, or { "between": [N, M] }. month and weekday also accept names or lists of names.
Tessl compiles shorthand strings and objects to cron before validation and storage. The timezone and 5-minute minimum apply to every input form.
A complete example
This declares two schedules. weekly-changelog runs the changelog-writer plugin every Friday at 9:00 New York time. api-docs-audit runs the docs-audit plugin every Monday at 8:00 UTC, steered at the API reference by its instructions.
Behaviors to know
Schedule identity is the key name. Renaming a key is therefore a delete and a create, not an update. For example, changing nightly-scan to nightly-check deletes the old schedule and its Temporal schedule, and creates a new schedule with the new key. Prior launch runs are preserved but are no longer linked to a schedule. If the new entry fails validation, neither the deletion nor the creation is applied.
The schedules block is the source of truth. Removing the block from tessl.json, or setting it to {}, means “no schedules” and deletes all schedules managed by that manifest. To recover from an accidental removal, revert the commit and push again.
Validate before you push
Run tessl schedule validate before you push to catch errors locally. You can also run the validator in your CI workflow:
It reads your tessl.json, checks the schedules block, and reports every problem against the schedule key it belongs to. It exits non-zero when the block is invalid, so it also works as a CI check. A file with no schedules block is valid.
validate checks the file only: its shape, its cron expressions, and the minimum interval. It cannot check what your workspace decides when the file syncs, so a file that passes here can still be rejected at sync time. It does not check:
whether the plugin reference resolves to a plugin your workspace can use
whether the named environment exists in your workspace, or carries a GitHub token
whether the agent and model are in your organization's catalogue
the limit on active schedules per workspace
How the sync works
When you push to your default branch, Tessl reads the schedules block and reconciles schedules across every workspace reachable through your GitHub App installation. For each workspace where the named environment resolves:
A key in the file that has no schedule yet is created.
A key whose entry changed is updated.
A key that was removed from the file is deleted.
If an entry cannot be applied in a given workspace (for example, the named environment does not exist there, or the workspace has reached its active-schedule limit), that entry is skipped for that workspace. Other entries and other workspaces are still reconciled.
Tessl only reads the schedules block. Changes to other parts of tessl.json are ignored. Most pushes that do not touch tessl.json are also ignored. A force push, branch creation, or push with an empty or truncated commit list bypasses that optimization. Tessl reads the manifest at the pushed revision and attempts reconciliation; individual entries can still be skipped if they cannot be applied.
The sync runs off the default branch only. A schedule declared on a feature branch does not sync until the change reaches your default branch.
The safety model
Tessl never deletes a schedule because it could not understand your file. Deletion only ever follows from a file it read cleanly.
Unparseable or invalid: nothing changes. If the file is malformed JSON, cannot be read, or its
schedulesblock fails validation, Tessl makes no changes at all. A typo in the block does not delete your schedules; it leaves them exactly as they are. Fix the file and push again.Valid and read cleanly: the file wins. Once Tessl reads a valid file, the file is the source of truth. Remove one key and that schedule is deleted. Remove the whole
schedulesblock, or delete atessl.json, and every repo-defined schedule managed by that manifest is deleted.
So the only way to delete a repo-defined schedule is to remove it from a valid file and push. An error in the file is always safe.
Read-only in the web app and CLI
When repo-defined schedules are enabled for your organization, a schedule defined in tessl.json is read-only from the web app and the CLI. Attempts to edit, pause, resume, or delete it through those surfaces return an error telling you it is defined in tessl.json. To change it, change the file and push.
You can still watch a repo-defined schedule and its runs in the web app and CLI. Only mutations are blocked.
A repo-defined schedule reflects the file for entries that were successfully applied at sync time. A skipped new entry is absent until the next sync reconciles it. A skipped update leaves the existing schedule at its previous configuration, and it can continue running until a later sync applies the update.
Create from the CLI or web app (alternative)
You can also create a schedule directly from the CLI or the web app, without a tessl.json block. This is the older path and is not version-controlled. It may be disabled for some users.
Use tessl schedule create. Pass the plugin as the first argument, then the repo, the cron, and the environment:
This runs the changelog-writer plugin against my-org/my-repo every Friday at 9:00 UTC.
Common flags:
--repo
The repository the plugin runs against. Required.
--cron
When the schedule fires, as a cron expression. Required.
--environment
The workspace environment the run executes in. It supplies the GitHub token and any other secrets or API keys the plugin needs. Required when you specify the plugin, repo, and environment directly.
--timezone
The timezone for the cron expression. Optional; defaults to UTC.
--agent
Which agent runs the plugin (for example claude-code, cursor, codex, gemini, or tessl-agent). Optional.
--model
Which model the agent uses. Optional.
--instructions
Extra directions layered on top of the plugin for this schedule. Optional.
--base-branch
The branch to check out and open PRs against. Optional.
The cron expression has five fields: minute, hour, day of month, month, and day of week. 0 9 * * 5 means "at 9:00 UTC on every Friday". 0 8 * * * means "at 8:00 UTC every day".
Use --instructions to point a generic plugin at a specific job:
Open Projects > [project] > Schedules and choose Create schedule. The form asks for the schedule (cron), a name, a description, and an environment. These are the primary fields.
Below the environment and mode selection is an Advanced options control. Expand it to set the agent, model, inputs, instructions, and base branch. Leave it collapsed to accept the defaults.
How instructions work
The instructions field layers extra directions on top of whatever the plugin already says. The plugin is the base recipe; instructions are the per-schedule customization. The plugin still runs its normal task. The instructions steer it for this one schedule.
This lets you reuse one plugin for several jobs. The same docs-audit plugin, pointed two ways in one schedules block:
Same plugin, two schedules, two different jobs.
Monitoring your runs
Each schedule keeps a table of its past runs. Open a schedule in the web app to see its full run history, including output and duration. From the CLI, tessl schedule runs <id> lists runs with their ID, status, skill, repo, workspace, creator, and creation time.
In Progress
Still running
Wait
Completed
The skill finished and reported a result
Nothing. Check the output.
Agent failed
The skill reported that it could not complete the task
Check the output for why
Error
The platform failed, or the run ended without a skill result
Check the run's error detail
Cancelled
Someone stopped the run
Nothing
Next steps
Last updated

