dabadataDocs
Concepts / Scheduled actions

Concepts

Scheduled actions

A scheduled action is one of your actions run by dabadata on a timetable: reset a daily quota at midnight, write a daily row that a webhook turns into a digest, ask a model for a summary every morning. No cron job, no server of your own. Only your declared action runs - its fixed steps, under its own allow rule - and every run is in the trace.

Declaring a schedule

ts
roles: ["admin", "member", "maintenance"],
actions: {
  reset_daily_quota: {
    allow: { role: "maintenance" },          // who may run it: the schedule's role
    params: { limit: { type: "int" } },
    steps: [{ op: "update", table: "quotas", id: "global", values: { used: 0, daily_limit: "$params.limit" } }],
  },
},
schedules: {
  dailyReset: {
    action: "reset_daily_quota",
    cron: "0 0 * * *",       // every day at 00:00 UTC
    as: "maintenance",       // the role the run acts as
    params: { limit: 100 },
  },
},

An action's steps work on rows by id (check, insert, update, delete), and it may end with a model call. A step may also change many rows at once with where, which is what cleanups need (Many rows at once):

ts
expire_old_likes: {
  allow: { role: "maintenance" },
  steps: [{ op: "delete", table: "likes", where: "created_at < $now - 30 days" }],
},
KeyWhat it does
actionA declared action.
cronWhen, in UTC: five fields, minute hour day-of-month month day-of-week. See below.
asA role your definition declares. The run's caller is { "id": "schedule:<name>", "role": "<as>" }, and the action's allow rule is checked against it like for anyone else. In the steps, $user.id is schedule:<name>.
paramsLiteral values for the action's params, checked when the definition is applied. No placeholders.
enabledfalse keeps it declared without running.

Give the schedule a role of its own (here maintenance) and let the action allow only that role: then nobody else can call the action, and the trace says which schedule did what.

Cron expressions

Five fields, always in UTC. Each field is *, a number, a range a-b, a list a,b,c or a step */n (or a-b/n). Day of week is 0-7, where 0 and 7 are Sunday. When both day fields are set, a day matching either runs (as in standard cron).

CronRuns
0 3 * * *every day at 03:00 UTC
0 * * * *every hour, on the hour
*/15 * * * *every 15 minutes (paid plans)
30 8 * * 1-5weekdays at 08:30 UTC
0 0 1 * *the first of every month at midnight UTC

Names (MON, JAN), @daily and seconds are not accepted.

How runs happen

  • The server checks every 15 seconds, so a run starts within about 15 seconds of its time.
  • A run happens once, even with several servers.
  • If the server was down at a run's time, it runs once when it is back - not once per missed time - and the next run is the next time after that.
  • A run is the same as calling POST /v1/actions/<action>: the action's trace rows (op: "action" and one per step) carry source: "schedule", its writes reach live subscribers, and its webhooks fire.
  • A refused or failed run is recorded with its reason (for example the allow rule's explanation) and the schedule goes on at its next time.
  • While a Free project is paused for being idle, its runs are skipped; they do not wake it.

Limits

FreeStarterProScale
Schedules per environment21050100
Shortest interval1 hour1 minute1 minute1 minute

A definition over these is refused when you apply it, for example schedules.cleanup: cron runs every 15 minutes; the free plan allows at most one run per 60 minutes.

On the console and the API

The console's Schedules page lists each schedule with its next run and its last run (status, error, a link to the trace), and Run now for developers and up. With the secret key:

bash
curl https://dabadata.com/v1/project/schedules -H "Authorization: Bearer $DABADATA_SECRET_KEY"
# -> { "schedules": [{ "name", "action", "cron", "as", "params", "enabled", "nextAt", "last": { "at", "status", "error", "traceId", "durationMs" } }] }

curl -X POST https://dabadata.com/v1/project/schedules/nightlyCleanup/run -H "Authorization: Bearer $DABADATA_SECRET_KEY"
# -> { "ok": true, "status": "ok", "error": null, "traceId": 1234, "durationMs": 18 }

Run now runs it once, enabled or not, and leaves the next scheduled time as it was. Need logic an action cannot express? Pair a schedule with a webhook on the rows it writes, or call dabadata from your backend on your own cron.

dabadata documentation, written against API contract v1.