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-session header, which this SDK never sends. Neither apiKey mode 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 getlist 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.