Tool tree
gradeNot graded
search_productsAnswer
## Use when A customer asks about products and you need to **find candidates in the public catalog** by name, SKU, brand, category, type, price range or country. Only **active** (publicly visible) products are returned. Use `get_product_detail` for full information about one product. ## Returns `items[]` with public product summaries (name, url, from-price, security rating, availability) and `next_cursor` for pagination (null = end). - `price` is the from-price in EUR including VAT as shown in the shop. - `in_stock` is a flag, not a quantity. - Max 100 items per call. ## Example `{"search":"cilinder","brand_slug":"dom","price_max":150,"limit":10}`
View tool JSON
{
"name": "search_products",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nA customer asks about products and you need to **find candidates in the public catalog** by name, SKU, brand, category, type, price range or country.\n\nOnly **active** (publicly visible) products are returned. Use `get_product_detail` for full information about one product.\n\n## Returns\n`items[]` with public product summaries (name, url, from-price, security rating, availability) and `next_cursor` for pagination (null = end).\n\n- `price` is the from-price in EUR including VAT as shown in the shop.\n- `in_stock` is a flag, not a quantity.\n- Max 100 items per call.\n\n## Example\n`{\"search\":\"cilinder\",\"brand_slug\":\"dom\",\"price_max\":150,\"limit\":10}`",
"inputSchema": {
"properties": {
"search": {
"description": "Keywords; every word must match the product name or brand name.",
"type": [
"string",
"null"
]
},
"sku": {
"description": "Substring match on linked stock_items.sku.",
"type": [
"string",
"null"
]
},
"category_id": {
"description": "Filter to products in this category.",
"type": [
"integer",
"null"
]
},
"brand_id": {
"description": "Filter by brand id.",
"type": [
"integer",
"null"
]
},
"brand_slug": {
"description": "Filter by brand slug (e.g. \"dom\", \"abus\").",
"type": [
"string",
"null"
]
},
"type": {
"description": "Product type: \"simple\" or \"configurable\" (cylinder sets).",
"type": [
"string",
"null"
]
},
"price_min": {
"description": "Minimum from-price in EUR.",
"type": [
"number",
"null"
]
},
"price_max": {
"description": "Maximum from-price in EUR.",
"type": [
"number",
"null"
]
},
"country": {
"description": "Only products available in this country: nl, be or es.",
"type": [
"string",
"null"
]
},
"cursor": {
"description": "Cursor from a previous response.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Number of items to return (max 100, default 25).",
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
}get_product_detailAnswer
## Use when You need **everything a customer can see about one product**: description, USPs, pros/cons, specifications, price (incl. any active sale), delivery expectation, security certifications, return policy, cross-sells and review score. Identify the product by `product_id` or `slug` (one of the two is required). Only active products are returned. ## Returns One `product` object. Long texts come as `{html, text}` pairs — quote from `text`. - `specs[]` are the frontend-visible attributes with their values. - `sale` is the active promotion (percentage) or null. - For product-specific FAQs call `search_faqs` with this `product_id`. - `locale` (nl/be/es, default nl) applies translated texts when available. ## Example `{"slug":"dom-plura-skg2-cilinder","locale":"nl"}`
View tool JSON
{
"name": "get_product_detail",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nYou need **everything a customer can see about one product**: description, USPs, pros/cons, specifications, price (incl. any active sale), delivery expectation, security certifications, return policy, cross-sells and review score.\n\nIdentify the product by `product_id` or `slug` (one of the two is required). Only active products are returned.\n\n## Returns\nOne `product` object. Long texts come as `{html, text}` pairs — quote from `text`.\n\n- `specs[]` are the frontend-visible attributes with their values.\n- `sale` is the active promotion (percentage) or null.\n- For product-specific FAQs call `search_faqs` with this `product_id`.\n- `locale` (nl/be/es, default nl) applies translated texts when available.\n\n## Example\n`{\"slug\":\"dom-plura-skg2-cilinder\",\"locale\":\"nl\"}`",
"inputSchema": {
"properties": {
"product_id": {
"description": "Product id (from search_products).",
"type": [
"integer",
"null"
]
},
"slug": {
"description": "Product slug; alternative to product_id.",
"type": [
"string",
"null"
]
},
"locale": {
"description": "Language for texts: nl (default), be or es.",
"type": [
"string",
"null"
]
}
},
"type": "object"
}
}get_product_optionsAnswer
## Use when A customer asks what is possible for a **configurable product** (cylinder sets): available sizes, colors, knobs, panic function, smartlock adapters, accessories — and what each option costs extra or adds to the delivery time. Identify the product by `product_id` or `slug`. Only active products. ## Returns - `options[]`: one entry per configurator option group with its choices. `surcharge` is the extra price in EUR (0 = included; null = unknown — never tell a customer an option is free or included when surcharge is null), `extra_delivery_days` adds to the base delivery time, `related_product` links choices that are themselves products (e.g. a smartlock added to a cylinder). - `included_keys`: how many keys come with the set for free. Either `keys_per_cylinder` (multiply by the number of cylinders) or `per_set_size` (total free keys per set size; a set larger than the largest listed size gets that largest amount). `null` = unknown for this product — never promise a number of keys then. - `sizes[]`: available cylinder size combinations (`a`/`b` in mm as the customer measures them) with the price per size. `sizes_total` may exceed the returned list (capped). Answers questions like "kan deze cilinder in het zwart met knop?" or "is maat 35/45 leverbaar en wat kost die?". ## Example `{"slug":"dom-plura-skg2-cilinder"}` or `{"product_id":3}`
View tool JSON
{
"name": "get_product_options",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nA customer asks what is possible for a **configurable product** (cylinder sets): available sizes, colors, knobs, panic function, smartlock adapters, accessories — and what each option costs extra or adds to the delivery time.\n\nIdentify the product by `product_id` or `slug`. Only active products.\n\n## Returns\n- `options[]`: one entry per configurator option group with its choices. `surcharge` is the extra price in EUR (0 = included; null = unknown — never tell a customer an option is free or included when surcharge is null), `extra_delivery_days` adds to the base delivery time, `related_product` links choices that are themselves products (e.g. a smartlock added to a cylinder).\n- `included_keys`: how many keys come with the set for free. Either `keys_per_cylinder` (multiply by the number of cylinders) or `per_set_size` (total free keys per set size; a set larger than the largest listed size gets that largest amount). `null` = unknown for this product — never promise a number of keys then.\n- `sizes[]`: available cylinder size combinations (`a`/`b` in mm as the customer measures them) with the price per size. `sizes_total` may exceed the returned list (capped).\n\nAnswers questions like \"kan deze cilinder in het zwart met knop?\" or \"is maat 35/45 leverbaar en wat kost die?\".\n\n## Example\n`{\"slug\":\"dom-plura-skg2-cilinder\"}` or `{\"product_id\":3}`",
"inputSchema": {
"properties": {
"product_id": {
"description": "Product id (from search_products).",
"type": [
"integer",
"null"
]
},
"slug": {
"description": "Product slug; alternative to product_id.",
"type": [
"string",
"null"
]
}
},
"type": "object"
}
}find_cylinder_sizeAnswer
## Use when A customer wants to know which cylinder size to order for a configurable cylinder. Always use this tool for size advice — never calculate a size yourself. Call it **directly** with the measured millimetres per side (outside named first; centre of the fixing bolt to the end on that side). Do not ask follow-up questions before calling — whether and what to ask the customer is defined by the client's agent instructions, not by this tool. `outside_fit`/`inside_fit` are **optional** refinements (default `flush`). Pass them, with the offset in mm, only when the customer already mentioned that the current cylinder protrudes or is recessed relative to the door hardware. ## What it does Per side it computes the real depth to the door face (flush = measured, protruding = measured − offset, recessed = measured + offset), then picks from this product's size series the size that protrudes at most 3 mm, or else recesses at most 3 mm. Sides are computed independently and combined (e.g. `32/47`). ## Returns Structured data only — how to phrase the advice is up to the client (chatbot prompt / agent instructions). - `status`: `ok`, `below_minimum`, `above_maximum`, `outside_3mm_rule` or `not_applicable` (product has no adjustable sizes). - `recommended_size`: the size to order (e.g. "32/47") with `price`, or null when a side falls outside the range. - `sides`: per-side detail — `effective_mm` (real depth), `recommended`, `overshoot_mm`, `within_rule`, `below_minimum`, `above_maximum`. - `half_cylinder_possible`: true when this product also offers a half cylinder (short 10 mm side); relevant when a side is below the smallest standard size. - `specialist`: name + phone of the cylinder specialist, for referral when no size fits. Sizes are symmetric data: 32/47 is the same catalog row as 47/32; order only matters when a knob is chosen (knob belongs on the inside). ## Example `{"product_id":3,"outside_mm":30,"outside_fit":"flush","inside_mm":45,"inside_fit":"flush"}`
View tool JSON
{
"name": "find_cylinder_size",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nA customer wants to know which cylinder size to order for a configurable cylinder. Always use this tool for size advice — never calculate a size yourself.\n\nCall it **directly** with the measured millimetres per side (outside named first; centre of the fixing bolt to the end on that side). Do not ask follow-up questions before calling — whether and what to ask the customer is defined by the client's agent instructions, not by this tool.\n\n`outside_fit`/`inside_fit` are **optional** refinements (default `flush`). Pass them, with the offset in mm, only when the customer already mentioned that the current cylinder protrudes or is recessed relative to the door hardware.\n\n## What it does\nPer side it computes the real depth to the door face (flush = measured, protruding = measured − offset, recessed = measured + offset), then picks from this product's size series the size that protrudes at most 3 mm, or else recesses at most 3 mm. Sides are computed independently and combined (e.g. `32/47`).\n\n## Returns\nStructured data only — how to phrase the advice is up to the client (chatbot prompt / agent instructions).\n- `status`: `ok`, `below_minimum`, `above_maximum`, `outside_3mm_rule` or `not_applicable` (product has no adjustable sizes).\n- `recommended_size`: the size to order (e.g. \"32/47\") with `price`, or null when a side falls outside the range.\n- `sides`: per-side detail — `effective_mm` (real depth), `recommended`, `overshoot_mm`, `within_rule`, `below_minimum`, `above_maximum`.\n- `half_cylinder_possible`: true when this product also offers a half cylinder (short 10 mm side); relevant when a side is below the smallest standard size.\n- `specialist`: name + phone of the cylinder specialist, for referral when no size fits.\n\nSizes are symmetric data: 32/47 is the same catalog row as 47/32; order only matters when a knob is chosen (knob belongs on the inside).\n\n## Example\n`{\"product_id\":3,\"outside_mm\":30,\"outside_fit\":\"flush\",\"inside_mm\":45,\"inside_fit\":\"flush\"}`",
"inputSchema": {
"properties": {
"product_id": {
"description": "Product id (from search_products).",
"type": [
"integer",
"null"
]
},
"slug": {
"description": "Product slug; alternative to product_id.",
"type": [
"string",
"null"
]
},
"outside_mm": {
"description": "Measured outside dimension in mm (named first).",
"type": "integer"
},
"inside_mm": {
"description": "Measured inside dimension in mm.",
"type": "integer"
},
"outside_fit": {
"description": "How the current cylinder sits on the outside: flush (default), protruding, or recessed.",
"type": [
"string",
"null"
]
},
"inside_fit": {
"description": "How the current cylinder sits on the inside: flush (default), protruding, or recessed.",
"type": [
"string",
"null"
]
},
"outside_offset_mm": {
"description": "mm the cylinder protrudes/recesses on the outside (required when not flush).",
"type": [
"integer",
"null"
]
},
"inside_offset_mm": {
"description": "mm the cylinder protrudes/recesses on the inside (required when not flush).",
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
}get_delivery_infoAnswer
## Use when A customer asks **"wanneer heb ik het in huis?"** for a specific product. Returns the same delivery expectation the shop shows: base delivery days, the expected delivery date (NL business days, holidays excluded, 17:00 cut-off, evening delivery via Red je Pakket when applicable) and which configurator options add extra days. Identify the product by `product_id` or `slug`. Pass `extra_days` to recompute for a chosen option (e.g. a color with +2 days from `option_delays`). ## Returns - `in_stock`, `delivery_days` and `expected` (ISO date + the Dutch human-readable text shown in the shop). - `option_delays[]`: option choices that extend the delivery time. The estimate is for delivery within the Netherlands, calculated at the moment of the call. ## Example `{"slug":"dom-plura-skg2-cilinder"}` then `{"slug":"dom-plura-skg2-cilinder","extra_days":2}`
View tool JSON
{
"name": "get_delivery_info",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nA customer asks **\"wanneer heb ik het in huis?\"** for a specific product. Returns the same delivery expectation the shop shows: base delivery days, the expected delivery date (NL business days, holidays excluded, 17:00 cut-off, evening delivery via Red je Pakket when applicable) and which configurator options add extra days.\n\nIdentify the product by `product_id` or `slug`. Pass `extra_days` to recompute for a chosen option (e.g. a color with +2 days from `option_delays`).\n\n## Returns\n- `in_stock`, `delivery_days` and `expected` (ISO date + the Dutch human-readable text shown in the shop).\n- `option_delays[]`: option choices that extend the delivery time.\n\nThe estimate is for delivery within the Netherlands, calculated at the moment of the call.\n\n## Example\n`{\"slug\":\"dom-plura-skg2-cilinder\"}` then `{\"slug\":\"dom-plura-skg2-cilinder\",\"extra_days\":2}`",
"inputSchema": {
"properties": {
"product_id": {
"description": "Product id (from search_products).",
"type": [
"integer",
"null"
]
},
"slug": {
"description": "Product slug; alternative to product_id.",
"type": [
"string",
"null"
]
},
"extra_days": {
"description": "Extra delivery days from a chosen option (see option_delays).",
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
}get_product_reviewsAnswer
## Use when A customer asks about **experiences with a product**, or you want to quote real customer feedback. Only approved, published reviews are returned — the same set shown on the product page, including reviews of the product's color/version variants. Identify the product by `product_id` or `slug`. ## Returns - `summary`: total count, average score and the score distribution (1-5). - `items[]`: most recent reviews first (title, content, score, date). Reviews are anonymous here; no customer names are exposed. ## Example `{"product_id":88,"limit":5}`
View tool JSON
{
"name": "get_product_reviews",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nA customer asks about **experiences with a product**, or you want to quote real customer feedback. Only approved, published reviews are returned — the same set shown on the product page, including reviews of the product's color/version variants.\n\nIdentify the product by `product_id` or `slug`.\n\n## Returns\n- `summary`: total count, average score and the score distribution (1-5).\n- `items[]`: most recent reviews first (title, content, score, date). Reviews are anonymous here; no customer names are exposed.\n\n## Example\n`{\"product_id\":88,\"limit\":5}`",
"inputSchema": {
"properties": {
"product_id": {
"description": "Product id (from search_products).",
"type": [
"integer",
"null"
]
},
"slug": {
"description": "Product slug; alternative to product_id.",
"type": [
"string",
"null"
]
},
"min_score": {
"description": "Only reviews with at least this score (1-5).",
"type": [
"integer",
"null"
]
},
"max_score": {
"description": "Only reviews with at most this score (1-5).",
"type": [
"integer",
"null"
]
},
"limit": {
"description": "Number of reviews to return (max 50, default 10).",
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
}search_faqsAnswer
## Use when A customer asks a question that may already be answered in the shop's **FAQ**: shipping, returns, certificates, keys, installation, product-specific questions. Search by keyword, browse a category, or pass a `product_id` to get the FAQs shown on that product's page (linked directly and via the product's FAQ category). ## Returns `items[]` with question, answer (`{html, text}`) and category. Ordered by category position; max 100 items. For keyword searches, `page_faqs[]` additionally holds matching Q&As that live inline on published advice pages (with a `page` title + URL to cite) — those are not in the FAQ table. - `product_only` FAQs are only shown on product pages in the shop; without a `product_id` filter they are still included here so you can answer customer questions. ## Example `{"search":"certificaat"}` or `{"product_id":42}`
View tool JSON
{
"name": "search_faqs",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nA customer asks a question that may already be answered in the shop's **FAQ**: shipping, returns, certificates, keys, installation, product-specific questions.\n\nSearch by keyword, browse a category, or pass a `product_id` to get the FAQs shown on that product's page (linked directly and via the product's FAQ category).\n\n## Returns\n`items[]` with question, answer (`{html, text}`) and category. Ordered by category position; max 100 items.\n\nFor keyword searches, `page_faqs[]` additionally holds matching Q&As that live inline on published advice pages (with a `page` title + URL to cite) — those are not in the FAQ table.\n\n- `product_only` FAQs are only shown on product pages in the shop; without a `product_id` filter they are still included here so you can answer customer questions.\n\n## Example\n`{\"search\":\"certificaat\"}` or `{\"product_id\":42}`",
"inputSchema": {
"properties": {
"search": {
"description": "Keyword match on question or answer.",
"type": [
"string",
"null"
]
},
"faq_category_id": {
"description": "Filter to one FAQ category.",
"type": [
"integer",
"null"
]
},
"product_id": {
"description": "FAQs shown on this product's page (direct links + product FAQ category).",
"type": [
"integer",
"null"
]
},
"limit": {
"description": "Number of items to return (max 100, default 25).",
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
}get_help_blockAnswer
## Use when You need one of the shop's **curated help texts** (table `blocks`) — the same texts shown to customers on product pages, the configurator and checkout. These are the canonical explanations; prefer them over improvising an answer. Fetch by exact `slug`, or search by keyword in slug/title/body. The full slug index is available as resource `products://blocks`. Common slugs: `cilinder-keuze-uitleg`, `certificaat`, `skg-keurmerk-uitleg`, `kerntrekbeveiliging-uitleg`, `gelijksluitend`, `paniekfunctie`, `laagsteprijsgarantie`, `maatgarantie`, and per brand `knoppen-merk-{brand-slug}` / `cilindermaat-merk-{brand-slug}`. ## Returns `items[]` with slug, title and body (`{html, text}`). Max 20 items. ## Example `{"slug":"skg-keurmerk-uitleg"}` or `{"search":"kerntrek"}`
View tool JSON
{
"name": "get_help_block",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nYou need one of the shop's **curated help texts** (table `blocks`) — the same texts shown to customers on product pages, the configurator and checkout. These are the canonical explanations; prefer them over improvising an answer.\n\nFetch by exact `slug`, or search by keyword in slug/title/body. The full slug index is available as resource `products://blocks`.\n\nCommon slugs: `cilinder-keuze-uitleg`, `certificaat`, `skg-keurmerk-uitleg`, `kerntrekbeveiliging-uitleg`, `gelijksluitend`, `paniekfunctie`, `laagsteprijsgarantie`, `maatgarantie`, and per brand `knoppen-merk-{brand-slug}` / `cilindermaat-merk-{brand-slug}`.\n\n## Returns\n`items[]` with slug, title and body (`{html, text}`). Max 20 items.\n\n## Example\n`{\"slug\":\"skg-keurmerk-uitleg\"}` or `{\"search\":\"kerntrek\"}`",
"inputSchema": {
"properties": {
"slug": {
"description": "Exact block slug (see resource products://blocks).",
"type": [
"string",
"null"
]
},
"search": {
"description": "Keyword match on slug, title or body; used when slug is not given.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Number of items to return (max 20, default 5).",
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
}search_adviceAnswer
## Use when A customer needs **guidance rather than a spec**: which lock to choose, how to measure a cylinder, how to remove or install one, burglary prevention. The shop's advice pages ("advies") are written buying guides and how-tos with recommended products. Search by keyword or browse a category (resource-style list when no filters are given). Results mirror what the website shows on /advies: published landing pages. ## Returns `items[]` with title, public URL, category, a plain-text `body` (truncated to ~2000 chars) and `recommended_product_ids` — feed those ids to `get_product_detail` to suggest concrete products. ## Example `{"search":"opmeten"}` or `{"advice_category_id":1}`
View tool JSON
{
"name": "search_advice",
"kind": "answer",
"impl": "imperative",
"description": "## Use when\nA customer needs **guidance rather than a spec**: which lock to choose, how to measure a cylinder, how to remove or install one, burglary prevention. The shop's advice pages (\"advies\") are written buying guides and how-tos with recommended products.\n\nSearch by keyword or browse a category (resource-style list when no filters are given). Results mirror what the website shows on /advies: published landing pages.\n\n## Returns\n`items[]` with title, public URL, category, a plain-text `body` (truncated to ~2000 chars) and `recommended_product_ids` — feed those ids to `get_product_detail` to suggest concrete products.\n\n## Example\n`{\"search\":\"opmeten\"}` or `{\"advice_category_id\":1}`",
"inputSchema": {
"properties": {
"search": {
"description": "Keyword match on title, slug or page content.",
"type": [
"string",
"null"
]
},
"advice_category_id": {
"description": "Filter to one advice category.",
"type": [
"integer",
"null"
]
},
"include_body": {
"description": "Include the page text (default true; truncated to ~2000 chars).",
"type": [
"boolean",
"null"
]
},
"limit": {
"description": "Number of pages to return (max 25, default 10).",
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
}resolve_certificate_codeAction
## Use when A customer sent a photo of a security card / certificate, or typed its code, and you need to know which model to reorder. Read the code off the card and pass it verbatim; never derive a model from a code yourself. A card can show other printed boxes next to the code, such as a generation marker (e.g. "G2"). Those do not belong to the code: leave them out, pass only the code itself. Pass `brand` whenever the card shows one (M&C, ABUS, DOM, NEMEF, EVVA). It is how a wrong reading gets caught: if the code resolves to models of another brand, `brand_conflict` comes back true and you must not answer from this result. Pass `reorder_type` only when the customer already said whether they need a cylinder (1) or keys (2). ## What it does Normalises the code (strips spaces and punctuation, uppercases) and matches it against the reorder code patterns of the webshop, the same list the shop's own reorder filter uses. Returns the candidate models plus the matching reorder products. ## Returns - `code`: the normalised code. - `models`: candidate models, most often one. Several means the code alone cannot tell them apart; the model name printed on the card decides, otherwise ask the customer. - `products`: reorder products per candidate model, with `url`, `brand` and `reorder_type` (1 = cylinder, 2 = keys). More than one product of the same `reorder_type` can come back for a single model, for example a sluitplan variant next to the regular version, or two different SKG levels, and they are genuinely different products. This tool never picks between them; ask the customer which one matches their situation instead of guessing. - `brand_conflict`: true when `brand` was given and none of the products carry it. Candidates are matched by name, so a `products` entry can belong to a model whose name only resembles the resolved model (e.g. a "+" variant of it). Have the customer confirm the exact product before ordering; never just take the first one. An empty `models` means the code is not covered. Do not guess a model; escalate. When `models` is not empty but `products` is empty, the catalog has no reorder product for that model. Do not invent a link; escalate instead. ## Example `{"code":"084E2658","brand":"M&C"}`
View tool JSON
{
"name": "resolve_certificate_code",
"kind": "act",
"impl": "imperative",
"description": "## Use when\nA customer sent a photo of a security card / certificate, or typed its code, and you need to know which model to reorder. Read the code off the card and pass it verbatim; never derive a model from a code yourself.\n\nA card can show other printed boxes next to the code, such as a generation marker (e.g. \"G2\"). Those do not belong to the code: leave them out, pass only the code itself.\n\nPass `brand` whenever the card shows one (M&C, ABUS, DOM, NEMEF, EVVA). It is how a wrong reading gets caught: if the code resolves to models of another brand, `brand_conflict` comes back true and you must not answer from this result.\n\nPass `reorder_type` only when the customer already said whether they need a cylinder (1) or keys (2).\n\n## What it does\nNormalises the code (strips spaces and punctuation, uppercases) and matches it against the reorder code patterns of the webshop, the same list the shop's own reorder filter uses. Returns the candidate models plus the matching reorder products.\n\n## Returns\n- `code`: the normalised code.\n- `models`: candidate models, most often one. Several means the code alone cannot tell them apart; the model name printed on the card decides, otherwise ask the customer.\n- `products`: reorder products per candidate model, with `url`, `brand` and `reorder_type` (1 = cylinder, 2 = keys). More than one product of the same `reorder_type` can come back for a single model, for example a sluitplan variant next to the regular version, or two different SKG levels, and they are genuinely different products. This tool never picks between them; ask the customer which one matches their situation instead of guessing.\n- `brand_conflict`: true when `brand` was given and none of the products carry it.\n\nCandidates are matched by name, so a `products` entry can belong to a model whose name only resembles the resolved model (e.g. a \"+\" variant of it). Have the customer confirm the exact product before ordering; never just take the first one.\n\nAn empty `models` means the code is not covered. Do not guess a model; escalate.\n\nWhen `models` is not empty but `products` is empty, the catalog has no reorder product for that model. Do not invent a link; escalate instead.\n\n## Example\n`{\"code\":\"084E2658\",\"brand\":\"M&C\"}`",
"inputSchema": {
"properties": {
"code": {
"description": "The code as printed or stamped on the card, verbatim.",
"type": "string"
},
"brand": {
"description": "Brand shown on the card (M&C, ABUS, DOM, NEMEF, EVVA), for the conflict check.",
"type": [
"string",
"null"
]
},
"reorder_type": {
"description": "1 = cylinder, 2 = keys. Omit when unknown.",
"type": [
"integer",
"null"
]
}
},
"type": "object",
"required": [
"code"
]
}
}current_promotionsAction
## Use when A customer asks whether there is **a sale or discount right now** ("is er korting?", "zijn er acties?"), without pointing at one specific product. For the effective sale price of one product, `get_product_detail` already returns its active `sale`. ## Returns `promotions[]`, one entry per active sale: - `name` and `discount_percent` (percentage off the shop price). - `brand`: the brand the sale applies to (sales are brand-scoped). - `product_overrides[]`: products with a deviating percentage; `excluded` true means the product does not join the sale at all. An empty list means there are no active promotions; say so, do not invent one. ## Example `{}` or `{"brand_slug":"dom"}`
View tool JSON
{
"name": "current_promotions",
"kind": "act",
"impl": "imperative",
"description": "## Use when\nA customer asks whether there is **a sale or discount right now** (\"is er korting?\", \"zijn er acties?\"), without pointing at one specific product. For the effective sale price of one product, `get_product_detail` already returns its active `sale`.\n\n## Returns\n`promotions[]`, one entry per active sale:\n- `name` and `discount_percent` (percentage off the shop price).\n- `brand`: the brand the sale applies to (sales are brand-scoped).\n- `product_overrides[]`: products with a deviating percentage; `excluded` true means the product does not join the sale at all.\n\nAn empty list means there are no active promotions; say so, do not invent one.\n\n## Example\n`{}` or `{\"brand_slug\":\"dom\"}`",
"inputSchema": {
"properties": {
"brand_id": {
"description": "Only promotions for this brand id.",
"type": [
"integer",
"null"
]
},
"brand_slug": {
"description": "Only promotions for this brand slug (e.g. \"dom\").",
"type": [
"string",
"null"
]
}
},
"type": "object"
}
}