D04 — A flower order, priced, confirmed and painted
A flower shop in Milan sells online to customers who write from Munich, London or around the corner. Every order needs a confirmation that is right to the cent and reads naturally in the customer’s language; and some orders are gifts, which travel with a card. This workflow prices the order with rules, writes every amount and the delivery day the way the customer reads them, lets an AI write the message around those facts — and, when the order is a gift, paints a card of the bouquet that was actually ordered and writes the customer’s own words on it by hand.

The card of the first reference run: the order was twelve pink peonies, two sprigs of eucalyptus and an amber blown-glass vase — and that is what is painted. The message is Lena’s, word for word, set in the Caveat handwriting font.
| Order | Customer | Priced | Confirmation | Card | Credits |
|---|---|---|---|---|---|
| Gift, inner districts | Lena, de-DE |
69.90 + delivery 9.00 = 78.90 | in German | painted | 5.03 |
| No gift, province, 10 % coupon | James, en-GB |
80.60, delivery free, − 8.06 = 72.54 | in English | skipped | 0.02 |
The two confirmations, as the AI wrote them:
Liebe Lena,
Deine Bestellung ist unterwegs und wird am Samstag, 3. Oktober 2026 geliefert:
• 1 Strauß rosa Pfingstrosen (12 Stück)• 2 Zweige Eukalyptus• 1 bernsteinfarbene Vase aus mundgeblasenem Glas
Eine handbemalte Karte liegt den Blumen bei.
Zwischensumme: 69,90 €Lieferung: 9,00 €Gesamt bezahlt: 78,90 €
Herzlich,Fiori di BreraHello James,
Your flowers are on their way for delivery on Monday, 5 October 2026:
- 1 large bunch of mixed ranunculus- 7 white garden roses
Subtotal: €80.60Delivery: freeDiscount: €8.06Total paid: €72.54
Warm wishes,Fiori di BreraBoth runs, with their priced lines, are in the runs’ outputs.
The case
Section titled “The case”The shop’s web store sends each order as one JSON document: the lines with their quantities and unit prices, the
delivery day and zone, the customer’s language, a coupon when there is one, and a gift section with the occasion
and the message when the customer asked for a card. The shop’s rules are simple and must be applied exactly:
delivery costs 6 € in the city centre, 9 € in the inner districts, 15 € in the province and 22 € anywhere else, and
it is free when the flowers come to 80 € or more; a coupon takes a percentage off the flowers.
{ "order_id": "FDB-2026-10417", "currency": "EUR", "customer": { "name": "Lena Hoffmann", "locale": "de-DE" }, "delivery": { "date": "2026-10-03", "zone": "semicentro", "recipient": "Giulia Ferri", "address": "Via Solferino 12, 20121 Milano" }, "lines": [ { "item": "Peonie rosa, mazzo da 12", "qty": 1, "unit_price": 42.0 }, { "item": "Rami di eucalipto", "qty": 2, "unit_price": 4.5 }, { "item": "Vaso in vetro soffiato color ambra", "qty": 1, "unit_price": 18.9 } ], "gift": { "occasion": "birthday", "message": "Cara Giulia, buon compleanno! Che quest'anno fiorisca come queste peonie. Un abbraccio da Monaco, Lena" }}| Input | Who provides it | Example |
|---|---|---|
| Order | the web store, one document per order | above; James’s order has coupon_percent and no gift |
What it shows
Section titled “What it shows”- Numbers come from rules, never from the AI. Each line is priced by Calculate inside an
iteration, the lines are collected and summed, the zone fee is chosen by Select by Key from four number
constants (an unknown zone takes the highest), delivery becomes free with
if(subtotal >= 80, 0, zone_fee), and a missing coupon is read as 0 by Extract with a default. The total is exact decimal arithmetic. - Written the way the customer reads it. Format as Text turns each
amount and the delivery day into text in the order’s own language — its
localeinput is connected to the customer’s language, so78.9becomes78,90 €for Lena and72.54becomes€72.54for James, and2026-10-03becomesSamstag, 3. Oktober 2026. The same node writes the priced lines as compact JSON for the prompt, and the gift flag as a sentence. - The AI writes around the facts, not the facts. The confirmation’s prompt lists the formatted facts and tells
the model to copy every amount and date character for character. The AI’s work is the part it is good at: a warm
message in German or English, the Italian product names translated (
Peonie rosa→rosa Pfingstrosen,Ranuncoli misti→mixed ranunculus), “free” instead of€0.00, no discount line when there is none. - An optional part costs only when it is asked for. Extract reads
gift.messagewithon_missing: absent: when the order has no gift section, the message is rejected and every step that needs it is skipped. The card’s AI prompt and image do not need the message, so a Filter on Extract’sfoundputs them behind the same decision — without a gift, the card costs nothing, and James’s run costs two hundredths of a credit. - Generated, but faithful to the order. The card is not a stock bouquet: an AI turns the priced lines into an image prompt — these flowers, this vase, in their colours, the lower half left blank — and the image model paints it. The message is then set on the blank half by Text Overlay in an exact font, so the customer’s words are never retyped or “improved” by a model.
The workflow
Section titled “The workflow”The figure tells the workflow as stages, with the reference runs’ results on the cards; the exact graph is the workflow (as a diagram), node by node in the table.
| Node | Type | Why |
|---|---|---|
| Order | input/json_value |
The whole order as one document; it never iterates |
| Each line / Line total / Priced lines / Subtotal | enumerate/json, utility/calculate qty * unit_price, aggregate/json, utility/calculate sum(priced_lines[*].line_total) |
Every line priced, then added up |
| Delivery zone, Customer name, Customer language, Currency, Delivery date | utility/extract, expected_type text |
The fields the rules and the message need, typed |
| City centre fee … Any other zone | utility/constant, type number |
The shop’s fees, as values to choose from |
| Fee for the zone | utility/select_by_key, number, use_default |
The fee of the order’s zone; 22 € when the zone is unknown |
| Delivery / Discount / Total | utility/calculate |
if(subtotal >= 80, 0, zone_fee), round(subtotal * coupon / 100, 2), subtotal - discount + delivery |
| Coupon percent | utility/extract, on_missing: default 0 |
No coupon is 0 percent |
| … as text | text/format: currency with locale and currency connected; date full; json compact; auto for the gift flag |
Every fact written for the reader |
| Confirmation facts / Confirmation message | text/template, ai/text_generation with low reasoning effort |
The facts in a list; the message around them |
| Card message | utility/extract gift.message, on_missing: absent |
Present only for a gift; its absence cuts the card |
| Only for a gift / Card brief / Card illustration prompt | utility/filter on found, text/template, ai/text_generation |
The image prompt, written only for a gift |
| Watercolour card / Handwritten message | ai/text_to_image 3:4, image/text_overlay with Caveat |
The painted bouquet, then the customer’s words |
| Outputs | output/text, output/json ×2, output/image |
The confirmation, the total, the priced lines, the card |
Reading a run: Total is the number to charge; Priced lines shows how it was reached. Without a gift, Card
comes back absent and the trace shows the card’s prompt, image and lettering as skipped.
Cost: the image of the card is almost the whole bill, about 5 credits; the two short AI texts cost under two hundredths each, and everything else — pricing, formatting, deciding — is free. An order without a gift costs about two hundredths of a credit.
Install it
Section titled “Install it”The example installs into your workspace with an API key, through the public API only — see Installing an example:
node madoo-install-example.mjs d04-florist-order --api <your Madoo API URL> --key <key.json> --run--run prices Lena’s gift order and paints its card (about 5 credits). James’s order is in
run-inputs-no-gift.json.
Change it
Section titled “Change it”- Your rules: the fees are constants and the threshold is in the Delivery expression; VAT, a minimum order or a surcharge for Sundays are one more Calculate each.
- Your shop’s voice: the confirmation’s system prompt holds the tone and the rules the AI must keep; the facts stay in the template.
- Other currencies: the order’s
currencyis already connected to every amount; a Swiss order inCHFwithde-CHis written the Swiss way. - Another card: the style lives in the illustration prompt (pencil, gouache, pressed flowers), the lettering in the Text Overlay typography — another font, colour or position.
- Other domains: the same shape serves a restaurant booking with a deposit, a print shop’s quote, an event ticket with an optional printed pass.