Scheduled jobs
A scheduled job is a cron-style recurring trigger. GET /v1/scheduled-jobs returns three kinds mixed into one list: built-in system jobs (maintenance sweeps), built-in agent/discrete jobs, and portal/API-created custom jobs. Only custom jobs (ids of the form custom-<int>) are the ones this namespace can create, update, or delete — the built-in ones are read-only here.
For the wire-level reference, see API → Scheduled jobs.
Auth. Every route in this namespace requires the control plane's
x-catentio-sessionheader, which this SDK never sends. NeitherapiKeymode nor Bearer-session mode authenticates here. See SDKs → Auth: what actually works.
Namespace
catentio.scheduledJobs — four methods:
catentio.scheduledJobs.list()
catentio.scheduledJobs.create(input)
catentio.scheduledJobs.update(id, patch)
catentio.scheduledJobs.delete(id)
No get — list returns enough detail to identify any single job.
Methods
scheduledJobs.list
Signature. catentio.scheduledJobs.list(token?): Promise<{ data: ScheduledJobRow[]; meta: { total: number; heartbeat_total: number; heartbeat_enabled: number } }>
import { CatentioSaasClient } from '@forjio/catentio-saas-node';
const catentio = new CatentioSaasClient({ session });
const { data: jobs } = await catentio.scheduledJobs.list();
for (const j of jobs) {
console.log(j.id, j.kind, j.agent, j.schedule, j.enabled, j.next_run_at);
}
Real row shape (built-in and custom jobs share these fields; custom jobs carry a few extra):
interface ScheduledJobRow {
id: string; // 'graphmem_sweep' (system/agent) or 'custom-42' (custom) — never 'sj_...'
kind: 'system' | 'agent' | 'custom';
agent: string | null;
schedule: string; // cron string, or a human-readable rendering of the default trigger
enabled: boolean;
next_run_at: string | null;
description: string;
// custom jobs only:
brief?: string;
name?: string;
model?: string; // 'auto' or a pinned model id
reasoning?: boolean | null; // tri-state: true/false pinned, null = auto
}
There is no workspaceId, label (it's name, custom-only), cron as a list-row field (it's schedule), timezone, prompt (it's brief, custom-only), workMode, metadata, lastRunAt, createdAt, or updatedAt — none of those exist.
scheduledJobs.create
Signature. catentio.scheduledJobs.create(input, token?): Promise<CustomJobCreated>
Real required fields: name (1–120 chars), cron, agent_slug, brief. Optional: enabled (default true), model (default "auto"), reasoning (default null/auto). There is no label, agentSlug (camelCase), prompt, timezone, workMode, or metadata field — those don't exist.
const job = await catentio.scheduledJobs.create({
name: 'Daily project board summary',
cron: '0 9 * * 1-5',
agent_slug: 'hachimi',
brief: 'Summarise the project board into three bullets and post to Discord #standup.',
});
Subject to a scheduled_jobs tier cap; a duplicate name gets 409.
scheduledJobs.update
Signature. catentio.scheduledJobs.update(id, patch, token?): Promise<CustomJob>
Real patchable fields: enabled, cron, brief, name, model, reasoning. At least one is required. There is no agentSlug, timezone, workMode, or metadata — the agent a job runs cannot be changed via update.
// Pause without deleting
await catentio.scheduledJobs.update('custom-42', { enabled: false });
// Re-enable with a new brief
await catentio.scheduledJobs.update('custom-42', {
enabled: true,
brief: 'Summarise the project board AND tag any pawpado-related work.',
});
scheduledJobs.delete
Signature. catentio.scheduledJobs.delete(id, token?): Promise<void>
await catentio.scheduledJobs.delete('custom-42');
Common patterns
"Pause everything overnight" (custom jobs only)
const { data: jobs } = await catentio.scheduledJobs.list();
const customEnabled = jobs.filter(j => j.kind === 'custom' && j.enabled);
await Promise.all(customEnabled.map(j => catentio.scheduledJobs.update(j.id, { enabled: false })));
// ...in the morning:
await Promise.all(customEnabled.map(j => catentio.scheduledJobs.update(j.id, { enabled: true })));
Mirror your scheduler from a config file
import schedule from './schedule.json'; // [{ name, cron, agent_slug, brief }, ...]
const { data: existing } = await catentio.scheduledJobs.list();
const byName = new Map(existing.filter(j => j.kind === 'custom').map(j => [j.name, j]));
for (const def of schedule) {
const found = byName.get(def.name);
if (found) {
await catentio.scheduledJobs.update(found.id, def);
} else {
await catentio.scheduledJobs.create(def);
}
}
A scheduled job's runs are ordinary runs, so an outbound webhook on run.succeeded / run.failed will fire for them — see Webhooks (outbound). There is no job-level completion event. To see what a job produced, filter runs.list by agent and transport (scheduled ticks show up as regular runs; there is no dedicated jobId field on a run row to filter by directly).
Errors
The real error body is FastAPI's {"detail": ...}. Because the wire response never has an error key, the SDK doesn't parse out a structured code/message — you get CatentioError with a generic HTTP_ERROR code and the HTTP reason phrase as the message.
| Status | Cause |
|---|---|
| 401 | Missing/invalid session — this SDK never sends x-catentio-session. |
| 400 | create()/update() missing a required field, or bad cron syntax. |
| 403 | Past the scheduled_jobs tier cap on create(). |
| 404 | update()/delete() on a job id that doesn't exist. |
| 409 | create() with a name that's already in use. |
Next
- Runs — each scheduled tick spawns one.
- API → Scheduled jobs — HTTP-level reference, including the built-in system/agent job catalogue.