Automate Tasks
Submit a Task on a fixed interval or after each change to a Resource.
Keep derived data up to date without running your own scheduler. An
Automation is a saved Task plus a rule for when to
submit it: every 15 minutes, say, or every time the frames Table changes.
Spiral runs the rule for you, so no notebook, script, cron job, or Studio
window has to stay open.
Prerequisites
- You have the Project
editorrole, and the permissions that the Task needs on its Resources. - You use a token from your own sign-in or from a workload. An Application token cannot create, change, or resume an Automation.
- You can submit the Task manually. An Automation uses the same specification, input, and options.
Choose when it runs
The rule for when an Automation runs is its source. The API calls an
Automation a task trigger (/task-triggers), and a resource-change source
has the type journalEvent; both mean the same thing as the names on this
page.
| Source | source value | Submits a Task |
|---|---|---|
| Interval | {"type": "interval", "interval": {"everySeconds": 900}} | Every interval, from 60 seconds to 365 days. The first interval starts when you save the Automation, unless you set startAtMicros. |
| Resource change | {"type": "journalEvent", "journalEvent": {"branchId": "<branch-id>", "resourceId": "<resource-id>", "eventType": "resource_updated"}} | After each commit on the branch that changes the live version of the Resource. |
A resource-change Automation can watch one Resource and act on another. For
example, refresh the frame-search Vector Index after each change to the
frames Table it indexes. Both Resources must be on the same branch of the
same Project. Fixed times of day and other event types are planned.
Create an Automation in Studio
Open Tasks, then Automations, and select Create automation. Select a Task, then a compatible Resource, then a schedule or an event hook. The form previews the next three runs in your local timezone. A Table's Tasks → Automations pane selects that Table as the target.
Each Automation has a page with its configuration, its controls, and its submission history. Test latest event and Run now match the API operations in Check the Automation. Task pages link back to the Automation that submitted them.
The next sections create the same Automations with the Rust SDK and HTTP.
Create an interval Automation
This Automation maintains the frames Table every 15 minutes. The IDs come
from Find the branch and Table IDs:
definition = TasksTriggerDefinition(
name="Maintain frames",
enabled=True,
source=TasksTriggerSource(interval=TasksTriggerSourceInterval(every_seconds=900)),
action=TasksTriggerAction(
task_spec_id=TasksTaskSpecId(name="spiral.table.maintenance", version=2),
input={
"readTarget": {"type": "latestOnBranch", "branchId": branch_id},
"tableResourceId": table_resource_id,
},
),
)
trigger = client.tasks.create_trigger("demo", definition)
print(f"created {trigger.trigger_id} at revision {trigger.revision}")The response is the Automation. It contains triggerId, revision,
nextFireAtMicros, and the resources that the Task references. Store
triggerId and revision to change the Automation later.
To select Worker capacity for each submission, set
"options": {"resourceProfile": "cpu_8"}. Without a profile, each submission
uses the specification's default.
To spread daily Automations across the day, set source.interval.everySeconds to
86400 and give each Automation a different source.interval.startAtMicros.
This optional field is a non-negative Unix timestamp in microseconds. A
future start is the first scheduled submission. If the start has elapsed,
saving selects the next strictly future occurrence at that interval from
the start. Saving or resuming with the same start preserves those scheduled
times. Without this field, the first submission remains one interval after
save. Intervals use elapsed seconds, so local clock times shift at daylight
saving changes.
Create a resource-change Automation
This Automation maintains the frames Table after each change to it:
curl -X POST https://api.spiraldb.dev/api/tasks/projects/demo/task-triggers \
-H "Authorization: Bearer $SPIRAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Maintain frames after each change",
"enabled": true,
"source": {
"type": "journalEvent",
"journalEvent": {
"branchId": "'"$BRANCH_ID"'",
"resourceId": "'"$TABLE_RESOURCE_ID"'",
"eventType": "resource_updated"
}
},
"action": {
"taskSpecId": {"name": "spiral.table.maintenance", "version": 2},
"input": {
"readTarget": {"type": "latestOnBranch", "branchId": "'"$BRANCH_ID"'"},
"tableResourceId": "'"$TABLE_RESOURCE_ID"'"
},
"options": {"resourceProfile": "cpu_8"}
}
}'This does not loop forever, even though maintenance itself changes the Table. Every commit that changes the live Table version counts, including maintenance, schema changes, and branch merges, so a maintenance run can cause one follow-up Task. When no work remains, that Task does not change the Table, and the sequence stops. The Automation reacts only to commits made after you create it.
Event sources need read permission on the watched Resource:
location:read for a Location, vector-index:read for a Vector Index, and
table:read for other Resources.
Check the Automation
Evaluate the latest event of the source without a submission. The response
says whether the event matches:
curl -X POST https://api.spiraldb.dev/api/tasks/projects/demo/task-triggers/$TRIGGER_ID/test \
-H "Authorization: Bearer $SPIRAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'Submit the Task once now, without a change to the schedule or the event
boundary. requestId is a UUID that you generate. A retry with the same
requestId does not submit again:
curl -X POST https://api.spiraldb.dev/api/tasks/projects/demo/task-triggers/$TRIGGER_ID/run \
-H "Authorization: Bearer $SPIRAL_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"requestId\": \"$(uuidgen)\"}"List the Automation's submissions, newest first. Each invocation has a
status of accepted or skipped, the taskId of an accepted Task, and the
occurrence that caused it:
curl https://api.spiraldb.dev/api/tasks/projects/demo/task-triggers/$TRIGGER_ID/invocations \
-H "Authorization: Bearer $SPIRAL_TOKEN"Read the Automation itself with
GET /api/tasks/projects/demo/task-triggers/$TRIGGER_ID. If error is set, an access
or input error blocks the Automation until you repair it.
Pause, change, or delete an Automation
Send the complete new definition with the revision that you read last,
here in $REVISION. Set enabled to false to pause, and to true to
resume:
curl -X PUT https://api.spiraldb.dev/api/tasks/projects/demo/task-triggers/$TRIGGER_ID \
-H "Authorization: Bearer $SPIRAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"expectedRevision": "'"$REVISION"'",
"definition": {
"name": "Maintain frames",
"enabled": false,
"source": {"type": "interval", "interval": {"everySeconds": 900}},
"action": {
"taskSpecId": {"name": "spiral.table.maintenance", "version": 2},
"input": {
"readTarget": {"type": "latestOnBranch", "branchId": "'"$BRANCH_ID"'"},
"tableResourceId": "'"$TABLE_RESOURCE_ID"'"
},
"options": {}
}
}
}'If another change happened after your read, the request fails with a conflict. Read the Automation again, then retry.
A pause stops new submissions. Tasks that already started continue. A resume
or an edit starts a fresh boundary: events and times before it do not replay.
An interval with startAtMicros resumes at its next future occurrence;
an interval without it restarts from the time of save.
Delete an Automation with DELETE /api/tasks/projects/demo/task-triggers/$TRIGGER_ID.
Deletion keeps the history of accepted and skipped submissions.
How submissions behave
- After downtime, an interval Automation submits once for the latest missed time.
- Automatic submissions, manual submissions, and Run now all follow the
specification's overlap policy.
When the policy has no room for another Task, the submission shows as
skipped. - Each Task reads the current state of its Resources when its Worker starts.
- The Automation stores its creator for audit only. Workers run the Tasks with their own identity and permissions. If the creator loses access, the Automation continues until an authorized principal pauses or deletes it.
- Creating, changing, and resuming an Automation check the caller's permissions, and Run now checks the caller's permission to submit the Task. Anyone who can cancel Tasks can pause or delete an Automation, so they can always stop one.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
Tasks:InvalidArgument for the interval | everySeconds is outside 60 to 31,536,000. | Select a value in the range. |
Tasks:Conflict on update | The Automation changed after your read. | Read it again, then send the new revision. |
Default:PermissionDenied | You lack a permission on the source or the Task Resources, or you used an Application token. | Use a user or workload token with the required roles. |
The Automation shows an error | An access or input error blocks it. | Fix the cause, then save the Automation again. |
Where next: inspect the submitted Tasks.