Skip to content

Jobs

An agent can schedule future turns for itself. When a job fires, Alfe sends the prompt to the agent as a new turn and posts the reply to the owner’s direct chat. See Scheduled jobs for how jobs work, their schedules, and who a job speaks as.

All examples use the @alfe.ai/agent-api-client. Every call acts on the calling agent’s own jobs, and another agent’s job comes back as not found.

jobs.ts
const job = await client.createJob({
name: "Order monitor",
prompt: "Check order 1234 and report any status change.",
schedule: { kind: "cron", expr: "0 7 * * 1-5" },
timezone: "Australia/Sydney",
staleAfterMinutes: 30, // optional, 1 to 1440, default 15
});
// POST /agent/jobs

The result includes jobId, status, ownerUserId, and nextRuns, the next fire times in UTC.

The schedule takes one of three shapes:

Shape Meaning
{ kind: "at", at } Once, at an ISO-8601 time. Without an offset, at is read in timezone.
{ kind: "every", minutes } A fixed interval of at least 5 minutes.
{ kind: "cron", expr } A standard 5-field cron, read in timezone.

timezone is an IANA name. You can leave it out only if the owner’s identity has a timezone.

The client never retries createJob, because a retry could create a duplicate.

const jobs = await client.listJobs();
// GET /agent/jobs
const job = await client.getJob(jobId);
// GET /agent/jobs/{jobId}

listJobs leaves out each job’s prompt. getJob includes it. Both include jobs a person created on this agent, which the agent can read but not change.

Send only the fields you want to change. enabled: false pauses the job, and enabled: true resumes it.

await client.updateJob(jobId, { enabled: false });
// PATCH /agent/jobs/{jobId}

updateJob accepts name, prompt, schedule, timezone, staleAfterMinutes, and enabled. To resume a completed one-time job, send a new schedule too.

await client.deleteJob(jobId);
// DELETE /agent/jobs/{jobId}

Run a job once, outside its schedule. You can do this once a minute per job. The client never retries it.

const run = await client.runJobNow(jobId);
// POST /agent/jobs/{jobId}/run
const runs = await client.listJobRuns(jobId);
// GET /agent/jobs/{jobId}/runs

Each run has a status and, when relevant, a reason.

status Meaning
claimed, dispatched The run is underway.
queued_offline The agent is offline, and Alfe is holding the run.
ok The turn finished.
error The run failed. See reason, such as turn_failed or fire_failed.
aborted Someone stopped it from the conversation.
skipped The run didn’t happen. See reason, such as overlap or insufficient_credits.

Use scheduledAt for the run’s time. Don’t parse a time out of runKey.

Thrown errors carry a status and a machine-readable code.

code Status Meaning
job_limit_reached 409 Too many active jobs on this agent or organization.
interval_too_short 400 Runs would be less than 5 minutes apart.
invalid_schedule 400 The schedule isn’t valid, or a one-time job is out of range.
invalid_timezone 400 No usable IANA timezone.
job_create_rate_limited 429 The agent has created its limit of jobs for today.
rate_limited 429 Run now was called too soon.
job_not_found 404 No such job on this agent.
data_boundary 409 Jobs aren’t available under this organization’s data boundary.

Two more refusals have no code:

  • A 403 means a person created the job, so the agent can only read it.
  • A 409 on create means an organization agent has no recorded creator. Someone must create the job in the dashboard.

The client exports JOB_LIMITS with the limits the service enforces, so you can validate before calling. The values are listed in Scheduled jobs.

import { JOB_LIMITS } from "@alfe.ai/agent-api-client";