For Agents
Designing requests people answer in seconds.
What a good request is
A Taskriz request is a small native screen your Agent sends to a person. The best ones have one clear task, show only what’s needed to do it, and take a tap or two to answer — on a phone, a laptop, wherever the person is. If it would be a form you’d skim past, make it shorter.
Principles
Taskriz requests follow world-leading human interface guidelines, so they feel familiar at first glance.
- Name the task in the title — “Approve invoice #4567”, not “Request”. Put what they need to know first.
- Choices over typing. A choice of two to five is a segmented control; more is a pop-up; yes/no is a switch; a date is a date picker. Prefill sensible defaults.
- Group related fields in an inset grouped list, with a header naming the group.
- One main button, labelled with a verb — “Approve”, “Book”, “Send” — not “OK”. Other choices are plain buttons.
- Ask only what you need. No scrolling forms; one request per decision.
- Use importance sparingly. High is for things that can’t wait.
The building blocks
An interface is built from these (and a few more — get_ui_guide has them all). You read what the person chose from the control when they tap a button.
| Building block | For | Read |
|---|---|---|
SegmentedControlMorph | A choice of 2–5 (Window · Middle · Aisle) | selected |
PopUpButtonMorph | A choice from more options | selected |
SwitchMorph | Yes / no, on / off | on |
StepperMorph | A small number in a range (quantities) | value |
SliderMorph | A value in a range | value |
DatePickerMorph | A date, a time, or both | value (ISO 8601) |
TextFieldMorph | One line of text (with keyboards for email, numbers, phone…) | value |
TextViewMorph | Several lines of text | value |
PhotoPickerMorph | A photo — chosen or taken on the spot | files |
DocumentPickerMorph | A document (PDF, Word…) | files |
CollectionViewMorph | A grid to choose from — pictures, cards (tapped cells show a checkmark) | selected (0-based) |
ListMorph | A form or table — group related fields (style: #insetGrouped) | — |
LabelMorph | Text: what they need to know (text styles: #headline, #footnote…) | — |
ImageMorph | A picture or symbol (always give alt:) | — |
ButtonMorph | The action — one primary (filled), others style: #plain | — |
Worked examples
These are the requests on the Taskriz home page — real, and tested.
Approve an invoice
A decision with an optional note: the facts in a grouped list, the main action filled, the alternative plain.
| note |
note := TextFieldMorph new label: 'Note'; placeholder: 'Optional'.
ScreenMorph new title: 'Invoice #4567';
addMorph: (LabelMorph new text: 'Acme Ltd sent invoice #4567 for October hosting.');
addMorph: (ListMorph new style: #insetGrouped; header: 'Details';
addMorph: (TextFieldMorph new label: 'Amount'; value: '$1,240.00');
addMorph: (TextFieldMorph new label: 'Due'; value: 'Friday, Oct 16');
addMorph: note; yourself);
addMorph: (ButtonMorph new label: 'Approve'; onClick: [Taskriz current reply: #{ #decision -> 'approve'. #note -> note value }]);
addMorph: (ButtonMorph new label: 'Query This Invoice'; style: #plain; onClick: [Taskriz current reply: #{ #decision -> 'query'. #note -> note value }]);
yourself
Order supplies
Quantities with steppers, an option with a switch — and a running total the person sees change as they adjust.
| paper coffee pens express cost |
paper := StepperMorph new label: 'Paper · $45'; min: 0 max: 10 value: 2.
coffee := StepperMorph new label: 'Coffee · $38'; min: 0 max: 10 value: 3.
pens := StepperMorph new label: 'Pens · $9'; min: 0 max: 20 value: 1.
express := SwitchMorph new label: 'Express delivery · $15'.
cost := [(paper value * 45) + (coffee value * 38) + (pens value * 9) + (express on ifTrue: [15] ifFalse: [0])].
ScreenMorph new title: 'Office Supplies';
addMorph: (LabelMorph new text: 'This week’s order for the office. Change the amounts if you need to.');
addMorph: (ListMorph new style: #insetGrouped; header: 'Items'; addMorph: paper; addMorph: coffee; addMorph: pens; yourself);
addMorph: (ListMorph new style: #insetGrouped; addMorph: express; yourself);
addMorph: (LabelMorph new textStyle: #headline; textBlock: ['Total: $', cost value printString]);
addMorph: (ButtonMorph new label: 'Place Order'; onClick: [Taskriz current reply: #{ #paper -> paper value. #coffee -> coffee value. #pens -> pens value. #expressDelivery -> express on. #total -> ('$', cost value printString) }]);
addMorph: (ButtonMorph new label: 'Skip This Week'; style: #plain; onClick: [Taskriz current reply: #{ #decision -> 'skip' }]);
yourself
Get a receipt photo
Ask for a file with a picker — never a link or a file path. A second button covers “I don’t have it”.
| photo |
photo := PhotoPickerMorph new label: 'Receipt'.
ScreenMorph new title: 'Receipt Needed';
addMorph: (LabelMorph new text: 'Your card was charged $86.50 at Bistro Moncur on Tuesday. Add a photo of the receipt so the expense can be filed.');
addMorph: (ListMorph new style: #insetGrouped; addMorph: photo; yourself);
addMorph: (ButtonMorph new label: 'Send'; onClick: [photo files isEmpty ifFalse: [Taskriz current reply: #{ #receipt -> photo files }]]);
addMorph: (ButtonMorph new label: 'I Don’t Have It'; style: #plain; onClick: [Taskriz current reply: #{ #receipt -> 'none' }]);
yourself
Book a time
A date picker limited to the days that work, and the times as a segmented choice.
| day time |
day := DatePickerMorph new label: 'Day'; mode: #date; value: '2026-10-20'; min: '2026-10-19'; max: '2026-10-23'.
time := SegmentedControlMorph new label: 'Time'; segments: #('9:30' '11:00' '14:00'); selected: '11:00'.
ScreenMorph new title: 'Find a Time';
addMorph: (LabelMorph new text: 'A 30-minute call next week. Which day and time suit you?');
addMorph: (ListMorph new style: #insetGrouped; addMorph: day; addMorph: time; yourself);
addMorph: (ButtonMorph new label: 'Book'; onClick: [Taskriz current reply: #{ #day -> day value. #time -> time selected }]);
yourself
Choose an album
A grid of pictures to pick from; the person can change their mind before tapping Choose.
| albums grid cover |
albums := #('Neon Static' 'Slow Orbit' 'Paper Lanterns' 'Low Tide').
cover := [:title :artist :file | StackViewMorph new axis: #vertical;
addMorph: (ImageMorph new src: '/renderers/web/task/covers/', file, '.svg'; alt: title);
addMorph: (LabelMorph new text: title; textStyle: #subheadline);
addMorph: (LabelMorph new text: artist; textStyle: #caption1); yourself].
grid := CollectionViewMorph new columns: 2;
addMorph: (cover value: 'Neon Static' value: 'The Glass Arcade' value: 'neon-static');
addMorph: (cover value: 'Slow Orbit' value: 'Mira Vale' value: 'slow-orbit');
addMorph: (cover value: 'Paper Lanterns' value: 'Kites & Co.' value: 'paper-lanterns');
addMorph: (cover value: 'Low Tide' value: 'The Lowlands' value: 'low-tide'); yourself.
ScreenMorph new title: 'Friday’s Album';
addMorph: (LabelMorph new text: 'Pick the album for Friday’s office playlist.');
addMorph: grid;
addMorph: (ButtonMorph new label: 'Choose'; onClick: [grid selected isNil ifFalse: [Taskriz current reply: #{ #album -> (albums at: grid selected + 1) }]]);
yourself
Designing the answer
A button sends the answer with Taskriz current reply: #{ … }. Shape it as the data your Agent wants to read: it arrives as JSON in the event’s reply — #{ #decision -> 'approve'. #note -> note value } becomes {"decision": "approve", "note": "…"}. Use clear keys, one answer per button, and put a picker’s files straight in (each arrives as {id, name, type, size}).
Testing
Before sending, run the interface with check_ui: it runs in the real Taskriz runtime and returns the error, or a text preview of what the person will see. The plugins check every request before sending it; on the API, test your interfaces with check_ui before you ship them.
Threads and follow-ups
Requests that share a correlationId are one thread for the person: ask a follow-up on the same thread and they see the conversation together. A request can allow one answer (the default) or several (replies: multiple). Sent the wrong thing? Recall it.
Respecting people
- Your Agent reaches only people who have paired with it — by scanning its QR code with their phone’s camera, by a code they enter, or by accepting an invitation. Scanning fills in the code; pairing still waits for them to tap Pair. Share a QR code or pairing code only with the person it’s meant for: each lasts 15 minutes and works once.
- An invitation email never shows the request itself. If they choose Decline, no Agent in your account can invite them again — tell the person you’re helping, and don’t look for a way round it.
- People can unpair at any time, and choose how they’re notified. Ask when it matters; don’t over-ask.
The request catalog
Give people actions they can start themselves — “Report a problem”, “Request a statement”. Keep labels short and specific (up to 60 characters), add a one-line description, and answer the initiate event with a request that collects what you need. How many actions an Agent can offer follows your plan: Free plans include 2 actions per Agent, Builder 10 and Business 50 (see Pricing). Developers shows how to set them.
Next: Developers — the API, the plugins and the MCP tools.