Jobs

⚠️

Jobs are deprecated. Use scheduled flows instead, which provide durable execution, automatic retries per step, and better error handling.

Jobs allow you to execute tasks in the background on a schedule. For new projects, use scheduled flows which offer the same scheduling capabilities with additional benefits:

  • Durable execution — Steps persist and resume on failure
  • Per-step retries — Failed steps retry independently
  • No time limit — Flows can run longer than 15 minutes
  • Better observability — Step-by-step traces in the Console

Migrating to scheduled flows

A scheduled job like this:

job EmailNewCustomerReport {
  @schedule("every weekday at 9am")
}
jobs/emailNewCustomerReport.ts
import { EmailNewCustomerReport, models } from "@teamkeel/sdk";
 
export default EmailNewCustomerReport(async (ctx) => {
  const customers = await models.customer.findMany({
    where: { createdAt: { greaterThan: yesterday() } }
  });
  
  await sendEmail({
    to: "team@company.com",
    subject: "New Customers",
    body: formatReport(customers),
  });
});

Becomes a scheduled flow:

flow EmailNewCustomerReport {
  @schedule("every weekday at 9am")
  @permission(roles: [System])
}
flows/emailNewCustomerReport.ts
import { EmailNewCustomerReport, models } from "@teamkeel/sdk";
 
export default EmailNewCustomerReport(async (ctx) => {
  const customers = await ctx.step("get customers", async () => {
    return await models.customer.findMany({
      where: { createdAt: { greaterThan: yesterday() } }
    });
  });
  
  await ctx.step("send email", async () => {
    await sendEmail({
      to: "team@company.com",
      subject: "New Customers",
      body: formatReport(customers),
    });
  });
});

The key differences:

  • Declare as flow instead of job
  • Add @permission(roles: [System]) for automated runs
  • Wrap work in ctx.step() calls for durability
  • Files go in /flows instead of /jobs

See Scheduled Flows for full documentation.


Legacy documentation

The following documentation is for existing jobs. New projects should use scheduled flows.

Limitations

  • Jobs have 512MB of disk space available
  • Jobs can only run for 15 minutes before being killed and marked as failed

Scheduled jobs

A job can be scheduled using the @schedule attribute. Job schedules are defined the same way as flow schedules.

job EmailNewCustomerReport {
  @schedule("every monday at 9am")
}

Manual jobs

To manually run a job from the console, define a permission rule:

job DoImportantThing {
  @permission(expression: true)
}

You can add @permission to a job that also uses @schedule, allowing both scheduled and manual runs.

Role-based permissions

job BigDataImport {
  @permission(roles: [Developer])
}
 
role Developer {
  emails {
    "sally@corp.com"
    "barry@corp.com"
  }
}

The expression true in a job permission means anyone with access to your project can trigger the job. Jobs are never publicly accessible.

Inputs

Manual jobs can accept inputs displayed as a form in the console:

job RefundOrder {
  inputs {
    orderId ID
  }
  @permission(expression: true)
}
⚠️

Scheduled jobs cannot have inputs.

Testing

Jobs are available in tests via @teamkeel/testing:

jobs/email-new-customer-report.test.ts
import { jobs, actions, resetDatabase } from "@teamkeel/testing";
import { test, expect, beforeEach } from "vitest";
 
beforeEach(resetDatabase);
 
test("EmailNewCustomerReport updates new customer flag", async () => {
  await jobs.emailNewCustomerReport();
 
  const res = await actions.getCustomer();
  expect(res.reportSent).toBeTruthy();
});