Retail and Commerce: the agent that notices
Track: Retail and Commerce · In the room: pre-assigned teams · Seed use case: delivery-exception agent — detect, decide, compensate, notify.
Who this is for
Furniture and home goods, fashion e-commerce, appliance delivery — different delivery problems (brand-critical returns, two-person installation slots), same shared pain: dashboards already detect late parcels fine. The pain is that detection produces a queue worked by humans in office hours, and by row 400 the customer has already tweeted.
Three agent use cases
1. Delivery-exception agent — the seed use case
- Trigger. Scheduled sweep over orders past their delivery window without a delivered scan. No human opens a dashboard.
- Decides. Real exception or noise (weekend, pickup-point not yet collected); how bad; which remedy — reship, refund shipping, goodwill credit, or just an early honest message.
- Tools.
check_carrier_statusreads tracking state;resolve_exceptionapplies the remedy and drafts the message. - Human in the loop? Selectively. Telling a customer is auto; a goodwill credit above a threshold needs approval. Gate the irreversible action, let the rest run.
2. Returns-fraud triage
- Trigger. A return request, or a refund claim on an item marked delivered.
- Decides. Ordinary (wrong size) vs. abuse signal (serial high-value returns, weight mismatch, non-delivery claim against a photographed handover).
- Tool.
assess_return— risk band with signals, auto-approves low-risk. - Human in the loop? One-sided. Auto-approve is safe; refusing accuses a customer, so that's always a human call with the evidence in front of them.
3. Product-content gap filler
- Trigger. New supplier catalogue, or a product page missing dimensions/materials/care instructions.
- Decides. Which attributes are missing, which are recoverable from supplier material, which need a human.
- Tool.
enrich_product— extracts from the supplier's spec page, writes back as a draft with a source URL per field. - Human in the loop? Yes, merchandiser review queue — wrong wardrobe dimensions fail a two-person delivery. The pattern: agent output is a diff for a human to accept.
The 60-minute cut
Build use case 1: detect on a clock, decide with a tool, gate the money, notify.
In scope:
- Six synthetic orders (id, item, promised date, carrier state, value in SEK) — two fine, two genuinely late, one at-pickup-point-not-late, one high value.
- Scheduled sweep via
this.schedule(), every 60 seconds so it fires on stage (say "production would be every 15 minutes"). - One tool deciding remedy + drafting the message.
- A Workflow applying the remedy, pausing for approval above a credit threshold you pick.
- A one-page view of exceptions, remedies, approval state.
Out of scope, and say so: real carrier integrations, order-management writes, actual email/SMS (log and render instead), auth, multi-market logic.
The demo that wins. Say nothing, let the schedule fire on stage — an exception appears,
the apology is drafted, the high-value one sits in PENDING APPROVAL. Approve it live.
Primitives — exactly three
- Schedules (
this.schedule()). Exceptions are discovered by looking, not being told — no webhook for "nothing happened." Keeps detection logic next to the state it reads. - Browser Rendering. The honest retail primitive: the carrier's tracking page exists,
the API doesn't. A screenshot attached to the case is evidence a human can check at a
glance — plain
fetchjust gets you a JS shell. - Workflows. Compensate-then-notify has money in the middle and must be exactly-once.
step.do()stops a retry double-crediting a customer;step.waitForEvent()holds the run open for a human on the big ones.
Skip AI Search — you're reasoning over order state, not documents. Skip Durable
Object state as a named choice — you get it free from the Agent class.
Paste-ready starter prompt
Scaffold the starter first:
npm create cloudflare@latest -- team-retail-delivery-exception --template cloudflare/agents-starter
cd team-retail-delivery-exception
Then paste this into your coding agent:
I'm working in a fresh clone of Cloudflare's agents-starter template. The agent code is in
src/server.ts and the React client is in src/client.tsx. I have 60 minutes. Keep the
existing chat agent working — extend it, don't replace it.
Build a delivery-exception agent for an online retailer.
1. Add a hardcoded array of 6 synthetic orders to src/server.ts. Each has: orderId, item
description, orderValueSek, promisedDate, and carrierState (one of in_transit,
at_pickup_point, delayed, delivered). Make two of them clearly overdue, one that looks
overdue but is actually waiting at a pickup point, and one overdue order worth more than
15000 SEK. Invented data only.
2. Add a tool called assess_exception. Given an order, it uses a Workers AI model to decide:
isException (boolean), severity (low, medium, high), remedy (one of none, notify_only,
refund_shipping, goodwill_credit), a creditAmountSek number, and a two-sentence draft
message to the customer in English. Ask the model for JSON. An order sitting at a pickup
point is NOT an exception — the customer just hasn't collected it.
3. Use this.schedule() on the Agent to run a sweep every 60 seconds. The sweep calls
assess_exception for every order past its promised date that is not delivered, and stores
the result in the Agent's SQLite storage via this.sql, keyed by orderId. Make it
idempotent: re-assessing the same order must update the row, not add a second one.
4. Add a Cloudflare Workflow called ExceptionRemedy in a new file, with steps as step.do():
a) apply_remedy — record the remedy that was applied
b) await_approval — ONLY if creditAmountSek is over 1000, use step.waitForEvent() with
a generous timeout to wait for an event of type "remedy-decision". Below the
threshold, skip this step entirely and proceed.
c) notify — log the customer message (do not actually send email)
Import WorkflowEntrypoint, WorkflowStep and WorkflowEvent from "cloudflare:workers", and
add the workflow binding to wrangler.jsonc.
5. Add a tool called check_carrier_status that uses Cloudflare Browser Rendering to load a
public tracking-style page and capture a screenshot. Use https://example.com as the URL so
it works without a real carrier. Store the screenshot key with the exception. If Browser
Rendering fails for any reason, catch it, carry on, and mark the exception
"screenshot unavailable" — this step must never break the sweep.
6. Add routes:
GET /exceptions HTML page listing every exception: order, severity, remedy,
credit amount, and approval state. Show PENDING APPROVAL
prominently for anything waiting.
POST /decide/:orderId sends the "remedy-decision" event with approved true or false
Requirements: TypeScript. Route every Workers AI call through AI Gateway by passing
{ gateway: { id: "agenthack" } } as the options argument. Run `npx wrangler types` after
changing wrangler.jsonc. Then deploy with `npx wrangler deploy` and tell me the URL and how
to watch the schedule fire.
The Worker name team-retail-delivery-exception follows the shared-account rule
<team_prefix>-<slug>. Eight teams deploy into one account today, so if the Scoping agent
issued you a different prefix, use that one instead.
What "production" looks like
- Real event source, agent stateful per order. One instance per order/shipment builds the timeline your support team currently reconstructs by hand.
- Compensation policy as data, not prose. Thresholds and remedy ladders in an editable table; the agent chooses within policy, doesn't invent it.
- Close the loop on carrier performance. Aggregate exceptions by lane/carrier and this becomes a procurement argument, not just a service tool.
Next: Level Up explains each primitive, the cheat sheet has the snippets.