Skip to main content

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_status reads tracking state; resolve_exception applies 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:

  1. 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.
  2. Scheduled sweep via this.schedule(), every 60 seconds so it fires on stage (say "production would be every 15 minutes").
  3. One tool deciding remedy + drafting the message.
  4. A Workflow applying the remedy, pausing for approval above a credit threshold you pick.
  5. 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​

  1. 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.
  2. 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 fetch just gets you a JS shell.
  3. 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​

  1. Real event source, agent stateful per order. One instance per order/shipment builds the timeline your support team currently reconstructs by hand.
  2. Compensation policy as data, not prose. Thresholds and remedy ladders in an editable table; the agent chooses within policy, doesn't invent it.
  3. 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.