kapruka_check_delivery
Check whether Kapruka can deliver to a given city on a given date, and the delivery fee.
Returns whether the requested date is available (if not, the next available
date plus reason) and the delivery fee the checkout will charge.
THE FEE DEPENDS ON THE CART AND THE CURRENCY — the city's base rate is not
what checkout charges. Sri Lankan customers (LKR) pay the city rate capped
by the cart value (25% of the item value, at least LKR 300; some remote
cities 50%), so a small cart to a far city pays far less than the rate.
Overseas customers (USD) pay a fixed USD fee per city, whatever the cart.
Pass `currency` and the `cart` you are about to order and the answer gives
the EXACT fee kapruka_create_order will charge ("Delivery fee for this
cart"). Without a cart, an LKR answer gives only the MOST the fee can be
("up to"). One shipment per order: the fee covers the whole cart.
Pass `product_id` whenever the customer has named a product: the answer then
also checks that ITEM's delivery scope (restaurant food, hotel cakes and
liquor only reach selected cities, typically the Colombo area). With a
product_id, `available` is true only if the date is open AND the item is
deliverable to that city. When `item_deliverable` is false, offer the
customer one of the returned `deliverable_cities` or an island-wide
alternative — do not attempt kapruka_create_order with the same city, it
will be rejected. An unknown product_id is silently ignored (no item fields
in the result), so check `item_deliverable` is present before relying on it.
product_id also selects the SAME-DAY rule the checkout applies to that item:
vendor-delivered items (restaurant food) can go today until late afternoon;
ordinary items move to the next date; ordinary same-day is only possible early
in the morning near Colombo. So a check without product_id can differ from one
with it — always pass it once an item is chosen. When `available` is false,
offer `next_available_date`. Do not interpret the `reason` text (it is the
website's wording and may say slots are full when the real cause is the cutoff).
ECONOMY DELIVERY (cheaper courier, a few days instead of a chosen date) is
worked out from the `cart` (and `gift_message`, which you should pass when
there is one). When the answer shows an economy option, offer it to the
customer as the cheaper choice, quoting its fee, promise wording and
"expected by" date as given; if they take it, call kapruka_create_order with
`delivery.type: "economy"` (the order is dated for you). When the answer says
the order goes by economy delivery, that is the ONLY option for these items:
tell the customer and order with `delivery.type: "economy"`. When economy is
not shown, do not mention it.
Perishable codes (CAKE*, FLOWER*, COMBO*) additionally get a freshness
warning when the chosen delivery date is more than 1 day out.
INTERNATIONAL GIFTS (store products from kapruka_gift_country_products): pass
product_id or cart (no city needed) and, for Australia / the UAE, `zone`. The
answer is that country's delivery: available or not, the fee for the whole order,
and the earliest date (3 days ahead; used when delivery_date is omitted).
Args:
params (CheckDeliveryInput):
- city (str): Canonical city name (e.g. 'Colombo 03', 'Galle'); not needed for
international gift store products
- zone (Optional[str]): international gifts in Australia / the UAE
- delivery_date (Optional[str]): YYYY-MM-DD; defaults to today (LK time)
- product_id (Optional[str]): Check the city against this item's delivery scope
- currency (Optional[str]): LKR (default) or USD/GBP/AUD/EUR (charged in USD)
- cart (Optional[list]): [{product_id, quantity, icing_text?}] for the exact fee
- other_items_total (Optional[float]): LKR value of items not in `cart` (custom cake quote)
- gift_message (Optional[str]): the order's gift message, for the economy rule
- response_format (str): 'markdown' (default) or 'json'
Returns:
str: Delivery feasibility + fee in the requested format.
JSON schema:
{
"city": str,
"now": str, # ISO timestamp, Sri Lanka time
"checked_date": str, # YYYY-MM-DD
"available": bool, # date open AND (if product_id) item deliverable
"delivery_fee": number, # what checkout charges (see fee_basis)
"fee_currency": "LKR" | "USD",
"fee_basis": "cart" | "max" | "fixed", # exact for the cart | LKR without a cart: the most it can be | USD: same for any cart
"fee_items_value": number, # LKR item value the fee was computed from (fee_basis=cart)
"fee_cart_error": str, # the cart could not be priced (fee falls back to max)
"rate": number, # the city's BASE rate (LKR) — not the checkout fee
"currency": "LKR",
"reason": str | null, # date-block message, else "This item is not delivered to <City>."
"next_available_date": str|null, # only for date blocks
"item_deliverable": bool, # only when product_id resolved to a real product
"deliverable_cities": [str], # only when item_deliverable=false (capped at 60)
"perishable_warning": str | null, # populated when product_id is perishable
"economy": { # only with `cart`
"available": bool, # false: say nothing about economy
"forced": bool, # true: economy is the ONLY option
"fee": number, "currency": "LKR",
"promise": str, # quote as given
"delivery_date": str, # "expected by" date; create_order uses it
"saving": number | null # vs the standard fee
}
}
kapruka_get_product
Fetch full details for a single Kapruka product by its product ID.
Returns name, description, price (with optional currency conversion), stock status,
images, variants, shipping info, delivery scope, and a direct product URL.
`economy_delivery` ("optional" | "only" | "only_5to7" | null) says whether the
item can go by the cheaper economy courier; kapruka_check_delivery with the
cart says whether an order is offered it.
International gift store products carry `international_gift` {"country", "orderable",
"store_url"}: they are delivered IN that country by Kapruka's partner (see
kapruka_gift_countries), not in Sri Lanka.
Discounts: `price` is what checkout charges, with any website discount already
applied; `compare_at_price` is the pre-discount price when one applies (null
otherwise). Each variant carries its own pair. Quote `price`.
Delivery scope: most gifts ship island-wide, but restaurant food, hotel cakes
and liquor only reach a limited city set (typically the Colombo area). The
`delivery` object is the authority — search results do NOT carry it. When
`delivery.island_wide` is false, tell the customer up front that the item is
delivered only to selected cities, and confirm their city with
kapruka_check_delivery(city, product_id) before promising anything. If
`deliverable_city_count` exceeds the returned list, the list is truncated —
say "and more", don't treat it as complete.
Note: Some IDs starting with 'CATSYM' are category landing pages, not purchasable
products — this tool will flag those clearly.
Args:
params (GetProductInput):
- product_id (str): Kapruka product ID (e.g. 'cakeXX000000')
- currency (str): Price currency — LKR (default), USD, GBP, AUD, EUR
- type (Optional[str]): Optional type hint (e.g. 'specialgifts')
- response_format (str): 'markdown' (default) or 'json'
Returns:
str: Product details in the requested format.
JSON schema:
{
"id": str,
"name": str,
"description": str,
"summary": str,
"price": {"amount": float, "currency": str},
"compare_at_price": {"amount": float, "currency": str} | null,
"in_stock": bool,
"stock_level": str, # "low" | "medium" | "high"
"category": {"id": str, "name": str, "slug": str, "path": str},
"variants": [{"id": str, "name": str, "sku": str, "price": {...},
"in_stock": bool, "stock_level": str, "attributes": {...}}],
"images": [str], # list of full-resolution image URLs
"attributes": {"type": str, "subtype": str, "weight": str, "vendor": str},
"shipping": {"ships_from": str, "ships_internationally": bool, "restricted_countries": [str]},
"delivery": {
"island_wide": bool,
"deliverable_city_count": int, # only when island_wide=false; TRUE total
"deliverable_cities": [str] # only when island_wide=false; capped at 60
},
"rating": null,
"url": str
}
Error: "Error: <message>" on failure.
kapruka_search_products
Search for products on Kapruka.com by keyword, with optional category filter and pagination.
Returns a ranked list of matching products with prices, stock status, images, and URLs,
plus the category facets available for the query (facets.categories) — the names that
work in `category` to narrow the same search.
Supports cursor-based pagination — pass next_cursor from one response into the next call.
Pagination is capped at 3 pages per query to discourage catalog enumeration; for broader
discovery, refine the query or narrow with a facet instead.
Queries must be at least 3 characters and contain specific terms — pure stopword queries
(e.g. "the", "a an") are rejected.
Relevance: the API matches ANY single query word, so extra words add loosely related
results rather than narrowing. Search the thing the customer wants (the head noun) and
check the product names actually contain it before presenting them.
Prices and min_price/max_price are both in `currency` — pass the customer's own budget
in their own currency; do not convert it. Gifts for recipients OUTSIDE Sri Lanka are not
in this search: use kapruka_gift_countries / kapruka_gift_country_products. A row's `price` is the discounted price
checkout charges (the bounds filter on it); `compare_at_price` is set when on sale.
Search results carry NO delivery-scope information. Food, hotel cakes and liquor
are delivered only to selected cities — never infer deliverability from a search
hit. Before quoting delivery on a specific item, call kapruka_get_product (read
`delivery.island_wide`) or kapruka_check_delivery with `product_id`.
Args:
params (SearchProductsInput):
- q (str): Search query (e.g. 'birthday cake', 'roses', 'tea gift'). Min 3 chars.
- category (Optional[str]): A facet name from a previous search's facets.categories
- limit (int): Results per page, 1–50 (default 10)
- cursor (Optional[str]): Pagination cursor from previous response
- currency (str): LKR (default), USD, GBP, AUD, EUR
- min_price (Optional[float]): Min price (inclusive) in `currency`
- max_price (Optional[float]): Max price (inclusive) in `currency`
- in_stock_only (bool): Restrict to in-stock items (default false)
- sort (str): 'relevance' | 'price_asc' | 'price_desc' | 'newest' | 'bestseller'
- include_stubs (bool): Deprecated, no effect
- response_format (str): 'markdown' (default) or 'json'
Returns:
str: Search results in the requested format.
JSON schema:
{
"results": [
{
"id": str,
"type": "product",
"name": str,
"summary": str,
"price": {"amount": float | null, "currency": str},
"compare_at_price": {"amount": float, "currency": str} | null,
"in_stock": bool,
"stock_level": str,
"image_url": str | null,
"category": {"id": str, "name": str, "slug": str},
"rating": null,
"ships_internationally": bool,
"url": str
}
],
"next_cursor": str | null, # null after page 3 even if upstream has more
"total_estimate": int, # index hits for the query words (any-word match)
"applied_filters": {"q": str, "category": str, "currency": str, ...},
"facets": {"categories": [{"name": str, "count": int}]},
"category_filter_dropped": str, # only when `category` was not a facet here
"valid_categories": [str] # ...and the facet names that are
}
Error: "Error: <message>" or "No products found for '<query>'" on failure.