# Laituri: agent ordering for Valio Aimo (demo) > An open front door that lets a buyer's purchasing agent buy from the food-service wholesaler Valio Aimo over MCP or REST. > Prices and stock are live from the challenge's mock API. Offers are signed by the wholesaler. Orders are simulated (simulated: true). > The agent orders alone only inside a buyer-signed mandate; larger or unusual orders wait for a named person. Every step is in a signed, hash-chained log. Base URL: https://laituri.wicflow.com/api/v1 OpenAPI 3.1: https://laituri.wicflow.com/api/v1/openapi.json Public keys (JWKS): https://laituri.wicflow.com/.well-known/jwks.json **Start here: create your own sandbox.** POST https://laituri.wicflow.com/api/v1/workspace with no credential makes a private copy of the demo and answers 201 with agentKeys: one key per demo buyer (Nordic Harbour Hotels first, then Kaisla School Kitchens and Ravintola Laine), each with its MCP address. No account, no cookie: the keys are the only way in, they work only in that sandbox, and an unused sandbox is dropped after an hour. Reading orders, offers, the mandate, approvals, activity or the audit log needs a key; without one the answer is 401 credential_required. An agent key reads its own buyer's records; in the audit log it sees its buyer's entries in full and every other entry as a stub that still verifies. **Connect by MCP.** Streamable HTTP, stateless, JSON responses: https://laituri.wicflow.com/mcp Header: Authorization: Bearer . Clients that cannot send a header use the mcpUrl the sandbox returned (https://laituri.wicflow.com/mcp?key=...). The shared demo workspace has fixed public tokens, used by the evals: demo-agent-token, demo-kaisla-token and demo-laine-token. Everyone shares it, so its weekly spend grows as people test and nobody can approve there without the passcode: use your own sandbox. Over REST, X-Agent-Client names your application (for example "Claude Code") in the wholesaler's view. Tools: search_catalog, get_product, request_quote, get_mandate, place_order, get_order, verify_audit. **REST flow: "1,000 litres of lactose-free milk by Monday".** 1. Create your sandbox and keep the first agent key (Nordic Harbour Hotels). KEY=$(curl -s -X POST https://laituri.wicflow.com/api/v1/workspace | jq -r '.agentKeys[0].key') 2. Search. Read interpretation.confidence. Hits carry offerPreview when the wholesaler has a price: indicative live price, stock, delivery date, order cut-off and orderable. Pass need and deliverBy to get pack maths (unitsNeeded, estimatedNet). Only a signed offer fixes the price. When the match is clear (confidence high), hits are sorted best buy first: orderable on time, then lowest price per litre or kilo. Every hit has facts the wholesaler vouches for: countryOfOrigin, labels, deliveryReliability (0 to 1) and categoryAverageCo2PerKg (the average of the product's category, not a measurement of the product). To rank a clear match by something other than price, send "prefer": "reliability" or "prefer": "sustainability" (certified labels such as organic or msc first, then lower category CO2). An uncertain match keeps its relevance order. Choose the first hit with offerPreview.orderable true. curl -s https://laituri.wicflow.com/api/v1/products/search -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"query":"laktoositon maito 1 l","filters":{"claims":["lactose_free"]},"limit":5,"need":{"amount":1000,"unit":"l"},"deliverBy":"2026-11-16"}' 3. Quote for the product you chose, and send the buyer's own words as intent: the wholesaler checks the offer against them and signs that check. Without intent the buyer's mandate sends the order to a person. Quantities in l or kg are converted to whole sales units. The response has primary (the offer for what you asked) and alternatives (signed counter-offers when the request cannot be met in full and on time). primary has offer and jws. Each alternative has tier, meaning, tradeoff and signed, and signed has offer and jws. Tiers: good is the cheapest like-for-like product, better keeps what is in stock and fills the rest or takes the most reliable product, best is an organic or Finnish-label product. Every offer line carries the same facts as search, inside the signed offer. The offer is valid for 15 minutes (offer.expiresAt). Keep the jws of the one you take exactly as returned. curl -s https://laituri.wicflow.com/api/v1/quotes -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"lines":[{"productId":"464306","quantity":{"amount":1000,"unit":"l"}}],"deliverBy":"2026-11-16","intent":"1,000 litres of lactose-free milk by Monday","matchConfidence":"high"}' 4. Read your mandate. The response has mandate (readable limits) and jws. Keep jws. curl -s https://laituri.wicflow.com/api/v1/mandate -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" 5. Order. Send the offer jws and the mandate jws unchanged, and a key you choose. curl -s https://laituri.wicflow.com/api/v1/orders -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -H "Idempotency-Key: milk-2026-11-16-001" -d '{"offerJws":"","mandateJws":""}' With jq: OFFER=$(curl -s ... /quotes ... | jq -r .primary.jws); MANDATE=$(curl -s ... /mandate | jq -r .jws) **Mandate rules.** - Up to the per-order and per-week net limits the agent may order alone; above them a human must approve. - Only allowed category groups; excluded allergens and required claims are hard rules. - Substitutes must follow the substitution policy and the price limit. - Late or partial delivery is checked against the requested date. - An expired offer, a passed order cut-off or an altered offer or mandate blocks the order. **Order outcomes.** - 201 confirmed: receiptJws holds the wholesaler-signed receipt. - 202 pending_approval: stop and tell your human. Do not retry with another key. Poll GET https://laituri.wicflow.com/api/v1/orders/{id}. - 422 order_blocked: nothing was ordered. The problem body carries the order; order.decision.checks says why. The wholesaler can suspend an agent. While it is suspended every call answers 403 with the code agent_suspended; tell your human. Retrying with the same Idempotency-Key returns the same order. One signed offer gives one order. For another order, ask for a new quote. Always send the current mandate. **Proof.** GET https://laituri.wicflow.com/api/v1/audit/verify checks the hash chain and every signature. GET https://laituri.wicflow.com/api/v1/audit/checkpoint returns a signed head. To trust an offer without a manual check: verify its JWS (ES256) with the supplier key from the JWKS, check expiresAt and orderBy are in the future, and send it unchanged. Errors are application/problem+json with a code and a plain detail. ## Docs - [Evidence](https://laituri.wicflow.com/evidence): the five judging criteria with what was built, where in the code, how to check it and measured results - [Connect an agent](https://laituri.wicflow.com/connect): set-up lines for MCP clients and plain HTTP, and a test order ## API - [OpenAPI 3.1](https://laituri.wicflow.com/api/v1/openapi.json): every REST operation with schemas and examples - [MCP endpoint](https://laituri.wicflow.com/mcp): Streamable HTTP; the tools mirror the REST operations - [JWKS](https://laituri.wicflow.com/.well-known/jwks.json): public keys of the supplier, buyer and approver roles - [Agent card](https://laituri.wicflow.com/.well-known/agent-card.json): interfaces, tools and security schemes - [AI catalog](https://laituri.wicflow.com/.well-known/ai-catalog.json): the service in a catalogue listing - [UCP profile](https://laituri.wicflow.com/.well-known/ucp): commerce capabilities in Universal Commerce Protocol terms - [OAuth protected-resource metadata](https://laituri.wicflow.com/.well-known/oauth-protected-resource): the scopes a production deployment would grant ## Evidence - [Evidence as JSON](https://laituri.wicflow.com/evidence.json): criteria, requirements, scenario coverage, assumptions and measured numbers - [Audit verification](https://laituri.wicflow.com/api/v1/audit/verify): live check of the hash chain and every signature - [Audit checkpoint](https://laituri.wicflow.com/api/v1/audit/checkpoint): a supplier-signed size and head hash to keep and compare ## Optional - [Guided walkthrough](https://laituri.wicflow.com/buyer?tour=1): the order, approval, exception and log steps in the running product - [Buyer side](https://laituri.wicflow.com/buyer): orders, mandate and log for the buyer's purchasing lead - [Valio Aimo side](https://laituri.wicflow.com/valio): connected agents, demand, unmet requests and the audit log