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
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):
expire_old_likes: {
allow: { role: "maintenance" },
steps: [{ op: "delete", table: "likes", where: "created_at < $now - 30 days" }],
},| Key | What it does |
|---|---|
action | A declared action. |
cron | When, in UTC: five fields, minute hour day-of-month month day-of-week. See below. |
as | A 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>. |
params | Literal values for the action's params, checked when the definition is applied. No placeholders. |
enabled | false 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).
| Cron | Runs |
|---|---|
0 3 * * * | every day at 03:00 UTC |
0 * * * * | every hour, on the hour |
*/15 * * * * | every 15 minutes (paid plans) |
30 8 * * 1-5 | weekdays 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) carrysource: "schedule", its writes reach live subscribers, and its webhooks fire. - A refused or failed run is recorded with its reason (for example the
allowrule'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
| Free | Starter | Pro | Scale | |
|---|---|---|---|---|
| Schedules per environment | 2 | 10 | 50 | 100 |
| Shortest interval | 1 hour | 1 minute | 1 minute | 1 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:
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.