- Agent job (
--message): sends a prompt to an agent session. The agent answers like it would in a chat. - Shell job (
--shellor--exec): runs a command directly. No agent, no tokens. An agent is involved only when the command fails and you asked for it with--on-error.
ravi daemon start must be running.
Create your first job
1
Add the job
@@SILENT@@ is a token the agent can answer with to stay quiet.2
Check where it runs
Agent, Account, Session, Reply session and the Routing section. They tell you which session runs the prompt and which chat gets the answer (see Where the answer goes). Also check Runs as: a job that uses your personal connectors or Knowledge must run as you, and one someone else created uses Bases and Pages in their name (see Who the job runs as).3
Run it once now
--every job’s next run moves one interval later, and a one-shot (--at or --delete-after) job is deleted afterwards, as after a scheduled run.4
Read the result
Who the job runs as
Every job has an owner, shown asRuns as in ravi cron show <id> and as ownerPrincipal and runsAsOperator in --json. The turn that creates the job sets it:
- From the terminal, from your own direct chat, or when you (the owner) ask in a group, the job runs as
operator. - When someone else asks in a chat, the job runs as that person’s contact (
contact:<id>). Acron setfrom such a turn that changes what the job runs or where it answers (its message, name, agent, reply session and so on) moves the job to that person too; editing its description, schedule, timeout ordelete-afterdoes not. For you it is narrower: someone else’s job moves to you only when you write its message or command, so you never run a prompt you did not write with your own authority.
ravi login. Your login authenticates this Ravi installation; it does not lend your permissions to a job someone else owns.
- A job someone else created acts as that person:
ravi basesand Pages share, invites and access requests go out in their name, through their ownravi link, with their own role in the organization. Until they link their account the calls are refused withACTOR_NOT_LINKED, and Ravi does not message them from the run, since nobody is asking. Personal connectors and Knowledge are your own, so they refuse the job (CONNECTOR_SPEAKER_NOT_OWNER,KNOWLEDGE_TURN_NOT_OPERATOR). - A job with no person as owner (
unknown) is refused (ACTOR_UNKNOWN).
ravi cron add and ravi cron set print a warning when the job does not run as you.
Only you can own a shell job. A shell command runs with this machine’s access and your credentials, so nothing can hold it to someone else’s permissions. From anyone else’s turn, ravi cron add --shell (and a cron set that would leave a shell job with someone else as owner) is refused with CRON_SHELL_NOT_OPERATOR, and the runner never runs a shell job someone else owns.
The run still records lastStatus: ok when the agent turn completes, even if a command inside it was refused. So prove a job that needs these calls: for a recurring job, one ravi cron run <id> --execute produced the result you expected. A job that needs your connectors or Knowledge must run as you (runsAsOperator: true in ravi cron show <id> --json; a contact linked to you also acts as you). Don’t run a one-shot job (--at or --delete-after) early to test it, because the manual run uses it up; check its settings and confirm after the scheduled run. An agent should report a refused job to you instead of disabling it.
Schedules
Pass exactly one of these:
Cron expressions use the usual five fields: minute, hour, day of month, month, day of week (0 is Sunday).
--tz applies only to --cron. Without it, the job stores the defaultTimezone setting in effect when you add it (changing the setting later does not move existing jobs), and without that setting, it uses the timezone of the machine running the daemon:
s, m, h or d (30s, 90m, 2d). Combined values such as 1h30m are rejected. The next run is computed from the scheduled time, not from when the run finished, so intervals do not drift. The flip side: after the daemon was down, an interval job fires once for every interval it missed, back to back, until it catches up. A cron-expression job fires once and moves on.
One-shot times are ISO 8601. A value without an offset (2026-11-03T14:45) is read in the local time of the machine where you run the command. Add Z or an offset (2026-11-03T14:45:00-03:00) to be exact.
Where the answer goes
Ravi posts an agent’s answer only to a chat that is attached to the session running the turn. Which session runs the job depends on how it was created:
When there is no chat to post to, the answer stays in the session and the daemon logs
Response target unresolved — dropping emit. Read the answer with:
--isolated job never posts to a chat, even when it has a reply session: a chat belongs to one session at a time, and the dedicated session is not that session. For background work in a dedicated session, tell the agent in the message how to deliver the result. For example, end the message with Send the summary with: ravi sessions inform <your-chat-session> "<summary>". The session you name then answers in its own chat.
ravi cron add has no reply-session flag. A job the agent creates records the agent’s session as its reply session; a job created from your terminal has none. To move an existing default (non-isolated) job’s answers to another chat, set its reply session to the session of that chat. The job then runs in that session:
--account (or ravi cron set <id> account <name>) only replaces the account on the routing source when there is one. It does not create routing on its own, and the answer still goes to the chat attached to the session. ravi cron show <id> shows the result under Routing.
What the agent receives
The message is prefixed with the job name and the current date and time:dd/mm/yyyy, HH:MM in São Paulo time, whatever --tz says.
Shell jobs
Use a shell job for deterministic work (ETL, sync, scraping, scripts) that does not need an agent to think:--shelland--execare the same flag. They cannot be combined with--message,--isolatedor--account.- The command runs through the system shell with the daemon’s environment and working directory. Use absolute paths.
--timeouttakes seconds (60) or a duration (30s,5m,1h). The default is 10 minutes.--env-filereadsKEY=valuelines (blank lines,#comments and a leadingexportare allowed) and adds them to the command’s environment.- Output goes to the daemon log, not to a chat.
- On success the run records
ok, the exit code and the duration. Nothing is sent to any agent. - On failure (non-zero exit or timeout) the run records
error,lastErrorwith stderr, andlastExitCode. With--on-error notify-session:<session>, that session receives a[System] Inform:message with the command, exit code, stderr and stdout, and its agent decides what to do. - A one-shot (
--ator--delete-after) shell job is deleted only after a successful run. - Only you can own a shell job: it is refused from anyone else’s turn, and a shell job someone else owns does not run (see Who the job runs as).
Run status
ravi cron show <id> and ravi cron list --json show nextRunAt, lastRunAt, lastStatus (ok or error), lastDurationMs, lastError and, for shell jobs, lastExitCode.
For agent jobs, the status comes from the agent turn, not from sending the prompt:
- When the prompt is sent,
lastRunAtandnextRunAtmove forward and the outcome fields are cleared. A run in progress shows no status. - When the turn completes, the status becomes
ok. When it fails or is interrupted, it becomeserrorwith the runtime error inlastError. - If the prompt cannot be sent at all, the job is marked
errorright away.
--at or --delete-after) are deleted as soon as their prompt is sent, so they never show an outcome. Use the daemon log or ravi sessions read to see how they went.
Manage jobs
ravi cron set <id> <key> <value>:
For keys that can be empty (
tz, agent, account, description, reply-session, timeout, env-file, on-error), pass - to clear the value.
Scripts and other agents that create jobs on retries should pass --idempotency-key <key>. A second add with the same key returns the first job (status deduplicated) instead of creating another one. The same key cannot be reused for a different job.
Troubleshooting
The job never fires
The job never fires
ravi cron show <id>: checkEnabled: yesandNext run.ravi daemon status: the daemon must be running.ravi daemon logs -f: look forcron:runnerentries.ravi cron run <id> --executeruns it once to separate a schedule problem from a job problem.
It runs at the wrong time
It runs at the wrong time
Check the timezone in the
Schedule line of ravi cron show <id>. Without --tz, the job uses defaultTimezone (ravi settings get defaultTimezone) or the daemon machine’s timezone. For --at, add an explicit offset.It ran but nothing showed up in the chat
It ran but nothing showed up in the chat
ravi cron show <id>: readSession,Reply sessionandRouting.ravi sessions read <session-name>: the answer may be in the session.ravi daemon logs -f:Response target unresolved — dropping emitmeans the session that ran the turn has no chat to post to. See Where the answer goes.- If the agent answered
@@SILENT@@, nothing is posted by design.
It shows ok, but Bases or a connector refused it
It shows ok, but Bases or a connector refused it
ravi cron show <id> --json: readownerPrincipalandrunsAsOperator.ACTOR_NOT_LINKEDwithlinkFlow: routine: the job runs as a contact who has not linked their Ravi account. They link it by messaging the agent in private; the job then acts as them.ACTOR_UNKNOWNwithrefusalReason: routine_not_operator_owned: no person owns the job. Read its message or command and recreate it from the turn of the person it should act for.CONNECTOR_SPEAKER_NOT_OWNERorKNOWLEDGE_TURN_NOT_OPERATOR: those are your own accounts, so only a job that runs as you can use them; recreate it from the terminal or your own chat.- For a recurring job, run
ravi cron run <id> --executeonce to check it.
A one-shot job shows (expired) in ravi cron list
A one-shot job shows (expired) in ravi cron list
Its time has passed without a successful run (the run failed, or the time was already past when you added it), and it will not run again. Delete it with
ravi cron rm <id> --execute.