Triggered Workflows API¶
Note
This API is available with the Robusta SaaS platform and self-hosted commercial plans. It is not available in the open-source version.
Use this API to keep your Triggered Workflows in git and have a CI job create, update and delete them on the Robusta platform.
The API has one endpoint with two methods:
GET returns every Triggered Workflow of the account.
POST syncs a list of workflows to the account. It creates the ones that are new, updates the ones that exist, and with
prune=truedeletes the ones that are not in the list.
The output of GET is a valid POST body as it is. You can save it to a file, edit it and post it back.
Prerequisites¶
An API key with the Triggered Workflows permission
Create one under Settings → API Keys → New API Key:
Triggered Workflows: Read is required for
GETTriggered Workflows: Write is required for
POST
Only an account admin can create an API key. Workflows the API creates are owned by the user who created the key.
Your account ID
Find it in the Robusta UI under Settings → General.
Warning
The key manages every Triggered Workflow of the account, including workflows made in the UI and private ones. Treat it like an admin credential.
Authentication¶
Send the API key as a Bearer token:
Authorization: Bearer YOUR_API_KEY
Endpoint¶
GET https://api.robusta.dev/api/triggered-workflows?account_id=YOUR_ACCOUNT_ID
POST https://api.robusta.dev/api/triggered-workflows?account_id=YOUR_ACCOUNT_ID[&prune=true]
Parameter |
Type |
Description |
Required |
|---|---|---|---|
|
uuid |
Your account ID. The API key must belong to this account. |
Yes |
|
boolean |
|
No |
Choose your region (US, EU or AP) in any example box on this page to switch every URL to it. On a self-hosted platform, replace https://api.robusta.dev with your relay URL.
Workflow Fields¶
The body of POST and the response of GET are both {"workflows": [ ... ]}, a list of entries with these fields:
Field |
Type |
Description |
Required |
|---|---|---|---|
|
uuid |
The workflow's ID. You choose it (for example with |
Yes |
|
string |
The workflow's name in the UI. |
Yes |
|
string |
What Holmes should do with each event the workflow receives. |
Yes |
|
string |
Extra instructions added to Holmes's system prompt for this workflow. |
No |
|
string |
The cluster Holmes investigates in. |
Yes |
|
string |
The model Holmes uses, as listed by the LLM Models API. When absent, the account's default model is used. |
No |
|
boolean |
Whether the workflow runs. A disabled workflow skips the events it receives. Default |
No |
|
list |
Up to 32 |
No |
|
boolean |
Create incidents from this workflow's investigations. Default |
No |
|
integer |
The most Holmes investigations the workflow starts in any 24 hours. Events beyond it are skipped. Must be greater than 0. When absent, there is no limit (the UI suggests 20 when you create a workflow there). |
No |
Any other field, such as a typo or access_mode, is rejected with a 400.
In a GET response every field is present, with null for an optional field that is not set.
Example:
{
"workflows": [
{
"id": "6f1c9a2e-3b7d-4e8f-9a0b-1c2d3e4f5a6b",
"title": "Pod crash loop triage",
"prompt": "Investigate the crashing pod in the alert. Find the root cause and suggest a fix.",
"additional_system_prompt": null,
"cluster_name": "prod-us-east-1",
"llm": null,
"enabled": true,
"filters": [
{"key": "alertname", "value_regex": "^KubePodCrashLooping$"},
{"key": "severity", "value_regex": "^(critical|high)$"}
],
"create_incident": true,
"daily_limit": 50
}
]
}
List Workflows (GET)¶
Returns every Triggered Workflow of the account, deleted ones excluded, ordered by id.
Request:
curl --fail-with-body -sS \
'https://api.robusta.dev/api/triggered-workflows?account_id=YOUR_ACCOUNT_ID' \
-H 'Authorization: Bearer YOUR_API_KEY'
To save the response to a file you can edit and post back, add -o workflows.json.
Response (200 OK): {"workflows": [ ... ]}, as in the example above.
The response never leaves a workflow out. If a workflow stored in the account cannot be written as a POST entry (for example it has no title or cluster, or a filter that does not compile), GET answers 409 and names every such workflow and field. Fix or delete those workflows in the UI and call GET again. A list with workflows missing would make a later POST ?prune=true delete them.
{
"msg": "1 of 12 workflows cannot be expressed as a POST entry; fix or delete them in the UI first",
"error_code": 4000,
"errors": [
{"id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d", "field": "cluster_name", "msg": "Field required"}
]
}
Sync Workflows (POST)¶
Sends the full list of workflows. For each entry:
a new
idcreates the workflow;an existing
idupdates it, replacing every field with the entry's value. A field left out of the entry is cleared or reset to its default;the
idof a deleted workflow restores it (see Pruning).
The whole sync runs in one transaction: either every change is applied or none is. A retry of a successful sync changes nothing more.
Request:
Save the workflows in a JSON file, for example workflows.json, in the shape shown under Workflow Fields. Then, from the folder that holds the file, run:
curl --fail-with-body -sS -X POST \
'https://api.robusta.dev/api/triggered-workflows?account_id=YOUR_ACCOUNT_ID' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data @workflows.json
--data @workflows.json tells curl to send the contents of workflows.json as the request body. The path is relative to the folder you run curl from; for a file elsewhere, write its path after the @, such as --data @robusta/workflows.json.
Response (200 OK):
{
"created": ["c3a8e0f2-51d4-4b9a-a7e1-6d2f8b4c9e13"],
"updated": ["6f1c9a2e-3b7d-4e8f-9a0b-1c2d3e4f5a6b"],
"deleted": [],
"warnings": [
"workflow c3a8e0f2-51d4-4b9a-a7e1-6d2f8b4c9e13: cluster 'staging' is not connected to this account"
]
}
created,updated,deleted: the IDs of the workflows the sync created (or restored), updated and deleted.warnings: one line per workflow whosecluster_nameis not connected to the account right now. A warning never fails the sync, so you can sync a workflow before its cluster comes up, but check the name for typos.
Workflows the API creates can be seen and edited by every member of the account. The API never changes who can see an existing workflow: a private workflow made in the UI stays private when a sync updates it.
Validation: the whole body is checked before anything is written. If any entry is invalid, the response is 400 with every error of every entry, and nothing changes:
{
"msg": "2 of 42 workflows are invalid",
"error_code": 4000,
"errors": [
{"id": "6f1c9a2e-3b7d-4e8f-9a0b-1c2d3e4f5a6b", "field": "filters[1].value_regex", "msg": "regex does not compile: missing ), unterminated subpattern at position 0"},
{"id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d", "field": "daily_limit", "msg": "Input should be greater than 0"}
]
}
id is the entry's id as sent, or workflows[<index>] when the entry has none.
The rules: every id is a valid, unique uuid; title, prompt and cluster_name are not empty; each filter's key is not empty and its value_regex compiles; at most 32 filters; daily_limit is greater than 0; no unknown fields. A private workflow cannot create incidents, so setting create_incident: true on a private workflow made in the UI also answers 400.
The body can be up to 5 MiB.
Pruning¶
Add prune=true to make the body the complete list of the account's workflows:
curl --fail-with-body -sS -X POST \
'https://api.robusta.dev/api/triggered-workflows?account_id=YOUR_ACCOUNT_ID&prune=true' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data @workflows.json
Warning
prune=true deletes every workflow of the account that is not in the body, including workflows made in the UI and private ones.
An empty list,
{"workflows": []}, deletes all of the account's workflows.There is no dry run and no confirmation.
A deleted workflow stops running.
Before the first sync with prune=true, export the account with GET and commit that file, so every existing workflow is in it.
A pruned workflow is deleted the same way as Delete workflow in the UI: its past runs and incidents are kept. Listing its id in a later POST restores it, returned in created. So syncing the file from before a prune undoes the prune. This also restores workflows deleted in the UI.
Example: Workflows in Git¶
Keep the workflows in a JSON file in your repository, for example robusta/workflows.json:
{
"workflows": [
{
"id": "6f1c9a2e-3b7d-4e8f-9a0b-1c2d3e4f5a6b",
"title": "Pod crash loop triage",
"prompt": "Investigate the crashing pod in the alert. Find the root cause and suggest a fix.",
"cluster_name": "prod-us-east-1",
"filters": [
{"key": "alertname", "value_regex": "^KubePodCrashLooping$"}
],
"create_incident": true,
"daily_limit": 50
},
{
"id": "c3a8e0f2-51d4-4b9a-a7e1-6d2f8b4c9e13",
"title": "Failed deployment rollout",
"prompt": "Find why the rollout in the event failed and which change caused it.",
"cluster_name": "prod-us-east-1",
"enabled": false
}
]
}
To start from the workflows you already have, export them into that file:
curl --fail-with-body -sS \
'https://api.robusta.dev/api/triggered-workflows?account_id=YOUR_ACCOUNT_ID' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-o robusta/workflows.json
This is also how to move a workflow you built and tested in the UI into git: export, then commit the file. Its id stays the same.
Then sync the file on every merge to your main branch. A GitHub Actions example:
# .github/workflows/robusta-triggered-workflows.yaml
name: Sync Robusta Triggered Workflows
on:
push:
branches: [main]
paths: [robusta/workflows.json]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Sync
env:
ROBUSTA_API_KEY: ${{ secrets.ROBUSTA_API_KEY }}
ROBUSTA_ACCOUNT_ID: ${{ vars.ROBUSTA_ACCOUNT_ID }}
run: |
curl --fail-with-body -sS -X POST \
"https://api.robusta.dev/api/triggered-workflows?account_id=${ROBUSTA_ACCOUNT_ID}&prune=true" \
-H "Authorization: Bearer ${ROBUSTA_API_KEY}" \
-H "Content-Type: application/json" \
--data @robusta/workflows.json
--fail-with-body makes the job fail on an error and print the response, so a 400 shows every invalid entry in the job log. Remove &prune=true if the file should add and update workflows but never delete the ones made in the UI.
Errors¶
Every error response has this shape. Validation errors (400, and the 409 from GET) also have an errors list.
{
"msg": "Error message",
"error_code": 4000
}
Status |
Meaning |
|---|---|
|
Success. |
|
|
|
The API key is missing, invalid, or does not belong to |
|
The API key does not have the Triggered Workflows permission (Read for |
|
|
|
The body is larger than 5 MiB. |
|
A server error. Retry: repeating a sync is safe. |
See Also¶
LLM Models API: list the models you can use in
llmRBAC Configuration API: manage RBAC from code the same way