Guided Business Tasks¶
A task is a recorded screen flow with named inputs and a named answer. Someone who knows the business question — but not the application — fills in a short form and reads the result, without navigating a green screen.
The whole life of a task, in one take — record a flow, name its inputs, mark the answer, run it and read the result:
Account balance enquiry
Retrieves the current cleared balance for a customer account.
Account number [ 40218855 ] required, 8 digits
As-at date [ 2026-08-08 ] optional
[ Back ] [ Run ]
3270Web drives the application, then shows a result card:
Account balance enquiry 2.4 s
Account 40218855
Name J MARGOLIS
Cleared balance 1,240.55
Available 1,190.55
[ Copy ] [ CSV ] [ Run again ] [ Close ]
Running a task¶
Open Tasks from the menu bar (the labelled button on the right) or the command palette. Tasks are business-user surface, so the control stays visible in Business mode.
Pick a task, fill in the form, press Run. The dialog closes and a card
appears in the corner — the terminal stays visible the whole time. That
is deliberate: operators need to see what the host is actually doing, and it
is how trust in the automation gets built. The card never covers the
Operator Information Area,
so X SYSTEM and the operator-error indicator stay readable.
Cancel stops the run. The terminal is left exactly where it stopped — the request was to stop the automation, not to undo it, and a 3270 transaction cannot be rolled back.
A run lives on the server, so reloading the page does not lose it; the card reappears and carries on reporting.
When a task does not finish¶
A task stops at the first step whose screen is not what it expected, and reports:
- which step it stopped at, and why
- what it expected against what it found at that position
- the screen it stopped on, behind a disclosure
The terminal is left on that screen so you can take over by hand.
This is the opposite of what recording playback does, on purpose. Playback logs a failed step and carries on, which suits a scripted regression. For a business transaction, carrying on means typing an account number into whatever field happens to be under the cursor on a screen nobody expected. An output that cannot be read counts as a failure too, unless it is marked optional — a result card showing a blank balance is worse than an honest error.
Defining a task¶
From a recording (the wizard)¶
Record the flow once, then press Save recording as a task on the recording controls — or find it in the command palette.
- Record. Start recording, work through the screens exactly as the task should run, then stop.
- Confirm the inputs. Everything typed during the recording arrives as an input the operator will be asked for, labelled with the text the screen itself uses. Switch any that should always be the same to Always this value; that removes the input from the form and bakes the value into the step.
- Mark the answer. Drag across the screen the flow ended on to mark each value the task should report back. This is the one thing a recording cannot work out for itself.
- Name it and save.
Above the form is What was assumed — every guess the draft made, listed rather than buried. Read it: a guard that could not be derived, a field with no label, or a value that was cleared rather than typed all show up there.
Marked regions extend to the next text on the row
If you mark ADA you get a region as wide as the slot the value sits
in, not as wide as those three characters. That is deliberate — a region
sized to the recorded value would silently truncate a longer one, so
GRACE would come back as GRA. Trailing spaces are trimmed from the
result.
From a chaos run¶
A chaos exploration maps an application's screens and the
fields that drive them, and a business function names one path through
that map. GET /chaos/business/task-draft?name=... converts one into a task
draft — or every function at once if you omit the name.
This is the join between exploration and use: the mind-map is the asset, the task is the product.
The conversion has one real difficulty, and it is worth knowing about. Chaos identifies a screen by hash, which is exact and useless to a person: when it stops matching, all it can say is "a different screen". A task guards with positional text from the screen's own labels. The bridge is the screen text the run captured, so a function whose screens were recorded converts with real guards — and one whose screens were not says so, per step, rather than producing a task that would act on any screen at all.
The draft also reports anything it dropped: a key a task cannot press, a field key it could not read, or a parameter no step fills. As with the wizard, it proposes no outputs.
By hand¶
The catalogue is server-side, so one person defines a task and everyone
else picks it off the menu. A task can also be written directly and posted to
/tasks/save:
{
"name": "Account balance enquiry",
"description": "Retrieves the current cleared balance for a customer account.",
"parameters": [
{
"name": "account_number",
"label": "Account number",
"required": true,
"maxLength": 8,
"pattern": "\\d{8}",
"example": "40218855"
}
],
"steps": [
{
"description": "Enter the account number",
"expect": [{ "row": 1, "column": 29, "text": "ACCOUNT ENQUIRY" }],
"inputs": [
{ "row": 5, "column": 21, "length": 8, "parameter": "account_number" }
],
"aidKey": "Enter"
},
{
"description": "Confirm the account was found",
"expect": [
{ "row": 22, "column": 2, "text": "INVALID ACCOUNT", "absent": true }
]
}
],
"outputs": [
{
"name": "cleared_balance",
"label": "Cleared balance",
"row": 8, "column": 21, "length": 14,
"pattern": "([\\d,]+\\.\\d{2})"
}
]
}
All rows and columns are 1-based, matching the CURSOR readout in the
OIA and the coordinates in a recording.
Parameters¶
| Field | Meaning |
|---|---|
name |
Machine identifier that steps refer to. Letters, digits, underscore. |
label |
What the form shows. Defaults to name. |
description |
Hint shown under the field. |
example |
Becomes the field's placeholder. |
default |
Pre-filled value. Not allowed on a sensitive parameter. |
required |
An empty value is refused before the run starts. |
maxLength |
Enforced in the form and again on the server. |
pattern |
RE2 expression. Anchored at both ends, so \d{8} means the whole value is eight digits, not "contains eight digits". |
sensitive |
Masked in the form and kept out of every result and log line. The host still receives it. |
Validation runs on the server on every run, not only in the browser. The form is a convenience, not the gate.
Steps¶
A step checks the screen, fills fields, then presses a key.
expectguards the step. Every entry must match before anything is typed. Setabsent: trueto require that text is not there — that is how a step refuses to read an error line as an answer.inputsset exactly one ofvalue(a literal, such as a menu selection) orparameter. An optional parameter left blank means leave the field alone, which on a 3270 is not the same as typing nothing.aidKeydefaults toEnter.PF1–PF24,PA1–PA3,Clear,AttnandSysReqare accepted. Leave it empty on a final step that only needs to confirm where the flow landed.
Guards are positional text, not a screen hash. A hash changes the moment the host paints a different date in the corner, and when it mismatches it can only report "a different screen". An anchor on the application's own label text survives cosmetic change and can say exactly what it wanted and what was there instead.
Outputs¶
An output names a region of the final screen.
| Field | Meaning |
|---|---|
name |
Machine identifier. |
label |
What the result card shows. |
row, column, length |
The region, 1-based, on a single line. |
pattern |
Optional RE2. With one capture group, the group is the value; without, the whole match is. |
optional |
Allows the value to be missing without failing the run. |
pattern is what turns Cleared balance: 1,240.55 CR into
1,240.55.
A region is a span on one line. An answer that wraps is two outputs — letting one run onto the next line would capture the label of whatever sits below it.
API¶
These endpoints use the browser session cookie.
| Method | Path | Purpose |
|---|---|---|
GET |
/tasks |
List the catalogue |
POST |
/tasks/save |
Add or replace a task, validated |
POST |
/tasks/delete |
Remove a task by name |
POST |
/tasks/run |
Start a run in this session |
GET |
/tasks/status |
Progress, then the result |
POST |
/tasks/cancel |
Stop a run |
/tasks/run returns 202 immediately and the run proceeds in the
background; poll /tasks/status for progress and the result. One run per
session — a task drives the single terminal that session owns, and two at
once would interleave keystrokes into the same screen buffer.
For bots and CI¶
The same capability is on the token-authenticated
REST API, which needs API_TOKEN set.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/tasks |
The catalogue — this is also export |
POST |
/api/v1/tasks |
Add or replace a task — this is also import |
POST |
/api/v1/sessions/{id}/tasks/run |
Run a task in a session and return the result |
curl -H "Authorization: Bearer $API_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"Account balance enquiry","parameters":{"account_number":"40218855"}}' \
https://3270web.example/api/v1/sessions/$SID/tasks/run
{
"task": "Account balance enquiry",
"durationMs": 152,
"completed": true,
"steps": [{ "index": 1, "description": "Enter the account number", "status": "ok" }],
"outputs": [{ "name": "cleared_balance", "label": "Cleared balance",
"value": "1,240.55", "found": true }]
}
The API run is synchronous, unlike the browser's. The two callers want opposite things: a browser has to show progress and offer Cancel while a transaction takes its seconds, so it polls; a bot wants the answer in the response and would otherwise have to implement a poll loop to get it. Both are bounded by the same five-minute ceiling, and both register in the same per-session slot — so an API run and a browser run cannot overlap on one terminal.
A task that stopped early returns 200 with completed: false and the
failure detail, not an HTTP error. The request succeeded; the body says what
the host did. An HTTP status cannot express "step 3 saw the wrong screen", and
collapsing it into a 500 would discard the only useful part of the answer.
Export and import¶
GET /api/v1/tasks returns exactly the documents POST /api/v1/tasks
accepts, so moving a catalogue between deployments — or keeping it in version
control — needs no separate format:
# Export
curl -H "Authorization: Bearer $API_TOKEN" \
https://source.example/api/v1/tasks | jq '.tasks' > tasks.json
# Import, one task at a time
jq -c '.[]' tasks.json | while read -r t; do
curl -H "Authorization: Bearer $API_TOKEN" -H 'Content-Type: application/json' \
-d "$t" https://target.example/api/v1/tasks
done
Imported tasks go through the same validation as everything else, so a task edited by hand in version control cannot reach the runner malformed.
For moving a whole catalogue rather than one task, GET and
POST /api/v1/library do it in one call each way — and carry the connection
profiles with it, which is usually what a task catalogue needs at the far end
to be worth anything. That import reports what it did entry by entry, can be
asked what it would do first, and refuses a file it cannot store in full
rather than storing half of it. See
REST API, or
Admin → Session screen → Library for the same thing without curl.
Limits¶
- 200 tasks in the catalogue, 100 steps in a task.
- A run is abandoned after five minutes.
- Parameter values are capped at 160 characters and may not contain CR, LF or TAB, which the 3270 data stream would read as actions rather than text.
Sharing tasks¶
A saved task can be shipped to other installations inside an extension, alongside the skills and instructions that explain when to use it. See Skills and Extensions.