Skip to main content

The automations API

Automation rules through the REST API: what Platform → Automations shows, for scripts and AI assistants that set the desk up. How rules behave, in the app's words, is in the automations guide; this page is the reference for the API.

Everything is under /api/v1/rules. A person needs Manage automations (rule.settings) in the organization; a token needs rule.settings itself (or admin), and its owner must hold it.

Endpoints​

EndpointWhat
GET /api/v1/rulesEvery rule you may manage, in their order: the rule with unit ({ id, name }), runs7d (how often it ran in the last 7 days) and lastRunAt.
GET /api/v1/rules/referencesThe names of the people, teams, accounts and forms the rules point at: { users, teams, accounts, forms }, each [{ id, name }].
GET /api/v1/rules/<id>One rule, with its last 20 runs: runs (on cases: { at, actions, case: { ref, subject } }) or recordRuns (on a plugin's records: { at, actions, record: { key, title, href } }). actions says in words what each action did, or why it was skipped; steps is each step as it ran, in order: { stepId, kind, outcome, text, ms } (outcome: done, nothing (nothing to change), failed, then or else for an If, stopped).
POST /api/v1/rulesA new rule (the body below), placed last in its organization.
PATCH /api/v1/rules/<id>Change any part; the rule is checked again as a whole.
DELETE /api/v1/rules/<id>Delete it. Its history goes with it.
POST /api/v1/rules/reorder{ ids }: the new order of the rules of one organization.
POST /api/v1/rules/flowsA new flow: { name, description?, unitId, subject, trigger }. It starts off, with an empty draft.
PUT /api/v1/rules/<id>/draft{ flow }: save the draft (see Flows). Any rule can have one; a simple rule keeps running as it is until the draft is published.
DELETE /api/v1/rules/<id>/draftDiscard the draft.
POST /api/v1/rules/<id>/publish{ note? }: check the draft and make it the version that runs. The first version turns the flow on. A problem answers 400 with the step in details[].path (when, or steps.<step id>).
GET /api/v1/rules/<id>/versionsThe published versions, newest first: { versions: [{ version, note, publishedAt, publishedBy }] }.
GET /api/v1/rules/<id>/versions/<n>One version with its flow.
POST /api/v1/rules/testA dry run: { unitId, subject, trigger, formId, matchAll, conditions }. Nothing is changed. On cases it checks the latest 200 (for submitted, those from the form) and answers { checked, matched, cases }, with up to 25 matches; on a plugin's records, the open ones (up to 500), as records.

Creating, changing, publishing and deleting a rule are in the audit log (rule.created, rule.updated, rule.published, rule.deleted).

A rule​

{
"name": "Laptop requests to IT",
"description": "Portal requests for a new laptop go to the IT team.",
"unitId": "<unit id>",
"subject": "case",
"trigger": "created",
"matchAll": true,
"conditions": [{ "field": "form", "op": "is", "value": "<form id>" }],
"actions": [
{ "type": "assign_team", "value": "<team id>" },
{ "type": "set_priority", "value": "high" }
],
"stopProcessing": false,
"enabled": true,
"runAs": "system"
}
FieldWhat
name, descriptionUp to 100 and 500 characters.
unitIdWhere it applies: the unit and everything below it. For submitted, the form's unit is used instead.
subjectWhat it runs on: case (the default), or a plugin's records, like changes.change or problems.problem.
triggerWhen it runs (below).
formIdFor submitted: the form whose cases it runs on.
matchAlltrue: every condition must hold; false: any one.
conditionsUp to 20: { field, op, value, key }. key names a form field (field) or a data property (case_prop, account_prop, contact_prop).
actions1 to 10: { type, value, target, key, fields }.
stopProcessingLater rules skip what this rule acted on.
enabledOff rules are kept and don't run.
runAs, runAsIdWho it creates records as: system (the default) or person, with the person's id (below).

Rules run in their order across the organization (reorder), each at most once per event.

A rule that doesn't hold together answers 400 validation_failed, with details naming the part and what is wrong: [{ "path": "actions.0.value", "message": "Pick a team" }]. Values must exist in the rule's organization (a unit, a person working there, a team, an account, a form), and a unit picked must be the rule's or one below it.

Flows​

A rule of kind flow is built as steps. Its flow is the version that runs, version its number, and draft the changes not yet published. The trigger and the conditions under when are copied to the rule's trigger, matchAll and conditions when it is published, and its actions (from every branch) to actions. On a flow, PATCH changes the name, description, unit, who it runs as and whether it is on; its trigger, conditions and steps change only through the draft (400 use_draft otherwise).

{
"trigger": "created",
"when": { "matchAll": true, "conditions": [] },
"steps": [
{ "id": "s1", "kind": "if", "matchAll": true,
"conditions": [{ "field": "priority", "op": "is", "value": "urgent" }],
"then": [{ "id": "s2", "kind": "action", "action": { "type": "add_labels", "value": "outage" }, "onError": "stop" }],
"else": [] },
{ "id": "s3", "kind": "action", "action": { "type": "assign_team", "value": "<team id>" } },
{ "id": "s4", "kind": "stop" }
]
}

A step is an action (any action a rule has; onError continue or stop), an if with its own conditions and two branches (then, else), or stop. Each step has its own id (letters, digits, - and _). A flow has at most 60 steps, and Ifs go at most 5 deep. Publish checks the trigger and its conditions, then each step on its own, as a rule is checked.

Triggers​

TriggerOnWhen
createdCases, plugins' recordsA new case (from any channel), or a plugin's record recorded.
submittedCasesA case from the form formId came in, wherever it was placed. Its answers are placeholders.
customer_repliedCasesThe requester wrote.
changedPlugins' recordsThe record moved to another status.
scheduledBothEvery 5 minutes, on open cases (or a plugin's open records). Needs a time condition; time conditions work only here. Each case acts once per situation, not every 5 minutes.

A case from a form runs both the created rules of its unit and the submitted rules of its form, in the rules' order.

Conditions on cases​

fieldopvalue
unitin, not_inA unit id (that unit and below).
sourceis, is_notform, portal, agent, api, email, import or automation.
formis, is_notA form id.
priorityis, is_not, at_leastlow, normal, high, urgent.
statusis, is_notnew, open, pending, on_hold, resolved, closed.
teamis, is_not, noneA team id.
labelshas, not_hasA label.
assigneenone, any, isA person's id, for is.
emaildomain, isThe requester's domain or address.
localeisen, no, sv or da.
accountis, is_not, any, noneAn account id.
account_labelshas, not_hasA label.
subject, textcontains_any, contains_noneWords, separated by commas. text is the subject or the messages.
fieldis, is_not, contains, set, emptyWith key: a form field.
case_prop, account_prop, contact_propis, is_not, contains, gt, lt, set, emptyWith key: a data property. gt and lt for numbers and dates.
hours_since_created, hours_waiting, hours_in_statusgtHours. Scheduled rules only.
sla_duewithin (minutes), overdueThe next service-level target. Scheduled rules only.

Plugins that are on add their own, named <plugin>.<name>: Service Configuration's itsm.service (is, is_not, under, any, none, a service id) and Problem Management's problems.linked (is, any, none, a problem id). GET /api/v1/plugins lists every plugin's conditions, actions and subjects under rules, with their operators, choices and the endpoints that look up their values (lookup, such as /api/v1/x/itsm/services/pick). A saved condition of a plugin that is off never matches, and its actions are skipped.

Actions on cases​

typevalue
set_prioritylow, normal, high, urgent.
set_statusopen, pending, on_hold, resolved (closing is left to people and auto-close).
add_labels, remove_labelsLabels, separated by commas.
assignA person who can see every case the rule applies to.
assign_round_robin, assign_least_busyA unit (the rule's or below): in turn, or to the agent with fewest open cases.
unassignNone.
assign_team, assign_team_round_robin, assign_team_least_busyA team: its queue, or one of its members.
moveA unit (the rule's or below).
noteThe text of an internal note.
set_propertyWith key: a case data property, and its value (several choices separated by commas). Not a unique one.
notifytarget: assignee (or the unit's agents if nobody), unit, or user with a person's id as value.
create_recordtarget and fields (below).

Plugins add itsm.set_service (a service id), itsm.assign_service_team, itsm.notify_service_owner and problems.link (a problem id).

On a plugin's records, the conditions and actions are the subject's own, without the prefix (for changes: type, status, risk, service, owner, implementer, created, updated, starts; notify, set_implementer, note); GET /api/v1/plugins lists them, and changes and problems describes them.

Create a record​

The action create_record makes a new record from what the rule runs on, linked back to it. target is the kind; fields maps each field's id to its value: text with {{placeholders}}, or the id of what is picked.

targetPluginNeedsFields (required in bold)
casecase.createsubject, description, requester (an email address; a new contact for one the desk doesn't know), unit, priority, team, assignee, labels (comma-separated).
incidents.incidentIncident Managementincident.writetitle, summary, impact, urgency (high, medium, low), unit, service, commander (empty: it starts as New).
problems.problemProblem Managementproblem.createtitle, description, impact, urgency, unit, service.
changes.changeChange Managementchange.writetitle, description, service, type (normal, emergency), owner, implementer. A draft.
changes.taskChange Managementchange.writetitle, assignee. Only from a rule on changes.
{
"type": "create_record",
"target": "incidents.incident",
"fields": {
"title": "{{title}}",
"summary": "Reported by {{requester.name}} ({{account.name}}): {{fields.what_happened}}\n\n{{url}}",
"impact": "high",
"urgency": "medium"
}
}

Placeholders, in text fields only:

PlaceholderOnWhat
{{title}}, {{ref}}, {{url}}AnythingThe subject or title, the reference (CASE-1042) or key (CHG-12), and its link.
{{description}}CasesThe request (the first message).
{{priority}}, {{status}}, {{unit}}, {{labels}}CasesAs they are when the rule runs.
{{requester.name}}, {{requester.email}}, {{account.name}}CasesThe requester and their account.
{{fields.<key>}}submitted rulesThe form's answer to that question (lists joined with commas).

A placeholder that doesn't exist for the rule is refused when it is saved ({{fields.…}} only works in a rule that runs when its form is submitted). A required field that comes out empty skips the action.

A case made this way has the source automation, gets its confirmation email and runs its own created rules. The new record names the rule in its history, and the rule's runs say Created incident INC-14.

What stops rules running away​

GuardHow
Not on its own recordsA rule never runs on a record it made itself.
A chain of 3A record made by a rule may make another, and that one a third, but no deeper.
50 an hourOne rule makes at most 50 records an hour.
Once per eventEach rule acts once on each event (a retry doesn't act twice), and a scheduled rule once per situation.

What a guard stops is skipped, and the rule's runs say why: Skipped: an incident (this rule made 50 records in the last hour).

Who a rule acts as​

Rules act as the System user: it can't sign in, isn't listed or picked, and isn't a staff seat. Their changes show on the timeline and in the audit log as made by the rule, through System. As an actor System may do anything inside the rule's organization and nothing outside it; who may write automations is what limits it.

runAs: "person" with runAsId makes a rule's Create a record actions act as that person instead, with what they may do when it runs:

  • the person must work in the organization and hold the kind's permission (incident.write …) in the rule's unit when the rule is saved, and again each time it runs;
  • only someone with user.write for the whole organization (its administrators) picks someone other than themselves (anyone may keep who it was; otherwise 400 on runAsId);
  • when the person is disabled or loses the permission, the action is skipped and the runs say why; it never falls back to System.

The rule's other actions (labels, priority, notes, assigning, notifying) act the same either way.

A form and its automation together​

The guided form builder (Platform → Forms → New form → Guided) saves a form and the rule that goes with it in one request, or neither. The same endpoints are there for scripts:

EndpointWhat
POST /api/v1/forms/guided/checkChecks the body below and saves nothing: { ok, checks, rule }. Each check is { id, level, text }, level being ok, warn or fail; rule is the rule it would write (name, actions), or null.
POST /api/v1/forms/guidedCreates the form, its place on a portal and its rule, in one transaction: { form: { id, publicId, name, enabled }, portalEntry, rule }. A failed check answers 400 with each failure in details (path is the check's id) and saves nothing.
{
"creates": "incidents.incident",
"form": {
"unitId": "<unit id>",
"name": "Report an outage",
"audience": "signed_in",
"askSubject": false,
"fields": [{ "key": "affected", "type": "short_text", "label": { "en": "What is affected?" }, "required": true }]
},
"portal": { "portalId": "<portal id>", "categoryId": "<category id>" },
"record": { "title": "Outage: {{fields.affected}}", "impact": "high", "urgency": "medium" },
"teamId": null,
"notify": "unit",
"live": true
}
  • creates: case (the form only opens cases), or a kind of Create a record that can be made from a case and whose plugin is on: incidents.incident, changes.change, changes.standard.
  • form: what POST /api/v1/forms takes for the unit, name, title, description, icon, fields, askName, askSubject, browserMode, defaultPriority, labels, successMessage and audience (anyone, signed_in or staff). On a portal, browserMode defaults to off and the title to the name.
  • record: the record's fields, as in Create a record; the form's answers are {{fields.<key>}} of its own questions.
  • teamId, notify (none, unit or user with notifyUserId): the rule also puts the case in the team's queue, or tells the unit's agents or a person.
  • ruleName: the rule's name; left out, the form's name and what it does.
  • live: false creates the form turned off.

A rule is written when the form creates a record, routes to a team or tells someone: trigger submitted on this form, in its unit, run as System. Whoever sends it needs form.settings in the form's unit (the endpoints are under Form settings); for a rule, rule.settings in the organization; for a portal, portal.settings on it; and for a record, the permission to create it in the form's unit themselves (incident.write, change.write, and change.template too for a standard change), though the rule then creates as System. A unit or portal of another organization, or one they don't manage, answers 400 (invalid_unit, invalid_portal). The audit log gets form.created, portal.form_added and rule.created.

Examples​

Declare an incident when a portal form for outages is submitted:

curl -X POST "$DESK_URL/api/v1/rules" -H "Authorization: Bearer $DESK_TOKEN" -H "Content-Type: application/json" -d '{
"name": "Outage form declares an incident",
"unitId": "<the form unit>",
"trigger": "submitted",
"formId": "<form id>",
"actions": [
{ "type": "create_record", "target": "incidents.incident",
"fields": { "title": "{{title}}", "summary": "{{fields.details}}\n\nFrom {{ref}}: {{url}}", "impact": "high", "urgency": "high" } }
]
}'

Remind the assignee of cases waiting more than 8 hours:

{
"name": "Waiting too long",
"unitId": "<unit id>",
"trigger": "scheduled",
"conditions": [{ "field": "hours_waiting", "op": "gt", "value": 8 }],
"actions": [{ "type": "notify", "target": "assignee" }]
}

[!TIP] For an AI assistant: find the ids first (describe_desk, list_units, or GET /api/v1/forms and GET /api/v1/teams), try the conditions with POST /api/v1/rules/test, and save the rule with enabled: false until a person has looked at it.