Skip to content
SurogateDocs

Actions

An action list is a short program written in the portal’s files. It runs when something happens. For now that is a form being sent (After it is sent), or a logic flow being called; later, endpoints and scheduled jobs run them too. Each action does one thing: works out a value, logs a line, tells the person something, sends them elsewhere.

yaml
actions:
submitted:
- type: set
values:
reference: '"REQ-" + string(now.getFullYear()) + "-" + form.county'
- type: log
level: info
message: '"Request " + vars.reference'
- type: message
text:
ro: "Cererea {{ vars.reference }} a fost primită."
en: "Request {{ vars.reference }} was received."

Every action has a type, and may also have:

  • description: a note for whoever reads the file;
  • condition: an expression that must be true for the action to run;
  • onSuccess: actions run after it succeeds;
  • onError: actions run when it fails; the list then goes on;
  • then: stop: the run ends after this action; then: continue lets a message or redirect go on;
  • errorMessage: what the person is told if the run fails at this action, in each language.

Values are worked out with CEL, the expression language Keel uses too. An expression can read:

NameWhat it holds
formWhat was sent, by field key
submissionid: the submission’s id
userThe person who sent it (id, name, email, roles), or null for a visitor
site, localeThe site and the language
nowWhen the run started
varsThe values earlier actions set
valuesThe portal’s named values, as values.<name>

Whole numbers are integers, the others decimals; the two mix in arithmetic, so form.price * 1.19 works whatever was typed. Reading a field that was not sent is an error; check it first with has(form.phone). A name that is not in the table above is refused when the file is saved. Beside CEL’s own functions there are replace(text, a, b), urlEncode(text), slice(list or text, from, to), sort(list), round, floor and ceil.

TypeWhat it doesTakes
setWorks out values into varsvalues: { name: expression }
groupRuns actions together; its onError catches theirsactions
eachRuns actions for each item of a listlist, as, actions
repeatRuns actions a number of timestimes, as (the count), actions
throwFails with a messagemessage
stopEnds the run
logWrites a line in the run’s loglevel (info or warn), message
waitWaits up to 5 secondsms
parseJsonReads JSON text into a valuefrom, into
toJsonWrites a value as JSON textfrom, into
messageTells the person something, and ends the run; {{ vars.name }} puts in a value an action settext, in each language
redirectSends the person to another page, and ends the runto
callFlowRuns a logic flow and keeps its outputsflow, or contract and pick; inputs, into
callEndpointRuns one of the portal’s endpoints and keeps its answerendpoint, params, okStatus, into
appActionRuns an app’s action and keeps its outputsapp, action, inputs, connector, into
startCase, startProcess, sendMessage, sendSignalStart a case or process in Keel, or send it a message or signalsee Keel
askAgentAsks one of your agents for an answer, a summary or a knowledge-base article, and keeps itagent, task, prompt, into
httpCalls a service through a connectorconnector, method, path, query, headers, body, okStatus, into
emailSends an email once the run ends wellto, subject, text
sendNotificationSends a notification to people of the portal once the run ends wellto, title, body, link
hash, hmac, randomToken, uuidHashes, signatures and random values
listFilesLists a folder’s filesfolder, into
readFileReads a filefolder, name, encoding (text or base64), into
writeFileWrites a filefolder, name, content, encoding
deleteFileDeletes a filefolder, name

A redirect goes to a page of the same site, or to another https site the form names under redirects.

askAgent asks one of the agents in agents.yaml (an agent with its key) and keeps the answer. Only a job asks an agent: an agent’s answer takes longer than a form, an endpoint or a flow waits. To summarize what a form receives, have the form create a record, and ask the agent from a job that starts when that record is created:

yaml
trigger:
record:
model: requests
on: [created]
site: main
runAs: user:[email protected]
actions:
- type: askAgent
agent: helper
task: summary
prompt: '"Request: " + string(trigger.after.subject) + ". " + string(trigger.after.description)'
into: summary
- type: updateRecord
model: requests
id: trigger.id
values: { summary: vars.summary.text }
  • task: chat (when left out) answers the prompt; summary summarizes it; article writes a knowledge-base article from it, with a title and sections. The answer comes in the prompt’s language.
  • prompt: an expression giving the words, up to 50,000 characters.
  • into: where the answer is kept, as { text }.

Each ask is a conversation of its own, and its use is billed to the agent’s project. A run asks agents at most 5 times. It waits at most 90 seconds for an answer, and stops waiting ten seconds before the run’s own time is up, so that an onError can still run. An answer is at most 256 KB. When the agent cannot answer, or stops to ask a question of its own, the run fails there, and an onError sees why. A failed run is not run again.

The file actions work on a folder of the portal’s files, one written in content/folders/.

  • Who may do what:
    • Someone who edits the folder does what they could under Files in the console.
    • Anyone else, a visitor too, may only make a new file, and never in a public folder. A file of the same name already there makes the action fail.
  • Names: a file’s name is lower-case letters, digits, dots, - and _, with an extension the portal keeps.
  • Limits:
    • a file is at most 700 KB;
    • a run reads at most 50 times and changes files at most 20 times;
    • a listing gives at most 1,000 names.
yaml
- type: writeFile
folder: requests
name: '"request-" + submission.id + ".txt"'
content: '"From " + form.name + ": " + form.message'

A file written stays written, even if a later action fails.

These actions work on the records of a data model. They act as the person whose run it is, a visitor too, with the model’s permissions.

TypeWhat it doesTakes
createRecordAdds a recordmodel, values (field: expression), into
updateRecordChanges the fields givenmodel, id, values
deleteRecordDeletes a recordmodel, id
getRecordReads one recordmodel, id, into
findRecordsFinds records by filterable fieldsmodel, where (field: expression), sort, limit (at most 100), into

Each change is made at once: when a later action fails, what was changed stays. A run makes at most 20 changes and 50 reads. A named value never reaches records. A flow’s test uses its own records: records: { <model>: [ { ... } ] }.

A form’s actions can tell the page what to do once everything else is done. These do not end the run, and at most 10 count:

TypeWhat the page doesTakes
notifyShows a notice above the thanks; {{ vars.name }} puts in a value an action settext, in each language; tone: info, success or warning
startGuideStarts a user guide that applies to this pageguide
reloadReloads the page after a moment

A redirect wins over them: the person goes elsewhere first. A form sent again answers what it answered the first time; one sent from an offline device shows none of them.

An action that fails runs its onError actions, and the list goes on. Without them, the failure goes up to the enclosing group’s onError, and otherwise ends the run. Inside onError, vars.error.message says what went wrong.

A run stops after 10 seconds or 10,000 steps, a loop goes over at most 1,000 items, and a value is at most 1 MB. When the portal is very busy, a run may not start; the form is still kept, and the person is told that what follows could not be done. Each run is kept for 30 days, with its log; reviewers see them under Runs on Forms.

An approved app can give actions of its own, such as sending an SMS:

yaml
- type: appAction
app: sms
action: send
inputs:
to: form.phone
text: '"Cererea dvs. a fost înregistrată."'
connector: sms-gateway
into: sent
  • What it takes: the inputs the app’s manifest declares; the portal checks them when the files are published. connector names a connector of the app’s type, when the action needs one: an administrator sets its credentials under Connectors.
  • What it gives: vars.sent holds the action’s outputs.
  • Where: in a form, flow, job or endpoint that may change records, never in a GET endpoint or one anyone may call, and never in a flow’s tests. A run calls at most 10.
  • When it fails: the app is not approved, it does not answer in time, or it answers what its manifest does not say. The run’s onError reads why.

To write one, see Build an app.