Sellers & feed integrators
Every field you fill in to publish a robot on Botsdex. Universal fields apply to every listing; each job you attach adds its own job-specific fields.
Botsdex is organized around jobs, the work a robot does. A listing has four layers:
attributes.supportedJobs[]), the work the robot can do. Attach one or more; exactly one is primary. A robot that pours drinks and runs food lists both.supportedJobs[].attributes.customAttributes), your own key-value spec rows for details none of the fields above capture yet. Searchable, shown on the listing page, never a filter.The second axis is workplaces, where the robot can work (bar, hotel, hospital). It is searchable across every job: a buyer can ask “what works at a bar”.
You provide facts. Botsdex normalizes them into the search index, filters, ranking, and comparisons, so the cleaner and more complete your fields, the more often buyers find your robot. You can't create a quote-only listing: every listing you publish carries a public price, unless Botsdex staff have marked it price on request. That needs inquiries turned on for your account, and buyers then reach you by message.
Saves are checked against the listing as it is now. If the listing changed after you opened the editor, Save is refused and nothing is written: reload the editor and re-apply your edits. If a background update lands while you save, nothing is written either; press Save again. Publish is checked the same way: if the listing changes while it is being published, it is not published, and the editor tells you whether to reload or to press Publish again.
The publish button refuses until every item below is filled in. Drafts can have any subset of these; the gate fires on publish and on every save of a live listing, so a save that would leave a live listing incomplete is refused until you fix the flagged items or unpublish. The one exception is linking a model that is still in review: that save goes through and moves a live listing to In review, off the marketplace, until we approve the model.
title, brandName (vetted brand), tagline, description.catalogModelRef, the link to a catalog model (powers cross-vendor rollups + BSIN).images[].transactionType set, with its matching pricing block carrying a public amount (exactly one transaction type per listing).workplaces[].supportedJobs[], with exactly one marked primary: true.operationProfile.autonomyLevel + operationProfile.generalityLevel.fulfillment.shipsTo.fulfillment.shipsTo. Buyers see a delivered total before they pay, so an unpriced destination blocks publish. Note that coverage is checked on what you declared: shipping to EU while pricing only DE and FR leaves Italy unpriced and will not publish. Use amount: 0 where shipping is included, or drop the region from shipsTo if you cannot price it.pool is tracked (non-null), pool.capacity must be greater than 0.Status
Universal fields (every listing)
The header block on the PDP and the card.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
title | string ≤120 | Required | Live | The name buyers read. For a listing linked to a catalog model it is set from the catalog's product name (brand included once, e.g. "PUDU BellaBot") and a title you send is ignored; the editor shows it read-only. An unlinked draft keeps it as a working title, which cannot publish. |
brandName | string ≤80 | Required | Live | Manufacturer. Must be a vetted brand (pick from the list). Free-text brand names that don't match a vetted brand are silently cleared by the sanitizer. The brand is never added in front of the title: a linked listing's title already carries it. Brand names keep the manufacturer's spelling, accents included, and if a brand's spelling is corrected your listings follow automatically. |
catalogModelRef | { brandId, slug } | { kind: "pending", pendingModelId } | Required | Live | Link to a catalog model in our shared registry. This is what powers cross-vendor rollups (same product, multiple sellers) and the BSIN. Pick a brand first, then a model from that brand. Can't be a free-text value. Model not in the catalog yet? Use "Request a new model" in the editor: your listing carries the pending reference, holds as "In review" when you submit it, and goes live automatically once we approve the model. Publishing requires the canonical (approved) link. |
tagline | string ≤90 | Required | Live | One-line pitch shown on cards and under the PDP title. |
description | string ≤5000 | Required | Live | Long-form body rendered as the "About the robot" section. Plain text; double newlines split paragraphs. |
Gallery images plus optional showcase videos. Through the bulk JSON importer we fetch every images[].src URL server-side and re-store it under your Blob prefix; through the editor uploader you upload bytes directly. Videos are stored as YouTube references and embedded on the PDP, we never download the video file.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
images | array<{ src, alt, width?, height?, subject? }> | Required | Live | Gallery. The first image is the card cover and PDP hero. At least 3 required to publish, up to 50. |
images[].src | string URL | Optional | Live | HTTPS URL. Mandatory within an image: entries without `src` are dropped by the sanitizer. Through the bulk JSON importer we fetch the URL server-side, convert to WebP (max 1600px wide, quality 82, EXIF-rotated), and re-store under your Blob prefix; the saved src will be a botsdex blob URL, not the URL you submitted. Through the editor uploader you skip this step and upload bytes directly. A photo you remove from the gallery is deleted from storage when you save (unless another of your listings still uses it), so re-adding it means uploading it again. |
images[].alt | string ≤200 | Optional | Live | Accessibility text. Improves screen-reader support and SEO. |
images[].width / height / subject | measured | Optional | Live | You never send these. When we process an image we record its pixel size and where the machine sits inside the frame, and we use that to show every product at the same size on a results page, whatever margin your file happens to carry. Nothing here gates an upload: any ratio and any size are accepted, and an image we could not measure simply displays whole. If you submit these fields we ignore anything that is not a plausible measurement. |
YouTube embeds rendered on the PDP below the gallery. Each entry is { provider, videoId, url, title? }; in the importer JSON you can also pass a bare URL string per entry and we’ll parse it.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
videos | array<{ provider, videoId, url, title? }> | Optional | Live | Showcase videos shown on the PDP directly below the gallery, in a "See it in action" section: the first plays in a large player and the rest appear as tiles beneath it, in the order given (the editor reorders with move up/down). Optional, a listing can publish with zero videos. Up to 10 per listing. YouTube only in v1. |
videos[].provider | enum: "youtube" | Optional | Live | Mandatory within a video: entries without `provider` are dropped. Only `"youtube"` is supported in v1. |
videos[].url | string URL | Optional | Live | Mandatory within a video: entries without `url` (or a `videoId` fallback) are dropped. Any YouTube URL shape works: watch?v=, youtu.be/, embed/, shorts/, m.youtube.com, etc. The 11-char video id is parsed out and stored. |
videos[].videoId | string (11 chars) | Optional | Live | If omitted, parsed from url. Useful when the source feed already has the canonical id. |
videos[].title | string ≤120 | Optional | Live | Caption shown under the embed. |
A listing has exactly one transaction type, set by transactionType (purchase, rental, or lease), and its pricing carries only the matching block, a listing never holds buy + rent + lease at once. To offer the same robot another way (buy and rent, or new and pre-owned), create a separate listing; siblings share one stock pool. Money is integer cents (Stripe convention) on the wire; in the editor you type whole dollars or euros only, never a cent fraction, so every amount you enter is stored as a multiple of 100.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
transactionType | enum: purchase | rental | lease | Required | Live | Exactly one per listing. To offer the same robot another way (buy and rent, or new and pre-owned), create a separate listing; siblings share one stock pool. |
pricing.currency | enum: usd | eur | gbp | Required | Live | One currency per listing. |
pricing.purchase | object | omitted | Optional | Live | Present only when transactionType is purchase. The robot is sold outright. |
pricing.rental | object | omitted | Optional | Live | Present only when transactionType is rental (short-term, no ownership). |
pricing.lease | object | omitted | Optional | Live | Present only when transactionType is lease (longer-term, often with end-of-term ownership). |
pricing.addons | array<object> | Optional | Live | Fixed-price extras, up to 20. |
Present when sold outright.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
amount | integer cents | Required | Live | One-time price. 8500000 = $85,000. |
included | string[] | Optional | Live | What the price bundles, e.g. ["1-year warranty", "Free shipping"]. |
notes | string | null | Optional | Live | Optional fine print. |
Same shape, independent values. endOfTermOptions is lease-only; basis, weeklyAmount, and dailyAmount are rental-only (lease is always monthly).
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
monthlyAmount | integer cents | Required | Live | Required for lease and monthly rentals. May be 0 for a daily/weekly-only rental. Rental and lease price independently. |
basis | enum: monthly | weekly | daily | Optional | Live | Rental only. Which amount is the headline rate and the duration unit. Default monthly. Lease ignores this (always monthly). |
weeklyAmount | integer cents | Optional | Live | Weekly rate, used when basis is weekly. Rental only. |
dailyAmount | integer cents | Optional | Live | Daily rate, used when basis is daily (e.g. event / show robots). Rental only. |
minTerm | { value: integer, unit: days | months } | Optional | Live | Minimum commitment. Blank = no minimum. Rental accepts days or months; lease terms are months. Caps: 3650 days / 120 months. Enforced at checkout against the booking's own calendar dates. |
maxTerm | { value: integer, unit: days | months } | Optional | Live | Maximum commitment. Blank = no maximum. Same units and caps as minTerm; a maximum below the minimum is dropped on save. |
minTermMonths | integer | Optional | Live | Legacy minimum commitment in months (pre-2026-08-11). Still accepted on import and read on old documents, but normalized into minTerm on every save; prefer minTerm. |
depositAmount | integer cents | Optional | Live | Refundable deposit, charged once at checkout. |
included | string[] | Optional | Live | What the rate bundles, e.g. ["Preventive maintenance"]. |
excluded | string[] | Optional | Live | What is explicitly not covered, e.g. ["Consumables"]. |
endOfTermOptions | enum[] (lease only) | Optional | Live | return, renew, buyout, ownership_transfer. |
notes | string | null | Optional | Live | Fine print, e.g. cancellation notice. |
Fixed-price extras offered at checkout.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
title | string ≤120 | Required | Live | Display label, e.g. "On-site deployment and training (2 days)". |
description | string ≤500 | null | Optional | Live | Optional sub-line. |
amount | integer cents | Required | Live | Fixed price, charged as a separate line item. |
required | boolean | Optional | Live | true = auto-added and non-removable at checkout (e.g. mandatory commissioning). |
availableFor | enum[] | Optional | Live | Which models the add-on applies to: any subset of purchase, rental, lease. Empty = applies to the listing's transaction type (the editor leaves it empty so switching type never drops the add-on). |
internalTitle | string ≤120 | null | Optional | Live | Private label, never shown to buyers. Identifies the add-on in the seller's reusable add-on library (collection sellerAddons); any add-on saved with one is retrievable on future listings via "Add from library". |
Optional, internal-only. Sellers see these; buyers never do. SKU is your reference; pool.capacity gates the listing’s visibility when set.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
sku | string ≤64 | null | Optional | Live | Your internal stock-keeping unit. Must be unique within your catalog. Never shown on buyer surfaces. |
pool | { capacity: integer ≥0, unitsSold: integer ≥0 } | null | Optional | Live | Supply pool, the single source of truth for stock. null = stock isn't tracked, listing publishes freely. capacity is the total units you own and gates publish (must be > 0). unitsSold is read-only / system-managed: only paid orders increment it, never the editor or import. In stock means capacity - unitsSold > 0. Always null for a pre-owned listing (a used unit is a single physical item, not a pool). |
inventoryId | string (id) | null | Optional | Live | Read-only. The shared inventory pool your buy/rent/lease siblings of this robot draw on, keyed by (seller, BSIN, condition). Set on publish when linked to a catalog model; null for pre-owned, untracked, or unlinked listings. When set, it (not pool) is the stock of record, so a sale reduces what the rental/lease siblings can book. |
Condition is a field, not a separate listing type. grade: "new" (the default) is a fungible item sold from stock; grade: "pre_owned" is one specific used unit with its own history. Condition only matters for a purchase, when you rent or lease, the buyer gets an outcome, not a particular unit, so it isn’t shown.
A pre-owned listing is a single physical unit: pool is forced to null and the listing is removed once sold. The detail fields below are all seller-asserted; the “verified provenance” badge is earned separately from Botsdex-witnessed on-platform history, never self-declared.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
condition.grade | "new" | "pre_owned" | Optional | Live | New (factory-fresh, sold from a pool) or used, sent as "pre_owned" (one specific unit). Defaults to "new". A used listing represents a single physical unit and forces pool to null. |
condition.operatingHours | integer ≥0 ≤200000 | null | Optional | Live | Used only. Lifetime hours on the unit. Powers the operating_hours search facet. |
condition.manufactureYear | integer 1950 to next year | null | Optional | Live | Used only. The year the unit was built. An implausible value is stored as null (not disclosed) rather than refused; a numeric string is accepted. Shown in the page title and the cart line of a used unit. |
condition.serviceRecords[] | { date, description ≤280 }[] (≤30) | Optional | Live | Used only. Dated maintenance / service history. |
condition.wearNotes | string ≤1000 | null | Optional | Live | Used only. Honest free-text wear / cosmetic notes. Like all listing copy, off-platform contact details (emails, phone numbers, websites) are stripped on save and on import; that also covers serviceRecords[].description and passport.acquiredFrom. |
condition.refurbished | boolean | Optional | Live | Used only. The unit was reconditioned to working order / spec. |
condition.passport.serial | string ≤64 | null | Optional | Live | Used only. Manufacturer per-unit id (optional). Normalized to uppercase. Links the unit to its passport and to Fleet. |
condition.passport.acquiredFrom | string ≤200 | null | Optional | Live | Used only. Where you sourced the unit (e.g. lease return, fleet trade-in). |
workplaces is a string[] of where the robot can work, the cross-cutting search axis. Required: at least one. Seeded from your jobs’ defaults in the editor, then adjust to match reality.
Accepted values (extensible): home, office, hotel, restaurant, bar, cafe, hospital, care_home, pharmacy, retail, supermarket, mall, warehouse, port, factory, airport, transit_hub, gym, school, laboratory, stadium, sports_field, golf_course, convention_center, museum, theme_park, parking_lot, data_center, apartment_building, yard, pool, construction_site, farm, greenhouse, fish_farm, street_sidewalk, park, beach, solar_farm, wind_farm, power_plant.
Two facts that place the robot on the automation grid. Botsdex derives the buyer-facing archetype badge from them; you don’t set the badge directly.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
operationProfile.autonomyLevel | enum | Required | Live | operated (a person runs it), supervised (attended / on-call), unattended (no human present). |
operationProfile.generalityLevel | enum | Required | Live | single_task, specialist (a few related jobs), general_purpose (many jobs). |
Shared physical specs. All optional; the listing page renders only what is set.
If your listing is linked to a catalog model, specs you leave blank are filled in from the catalog, so you do not have to retype a robot’s height and weight that we already hold. Those rows show in the editor as read-only, with a note saying where the number came from. Clearing a field is not a way to state zero: a blank inherits.
You are never stuck with our number. Use my own value takes the row over for this listing, instantly, and your figure wins from then on, because you have the actual unit in front of you. The listing page marks a row “seller-stated” when yours contradicts ours, so buyers can see whose figure they are reading. Report it as wrong is the other route: it asks us to fix the number for every seller of that robot, so a person checks it first. Use the first when your unit differs, the second when we are simply wrong.
If you requested a new model, the specs on your listing are what we review to build that model’s catalog entry. Values we keep become the starting point for every seller who lists the same robot after you, so a spec sheet link on the request is the fastest way through, and a guessed number is worth leaving blank.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
specs.formFactor | "humanoid" | "quadruped" | "wheeled" | "arm" | "drone" | "other" | Optional | Live | Robot morphology. Optional, single-select. Source for the Form factor filter. arm = stationary manipulator, wheeled = any mobile wheeled base, other = escape hatch. |
specs.materials | ("aluminum" | "steel" | "titanium" | "magnesium" | "carbon_fiber" | "plastic" | "composite")[] | Optional | Live | Primary construction materials (chassis, body). Optional, multi-select over material classes, never trade names. Source for the buyer Material filter; omit when unspecified. |
specs.heightCm | number | Optional | Live | Total height in centimeters. |
specs.widthCm | number | Optional | Live | Chassis footprint width. |
specs.lengthCm | number | Optional | Live | Chassis footprint length. |
specs.weightKg | number | Optional | Live | Curb weight. |
specs.payloadKg | number | Optional | Live | Max carrying capacity. The single source for the payload filter. |
specs.batteryHours | number | Optional | Live | Raw battery capacity in hours. |
specs.nominalRuntimeHours | number | Optional | Live | Realistic continuous runtime per charge. Source for the runtime filter. |
specs.maxSpeedKmh | number | Optional | Live | Top speed in km/h. |
specs.degreesOfFreedom | number | Optional | Live | Articulation DoF (arms / manipulators). |
specs.ipRating | string | Optional | Live | Ingress protection rating, e.g. "IP54". Also the outdoor weather rating. |
specs.noiseDb | number | Optional | Live | Operating noise in decibels. |
Cross-job facts under attributes that are not physical specs. Each one powers a filter that appears on every job where it applies. Send the value, not the label.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
attributes.environment | enum[] | Optional | Live | Multi-select: indoor, outdoor. Where the robot operates. Supplying both values is how a robot says it works indoors and outdoors (there is no "both" value). |
attributes.navigationType | enum[] | Optional | Live | lidar, vision, qr, magnetic, gps, slam. |
attributes.chargingMode | enum[] | Optional | Live | Multi-select: manual, auto_dock, battery_swap. How the battery is replenished. Leave it empty for a mains-powered robot, and say so in powerSource. |
attributes.powerSource | enum[] | Optional | Live | Multi-select: battery, mains, combustion, fuel_cell. What the robot runs on. There is no "hybrid" value: a robot that carries a battery and can also run plugged in supplies both battery and mains. corded, plug_in and tethered are accepted as aliases for mains. |
attributes.fleetManaged | boolean | Optional | Live | Coordinates as a managed multi-robot fleet. |
attributes.obstacleDetection | boolean | Optional | Live | Detects and avoids obstacles. |
attributes.certifications | enum[] | Optional | Live | Product conformity marks (closed vocab): ce, rohs, fcc, emc, cb, ul, ukca. Powers the certifications filter. |
attributes.isoCertifications | string[] | Optional | Live | ISO standards, free text (write any, e.g. "ISO 9001"). Shown on the listing; not a search filter. |
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
integrations | string[] | Optional | Live | Named systems this robot integrates with, free text, e.g. ["Toast POS", "Oracle OPERA Cloud PMS"]. Searchable across jobs. |
software | string | Optional | Live | Robot software / firmware / update policy, free text, e.g. "Vendor Fleet OS 4.8, stable channel, API available." |
Buyers never discover freight after they have paid. The product page shows the price for their country with shipping broken out as its own line, so your rate card has to cover everywhere you ship before the listing can go live.
A more specific region always wins. Price EU once and every member state is covered; add a DE row and German buyers get that instead. Where shipping is already inside your price, enter 0 and the buyer sees “Included”.
Price one robot, not one order. A buyer who orders three is charged three times your rate, because that is three crates leaving your dock. And your rate card is not a substitute for fulfillment.shipsTo: a buyer can only check out to a country that is in shipsTo and priced, so removing a region from shipsTo closes it even if an old rate row is still there.
Country of origin is yours to declare. It is where the units you are selling were manufactured, not where they ship from, and it prints on the customs line of the buyer’s invoice. We ask you rather than filling it in from the catalog because you are the exporter of record answerable for it, and because the same model is often built in more than one country. If you are not certain, leave it blank: the line is simply omitted, and an omitted line is better than a wrong one.
Your rate card is also published as structured data (offers.shippingDetails), so a search engine can show your delivery cost next to your price. That is why shipping stays a separate line rather than being folded into the listing price: your price keeps meaning the robot.
Returns are required too. returns must be present before you can publish, and windowDays: 0 (final sale) is a perfectly good answer for a machine that cannot be resold as new. The rule is not that you accept returns, it is that a buyer knows the terms before they pay rather than after.
Separately, the Botsdex Guarantee always applies and you cannot waive it: if a robot never arrives, arrives damaged, or is not the machine you listed, you refund it in full including freight both ways. For rentals and leases, the same protection covers the affected handoff or charge, and the reservation is released when a refund is confirmed. After a buyer messages you, they can start a structured resolution request. You have 48 hours to make an offer before Botsdex support steps in, and a person reviews the case before anything is decided. See the Seller Agreement for the full obligations.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
fulfillment.shipsTo | string[] | Required | Live | Where you ship. The editor offers the full ISO country list plus the WW (worldwide) and EU sentinels; store the alpha-2 codes (case-insensitive), e.g. us, ca, gb, or WW. At least one is required to publish. |
fulfillment.estimatedLeadTimeDays | integer | Optional | Live | Days until it ships. This is how a pre-order is expressed, there is no separate availability field. |
fulfillment.shippingRates | { regions, amount }[] | Required | Live | Your freight rate card. Buyers see a delivered total before they pay, so every destination in shipsTo must be priced or the listing cannot publish. A region you cannot price belongs out of shipsTo instead. |
fulfillment.shippingRates[].regions | string[] | Required | Live | Destinations this price covers. Same code set as shipsTo: alpha-2 codes plus the WW and EU sentinels. A more specific region wins, so an EU row covers Italy while a DE row overrides it for German buyers. |
fulfillment.shippingRates[].amount | integer | Required | Live | Shipping cost in minor units (cents) of your pricing currency, ADDED to the listing price for buyers in those regions. Price ONE robot: a buyer ordering three is charged three times this, since that is three crates. Use 0 where shipping is already included in your price; 0 shows the buyer 'Included'. Always a one-time charge, never multiplied by a rental period. |
fulfillment.incoterms | "EXW" | "DAP" | "DDP" | Optional | Live | Who clears customs and pays duty. EXW = buyer collects and arranges everything. DAP = you deliver, buyer clears import and pays duty + VAT. DDP = you deliver duty paid. Buyers comparing a delivered price need this to compare like with like, so state it on any cross-border listing. |
fulfillment.shipsFrom | string | Optional | Live | Where the robot ships from, one alpha-2 code (or WW / EU). Separate from your payout country. |
countryOfOrigin | string | null | Optional | Live | TOP-LEVEL, not inside fulfillment. Where the units you are selling were MANUFACTURED, one alpha-2 code. This is your customs declaration: it prints on the customs line of the buyer's invoice, and you are the exporter of record answerable for it. Distinct from shipsFrom (where they leave from). Leave it null if you are not certain, an omitted line is fine and a wrong one is not. |
returns | object | Required | Live | Your change-of-mind returns terms. Required to publish: buyers must know before they pay, not after. Top-level, not inside fulfillment, because these are commercial terms rather than shipping. Omit the object entirely and the listing cannot go live. |
returns.windowDays | integer | Required | Live | Days after delivery in which you accept a change-of-mind return. 0 means FINAL SALE, which is a valid answer and a common one for configured machines. Max 180. |
returns.restockingFeeBps | integer | Required | Live | Restocking fee withheld from the refund, in basis points of the item price (1000 = 10%). Capped at 3000 (30%). Change-of-mind returns only. |
returns.returnShippingPaidBy | "seller" | "buyer" | Required | Live | Who pays return freight on a change-of-mind return. Ignored when windowDays is 0. If the robot arrives damaged or is not what you listed, you pay return freight regardless of this field. |
returns.conditions | string | Optional | Live | Your own conditions in your words, e.g. original crate required, under 40 operating hours. Contact-scrubbed like every other free text. |
Install requirements. Water-line and drain power the utility-hookup filter, so answering them is what puts your robot in front of a buyer filtering for a machine their site can actually take. In the editor this is Install & site requirements.
Both are worth answering even when the answer is no: “no plumbing needed” is a selling point on a machine buyers expect to need it. Leaving a question blank is not the same as answering no, and we never turn one into the other.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
deployment.installationRegions | string[] | Optional | Live | Regions where you install on site. Same code set as shipsTo: alpha-2 country codes plus the WW and EU sentinels; unrecognized tokens are dropped. |
deployment.setupTimeDays | integer | Optional | Live | Typical setup duration. |
deployment.siteSurveyRequired | boolean | Optional | Live | A site survey is required before deployment. |
deployment.staffTrainingHours | number | Optional | Live | Operator training time included. |
deployment.internetRequired | boolean | Optional | Live | Requires internet connectivity to operate. |
deployment.powerRequirements | string | Optional | Live | Power needs, e.g. standard outlet, 240V. |
deployment.requiresWaterLine | boolean | Optional | Live | Needs a plumbed water line. Powers the utility-hookup filter. |
deployment.requiresDrain | boolean | Optional | Live | Needs a drain. Powers the utility-hookup filter. |
All buyer-facing. Shown on the PDP and used for ranking.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
support.responseTime | string ≤120 | Optional | Live | Buyer-facing response SLA. The editor captures it as a number of hours and stores it as e.g. "24h"; legacy free-text values still render. |
support.warranty | { kind: "months", months: 1..600 } | { kind: "lifetime" } | null | Optional | Live | Seller warranty statement. Duration is stored in months (enter years as months x 12); "lifetime" is its own kind. null or omitted = not specified. Source for the buyer Warranty filter (bucketed: under 1 year / 1 year / 2 years / 3+ years / lifetime) and snapshotted onto orders. |
support.languages | string[] | Optional | Live | Support languages from the known list, e.g. ["English", "Spanish"]. The value is the English name (case-insensitive input); languages outside the list are dropped. |
support.channels | enum[] | Optional | Live | email, phone, video, on_site. |
support.serviceRegions | string[] | Optional | Live | Regions where on-site service is offered. |
support.maintenanceIncluded | boolean | Optional | Live | Maintenance is included with the listing. |
support.remoteMonitoringIncluded | boolean | Optional | Live | Remote monitoring is included. |
Optional rich content rendered on the product page.
Heads up: email addresses, phone numbers, and websites are automatically removed from all of these text fields (and the rest of your listing copy) on save and on import. Buyers contact you through Botsdex, so off-platform contact details are stripped rather than published.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
whatsInTheBox | string[] | Optional | Live | Bullet list of included items, up to 30 entries, each ≤120 chars. |
faq | array<{ question, answer }> | Optional | Live | Seller-authored Q&A, up to 5 pairs. question ≤200, answer ≤1000. Both required per pair (a pair missing either is dropped). Shown as a 'Questions & answers' section on the listing page. |
setupAndDeployment | string ≤5000 | Optional | Live | Long-form setup / deployment prose. Double newlines split paragraphs. |
disclaimers | string ≤5000 | Optional | Live | Legal or use-case caveats. Double newlines split paragraphs. |
Job types & job-specific fields
Attach the jobs your robot can do under supportedJobs[]. Each entry is { jobSlug, primary, attributes }, where attributes holds that job’s fields below. The fields marked Required must be set to publish under that job. Send the value, not the label (send scrub, not “Scrub”).
workRate is { value, unit } with the unit fixed per job; it is your headline throughput number and powers the comparison and the throughput filter.
Job-specific filters are shown dynamically: a job filter appears once more than 2 listings carry its attribute (on a job page that is counted across the job’s catalog, on search across the current results), below that it cannot narrow anything. The general filters (operation, workplaces, price, brand, ships-to, autonomy) always show. A filter a buyer has applied is never hidden. Filling your job fields is what makes your robot show up under those filters.
Can’t find your job? Attach the catch-all jobSlug: other. It asks one required field, taskTags (short task phrases like “window cleaning”), and leans on the universal fields plus customAttributes until a real job category exists. Listings under it are searchable but not browsable: the “other” job has no category tile and its landing page is not indexed.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
supportedJobs | array<{ jobSlug, primary, attributes }> | Required | Live | Every job this robot does. At least 1 required to publish, exactly 1 marked primary. Up to 10. Each entry pulls in the job's own field set (see Job types below). |
supportedJobs[].jobSlug | enum (job slug) | Optional | Live | Mandatory within a job entry. Must be one of the slugs in Job types below; unknown slugs are dropped by the sanitizer. |
supportedJobs[].primary | boolean | Optional | Live | Mandatory within a job entry: exactly one entry across the list must be true. The editor enforces this; the importer auto-promotes the first entry if none is marked. |
supportedJobs[].attributes | object | Optional | Live | Job-specific fields, see the per-job tables below. The fields marked Required there must be set for the listing to publish under that job. |
jobSlug: floor-cleaning · /jobs/floor-cleaningDefault workplaces: office, hotel, restaurant, retail, supermarket, mall, warehouse, factory, hospital, care_home, gym, airport, transit_hub, school, convention_center, museum.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
cleaningTypes | enum[] | Required | Live | vacuum, mop, scrub, sweep, disinfect. |
floorTypes | enum[] | Required | Live | tile, concrete, vinyl, epoxy, terrazzo, sealed_hardwood, carpet. |
workRate | { value, unit: m2/h } | Required | Live | Coverage rate in square meters per hour. |
dockAutomation | enum[] | Optional | Live | auto_charging, auto_water_refill, auto_wastewater_drain, self_emptying. |
smartFeatures | enum[] | Optional | Live | cleaning_reports, scheduled_cleaning, remote_monitoring. |
cleaningWidthMm | number (mm) | Optional | Live | Clean-path width per pass. |
solutionTankL | number (L) | Optional | Live | Clean-water tank capacity. |
recoveryTankL | number (L) | Optional | Live | Dirty-water recovery tank capacity. |
staffSetupRequired | boolean | Optional | Live | Staff must stage the robot before each run. |
jobSlug: food-running · /jobs/food-runningDefault workplaces: restaurant, hotel, cafe, bar, care_home.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
useCases | enum[] | Required | Live | food_delivery, dish_return, host_greeting, promotion. |
trayCount | number | Required | Live | Serving trays / shelf levels. |
workRate | { value, unit: trips/h } | Optional | Live | Delivery trips per hour. |
interactionModes | enum[] | Optional | Live | audio_prompts, touchscreen, face_display, lights. |
tableMappingSupported | boolean | Optional | Live | Per-table point-to-point delivery. |
returnStationSupported | boolean | Optional | Live | Docks at a dish-return station. |
staffLoadingRequired | boolean | Optional | Live | Staff must load the robot. |
staffUnloadingRequired | boolean | Optional | Live | Staff must unload the robot. |
maxRouteCount | number | Optional | Live | Saved delivery routes. |
voiceLanguages | string[] | Optional | Live | Languages the voice prompts speak. |
screenSizeInch | number (in) | Optional | Live | Guest-facing display diagonal. |
jobSlug: room-delivery · /jobs/room-deliveryDefault workplaces: hotel, office, care_home, apartment_building.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
deliveryTypes | enum[] | Required | Live | amenities, room_service, towels, toiletries, mail. |
workRate | { value, unit: deliveries/h } | Optional | Live | Deliveries per hour. |
compartmentCount | number | Optional | Live | Separate cargo bins / drawers. |
lockableCompartments | boolean | Optional | Live | Lockable, with PIN / badge / app access. |
temperatureControlled | boolean | Optional | Live | At least one heated/cooled compartment. |
temperatureRangeC | { min, max } (°C) | Optional | Live | Compartment temperature window (when controlled). |
buildingIntegration | enum[] | Optional | Live | elevator, access_control, pms. Name the system in integrations. |
guestNotificationModes | enum[] | Optional | Live | app, sms, phone, screen, voice. |
jobSlug: drink-pouring · /jobs/drink-pouringDefault workplaces: bar, cafe, restaurant, hotel, stadium, office, convention_center.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
beverageTypes | enum[] | Required | Live | coffee, espresso, cocktail, spirits, beer, soft_drink, boba, juice, smoothie, tea. |
drinkMechanism | enum | Required | Live | fixed_kiosk, robotic_arm, countertop_dispenser. |
footprint | enum | Optional | Live | countertop, freestanding, mobile. |
prepActions | enum[] | Optional | Live | dispense, pour_only, mix_shake, brew_extract, blend. |
workRate | { value, unit: drinks/h } | Optional | Live | Drinks per hour. |
requiresHumanLoading | boolean | Optional | Live | A human restocks ingredients. |
requiresHumanCleaning | boolean | Optional | Live | A human runs cleaning. |
paymentIntegration | boolean | Optional | Live | Accepts payment. Name the POS / payment system in integrations. |
recipesSupported | number | Optional | Live | Number of recipes. |
ingredientCapacity | enum | Optional | Live | low, medium, high. |
cupSizesSupported | string[] | Optional | Live | Supported cup sizes. |
cleaningCycleMinutes | number (min) | Optional | Live | Cleaning-cycle downtime. |
foodSafetyCertifications | string[] | Optional | Live | Named food-safety certifications. |
jobSlug: hospital-delivery · /jobs/hospital-deliveryDefault workplaces: hospital, care_home, laboratory.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
deliveryTypes | enum[] | Required | Live | medication, lab_samples, linen, meals, supplies, waste. |
workRate | { value, unit: deliveries/h } | Optional | Live | Deliveries per hour. |
compartmentCount | number | Optional | Live | Separate cargo bins / drawers. |
lockableCompartments | boolean | Optional | Live | Required for medication. |
chainOfCustodyLogging | boolean | Optional | Live | Logs custody. Required for medication and HIPAA-relevant deployments. |
temperatureControlled | boolean | Optional | Live | Cold-chain capable. |
hospitalSystemIntegrations | enum[] | Optional | Live | ehr, elevator, access_control. Name the system in integrations. |
sanitationProtocol | enum | Optional | Live | wipeable, uv_self_clean, sealed_ip_rated, none. |
hipaaRelevant | boolean | Optional | Live | Configured for HIPAA-relevant handling. |
jobSlug: shelf-scanning · /jobs/shelf-scanningDefault workplaces: retail, supermarket, warehouse, pharmacy.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
scanTypes | enum[] | Required | Live | out_of_stock, price_check, planogram, misplaced_items, inventory_count, hazard_detection, promo_compliance. |
cameraTypes | enum[] | Required | Live | rgb, depth, barcode, rfid. |
storeFormats | enum[] | Optional | Live | grocery, mass_merchant, pharmacy, convenience, apparel. |
workRate | { value, unit: scans/h } | Optional | Live | Aisles scanned per hour. |
shelfHeightMaxCm | number (cm) | Optional | Live | Maximum shelf reach. |
minAisleWidthCm | number (cm) | Optional | Live | Narrowest aisle it fits. |
systemIntegrations | enum[] | Optional | Live | pos, inventory_management, planogram_software, erp. Name the system in integrations. |
reportingDashboard | boolean | Optional | Live | Ships with a reporting dashboard. |
afterHoursOperation | boolean | Optional | Live | Operates after hours. |
staffReviewRequired | boolean | Optional | Live | Staff review the captured data. |
jobSlug: material-transport · /jobs/material-transportDefault workplaces: warehouse, factory, airport, port, construction_site.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
tasks | enum[] | Required | Live | picking, goods_to_person, tugging, pallet_move, sortation, inventory_scan. |
transportTypes | enum[] | Required | Live | totes, carts, bins, pallets, mail, supplies. |
workRate | { value, unit: trips/h } | Optional | Live | Trips per hour. |
towingCapacityKg | number (kg) | Optional | Live | Max towed load (distinct from on-deck payload in specs). |
liftHeightCm | number (cm) | Optional | Live | Max lift height. 0 for flat-deck units. |
minAisleWidthCm | number (cm) | Optional | Live | Narrowest safe operating aisle. |
maxGradePercent | number (%) | Optional | Live | Max ramp / incline loaded. |
wmsIntegration | enum[] | Optional | Live | sap, oracle, manhattan, blue_yonder, korber, other. Name the system in integrations. |
jobSlug: lawn-mowing · /jobs/lawn-mowingDefault workplaces: yard, park, golf_course, sports_field, school, stadium, hotel, office.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
workRate | { value, unit: m2/charge } | Required | Live | Area mowed per charge. |
mowingWidthMm | number (mm) | Required | Live | Cutting deck width. |
boundaryType | enum | Required | Live | perimeter_wire, rtk_gps, vision, beacon. |
propertyTypes | enum[] | Required | Live | residential, sports_turf, golf, municipal, solar_farm. |
maxSlopePercent | number (%) | Optional | Live | Steepest grade it can mow. |
cuttingHeightRangeMm | { min, max } (mm) | Optional | Live | Cutting-height range. |
rainOperation | boolean | Optional | Live | Operates in rain. |
theftProtection | boolean | Optional | Live | GPS tracking / anti-theft. |
jobSlug: pool-cleaning · /jobs/pool-cleaningDefault workplaces: pool, hotel, gym, school.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
poolTypes | enum[] | Required | Live | inground, aboveground, commercial, spa. |
cleaningZones | enum[] | Required | Live | floor, wall, waterline, surface. |
driveType | enum | Required | Live | robotic_corded, robotic_cordless, pressure_side, suction_side. |
workRate | { value, unit: m2/charge } | Optional | Live | Area cleaned per charge. |
poolSizeSupportedM2 | number (m²) | Optional | Live | Largest pool it services. |
surfaceTypes | enum[] | Optional | Live | tile, vinyl, fiberglass, concrete, plaster. |
debrisTypes | enum[] | Optional | Live | leaves, sand, silt, algae, insects. |
requiresManualRetrieval | boolean | Optional | Live | Must be retrieved by hand (vs self-parking). |
filterCapacityL | number (L) | Optional | Live | Debris canister volume. |
filterFinenessMicron | number (µm) | Optional | Live | Filter fineness. |
climbControl | enum | Optional | Live | none, programmed_path, smart_scan. |
jobSlug: security-patrol · /jobs/security-patrolDefault workplaces: office, warehouse, retail, hospital, parking_lot, airport, stadium, factory, school, park, construction_site, port, data_center, solar_farm, power_plant.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
patrolMode | enum | Required | Live | fixed_route, dynamic_route, remote_operated. |
patrolForm | enum[] | Required | Live | wheeled, quadruped, drone, turret. |
detections | enum[] | Required | Live | license_plate, intrusion, fire, anomaly, weapon, face. |
sensors | enum[] | Optional | Live | thermal, lidar, night_vision, microphone, gas. |
workRate | { value, unit: m2/charge } | Optional | Live | Area covered per patrol / charge. |
cameraCount | number | Optional | Live | Onboard cameras. |
videoRetentionDays | number (days) | Optional | Live | Video retention window. |
twoWayAudio | boolean | Optional | Live | Live two-way voice. |
remoteGuardingAvailable | boolean | Optional | Live | Human-in-the-loop remote takeover. |
monitoringIntegrations | enum[] | Optional | Live | alarm_vms, access_control, soc_platform. Name the system in integrations. |
privacyControls | enum[] | Optional | Live | face_blurring, audio_optout, data_residency, gdpr_mode. Expected when using face / license-plate detection. |
jobSlug: event-hosting · /jobs/event-hostingDefault workplaces: stadium, mall, hotel, retail, convention_center, museum.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
hostingTasks | enum[] | Required | Live | greeting, wayfinding, info_lookup, ticket_scanning, registration, lead_capture. |
interactionModes | enum[] | Optional | Live | touchscreen, voice, face_display, gestures, app. |
voiceLanguages | string[] | Optional | Live | Voice languages. |
screenSizeInch | number (in) | Optional | Live | Screen size. |
jobSlug: entertainment-performance · /jobs/entertainment-performanceDefault workplaces: stadium, mall, hotel, restaurant, bar, convention_center, theme_park.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
performanceTypes | enum[] | Required | Live | dancing, interactive_play, animatronics, music, light_show, mascot. |
controlMode | enum | Required | Live | autonomous, operator_controlled, pre_programmed. |
interactionModes | enum[] | Optional | Live | voice, gestures, touchscreen, app. |
heightCm | number (cm) | Optional | Live | Height. |
jobSlug: promo-advertising · /jobs/promo-advertisingDefault workplaces: mall, retail, stadium, airport, transit_hub, convention_center.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
displayTypes | enum[] | Required | Live | led_screen, projector, static_banner, holographic. |
contentCapabilities | enum[] | Optional | Live | video, images, qr_codes, live_data, audio. |
leadCapture | boolean | Optional | Live | Lead capture. |
screenSizeInch | number (in) | Optional | Live | Screen size. |
brightnessNits | number (nits) | Optional | Live | Brightness. |
jobSlug: photo-capture · /jobs/photo-captureDefault workplaces: mall, hotel, stadium, retail, restaurant, convention_center, theme_park.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
captureTypes | enum[] | Required | Live | photo, video, boomerang, gif, green_screen, booth_360. |
deliveryMethods | enum[] | Optional | Live | instant_print, email, sms, qr_download, social_share. |
onboardPrinting | boolean | Optional | Live | Onboard printing. |
cameraResolutionMp | number (MP) | Optional | Live | Camera resolution. |
jobSlug: machine-tending · /jobs/machine-tendingDefault workplaces: factory, warehouse.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
machineTypes | enum[] | Required | Live | cnc_lathe, cnc_mill, injection_molding, press, grinder, inspection_station. |
workRate | { value, unit: parts/h } | Optional | Live | Parts per hour. |
reachMm | number (mm) | Required | Live | Reach. |
axes | number | Optional | Live | Axes. |
repeatabilityMm | number (mm) | Optional | Live | Repeatability. |
integrationFeatures | enum[] | Optional | Live | cnc_interface, plc_io, vision_guided, auto_door, gripper_changer. |
jobSlug: palletizing · /jobs/palletizingDefault workplaces: factory, warehouse.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
loadTypes | enum[] | Required | Live | cases, bags, trays, drums, pails. |
patternProgramming | enum | Required | Live | software_library, teach_pendant, vision_adaptive. |
workRate | { value, unit: cases/h } | Optional | Live | Cases per hour. |
maxStackHeightCm | number (cm) | Optional | Live | Max stack height. |
reachMm | number (mm) | Required | Live | Reach. |
conveyorIntegration | boolean | Optional | Live | Conveyor integration. |
jobSlug: welding · /jobs/weldingDefault workplaces: factory.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
weldProcesses | enum[] | Required | Live | mig, tig, spot, laser, plasma, arc. |
materials | enum[] | Optional | Live | steel, stainless, aluminum, titanium. |
workRate | { value, unit: welds/h } | Optional | Live | Welds per hour. |
reachMm | number (mm) | Required | Live | Reach. |
seamTracking | boolean | Optional | Live | Seam tracking. |
jobSlug: assembly · /jobs/assemblyDefault workplaces: factory.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
assemblyTasks | enum[] | Required | Live | screwdriving, fastening, press_fit, gluing, snap_fit, soldering. |
workRate | { value, unit: cycles/h } | Optional | Live | Cycles per hour. |
reachMm | number (mm) | Required | Live | Reach. |
axes | number | Optional | Live | Axes. |
repeatabilityMm | number (mm) | Optional | Live | Repeatability. |
forceTorqueSensing | boolean | Optional | Live | Force/torque sensing. |
visionGuided | boolean | Optional | Live | Vision guided. |
Your own key-value spec rows under customAttributes, the escape valve for details no universal or job field models yet. A drink-pouring robot whose template stops at beverageTypes can still say { "label": "Beverages", "value": "hot chocolate, boba" }. Buyers see each row as “Label: value” text under “Additional details” on the listing page.
A row can optionally say what its value is. Add "type": "number" with a unit and { "label": "Water tank", "value": "12", "unit": "l" } reads as a quantity; "type": "boolean" reads Yes/No. Typing is never required, and a row that declares a type its value cannot honour just falls back to plain text rather than being rejected. We store your value exactly as you send it, so nothing is rounded or reformatted behind your back.
Rows are searchable but not filterable: a buyer typing the text can find your listing, but custom rows never become a search filter (an open vocabulary would fragment the filters). Nor can a custom row take over a field we already have: label one “Payload” and it stays plain text, because the real Payload filter is built from specs. If an existing field covers your fact, use that field. A custom row that keeps appearing across listings is a candidate for promotion into a real field, which is how it earns a filter.
| Field | Type | Required | Status | Description |
|---|---|---|---|---|
customAttributes | array<{ label, value, type?, unit? }> | Optional | Live | Your own spec rows, up to 10, for details no universal or job field captures yet. Rendered on the listing page as "Label: value" rows under "Additional details". The text is keyword-searchable (a buyer typing it can find your robot) but never becomes a filter; filters only come from the closed-vocabulary fields above. Optional, not required to publish. |
customAttributes[].label | string ≤40 | Optional | Live | The fact's name, e.g. "Beverages". Mandatory within a row: rows missing either half are dropped by the sanitizer. |
customAttributes[].value | string ≤120 | Optional | Live | The fact's value, e.g. "hot chocolate, boba". Always send it as a STRING, even for a number: we store exactly what you send, so "1 200,5" keeps your separators and precision. Like all listing copy, off-platform contact details (emails, phone numbers, websites) are stripped on save and on import. |
customAttributes[].type | "text" | "number" | "boolean" | Optional | Live | Optional. Omit it and the row is plain text, which is the default and always fine. "number" lets the row carry a unit and read as a quantity; "boolean" renders Yes/No (send "yes"/"no" or true/false as a string). If the value does not fit the type you declared, the row falls back to text rather than being rejected. |
customAttributes[].unit | string | Optional | Live | Optional, and only meaningful with type "number". Must be one of our units (kg, g, lb, t, mm, cm, m, in, ft, m2, l, gal, s, min, h, days, months, years, km/h, W, kW, kWh, V, A, N, Nm, bar, psi, deg, degC, %, dB, GB, /h, and similar). Anything else is not stored and the row falls back to text, so send a unit from the list or leave it out and put the unit in your label. |
A complete floor-cleaning listing as a feed would submit it. Only seller-set fields, Botsdex builds search from these.
{
"title": "Avidbots Neo 2",
"brandName": "Avidbots",
"catalogModelRef": { "brandId": "avidbots", "slug": "neo-2" },
"tagline": "Autonomous commercial floor scrubber for large facilities.",
"description": "The Neo 2 scrubs large commercial floors with mapped routes, reporting, and remote monitoring.",
"sku": "AVID-NEO-2",
"pool": null,
"condition": {
"grade": "new",
"operatingHours": null,
"serviceRecords": [],
"wearNotes": null,
"refurbished": false,
"passport": { "serial": null, "acquiredFrom": null }
},
"images": [
{ "src": "https://manufacturer.example/media/neo-2-hero.jpg", "alt": "Neo 2 scrubbing a warehouse floor" },
{ "src": "https://manufacturer.example/media/neo-2-side.jpg", "alt": "Neo 2 side profile" },
{ "src": "https://manufacturer.example/media/neo-2-rear.jpg", "alt": "Neo 2 rear view with charging dock" }
],
"videos": [
{ "provider": "youtube", "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "title": "Avidbots Neo 2 overview" }
],
"whatsInTheBox": [
"Neo 2 robot scrubber",
"Charging dock with cable",
"On-site installation + staff training"
],
"faq": [
{ "question": "Does it work on carpet?", "answer": "It is built for hard floors and skips carpet automatically." },
{ "question": "How much supervision does it need?", "answer": "It runs unattended on a schedule; staff refill water and empty the tank about once per shift." }
],
"setupAndDeployment": "Avidbots' deployment team scans the facility, generates a cleaning plan, and trains designated staff.\n\nNeo 2 exposes job telemetry over REST + webhook APIs.",
"disclaimers": "Pricing shown is for the base configuration in North America. Outdoor operation requires the optional outdoor wheel kit.",
"transactionType": "purchase",
"pricing": {
"currency": "usd",
"purchase": { "amount": 8500000, "included": ["1-year warranty", "Remote support"] }
},
"workplaces": ["hotel", "hospital", "retail", "warehouse"],
"supportedJobs": [
{
"jobSlug": "floor-cleaning",
"primary": true,
"attributes": {
"cleaningTypes": ["scrub", "sweep"],
"floorTypes": ["tile", "concrete", "vinyl", "epoxy"],
"workRate": { "value": 3700, "unit": "m2/h" },
"dockAutomation": ["auto_charging", "auto_water_refill"],
"smartFeatures": ["cleaning_reports", "scheduled_cleaning", "remote_monitoring"],
"cleaningWidthMm": 711,
"solutionTankL": 60
}
}
],
"operationProfile": { "autonomyLevel": "unattended", "generalityLevel": "single_task" },
"specs": { "formFactor": "wheeled", "heightCm": 142, "widthCm": 75, "weightKg": 245, "nominalRuntimeHours": 4, "maxSpeedKmh": 5, "ipRating": "IP54", "noiseDb": 68 },
"attributes": { "environment": ["indoor"], "powerSource": ["battery"], "chargingMode": ["auto_dock"], "obstacleDetection": true },
"integrations": ["Avidbots Fleet API"],
"software": "Avidbots Autonomy Platform 4.8, API available.",
"customAttributes": [
{ "label": "Brush system", "value": "Dual counter-rotating disc brushes" },
{ "label": "Water tank", "value": "12", "type": "number", "unit": "l" },
{ "label": "Refill sensor", "value": "yes", "type": "boolean" }
],
"fulfillment": {
"shipsTo": ["us", "ca", "EU"],
"estimatedLeadTimeDays": 30,
"shippingRates": [
{ "regions": ["us"], "amount": 0 },
{ "regions": ["ca"], "amount": 120000 },
{ "regions": ["EU"], "amount": 340000 }
],
"incoterms": "DAP",
"shipsFrom": "us"
},
"returns": {
"windowDays": 30,
"restockingFeeBps": 1000,
"returnShippingPaidBy": "buyer",
"conditions": "Original crate and packaging required. Under 20 operating hours."
},
"deployment": { "siteSurveyRequired": true, "staffTrainingHours": 4, "internetRequired": true, "requiresWaterLine": true, "requiresDrain": true },
"support": { "responseTime": "24h", "warranty": { "kind": "months", "months": 24 }, "languages": ["English"], "channels": ["email", "phone", "on_site"], "serviceRegions": ["us"], "maintenanceIncluded": true }
}