Local businesses for AI agents: what they sell, prices, delivery quotes, order and booking links.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
For a service whose business’s card shows a `book` action with method `connected` and no `book` in its `cannot` (a door the platform refuses for this account is marked there, with the reason, at every sync). Take a `start_at` from check_availability (slots) and the platform books it for the named customer; the answer is the platform’s own status (created = on the calendar awaiting confirmation · confirmed · declined). A time the platform does not offer is an `unavailable` error and nothing is booked. Idempotent by `idempotency_key` like place_order. Get the user’s explicit yes first.
ISO date-time with an offset, as check_availability gave it
the service, from the card
the service variation when the card lists several
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
claude mcp add --transport http versum-agent-assistant https://versum.suverse.io/mcp
For a service whose business’s card shows a `book` action with method `connected` and no `book` in its `cannot` (a door the platform refuses for this account is marked there, with the reason, at every sync). Take a `start_at` from check_availability (slots) and the platform books it for the named customer; the answer is the platform’s own status (created = on the calendar awaiting confirmation · confirmed · declined). A time the platform does not offer is an `unavailable` error and nothing is booked. Idempotent by `idempotency_key` like place_order. Get the user’s explicit yes first.
| Argument | Type | Description |
|---|---|---|
| note | any | |
| customer* | object | |
| start_at* | string | ISO date-time with an offset, as check_availability gave it |
| variant_id | any | the service variation when the card lists several |
| business_id* | string | |
| offering_id* | string | the service, from the card |
| idempotency_key | any |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"business_id",
"offering_id",
"start_at",
"customer"
],
"properties": {
"note": {
"anyOf": [
{
"type": "string",
"maxLength": 500
},
{
"type": "null"
}
]
},
"customer": {
"type": "object",
"properties": {
"name": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
],
"description": "the buyer’s name (required for a booking)"
},
"email": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "the buyer’s e-mail: the platform sends the checkout link and the receipt there (for a booking; NOT asked for an order on Shopify or Square, whose checkout asks the buyer)"
},
"phone": {
"anyOf": [
{
"type": "string",
"maxLength": 40
},
{
"type": "null"
}
],
"description": "the buyer’s telephone with country code"
}
},
"additionalProperties": false
},
"start_at": {
"type": "string",
"maxLength": 60,
"description": "ISO date-time with an offset, as check_availability gave it"
},
"variant_id": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
],
"description": "the service variation when the card lists several"
},
"business_id": {
"type": "string",
"format": "uuid"
},
"offering_id": {
"type": "string",
"format": "uuid",
"description": "the service, from the card"
},
"idempotency_key": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}Only for a business whose card shows `live_data` (its items come from its connected Shopify or Square, not from a page read). For a product: the platform’s stock right now — `state: confirmed | refuted | unknown` (unknown = the platform does not track stock, which is not «no»). For a service on a platform with bookings: the free `slots` between `from` and `to` (default: the next 7 days), each with a `start_at` that book_slot takes as it is — on the business’s own clock with its offset (`timezone` names the zone; `start_at_utc` is the same instant). If the card’s `cannot` lists `book`, the slots are information only and the answer says so. A business without a connection answers `not_connected`.
| Argument | Type | Description |
|---|---|---|
| to | any | slots: the window’s end, ISO with an offset |
| from | any | slots: the window’s start, ISO with an offset |
| quantity | any | how many — stock is judged against it |
| variant_id | any | the variant (the card lists them when there are several) |
| business_id* | string | the business id from the card |
| offering_id* | string | the offering id from the card |
The business the user NAMED, by name matching only: case, punctuation, possessives and articles are ignored, the words may come in any order, a part of the name is enough («Bern’s» finds «Bern’s Steak House»), up to two typos per word, and Cyrillic is transliterated. No meaning, no ranking: `found` is ordered by exactness, then — with a `location` — inside the radius first and nearer first. Several places with one name all come back with their addresses; without a location and across cities the answer asks which place. `found: []` means the business is NOT in this catalogue; then `nearby_in_same_niche` lists up to five cards of the trade the name suggests, every one marked `not_the_requested_business: true` — never present them as the business asked for. The best matches carry `task_card` (≤ 2.5 KB): `time` for `needed_by` (else `hours_today`), up to 5 `items` under `query`, `terms`, `deliver_to`, and `next` {promise[], candidate[], action}. `intent` (book / order / quote / info) chooses the action. The full menu is get_card.
| Argument | Type | Description |
|---|---|---|
| name* | string | the business name as the user said it |
| query | any | the thing under the task («red roses», «ribeye»): picks the ≤ 5 items on the card |
| intent | any | what the user means to do with this business |
| location | any | a postcode, a city or "lat,lng" — orders the matches by nearness and bounds the nearby list |
| needed_by | any | when: «sunday», «tomorrow 19:00», «now», «tomorrow after 15:00» — read on the business’s own clock, three ways (on_day / open_at / slot_after) |
SYNONYM of get_card, kept for agents that already know this name. Free. One business in full, in the same shape for every trade: who it is (name, trade, address, telephone, e-mail, opening hours, time zone), everything it sells (up to 60 offerings, cheapest first), the six actions with their links and `required_fields`, how the thing reaches the buyer (delivery, pickup, on_site, at_customer — with the published area, the fee and the order deadline), and how fresh each part of that is. `live_data` (platform, synced_at) when the shelf is the business’s own connected system: then every offering carries `source: connector` with its variants (each with `inventory`: units in stock as the platform counts them, null = unknown), and an action with method `connected` is taken through place_order / book_slot on this door. Use it when the user asks «can they deliver to me», «are they open now», «what else do they do», or when search_catalog offered a business with nothing listed and the next step is a telephone call.
| Argument | Type | Description |
|---|---|---|
| id* | string | the business id from search_catalog |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "the business id from search_catalog"
}
},
"additionalProperties": false
}Free. One business in full, in the same shape for every trade: who it is (name, trade, address, telephone, e-mail, opening hours, time zone), everything it sells (up to 60 offerings, cheapest first), the six actions with their links and `required_fields`, how the thing reaches the buyer (delivery, pickup, on_site, at_customer — with the published area, the fee and the order deadline), and how fresh each part of that is. `live_data` (platform, synced_at) when the shelf is the business’s own connected system: then every offering carries `source: connector` with its variants (each with `inventory`: units in stock as the platform counts them, null = unknown), and an action with method `connected` is taken through place_order / book_slot on this door. Use it when the user asks «can they deliver to me», «are they open now», «what else do they do», or when search_catalog offered a business with nothing listed and the next step is a telephone call.
| Argument | Type | Description |
|---|---|---|
| id* | string | the business id from search_catalog |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "the business id from search_catalog"
}
},
"additionalProperties": false
}Free. One offering in full, with ONE live request to the shop asking for that item’s price and stock right now (`live_check`). Call it before you quote a price to a person: a catalogue read yesterday is yesterday’s price. One business is asked at most once every ten minutes however many of its offerings you open, so walking a shop is one request and not twenty; `from_cache` and `checked_at` say which. A price list, a menu or a quote form has no single-item door to ask (`ok: false` with the reason) and the stored reading stands with its date. Hand `action_url` to the user as it is, ref=versum included.
| Argument | Type | Description |
|---|---|---|
| id* | string | the offering id from search_catalog |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "the offering id from search_catalog"
}
},
"additionalProperties": false
}SYNONYM of get_offering, kept for agents that already know this name. A «product» is just an offering whose kind is `product`; the same id works in both. Free. One offering in full, with ONE live request to the shop asking for that item’s price and stock right now (`live_check`). Call it before you quote a price to a person: a catalogue read yesterday is yesterday’s price. One business is asked at most once every ten minutes however many of its offerings you open, so walking a shop is one request and not twenty; `from_cache` and `checked_at` say which. A price list, a menu or a quote form has no single-item door to ask (`ok: false` with the reason) and the stored reading stands with its date. Hand `action_url` to the user as it is, ref=versum included.
| Argument | Type | Description |
|---|---|---|
| id* | string | the offering id from search_catalog |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "the offering id from search_catalog"
}
},
"additionalProperties": false
}\uD83D\uDD34 THE TASK CARD IS THE DEFAULT (≤ 2 KB): id, name, distance_miles, phone; `time` for `needed_by` (or `hours_today`); `items` — up to 5 under `query` (3 for intent book), placeholders («Info», «Menu») never; `budget`; `terms` — the deal’s conditions as published; `asked` — your `properties` (confirmed <value> / refuted / unknown); `deliver_to`; and `next` {promise[], candidate[], action {do: order | book | check_slot | quote_order | call, via, url | phone | tool, needs[]}}. `intent` (book / order / quote / info) shapes the items and the action. With `properties` the answer carries `property_counts` over the whole niche here and says it plainly when none confirm. `detail: "shelf"` gives the older shelf card below. `needed_by` is read THREE ways and says which (`needed_by.read_as`): on_day («sunday», «today» = the day, not 23:59), open_at («tomorrow 19:00», «now»), slot_after («after 15:00»: open after then; a free slot is never confirmed by hours). With `max_price` the order is in_budget → budget_unknown → over_budget, each by distance. THE OLDER SHELF CARD (`detail: "shelf"`) CARRIES `offering` — the thing under `query` (else the cheapest main thing of the niche) with its `title`, `price`, `price_type`, `unit`, `kind`, a one-line `note` in the page’s own words, `match_level` (exact = the query’s words AND the question’s kind; kind = the kind alone; partial = the words alone, not the kind; none = nothing) and `action_url` (ITS OWN basket or page, or null). With a query and no row that matches, `offering` is null and `offering_note` says `no_match` — nothing is substituted. A price written inside the title that disagrees with the price column is shown as `price_conflict` with both figures and their sources. A price far from every price of its kind across the index is `suspect_price: true` and never decides a budget. And `properties` — what this business publishes of every fact any business on the page publishes (`confirmed <value>`, or `refuted` for an ask), with `properties_unknown` naming the page’s other keys it does not (`properties_on_page` is the full list; a key there and not in a card’s `properties` is unknown for it). All the cards of one niche inside `radius_miles` of `location`, 20 a page (`limit` up to 50; follow `next_page` — a stable token, no duplicates, no skips). THE ORDER IS FIXED AND IS NOT A RANKING: plain businesses first, then chains, then marketplaces (nothing is left out); inside each group by distance. For a trade that travels to the customer (movers, plumbers …) the businesses whose PUBLISHED service area covers the place come first, then the ones that say nothing, then the ones that refuse. You choose. `niche` is a key from list_niches or your own words («barbershop», «flowers», «pizza»): the answer always says `matched_niche`, `confidence`, `matched_by` and the nearest neighbours; when the words are not clearly one niche you get NO cards and the candidates to put back to your user. `query` only chooses which offering is SHOWN on each card (the cheapest main offering that fits the words; without `query`, the cheapest main offering of the niche) — it never changes the order. `deliver_to` adds `can_deliver_to: confirmed | refuted | unknown` per card from the published area; `needed_by` adds `open_at: yes | no | unknown` from the published opening hours (which know nothing about free slots or tables) — and FILTERS: a card leaves only when the business cannot do it by then (closed at that moment for a trade done on site; a published deadline passed for delivery or pickup); a trade that travels to the customer is never judged by its office hours; hours alone with an online door = unknown, kept, marked `time`. EVERYTHING a filter leaves out is in `excluded` — id, name, one phrase from the data («closed Sunday (Sunday: Closed)») — the first five always, the rest cut by the answer budget (`excluded_cut`). A `needed_by` behind us is an error of the question (`needed_by_in_past`), not an exclusion. `max_price` groups the cards in_budget · budget_unknown · over_budget (compared with FIXED prices in the same currency, or a per-unit price with its quantity; a «from» price is a lower bound: above the budget it is over, under it unknown), excludes nobody, and reports `in_budget_count`; an over_budget card shows no offering (`no_match_under_budget`). `properties` (the argument) asks for keys with min/max/equals and adds their three-state answer (confirmed <value> | refuted | unknown) to the card’s `properties`, filtering nothing. `tags` FILTERS: only the cards carrying one of the niche’s own tags (list_niches hands them out — Google’s type words such as «steak house», «italian», or a kind on the shelf) are listed, in the same order; a free-text `niche` that is itself a tag («steakhouse») applies it. Each card is ≤ 1 KB: id, name, niche, offering {id, title, price, price_type, unit, action_url}, distance_miles, phone, type, price_range_primary, can[] (confirmed actions with method widget | form | link | phone | connected — a form and a telephone are two entries; `connected` = the business’s OWN system takes it through this door: place_order / book_slot / check_availability), can_unknown[], properties {key: "confirmed <value>"} (a CONDITION of the deal — a deposit, a minimum, a cancellation rule — is never cut from a card; descriptive keys go first when the card is over its byte budget), properties_unknown [keys], checked_at, owner_verified, chain, marketplace, and `live_data` {platform, synced_at, trust} on a business whose items, prices and stock come from its own connected system rather than a page read (confirmed by the system, not a paid placement; the three states still hold — stock the platform does not track is unknown). `total: 0` always says why (not scanned here / outside the radius with `nearest_beyond_radius` / no card with the tag / niche unknown) and is logged (`gap_logged`). Niches on the shelf today: restaurant (1042) · florist (316) · hair salon (274) · gas station (253) · barber (229) · pizza (202) · nail salon (201) · print shop (180) · grocery (168) · bakery (159) · oil change (148) · pharmacy (147) · medical clinic (145) · storage (142) · ev charging station (141) · catering (138) · bar (124) · pet grooming (123) · auto repair (120) · hotel (107) · optometrist (106) · fuel delivery (100) · clothing store (96) · tire shop (95) · beauty salon (94) · liquor store (92) · massage (92) · auto parts store (91) · parking (91) · veterinarian (88) · coffee shop (86) · furniture (82) · truck tire service (82) · locksmith (80) · movers (80) · party rental (79) · towing (79) · event venue (78) · plumber (78) · body shop (77) · shipping and packing store (76) · dentist (75) · electrician (73) · appliance repair (72) · phone repair (67) · real estate agent (66) · jewelry (65) · car wash (64) · notary (64) · urgent care (64) · insurance agent (63) · pool service (63) · tutoring (63) · boat rental (62) · accountant (60) · car rental (59) · garden center and nursery (59) · hvac (59) · chiropractor (57) · jewelry and watch repair (57) · photographer (57) · music lessons (56) · physical therapy (56) · wedding planner (56) · tattoo studio (55) · travel agency (54) · septic and well service (53) · roofer (52) · tailor (52) · bike repair (51) · landscaping (51) · pressure washing (49) · dance studio (48) · elder care and home care (48) · auto glass (47) · fishing charter and boat tours (46) · ice cream shop (46) · lawyer (46) · mattress (42) · tree service (41) · pet boarding (40) · yoga (40) · handyman (39) · window cleaning (39) · carpet cleaning (38) · driving school (38) · dry cleaner (38) · hardware store (36) · windows and doors (35) · cleaning (34) · daycare (34) · car dealer (32) · thrift store (32) · garage door (30) · pet store (27) · funeral home (26) · butcher and seafood market (25) · dog walking and pet sitting (25) · medical equipment rental (25) · truck repair (24) · junk removal (20) · swim school (20) · bike and jet ski rental (15) · golf course (15) · juice bar (15) · med spa (14) · cinema (13) · general contractor (13) · escape room (11) · kids play center (11) · painter (11) · pest control (11) · meal prep (10) · tennis and sports courts (10) · bowling and billiards (8) · shooting range (8) · babysitting and nanny agency (7) · specialty grocery and farmers market (6) · computer repair (4) · kids sports and activities (4) · martial arts (4) · taxi and transfer service (4) · shoe repair (3) · museum (2) · aquarium (1) · butcher (1) · motel (1) · transportation service (1); known but not scanned anywhere: candle shop, confectionery, garden center, gift shop, party shop, toy store.
The niches (trades) this catalogue has read, each with its key, its other names, its TAGS (Google’s own type words and the kinds of thing on its shelves — e.g. a restaurant niche tagged with the cuisines its businesses carry) and how many businesses and cards it holds. With `location`: only the niches that have cards inside the radius, plus `known_not_scanned_here` — niches the catalogue knows but has not read there (a honest «no» before you search). Use a key, a name or a tag as `niche` in list_cards. `conditions`: the keys of the deal (a deposit, walk-ins, emergency service, new patients…) with how many cards confirm each and how many say nothing — the three most published per niche, every one with `niche`. Every niche carries `coverage`: `complete` (every city passed cell by cell, no cell at Google’s cap) or `partial` with `count` (the cards it holds) and `why` — a partial niche may have businesses the catalogue has not read. Current coverage: florists in Austin, Aventura, Brickell, Gibsonton (+19 more); restaurants in Apollo Beach, Gibsonton, Lutz, Miami (+8 more); jewelrys in Tampa; furnitures in St. Petersburg, Tampa; nail salons in Apollo Beach, Gibsonton, Lutz, Riverview (+3 more); jewelry and watch repairs in St. Petersburg, Tampa; bike repairs in Apollo Beach, St. Petersburg, Tampa; mattresss in St. Petersburg, Tampa; clothing stores in Carrollwood, Hollywood, Tampa; party rentals in Lutz, Riverview, Ruskin, Tampa; hair salons in Gibsonton, Riverview, Ruskin, St. Petersburg (+3 more); garden center and nurserys in Ruskin, St. Petersburg, Tampa; pet groomings in St. Petersburg, Tampa; car dealers in Tampa; boat rentals in St. Petersburg, Tampa; tailors in St. Petersburg, Tampa; liquor stores in Lutz, Tampa; shooting ranges in St. Petersburg, Tampa; unknown trades in Tampa; truck repairs in Tampa; barbers in Apollo Beach, New York, Riverview, St. Petersburg (+2 more); massages in Tampa, Temple Terrace; phone repairs in Tampa; landscapings in Tampa; plumbers in Brandon, Gibsonton, Lutz, Riverview (+1 more); medical equipment rentals in Gibsonton, Tampa; coffee shops in Chicago, Tampa; carpet cleanings in Tampa; moverss in Tampa; meal preps in St. Petersburg, Tampa; print shops in Tampa, Temple Terrace; pizzas in Tampa, Temple Terrace; electricians in Apollo Beach, Odessa, Tampa; bakerys in Tampa; spas in St. Petersburg, Tampa, Temple Terrace; wedding planners in St. Petersburg, Tampa; car rentals in Tampa; hair-salons in St. Petersburg, Tampa, Temple Terrace; cafes in St. Petersburg, Tampa; medical clinics in Tampa; kids play centers in Brandon, Tampa; cleanings in Riverview, Tampa; travel agencys in St. Petersburg; handymans in Apollo Beach, St. Petersburg, Tampa; event venues in Lutz, Tampa; yogas in St. Petersburg, Tampa; optometrists in Tampa; caterings in Riverview, Tampa, Temple Terrace; specialty grocery and farmers markets in St. Petersburg, Tampa; notarys in Riverview, Sun City Center, Tampa; photographers in Apollo Beach, St. Petersburg, Tampa; music lessonss in St. Petersburg, Tampa; tattoo studios in Tampa; braids-locss in Tampa; driving schools in Tampa; juice bars in Riverview, Tampa; cleaning services in Ruskin, Tampa; barber-shops in St. Petersburg, Tampa; butcher and seafood markets in Tampa; tutorings in Gibsonton, St. Petersburg, Tampa, Westchase; painters in Kissimmee; roofers in Tampa; pressure washings in Apollo Beach, Tampa; med spas in Tampa; locksmiths in Riverview, Tampa; car washs in Tampa, Temple Terrace; accountants in Greater Northdale, Tampa; pool services in St. Petersburg, Tampa; daycares in St. Petersburg, Tampa; dance studios in Tampa; pet boardings in Tampa; pet stores in Tampa; bowling and billiardss in St. Petersburg, Tampa; parkings in Tampa; dog walking and pet sittings in Tampa; bike and jet ski rentals in Apollo Beach, Tampa; shoe repairs in Tampa; storages in Tampa; physical therapys in Tampa; window cleanings in Gibsonton, Riverview, St. Petersburg, Tampa; bars in Tampa; fishing charter and boat tourss in Ruskin, St. Petersburg, Tampa; gas stations in Gibsonton, Riverview, Sun City Center; oil changes in Riverview, Tampa; dentists in Tampa; tree services in Tampa; appliance repairs in Riverview, St. Petersburg, Tampa; hotels in Tampa; real estate agents in Apollo Beach, Tampa, Westchase; truck tire services in Tampa; swim schools in Tampa; fuel deliverys in Riverview; dry cleaners in St. Petersburg, Tampa; kids sports and activitiess in Tampa; cinemas in Riverview, Ruskin, Tampa; elder care and home cares in Tampa; lawyers in Tampa; HVACs in Tampa; golf courses in Tampa; tennis and sports courtss in St. Petersburg; escape rooms in Tampa; veterinarians in Tampa; junk removals in Tampa; EV charging stations in Tampa; garage doors in Tampa; thrift stores in Tampa.
The status of an `order_id` from place_order or book_slot, asked of the platform again when it is not final (paid? confirmed? declined by the owner?). A Shopify cart link answers `unknown` — payment is on Shopify’s side and the shop does not report it here. Free.
| Argument | Type | Description |
|---|---|---|
| order_id* | string |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"order_id"
],
"properties": {
"order_id": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
}For a business whose card shows an `order` action with method `connected`. Shopify: returns the shop’s own checkout link with these items and quantities already in the cart (`status: unknown`, `mode: cart`) — no customer data is needed or passed, the buyer gives their details and pays on Shopify, and nothing exists on the shop until they do. Square: an order with a payment link (no customer data either). WhatsApp: a message to the owner (customer phone or email). Returns `order_id`, `status`, `checkout_url` — HAND THE CHECKOUT URL TO THE USER AS IT IS: that page takes the money, Versum holds no card and charges nothing. Stock is checked live first on a platform that tracks it; «not in stock» is an `unavailable` error and no order or link is given. Send an `idempotency_key` of your own and keep it for a retry: the same key returns the same answer and the same link (`repeated: true`), never a second one. Lines name offerings by their card id (and `variant_id` when the card lists several). `status`: unknown (a cart link: payment is on the platform’s side) · created (the platform holds it) · pending_owner (WhatsApp) · confirmed · declined · failed. Get the user’s explicit yes before calling this.
| Argument | Type | Description |
|---|---|---|
| note | any | a note to the business, in the user’s words (not passed on a Shopify cart link) |
| lines* | array | |
| customer | any | only where the platform needs it (WhatsApp); omit for Shopify and Square |
| business_id* | string | |
| idempotency_key | any | your own key for this order; reuse it on a retry |
Free. For a business on WooCommerce or Shopify (its card says `platform`), with no owner connection: puts the offering in a cart session of OUR OWN on the shop’s public cart, gives it the delivery address and reads back what the shop itself computes — `delivery.state` confirmed | refuted | unknown for that address, `options` (each delivery / pickup with its price, tax and the whole order’s `total`), `tax`, `total`, `currency`, and `delivery_date` ONLY when the shop publishes one in its standard fields (otherwise `unknown` — ask the shop). Nothing is ordered, the cart is emptied afterwards, and only the address is sent (no name, e-mail or phone; nothing is stored). Shopify’s public cart has no tax: `tax.state` is unknown and the total is before tax. A shop whose cart is closed answers `not_available` with the reason; so does any other platform. When the shop needs more of the address than you gave, `status: needs_address` names the `missing` fields. A required add-on the page asks for («a card with a message?») is answered «no» unless you pass `options`; `options_used` says what was chosen. Hand `order_url` to the user to buy.
| Argument | Type | Description |
|---|---|---|
| items | any | several different items of the SAME shop in one cart (delivery and tax on the whole order); offering_id is then the first of them |
| options | any | answers to the product page’s own add-on questions; the rest are answered «no» |
| quantity | integer | default 1 |
| deliver_to* | any | where it must be brought: "33139", "Miami Beach, FL 33139", or {postcode, state, city, address_1} |
| variant_id | any |
Free search of local US businesses by MEANING (current coverage: florists in Austin, Aventura, Brickell, Gibsonton (+19 more); restaurants in Apollo Beach, Gibsonton, Lutz, Miami (+8 more); jewelrys in Tampa; furnitures in St. Petersburg, Tampa; nail salons in Apollo Beach, Gibsonton, Lutz, Riverview (+3 more); jewelry and watch repairs in St. Petersburg, Tampa; bike repairs in Apollo Beach, St. Petersburg, Tampa; mattresss in St. Petersburg, Tampa; clothing stores in Carrollwood, Hollywood, Tampa; party rentals in Lutz, Riverview, Ruskin, Tampa; hair salons in Gibsonton, Riverview, Ruskin, St. Petersburg (+3 more); garden center and nurserys in Ruskin, St. Petersburg, Tampa; pet groomings in St. Petersburg, Tampa; car dealers in Tampa; boat rentals in St. Petersburg, Tampa; tailors in St. Petersburg, Tampa; liquor stores in Lutz, Tampa; shooting ranges in St. Petersburg, Tampa; unknown trades in Tampa; truck repairs in Tampa; barbers in Apollo Beach, New York, Riverview, St. Petersburg (+2 more); massages in Tampa, Temple Terrace; phone repairs in Tampa; landscapings in Tampa; plumbers in Brandon, Gibsonton, Lutz, Riverview (+1 more); medical equipment rentals in Gibsonton, Tampa; coffee shops in Chicago, Tampa; carpet cleanings in Tampa; moverss in Tampa; meal preps in St. Petersburg, Tampa; print shops in Tampa, Temple Terrace; pizzas in Tampa, Temple Terrace; electricians in Apollo Beach, Odessa, Tampa; bakerys in Tampa; spas in St. Petersburg, Tampa, Temple Terrace; wedding planners in St. Petersburg, Tampa; car rentals in Tampa; hair-salons in St. Petersburg, Tampa, Temple Terrace; cafes in St. Petersburg, Tampa; medical clinics in Tampa; kids play centers in Brandon, Tampa; cleanings in Riverview, Tampa; travel agencys in St. Petersburg; handymans in Apollo Beach, St. Petersburg, Tampa; event venues in Lutz, Tampa; yogas in St. Petersburg, Tampa; optometrists in Tampa; caterings in Riverview, Tampa, Temple Terrace; specialty grocery and farmers markets in St. Petersburg, Tampa; notarys in Riverview, Sun City Center, Tampa; photographers in Apollo Beach, St. Petersburg, Tampa; music lessonss in St. Petersburg, Tampa; tattoo studios in Tampa; braids-locss in Tampa; driving schools in Tampa; juice bars in Riverview, Tampa; cleaning services in Ruskin, Tampa; barber-shops in St. Petersburg, Tampa; butcher and seafood markets in Tampa; tutorings in Gibsonton, St. Petersburg, Tampa, Westchase; painters in Kissimmee; roofers in Tampa; pressure washings in Apollo Beach, Tampa; med spas in Tampa; locksmiths in Riverview, Tampa; car washs in Tampa, Temple Terrace; accountants in Greater Northdale, Tampa; pool services in St. Petersburg, Tampa; daycares in St. Petersburg, Tampa; dance studios in Tampa; pet boardings in Tampa; pet stores in Tampa; bowling and billiardss in St. Petersburg, Tampa; parkings in Tampa; dog walking and pet sittings in Tampa; bike and jet ski rentals in Apollo Beach, Tampa; shoe repairs in Tampa; storages in Tampa; physical therapys in Tampa; window cleanings in Gibsonton, Riverview, St. Petersburg, Tampa; bars in Tampa; fishing charter and boat tourss in Ruskin, St. Petersburg, Tampa; gas stations in Gibsonton, Riverview, Sun City Center; oil changes in Riverview, Tampa; dentists in Tampa; tree services in Tampa; appliance repairs in Riverview, St. Petersburg, Tampa; hotels in Tampa; real estate agents in Apollo Beach, Tampa, Westchase; truck tire services in Tampa; swim schools in Tampa; fuel deliverys in Riverview; dry cleaners in St. Petersburg, Tampa; kids sports and activitiess in Tampa; cinemas in Riverview, Ruskin, Tampa; elder care and home cares in Tampa; lawyers in Tampa; HVACs in Tampa; golf courses in Tampa; tennis and sports courtss in St. Petersburg; escape rooms in Tampa; veterinarians in Tampa; junk removals in Tampa; EV charging stations in Tampa; garage doors in Tampa; thrift stores in Tampa). Use it when you do not know which niche the user’s words belong to; verify what comes back on the cards (get_card), and for a known niche prefer list_cards, which lists every card in a fixed order instead of ranking a few. An empty answer says why and carries `gap_logged: true`. One sentence — what, where, when, budget — returns business cards with prices, availability and order/booking links. Data comes from the businesses’ own websites; owner-verified cards are marked. Call it whenever your user wants to buy, order, book, hire or get anything from a local business. Searching costs nothing, so search before you say you cannot help. Outside the coverage above the answer is empty and says so, which is itself useful — it is logged as a gap so the scanner learns where to go next. This tool neither buys nor reserves anything.
We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 2, 2026.
A very long tool description (over 4,000 characters) is hard to review and easy to hide instructions in.
Fix: Keep the description to what the tool does and when to use it; move reference material to resources or docs.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"business_id",
"offering_id"
],
"properties": {
"to": {
"anyOf": [
{
"type": "string",
"maxLength": 60
},
{
"type": "null"
}
],
"description": "slots: the window’s end, ISO with an offset"
},
"from": {
"anyOf": [
{
"type": "string",
"maxLength": 60
},
{
"type": "null"
}
],
"description": "slots: the window’s start, ISO with an offset"
},
"quantity": {
"anyOf": [
{
"type": "integer",
"maximum": 999,
"minimum": 1
},
{
"type": "null"
}
],
"description": "how many — stock is judged against it"
},
"variant_id": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
],
"description": "the variant (the card lists them when there are several)"
},
"business_id": {
"type": "string",
"format": "uuid",
"description": "the business id from the card"
},
"offering_id": {
"type": "string",
"format": "uuid",
"description": "the offering id from the card"
}
},
"additionalProperties": false
}| deliver_to | any | where it must be brought (a postcode is best): the card says confirmed / refuted / unknown |
| radius_miles | number | Default 25. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "the business name as the user said it"
},
"query": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "the thing under the task («red roses», «ribeye»): picks the ≤ 5 items on the card"
},
"intent": {
"anyOf": [
{
"enum": [
"book",
"order",
"quote",
"info"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "what the user means to do with this business"
},
"location": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "a postcode, a city or \"lat,lng\" — orders the matches by nearness and bounds the nearby list"
},
"needed_by": {
"anyOf": [
{
"type": "string",
"maxLength": 60
},
{
"type": "null"
}
],
"description": "when: «sunday», «tomorrow 19:00», «now», «tomorrow after 15:00» — read on the business’s own clock, three ways (on_day / open_at / slot_after)"
},
"deliver_to": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "where it must be brought (a postcode is best): the card says confirmed / refuted / unknown"
},
"radius_miles": {
"type": "number",
"maximum": 250,
"minimum": 1,
"description": "Default 25."
}
},
"additionalProperties": false
}| Argument | Type | Description |
|---|---|---|
| page | any | the `next_page` token of the previous answer |
| tags | any | FILTER by the niche’s own tags, as list_niches gives them («steak house», «italian», «dish»): a card without one of them is left out; the order does not change. Spelling is folded («steakhouse» = «steak house»). |
| limit | integer | cards per page. Default 10. |
| niche* | string | a niche key from list_niches, or the trade in your own words |
| query | any | words that pick the offering SHOWN on each card («red roses», «balayage»); the order of the cards does not depend on it |
| detail | any | task (default): the short card under the task; shelf: the older ≤ 1 KB shelf card |
| intent | any | what the user means to do: shapes the items shown and the ONE action in `next` (book → book / check_slot / call; order → order / quote_order / call; quote → quote_order / call; info → call) |
| location* | string | a 5-digit US postcode (best), a city ("Tampa, FL") or "lat,lng". Required: the shelf is a place. |
| max_price | any | the most for ONE offering, in USD: groups the cards in_budget · budget_unknown · over_budget (nobody is left out) and picks the shown offering inside it (exact, then kind, then partial matches); fixed prices compare; a «from» price above the budget is over, under it budget_unknown |
| needed_by | any | when: «sunday» / «today» / «2026-11-08» (open that day), «tomorrow 19:00» / «now» (open at that minute), «after 15:00» / «tomorrow after 3pm» (open after then; a free slot only where the source knows slots) — on each business’s own clock; adds `time` {asked_as, day, hours, state, why} and LEAVES OUT only the cards refuted for the way the trade is done (see excluded) |
| deliver_to | any | where it must be brought (a postcode is best): adds can_deliver_to per card — confirmed when the published area names the place, refuted when the business says no, unknown otherwise |
| properties | array | facts to read off each card without filtering: property_states {key: "confirmed <value>" | "refuted" | "unknown"} |
| radius_miles | number | Default 10. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"niche",
"location"
],
"properties": {
"page": {
"anyOf": [
{
"type": "string",
"maxLength": 300
},
{
"type": "null"
}
],
"description": "the `next_page` token of the previous answer"
},
"tags": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"maxLength": 60,
"minLength": 1
},
"maxItems": 8
},
{
"type": "null"
}
],
"description": "FILTER by the niche’s own tags, as list_niches gives them («steak house», «italian», «dish»): a card without one of them is left out; the order does not change. Spelling is folded («steakhouse» = «steak house»)."
},
"limit": {
"type": "integer",
"maximum": 50,
"minimum": 1,
"description": "cards per page. Default 10."
},
"niche": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "a niche key from list_niches, or the trade in your own words"
},
"query": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "words that pick the offering SHOWN on each card («red roses», «balayage»); the order of the cards does not depend on it"
},
"detail": {
"anyOf": [
{
"enum": [
"task",
"shelf"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "task (default): the short card under the task; shelf: the older ≤ 1 KB shelf card"
},
"intent": {
"anyOf": [
{
"enum": [
"book",
"order",
"quote",
"info"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "what the user means to do: shapes the items shown and the ONE action in `next` (book → book / check_slot / call; order → order / quote_order / call; quote → quote_order / call; info → call)"
},
"location": {
"type": "string",
"maxLength": 200,
"description": "a 5-digit US postcode (best), a city (\"Tampa, FL\") or \"lat,lng\". Required: the shelf is a place."
},
"max_price": {
"anyOf": [
{
"type": "number",
"minimum": 0
},
{
"type": "null"
}
],
"description": "the most for ONE offering, in USD: groups the cards in_budget · budget_unknown · over_budget (nobody is left out) and picks the shown offering inside it (exact, then kind, then partial matches); fixed prices compare; a «from» price above the budget is over, under it budget_unknown"
},
"needed_by": {
"anyOf": [
{
"type": "string",
"maxLength": 60
},
{
"type": "null"
}
],
"description": "when: «sunday» / «today» / «2026-11-08» (open that day), «tomorrow 19:00» / «now» (open at that minute), «after 15:00» / «tomorrow after 3pm» (open after then; a free slot only where the source knows slots) — on each business’s own clock; adds `time` {asked_as, day, hours, state, why} and LEAVES OUT only the cards refuted for the way the trade is done (see excluded)"
},
"deliver_to": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "where it must be brought (a postcode is best): adds can_deliver_to per card — confirmed when the published area names the place, refuted when the business says no, unknown otherwise"
},
"properties": {
"type": "array",
"items": {
"type": "object",
"required": [
"key"
],
"properties": {
"key": {
"type": "string",
"maxLength": 60,
"minLength": 1
},
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"equals": {
"type": [
"string",
"number",
"boolean"
]
}
},
"additionalProperties": false
},
"maxItems": 8,
"description": "facts to read off each card without filtering: property_states {key: \"confirmed <value>\" | \"refuted\" | \"unknown\"}"
},
"radius_miles": {
"type": "number",
"maximum": 250,
"minimum": 1,
"description": "Default 10."
}
},
"additionalProperties": false
}| Argument | Type | Description |
|---|---|---|
| niche | any | one niche (a key or your words): only it, with EVERY key of the deal it publishes (`conditions`: {key: {confirmed, refuted, unknown}} over the cards inside the radius) — the key to pass as list_cards `properties` |
| location | any | a 5-digit US postcode, a city ("Tampa, FL") or "lat,lng". Optional: without it every niche is listed with its totals. |
| radius_miles | number | How far from the location counts as «here». Default 10. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"niche": {
"anyOf": [
{
"type": "string",
"maxLength": 80
},
{
"type": "null"
}
],
"description": "one niche (a key or your words): only it, with EVERY key of the deal it publishes (`conditions`: {key: {confirmed, refuted, unknown}} over the cards inside the radius) — the key to pass as list_cards `properties`"
},
"location": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "a 5-digit US postcode, a city (\"Tampa, FL\") or \"lat,lng\". Optional: without it every niche is listed with its totals."
},
"radius_miles": {
"type": "number",
"maximum": 250,
"minimum": 1,
"description": "How far from the location counts as «here». Default 10."
}
},
"additionalProperties": false
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"business_id",
"lines"
],
"properties": {
"note": {
"anyOf": [
{
"type": "string",
"maxLength": 1000
},
{
"type": "null"
}
],
"description": "a note to the business, in the user’s words (not passed on a Shopify cart link)"
},
"lines": {
"type": "array",
"items": {
"type": "object",
"required": [
"offering_id"
],
"properties": {
"quantity": {
"type": "integer",
"maximum": 999,
"minimum": 1,
"description": "default 1"
},
"variant_id": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
]
},
"offering_id": {
"type": "string",
"format": "uuid",
"description": "from the card"
}
},
"additionalProperties": false
},
"maxItems": 20,
"minItems": 1
},
"customer": {
"anyOf": [
{
"type": "object",
"properties": {
"name": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
],
"description": "the buyer’s name (required for a booking)"
},
"email": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "the buyer’s e-mail: the platform sends the checkout link and the receipt there (for a booking; NOT asked for an order on Shopify or Square, whose checkout asks the buyer)"
},
"phone": {
"anyOf": [
{
"type": "string",
"maxLength": 40
},
{
"type": "null"
}
],
"description": "the buyer’s telephone with country code"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "only where the platform needs it (WhatsApp); omit for Shopify and Square"
},
"business_id": {
"type": "string",
"format": "uuid"
},
"idempotency_key": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
],
"description": "your own key for this order; reuse it on a retry"
}
},
"additionalProperties": false
}| a variant id from the card when it lists several |
| offering_id* | string | the offering id from a card |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"offering_id",
"deliver_to"
],
"properties": {
"items": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"offering_id"
],
"properties": {
"options": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"field",
"value"
],
"properties": {
"field": {
"type": "string",
"maxLength": 120,
"minLength": 1
},
"value": {
"type": "string",
"maxLength": 400
}
},
"additionalProperties": false
},
"maxItems": 10
},
{
"type": "null"
}
]
},
"quantity": {
"type": "integer",
"maximum": 99,
"minimum": 1
},
"variant_id": {
"anyOf": [
{
"type": "string",
"maxLength": 40
},
{
"type": "null"
}
]
},
"offering_id": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
},
"maxItems": 20
},
{
"type": "null"
}
],
"description": "several different items of the SAME shop in one cart (delivery and tax on the whole order); offering_id is then the first of them"
},
"options": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"field",
"value"
],
"properties": {
"field": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "words of the page’s own question, e.g. «card»"
},
"value": {
"type": "string",
"maxLength": 400,
"description": "the answer, e.g. «No», or the card message"
}
},
"additionalProperties": false
},
"maxItems": 10
},
{
"type": "null"
}
],
"description": "answers to the product page’s own add-on questions; the rest are answered «no»"
},
"quantity": {
"type": "integer",
"maximum": 99,
"minimum": 1,
"description": "default 1"
},
"deliver_to": {
"anyOf": [
{
"type": "string",
"maxLength": 200,
"minLength": 5
},
{
"type": "object",
"required": [
"postcode"
],
"properties": {
"city": {
"anyOf": [
{
"type": "string",
"maxLength": 80
},
{
"type": "null"
}
]
},
"state": {
"anyOf": [
{
"type": "string",
"maxLength": 40
},
{
"type": "null"
}
],
"description": "two letters; read from the postcode when omitted"
},
"country": {
"anyOf": [
{
"type": "string",
"maxLength": 20
},
{
"type": "null"
}
],
"description": "US only for now"
},
"postcode": {
"type": "string",
"maxLength": 10,
"minLength": 5,
"description": "5-digit US postcode"
},
"address_1": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"description": "street line, only for the price: some shops price by it"
}
},
"additionalProperties": false
}
],
"description": "where it must be brought: \"33139\", \"Miami Beach, FL 33139\", or {postcode, state, city, address_1}"
},
"variant_id": {
"anyOf": [
{
"type": "string",
"maxLength": 40
},
{
"type": "null"
}
],
"description": "a variant id from the card when it lists several"
},
"offering_id": {
"type": "string",
"format": "uuid",
"description": "the offering id from a card"
}
},
"additionalProperties": false
}| Argument | Type | Description |
|---|---|---|
| limit | integer | How many BUSINESS CARDS to return. Default 10. |
| query* | string | What the user wants, IN THEIR OWN WORDS AND THEIR OWN LANGUAGE: "red roses", "steakhouse", "funeral flowers", "movers 2 bedroom", "ramo de rosas", or the same question in a language not written in the Latin alphabet. Matched by MEANING as well as by words, through one multilingual model — so do NOT translate for us and do NOT list synonyms yourself. A place, a date, a budget and a delivery word may be left in (they are parsed out), but the fewer extra words the better the match. |
| detail | string | compact (default): the business, its 1–2 best offerings with prices and links, what can be done, and the date it was read — a few hundred bytes a card. full: everything, including images, the sentence each price was read from, variants, all four fulfilment modes with published areas, opening hours and per-field evidence. Ask for full only when the user has chosen a business and you need its delivery area or its hours. |
| location | string | Where the user is, or where it must happen. A 5-DIGIT US POSTCODE IS BEST ("33139"): it is the only form a business’s own published delivery or service area can be matched against exactly, so a postcode gets you `how: "zips"` and a city name gets you a distance. A city ("Orlando, FL") or "lat,lng" also work. OPTIONAL IN NAME ONLY — without it nothing is measured and nothing is narrowed, and the answer comes back with `needs` carrying the question to put to your user. "near me" is not a location: ask which town. |
| max_price | number | The most the user will pay for ONE offering, in USD, compared against the lowest number the business published — so "from $45" passes "under $50". An offering priced on request is NOT filtered out: it comes back `price_type: "quote_only"`, because "they quote it" is an answer. |
| needed_by | string | When it has to happen: "today", "tomorrow", "tomorrow 9am", or an ISO date-time — WITH an offset (2026-10-31T14:00:00-04:00) if you know the user’s zone, because a business whose cut-off is on the RECIPIENT’s clock can only be judged against an offset you give. |
| only_local | boolean | true = leave out marketplaces: a business whose shelf names places far from its own address (one local address, hundreds of listings in other cities and countries). Default false: a marketplace is kept, ranked lower, and its card says `marketplace: true` (the full card says why). |
| properties | array | THE OPEN LAYER AS A FILTER: facts a business publishes beyond price and availability — a capacity, a deposit, a duration, whether something is included. Three states per key: a published value that fits = confirmed; a published value that does not = refuted (left out, a fact); nothing published = unknown (KEPT, ranked lower, named in the card’s `unknown`). Ask for the user’s real conditions here instead of putting them in `query`: «for 8 people» is {key:"capacity",min:8}, «no deposit» is {key:"deposit",max:0}. |
| fulfillment | string | HOW it must reach the user, and the four are different questions: delivery = brought to the address; pickup = the user collects; on_site = the user goes there (a salon chair, a dining room); at_customer = the business travels (a mover, a plumber). Default any. A business that has not published an answer is KEPT; one that ruled it out is dropped. |
| radius_miles | number | How far the business may be, in miles. Default 15. |
| only_independent | boolean | true = leave out chains (a business whose website serves several of the places found). Default false: a chain is kept as a name, a telephone and a website — it was never read, so it has no offerings — and its card says `chain: true`. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"query"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 50,
"minimum": 1,
"description": "How many BUSINESS CARDS to return. Default 10."
},
"query": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "What the user wants, IN THEIR OWN WORDS AND THEIR OWN LANGUAGE: \"red roses\", \"steakhouse\", \"funeral flowers\", \"movers 2 bedroom\", \"ramo de rosas\", or the same question in a language not written in the Latin alphabet. Matched by MEANING as well as by words, through one multilingual model — so do NOT translate for us and do NOT list synonyms yourself. A place, a date, a budget and a delivery word may be left in (they are parsed out), but the fewer extra words the better the match."
},
"detail": {
"enum": [
"compact",
"full"
],
"type": "string",
"description": "compact (default): the business, its 1–2 best offerings with prices and links, what can be done, and the date it was read — a few hundred bytes a card. full: everything, including images, the sentence each price was read from, variants, all four fulfilment modes with published areas, opening hours and per-field evidence. Ask for full only when the user has chosen a business and you need its delivery area or its hours."
},
"location": {
"type": "string",
"maxLength": 200,
"description": "Where the user is, or where it must happen. A 5-DIGIT US POSTCODE IS BEST (\"33139\"): it is the only form a business’s own published delivery or service area can be matched against exactly, so a postcode gets you `how: \"zips\"` and a city name gets you a distance. A city (\"Orlando, FL\") or \"lat,lng\" also work. OPTIONAL IN NAME ONLY — without it nothing is measured and nothing is narrowed, and the answer comes back with `needs` carrying the question to put to your user. \"near me\" is not a location: ask which town."
},
"max_price": {
"type": "number",
"minimum": 0,
"description": "The most the user will pay for ONE offering, in USD, compared against the lowest number the business published — so \"from $45\" passes \"under $50\". An offering priced on request is NOT filtered out: it comes back `price_type: \"quote_only\"`, because \"they quote it\" is an answer."
},
"needed_by": {
"type": "string",
"maxLength": 60,
"description": "When it has to happen: \"today\", \"tomorrow\", \"tomorrow 9am\", or an ISO date-time — WITH an offset (2026-10-31T14:00:00-04:00) if you know the user’s zone, because a business whose cut-off is on the RECIPIENT’s clock can only be judged against an offset you give."
},
"only_local": {
"type": "boolean",
"description": "true = leave out marketplaces: a business whose shelf names places far from its own address (one local address, hundreds of listings in other cities and countries). Default false: a marketplace is kept, ranked lower, and its card says `marketplace: true` (the full card says why)."
},
"properties": {
"type": "array",
"items": {
"type": "object",
"required": [
"key"
],
"properties": {
"key": {
"type": "string",
"maxLength": 60,
"minLength": 1,
"description": "a property key this index has read — get_card lists the ones established for a niche under `established_properties`, and every card shows its own under `properties`"
},
"max": {
"type": "number",
"description": "the value must be at most this (a number); max 0 = «none»"
},
"min": {
"type": "number",
"description": "the value must be at least this (a number)"
},
"equals": {
"type": [
"string",
"number",
"boolean"
],
"description": "the value must be this, as the page wrote it («included», «yes», 8)"
},
"exists": {
"type": "boolean",
"description": "true = the business must publish this property at all"
}
},
"additionalProperties": false
},
"maxItems": 8,
"description": "THE OPEN LAYER AS A FILTER: facts a business publishes beyond price and availability — a capacity, a deposit, a duration, whether something is included. Three states per key: a published value that fits = confirmed; a published value that does not = refuted (left out, a fact); nothing published = unknown (KEPT, ranked lower, named in the card’s `unknown`). Ask for the user’s real conditions here instead of putting them in `query`: «for 8 people» is {key:\"capacity\",min:8}, «no deposit» is {key:\"deposit\",max:0}."
},
"fulfillment": {
"enum": [
"delivery",
"pickup",
"on_site",
"at_customer",
"any"
],
"type": "string",
"description": "HOW it must reach the user, and the four are different questions: delivery = brought to the address; pickup = the user collects; on_site = the user goes there (a salon chair, a dining room); at_customer = the business travels (a mover, a plumber). Default any. A business that has not published an answer is KEPT; one that ruled it out is dropped."
},
"radius_miles": {
"type": "number",
"maximum": 250,
"minimum": 1,
"description": "How far the business may be, in miles. Default 15."
},
"only_independent": {
"type": "boolean",
"description": "true = leave out chains (a business whose website serves several of the places found). Default false: a chain is kept as a name, a telephone and a website — it was never read, so it has no offerings — and its card says `chain: true`."
}
},
"additionalProperties": false
}