[{"data":1,"prerenderedAt":608},["ShallowReactive",2],{"blog-ai-assistant-b2b-shop-customer-support-en":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":14,"about":26,"sources":36,"cover":58,"og":59,"expertise":60,"locales":61,"lang":62,"title":65,"description":66,"coverAlt":67,"metaTitle":68,"takeaways":69,"faq":75,"toc":91,"blocks":119,"others":366},"ai-assistant-b2b-shop-customer-support","2026-10-17",9,"web",[9,10,11,12,13],"B2B e-commerce","AI assistant","Customer support","LLM agents","EU AI Act",[15,16,17,18,19,20,21,22,23,24,25],"ai assistant b2b shop","ai customer support b2b ecommerce","llm chatbot order status","ai agent customer service tools","chatbot liability air canada","klarna ai assistant results","human handoff chatbot design","eu ai act chatbot disclosure","owasp excessive agency","ai support resolution rate","b2b customer portal chatbot",[27,30,33],{"name":28,"url":29},"Customer service","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCustomer_service",{"name":31,"url":32},"Chatbot","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FChatbot",{"name":34,"url":35},"Business-to-business","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBusiness-to-business",[37,40,43,46,49,52,55],{"title":38,"url":39},"Klarna: AI assistant handles two-thirds of customer service chats in its first month (press release, 2024)","https:\u002F\u002Fwww.klarna.com\u002Finternational\u002Fpress\u002Fklarna-ai-assistant-handles-two-thirds-of-customer-service-chats-in-its-first-month\u002F",{"title":41,"url":42},"Entrepreneur: Klarna is hiring customer service agents again, according to its CEO (May 2025)","https:\u002F\u002Fwww.entrepreneur.com\u002Fbusiness-news\u002Fklarna-ceo-reverses-course-by-hiring-more-humans-not-ai\u002F491396",{"title":44,"url":45},"ABA Business Law Today: BC tribunal confirms companies remain liable for information provided by AI chatbot","https:\u002F\u002Fwww.americanbar.org\u002Fgroups\u002Fbusiness_law\u002Fresources\u002Fbusiness-law-today\u002F2024-february\u002Fbc-tribunal-confirms-companies-remain-liable-information-provided-ai-chatbot\u002F",{"title":47,"url":48},"OWASP GenAI Security Project: LLM06:2025 Excessive Agency","https:\u002F\u002Fgenai.owasp.org\u002Fllmrisk\u002Fllm062025-excessive-agency\u002F",{"title":50,"url":51},"OWASP GenAI Security Project: LLM05:2025 Improper Output Handling","https:\u002F\u002Fgenai.owasp.org\u002Fllmrisk\u002Fllm052025-improper-output-handling\u002F",{"title":53,"url":54},"EU AI Act, Article 50: Transparency obligations","https:\u002F\u002Fartificialintelligenceact.eu\u002Farticle\u002F50\u002F",{"title":56,"url":57},"Intercom: Fin pricing and billable outcomes","https:\u002F\u002Ffin.ai\u002Fpricing","\u002Fimages\u002Fblog\u002Fai-assistant-b2b-shop-customer-support\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fai-assistant-b2b-shop-customer-support\u002Fog.jpg","b2b-ecommerce-developer",[62,63,64],"en","de","hu","An AI assistant for B2B shop support: tools, permissions and the human handoff","How to build an AI support assistant for a B2B shop: which requests to automate, tools that run in the customer context, handoff, AI Act and metrics.","A tall wall with one narrow gap; an arrow passes through it into a boxed enclosure of three squares, and another leads down to a tray.","AI assistants for B2B shop support · Balázs Csorba",[70,71,72,73,74],"Most B2B support questions are lookups in systems you already have: order status, delivery dates, invoices, certificates and spare parts.","The assistant is part of your website, legally and reputationally; a tribunal held Air Canada liable for what its chatbot said.","Every tool must run in the logged-in customer context, with the customer ID taken from the session, never from model arguments.","Read-only first, write actions with confirmation later, and prices, quotes and commitments stay with people.","A visible way to reach a human is part of the design, and the EU AI Act requires telling users they are talking to an AI from 2 August 2026.",[76,79,82,85,88],{"q":77,"a":78},"What can an AI assistant safely automate in a B2B shop?","Lookups with clear answers in existing systems: order and delivery status, invoice and credit note copies, product documents such as datasheets and declarations of conformity, and questions answered by your own documentation. Write actions such as reorders or return requests can follow later with explicit confirmation.",{"q":80,"a":81},"Who is liable when the support chatbot gives wrong information?","Usually the company. In Moffatt v. Air Canada, a Canadian tribunal rejected the argument that the chatbot was a separate entity and held the airline responsible for all information on its website, whether from a static page or a chatbot. It awarded about 650 Canadian dollars plus interest and fees.",{"q":83,"a":84},"How do I stop the assistant from showing another customer’s orders?","Do not let the model choose whose data to read. Tools take the customer and company ID from the authenticated session on the server, and the back-end API enforces the same scope. OWASP lists executing extensions in the user’s context as a mitigation for excessive agency.",{"q":86,"a":87},"Does the EU AI Act apply to a shop support chatbot?","Article 50 applies from 2 August 2026: providers must design AI systems that interact directly with people so that they are informed they are interacting with an AI, unless this is obvious. In practice, label the assistant clearly in the chat window.",{"q":89,"a":90},"How should I measure the assistant?","Measure resolved conversations, repeat contacts within a few days, handoff rate and the quality of handoffs, not only deflection. Vendors define resolution differently; Intercom, for example, bills an outcome when no further help is requested after the last answer.",[92,95,98,101,104,107,110,113,116],{"id":93,"title":94},"what-customers-ask","What B2B customers actually ask support",{"id":96,"title":97},"lessons-from-consumer-support","Two lessons from consumer support",{"id":99,"title":100},"architecture","Architecture: retrieval, tools and a handoff path",{"id":102,"title":103},"tools-in-customer-context","Tools that run in the customer’s context",{"id":105,"title":106},"what-stays-human","What stays with people",{"id":108,"title":109},"handoff","Designing the handoff",{"id":111,"title":112},"transparency-and-gdpr","Transparency, the AI Act and data protection",{"id":114,"title":115},"measuring-success","Measuring whether it helps",{"id":117,"title":118},"sources","Sources",[120,124,127,130,133,185,188,189,192,195,202,203,206,215,224,225,228,231,234,242,243,251,264,267,268,271,274,275,283,291,292,295,332,340,341],{"type":121,"content":122},"paragraph",[123],"Support in a B2B shop looks different from consumer support. The people writing in are purchasers, technicians and accountants who know exactly what they want: the delivery date of order 4711, a copy of last month’s invoice, the declaration of conformity for a part, the replacement for a discontinued article. Most of those answers already exist in the ERP, the PIM or a document store; what costs time is finding them and writing them down.",{"type":121,"content":125},[126],"That makes B2B support a good fit for an LLM assistant, and also a place where mistakes are expensive: a wrong delivery date stops a production line, a wrong price becomes a dispute. This post describes how I would design such an assistant for a B2B shop: which requests to take on, how tools get their permissions, where people stay in the loop, and how to tell whether it works.",{"type":128,"level":129,"id":93,"text":94},"heading",2,{"type":121,"content":131},[132],"Before choosing a model or a vendor, sort a few hundred real tickets into request types. In the B2B shops I work on, the same categories come back, and they differ mainly in where the answer lives and what a wrong answer costs.",{"type":134,"head":135,"rows":142},"table",[136,138,140],[137],"Request type",[139],"Where the answer lives",[141],"Assistant role",[143,150,157,164,171,178],[144,146,148],[145],"Order and delivery status",[147],"ERP, carrier tracking",[149],"Answer directly, read-only",[151,153,155],[152],"Invoice, credit note, delivery note copies",[154],"ERP or document archive",[156],"Answer directly with a download link",[158,160,162],[159],"Datasheets, certificates, declarations",[161],"PIM, DAM",[163],"Answer directly from the document",[165,167,169],[166],"Compatibility, successor articles",[168],"PIM relations, technical docs",[170],"Suggest with sources, offer a specialist",[172,174,176],[173],"Returns and complaints",[175],"Terms, ERP, quality team",[177],"Collect details, create a case, hand off",[179,181,183],[180],"Prices, quotes, payment terms",[182],"Sales, contracts",[184],"Hand off; never commit",{"type":121,"content":186},[187],"The first three rows are lookups with one correct answer in a system you control. They are where an assistant earns its keep. The last row is where it should never improvise.",{"type":128,"level":129,"id":96,"text":97},{"type":121,"content":190},[191],"The best-known numbers come from Klarna. In February 2024 the company reported that its AI assistant had handled 2.3 million conversations in its first month, two-thirds of its customer service chats, doing the equivalent work of 700 full-time agents, with a 25% drop in repeat inquiries and errands resolved in under 2 minutes instead of 11. In May 2025 its CEO told Bloomberg that Klarna was hiring human support staff again, because the cheaper AI service had produced lower quality, and that customers must always know there will be a human if they want one.",{"type":121,"content":193},[194],"The second lesson is legal. In Moffatt v. Air Canada, a British Columbia tribunal dealt with a chatbot that told a customer he could apply for a bereavement fare retroactively, which contradicted another page on the airline’s site. Air Canada argued that the chatbot was a separate legal entity responsible for its own actions. The tribunal rejected that, held the airline responsible for all information on its website, whether from a static page or a chatbot, found it had not taken reasonable care to ensure the chatbot was accurate, and awarded about 650 Canadian dollars plus interest and fees. It is a small-claims decision, not a precedent for Austria or Germany, but the reasoning is the one I would expect any court to follow.",{"type":196,"variant":197,"title":198,"body":199},"callout","note","The assistant speaks for the company",[200],[201],"Design it as you would a new support employee who is fast but has no authority: it may look things up and explain them, it may not promise delivery dates the ERP does not show, grant discounts, or interpret contracts.",{"type":128,"level":129,"id":99,"text":100},{"type":121,"content":204},[205],"A B2B support assistant has three kinds of capability, and keeping them separate is what makes it safe. Retrieval over public knowledge: product documentation, FAQs, terms and conditions, shipping rules. Tools over account data: orders, deliveries, invoices, all scoped to the logged-in company. And a handoff tool that creates a case in the ticket system or CRM with a summary, so the person who takes over does not start from zero.",{"type":207,"attrs":208,"inner":212,"caption":213},"diagram",{"viewBox":209,"role":210,"aria-labelledby":211},"0 0 720 180","img","b2bsup-arch-t b2bsup-arch-d","\u003Ctitle id=\"b2bsup-arch-t\">B2B support assistant architecture\u003C\u002Ftitle>\u003Cdesc id=\"b2bsup-arch-d\">The chat goes through a server route that holds the session; the model uses retrieval, scoped account tools and a handoff tool.\u003C\u002Fdesc>\u003Cdefs>\u003Cmarker id=\"b2bsup-arch-ah\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\">\u003Cpath d=\"M0 0L10 5L0 10z\" class=\"d-head\" \u002F>\u003C\u002Fmarker>\u003C\u002Fdefs>\u003Crect x=\"20\" y=\"52\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"79\" y=\"86\" text-anchor=\"middle\" class=\"d-text\">Buyer\u003C\u002Ftext>\u003Ctext x=\"79\" y=\"108\" text-anchor=\"middle\" class=\"d-small\">logged in\u003C\u002Ftext>\u003Cpath d=\"M138 90H158\" class=\"d-line\" marker-end=\"url(#b2bsup-arch-ah)\" \u002F>\u003Crect x=\"160\" y=\"52\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"220\" y=\"86\" text-anchor=\"middle\" class=\"d-text\">Server route\u003C\u002Ftext>\u003Ctext x=\"220\" y=\"108\" text-anchor=\"middle\" class=\"d-small\">session, limits\u003C\u002Ftext>\u003Cpath d=\"M279 90H299\" class=\"d-line\" marker-end=\"url(#b2bsup-arch-ah)\" \u002F>\u003Crect x=\"301\" y=\"52\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-fill-accent\" \u002F>\u003Ctext x=\"360\" y=\"86\" text-anchor=\"middle\" class=\"d-text\">Model\u003C\u002Ftext>\u003Ctext x=\"360\" y=\"108\" text-anchor=\"middle\" class=\"d-small\">plans, answers\u003C\u002Ftext>\u003Cpath d=\"M419 90H439\" class=\"d-line\" marker-end=\"url(#b2bsup-arch-ah)\" \u002F>\u003Crect x=\"441\" y=\"52\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"500\" y=\"86\" text-anchor=\"middle\" class=\"d-text\">Tools\u003C\u002Ftext>\u003Ctext x=\"500\" y=\"108\" text-anchor=\"middle\" class=\"d-small\">docs, orders, invoices\u003C\u002Ftext>\u003Cpath d=\"M560 90H580\" class=\"d-line\" marker-end=\"url(#b2bsup-arch-ah)\" \u002F>\u003Crect x=\"582\" y=\"52\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"641\" y=\"86\" text-anchor=\"middle\" class=\"d-text\">Handoff\u003C\u002Ftext>\u003Ctext x=\"641\" y=\"108\" text-anchor=\"middle\" class=\"d-small\">case + summary\u003C\u002Ftext>",[214],"The session, not the model, decides whose data the tools may read.",{"type":121,"content":216},[217,218,223],"For the retrieval side the usual RAG engineering applies: chunking, hybrid search for part numbers and reranking, as in ",{"tag":219,"to":220,"children":221},"link","\u002Fblog\u002Frag-pipeline-chunking-hybrid-search-reranking",[222],"a production RAG pipeline",". One B2B-specific point: terms, shipping rules and warranty conditions change, and an assistant that quotes last year’s return window is the Air Canada scenario in miniature. Index documents with a validity date and a single current version, and make the assistant cite the document it answered from.",{"type":128,"level":129,"id":102,"text":103},{"type":121,"content":226},[227],"OWASP’s LLM06:2025 Excessive Agency names three root causes: excessive functionality, excessive permissions and excessive autonomy. Its mitigations read like a design spec for a support assistant: offer only the tools you need, keep each tool narrow, execute extensions in the user’s context with minimum privileges, require approval for high-impact actions, and enforce authorisation in downstream systems rather than relying on the model to decide what is allowed.",{"type":121,"content":229},[230],"In code the rule is simple: the model may pass an order number, never a customer number. The server takes the company ID from the session and the back-end API checks it again. An injected instruction in a product review cannot make the assistant read someone else’s invoices, because no tool accepts that parameter.",{"type":232,"code":233},"code","\u002F\u002F server\u002Fapi\u002Fassistant.post.ts (Nuxt, AI SDK style tools)\nimport { z } from 'zod'\nimport { tool } from 'ai'\n\nexport default defineEventHandler(async (event) => {\n  const { user } = await requireUserSession(event)        \u002F\u002F 401 if not logged in\n  const companyId = user.companyId                          \u002F\u002F from the session, never from the model\n\n  const tools = {\n    getOrderStatus: tool({\n      description: 'Status, delivery date and tracking for one order of the current customer.',\n      inputSchema: z.object({ orderNumber: z.string().regex(\u002F^[0-9]{6,10}$\u002F) }),\n      execute: async ({ orderNumber }) => {\n        const order = await erp.getOrder(orderNumber, { companyId })   \u002F\u002F ERP enforces the scope too\n        if (!order) return { found: false }\n        return { found: true, status: order.status, confirmedDelivery: order.confirmedDate ?? null, tracking: order.trackingUrl ?? null }\n      },\n    }),\n    handOffToSupport: tool({\n      description: 'Create a support case with a summary when the request needs a person.',\n      inputSchema: z.object({ topic: z.enum(['price', 'complaint', 'return', 'technical', 'other']), summary: z.string().max(1500) }),\n      execute: async ({ topic, summary }) => crm.createCase({ companyId, userId: user.id, topic, summary }),\n    }),\n  }\n  \u002F\u002F ... streamText({ model, system, messages, tools, stopWhen: stepCountIs(5) })\n})\n",{"type":121,"content":235},[236,237,241],"Note what the order tool returns: a confirmed delivery date or null. If the ERP has no confirmed date, the assistant says so instead of estimating. That one design choice prevents the most damaging class of B2B support errors. The broader identity questions, service accounts and token scopes, are in ",{"tag":219,"to":238,"children":239},"\u002Fblog\u002Fai-agent-identity-least-privilege",[240],"AI agents are identities",".",{"type":128,"level":129,"id":105,"text":106},{"type":121,"content":244},[245,246,250],"Rollout is a sequence of permissions, not a launch. I start read-only: status, documents, invoices. Write actions such as reordering a previous order or opening a return request come next, and only with an explicit confirmation step in the UI that shows exactly what will be submitted, the pattern described in ",{"tag":219,"to":247,"children":248},"\u002Fblog\u002Fhuman-in-the-loop-ai-agents",[249],"human-in-the-loop approval gates",". Some things never move to the assistant:",{"type":252,"items":253},"list",[254,256,258,260,262],[255],"Prices, discounts and quotes outside the published price list.",[257],"Delivery commitments that the ERP has not confirmed.",[259],"Interpretation of contracts, warranty or liability.",[261],"Technical approval of a substitute part for a safety-relevant application.",[263],"Anything involving an upset key account; route by customer tier, not only by topic.",{"type":121,"content":265},[266],"For those cases the assistant’s job is to collect the facts, order number, article, quantity, what went wrong, and hand off with a summary. That is still valuable work, and it is the part customers notice when the person who calls back already knows the case.",{"type":128,"level":129,"id":108,"text":109},{"type":121,"content":269},[270],"Klarna’s CEO put the requirement plainly: customers must know there will always be a human if they want one. In a B2B shop that means a visible \"talk to a person\" option in the chat at all times, not only after three failed answers, plus clear information about support hours. When the assistant hands off, the case should contain the conversation, the tool results it used, and its own summary, marked as machine-generated so the agent checks it rather than trusts it.",{"type":121,"content":272},[273],"Two triggers for automatic handoff work well: the request falls into a topic on the \"stays with people\" list, or the assistant has no tool result or source for its answer. The second trigger is the important one. An assistant that says \"I could not find a confirmed delivery date, I have passed this to our order team\" is useful; one that guesses is a liability.",{"type":128,"level":129,"id":111,"text":112},{"type":121,"content":276},[277,278,282],"Article 50 of the EU AI Act applies from 2 August 2026. Its first paragraph requires providers to design AI systems that interact directly with people so that the people concerned are informed they are interacting with an AI system, unless that is obvious to a reasonably well-informed, observant and circumspect person. A chat widget that looks like live chat with a named agent is exactly the case where it is not obvious, so label it. My ",{"tag":219,"to":279,"children":280},"\u002Fblog\u002Feu-ai-act-article-50-developer-checklist",[281],"Article 50 checklist"," covers the implementation details.",{"type":121,"content":284},[285,286,290],"B2B does not mean no personal data. Contact names, email addresses and delivery addresses of people at your customers are personal data under the GDPR, and the conversation log will contain them. Decide the retention period for transcripts, what reaches the model provider and under which data processing terms, and whether you need a DPIA; the ",{"tag":219,"to":287,"children":288},"\u002Fblog\u002Fdpia-llm-feature-worked-example",[289],"worked DPIA example"," uses a support assistant as its case.",{"type":128,"level":129,"id":114,"text":115},{"type":121,"content":293},[294],"Deflection, the share of chats that never reach a person, is the number vendors like and the one that hides failures: a customer who gives up also never reaches a person. Vendors also define resolution in their own way. Intercom, which prices its Fin agent per outcome at 0.99 dollars, counts a resolution when no further help is requested after Fin’s last answer, and charges one outcome per conversation. That is a reasonable billing rule and a weak quality metric, so measure your own.",{"type":134,"head":296,"rows":303},[297,299,301],[298],"Metric",[300],"What it shows",[302],"How to collect it",[304,311,318,325],[305,307,309],[306],"Verified resolution rate",[308],"Answers that were correct and complete",[310],"Weekly human review of a random sample",[312,314,316],[313],"Repeat contact within 7 days",[315],"Problems that were not actually solved",[317],"Join chat and ticket data by customer",[319,321,323],[320],"Handoff rate and handoff quality",[322],"Whether cases arrive ready to work on",[324],"Agents rate the summary in the ticket",[326,328,330],[327],"Unsupported answers",[329],"Answers without a tool result or source",[331],"Automatic check in the trace",{"type":121,"content":333},[334,335,339],"Turn the reviewed conversations into an eval set and run it before every prompt or model change, as described in ",{"tag":219,"to":336,"children":337},"\u002Fblog\u002Fllm-evals-for-product-features",[338],"LLM evals for product features",". And before customers see anything, run the assistant for a few weeks as an internal tool for your own support team, drafting answers they send or discard. You get labelled data, the team learns where it fails, and the first customer-facing version starts with known limits instead of hopes.",{"type":128,"level":129,"id":117,"text":118},{"type":252,"ordered":342,"items":343},true,[344,348,351,354,357,360,363],[345],{"tag":346,"href":39,"children":347},"a",[38],[349],{"tag":346,"href":42,"children":350},[41],[352],{"tag":346,"href":45,"children":353},[44],[355],{"tag":346,"href":48,"children":356},[47],[358],{"tag":346,"href":51,"children":359},[50],[361],{"tag":346,"href":54,"children":362},[53],[364],{"tag":346,"href":57,"children":365},[56],[367,437,489,557],{"slug":368,"published":5,"minutes":369,"category":7,"tags":370,"keywords":375,"about":387,"sources":394,"cover":431,"og":432,"expertise":60,"locales":433,"lang":62,"title":434,"description":435,"coverAlt":436},"schema-org-structured-data-b2b-shop",10,[371,372,9,373,374],"Schema.org","Structured data","Technical SEO","Nuxt",[376,377,378,379,380,381,382,383,384,385,386],"schema.org b2b shop","product structured data b2b","merchant listing structured data","product snippet vs merchant listing","unitpricespecification referencequantity","productgroup variants structured data","organization schema vatid","b2b prices behind login seo","json-ld nuxt schema org","merchant return policy structured data","mpn gtin structured data",[388,390,393],{"name":371,"url":389},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSchema.org",{"name":391,"url":392},"JSON-LD","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJSON-LD",{"name":34,"url":35},[395,398,401,404,407,410,413,416,419,422,425,428],{"title":396,"url":397},"Google Search Central: Introduction to Product structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fproduct",{"title":399,"url":400},"Google Search Central: Merchant listing (Product, Offer) structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fmerchant-listing",{"title":402,"url":403},"Google Search Central: Product snippet structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fproduct-snippet",{"title":405,"url":406},"Google Search Central: Product variant structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fproduct-variants",{"title":408,"url":409},"Google Search Central: Organization structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Forganization",{"title":411,"url":412},"Google Search Central: Merchant return policy structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Freturn-policy",{"title":414,"url":415},"Google Search Central: General structured data guidelines","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fsd-policies",{"title":417,"url":418},"Google Search Central: Generate structured data with JavaScript","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fgenerate-structured-data-with-javascript",{"title":420,"url":421},"Schema.org: Offer","https:\u002F\u002Fschema.org\u002FOffer",{"title":423,"url":424},"Schema.org: UnitPriceSpecification","https:\u002F\u002Fschema.org\u002FUnitPriceSpecification",{"title":426,"url":427},"Unhead: Product schema (defineProduct)","https:\u002F\u002Funhead.unjs.io\u002Fdocs\u002Fschema-org\u002Fapi\u002Fschema\u002Fproduct",{"title":429,"url":430},"Nuxt SEO: Schema.org identity setup","https:\u002F\u002Fnuxtseo.com\u002Fdocs\u002Fschema-org\u002Fguides\u002Fsetup-identity","\u002Fimages\u002Fblog\u002Fschema-org-structured-data-b2b-shop\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fschema-org-structured-data-b2b-shop\u002Fog.jpg",[62,63,64],"Schema.org structured data for B2B shops: prices behind logins, units and variants","Product markup for B2B shops: snippet or merchant listing, which price to publish, part numbers, unit pricing, variants, Organization and SSR in Nuxt.","A product rectangle with a framed outline and a small price slot hidden behind a low wall.",{"slug":438,"published":439,"minutes":440,"category":7,"tags":441,"keywords":446,"about":457,"sources":466,"cover":482,"og":483,"expertise":484,"locales":485,"lang":62,"title":486,"description":487,"coverAlt":488},"nuxt-i18n-multilingual-seo-hreflang","2026-10-16",8,[374,442,443,444,445],"i18n","SEO","hreflang","Multilingual",[447,448,449,450,451,452,453,454,455,456],"nuxt i18n seo","nuxt hreflang","useLocaleHead","nuxt i18n canonical","hreflang x-default","prefix_except_default","multilingual seo nuxt","nuxt i18n sitemap hreflang","hreflang self-referencing","nuxt i18n baseUrl",[458,460,463],{"name":374,"url":459},"https:\u002F\u002Fnuxt.com\u002F",{"name":461,"url":462},"Hreflang","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHreflang",{"name":464,"url":465},"Internationalization and localization","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FInternationalization_and_localization",[467,470,473,476,479],{"title":468,"url":469},"Nuxt i18n docs: SEO (useLocaleHead)","https:\u002F\u002Fi18n.nuxtjs.org\u002Fdocs\u002Fguide\u002Fseo",{"title":471,"url":472},"Nuxt i18n docs: Routing strategies","https:\u002F\u002Fi18n.nuxtjs.org\u002Fdocs\u002Fguide",{"title":474,"url":475},"Google Search Central: Tell Google about localized versions of your page","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fspecialty\u002Finternational\u002Flocalized-versions",{"title":477,"url":478},"Google Search Central: Managing multi-regional and multilingual sites","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fspecialty\u002Finternational\u002Fmanaging-multi-regional-sites",{"title":480,"url":481},"Google Search Central: How to specify a canonical URL","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fcrawling-indexing\u002Fconsolidate-duplicate-urls","\u002Fimages\u002Fblog\u002Fnuxt-i18n-multilingual-seo-hreflang\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fnuxt-i18n-multilingual-seo-hreflang\u002Fog.jpg","vue-nuxt-developer",[62,63,64],"Multilingual SEO with Nuxt i18n: URL strategy, hreflang, canonicals and sitemaps","How to set up Nuxt i18n so search engines index every language: routing strategy, useLocaleHead, hreflang rules, partial translations and sitemaps.","A large sheet linked by three horizontal connectors to a column of three smaller sheets.",{"slug":490,"published":439,"minutes":491,"category":7,"tags":492,"keywords":497,"about":510,"sources":517,"cover":551,"og":552,"expertise":484,"locales":553,"lang":62,"title":554,"description":555,"coverAlt":556},"llm-endpoint-abuse-denial-of-wallet",14,[374,493,494,495,496],"LLM security","Rate limiting","OWASP","Cost control",[498,499,500,501,502,503,504,505,506,507,508,509],"denial of wallet llm","owasp llm10 unbounded consumption","rate limit llm api nuxt","nuxt security rate limiter","protect openai api key frontend","llm token budget per user","limit llm cost per user","nitro api rate limiting","cloudflare turnstile nuxt","anthropic spend limit","openai hard spend limit","llm prompt size limit",[511,513,516],{"name":494,"url":512},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRate_limiting",{"name":514,"url":515},"Denial-of-service attack","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDenial-of-service_attack",{"name":374,"url":459},[518,521,524,527,530,533,536,539,542,545,548],{"title":519,"url":520},"OWASP GenAI Security Project: LLM10:2025 Unbounded Consumption","https:\u002F\u002Fgenai.owasp.org\u002Fllmrisk\u002Fllm102025-unbounded-consumption\u002F",{"title":522,"url":523},"Nuxt Security: Rate Limiter middleware","https:\u002F\u002Fnuxt-security.vercel.app\u002Fmiddleware\u002Frate-limiter",{"title":525,"url":526},"Nuxt Security: Request Size Limiter middleware","https:\u002F\u002Fnuxt-security.vercel.app\u002Fmiddleware\u002Frequest-size-limiter",{"title":528,"url":529},"nuxt-auth-utils: README (GitHub)","https:\u002F\u002Fgithub.com\u002Fatinux\u002Fnuxt-auth-utils",{"title":531,"url":532},"h3: request utilities (getRequestIP, readValidatedBody)","https:\u002F\u002Fh3.dev\u002Futils\u002Frequest",{"title":534,"url":535},"Upstash: Ratelimiting algorithms","https:\u002F\u002Fupstash.com\u002Fdocs\u002Fredis\u002Fsdks\u002Fratelimit-ts\u002Falgorithms",{"title":537,"url":538},"Cloudflare Turnstile: Validate the token","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fturnstile\u002Fget-started\u002Fserver-side-validation\u002F",{"title":540,"url":541},"Claude Platform Docs: Rate limits and spend limits","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fapi\u002Frate-limits",{"title":543,"url":544},"Claude Platform Docs: Token counting","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fbuild-with-claude\u002Ftoken-counting",{"title":546,"url":547},"OpenAI API Docs: Spend limits","https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Fspend-limits",{"title":549,"url":550},"OpenAI API Docs: Rate limits","https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fguides\u002Frate-limits","\u002Fimages\u002Fblog\u002Fllm-endpoint-abuse-denial-of-wallet\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllm-endpoint-abuse-denial-of-wallet\u002Fog.jpg",[62,63,64],"Denial of wallet: protecting public LLM endpoints in a Nuxt app","How to stop abuse and runaway cost on a public LLM route in Nuxt: auth, per-user rate limits, prompt caps, token budgets, Turnstile and spend limits.","Tiny dots crowding at a narrow slot in a wall, two through, a rail capping the far side.",{"slug":558,"published":559,"minutes":6,"category":7,"tags":560,"keywords":564,"about":572,"sources":579,"cover":601,"og":602,"expertise":603,"locales":604,"lang":62,"title":605,"description":606,"coverAlt":607},"llms-txt-vs-markdown-content-negotiation","2026-10-07",[561,562,563,374],"llms.txt","Content negotiation","AI agents",[561,565,566,567,568,569,570,571],"markdown for agents","content negotiation","AI crawlers","generative engine optimization","Accept text\u002Fmarkdown header","llms-full.txt","agent-ready website",[573,576],{"name":574,"url":575},"Markdown","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMarkdown",{"name":577,"url":578},"Hypertext Transfer Protocol","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHypertext_Transfer_Protocol",[580,583,586,589,592,595,598],{"title":581,"url":582},"llmstxt.org: The \u002Fllms.txt file (proposal, version 2)","https:\u002F\u002Fllmstxt.org\u002F",{"title":584,"url":585},"llmstxt.org: Changes from v1 to v2","https:\u002F\u002Fllmstxt.org\u002Fchanges.html",{"title":587,"url":588},"Cloudflare changelog: Markdown for Agents (12 February 2026)","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fchangelog\u002Fpost\u002F2026-02-12-markdown-for-agents\u002F",{"title":590,"url":591},"Cloudflare docs: Markdown for Agents","https:\u002F\u002Fdevelopers.cloudflare.com\u002Ffundamentals\u002Freference\u002Fmarkdown-for-agents\u002F",{"title":593,"url":594},"Ahrefs: 137K sites analyzed, 97% of llms.txt files never get read (June 2026)","https:\u002F\u002Fahrefs.com\u002Fblog\u002Fllmstxt-study\u002F",{"title":596,"url":597},"Checkly: The current state of content negotiation for AI agents (February 2026)","https:\u002F\u002Fwww.checklyhq.com\u002Fblog\u002Fstate-of-ai-agent-content-negotation\u002F",{"title":599,"url":600},"Suganthan: tracking Cloudflare Markdown for Agents on one site","https:\u002F\u002Fsuganthan.com\u002Fblog\u002Fcloudflare-markdown-for-agents\u002F","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fog.jpg","ai-engineer",[62,63,64],"llms.txt vs Markdown content negotiation: what agents actually fetch","llms.txt is a proposal, Markdown content negotiation is a header. What AI agents fetch, what the logs show, and how to serve both from Nuxt and nginx.","Two doors with a square and a circle handle, an incoming square turning toward the right one.",1792222776124]