Connector Field Mapping Reference
Per-entity mapping rules: products, categories, variants, prices, stock, images and locales. The contract every CMS module follows when shipping data into AACsearch.
This page is the definitive reference for what a CMS module must send to AACsearch. The endpoint shapes live in Connector API Lifecycle; this page covers the semantics — which CMS-side field maps to which AACsearch field and why.
Every connector ships SyncProductInput documents. The shape is intentionally narrow (it's a product index, not a generic content store) but flexible enough to model articles, recipes, or any other entity through attributes.
Product fields
| Field | Type | Required | Notes |
|---|---|---|---|
external_id | string | yes | Stable platform primary key. Never a slug, never a SKU. |
title | string | yes | Plain text in the configured locale. Strip HTML and shortcodes. |
description | string | no | Plain text. Strip HTML, render rich-text JSON / markdown to text. |
sku | string | no | Primary or default-variant SKU. Send the displayed SKU, not internal IDs. |
brand | string | no | Manufacturer or vendor name. Empty string → drop the field. |
categories | string[] | no | See Categories. |
category_ids | string[] | no | Platform category IDs for filters. Stable across renames. |
tags | string[] | no | Flat list of free-form labels. |
price | number | no | Major units. See Prices. |
sale_price | number | no | Major units. See Prices. |
currency | string | no | ISO 4217, 3 letters. Lowercase rejected by the validator. |
image_url | string (URL) | no | Primary image. See Images. |
product_url | string (URL) | no | Storefront URL used by the widget for click-through. |
availability | enum | no | "in_stock", "out_of_stock", "preorder". See Stock. |
stock_quantity | number | no | Total available units across locations. |
attributes | Record<string,unknown> | no | Free-form facet/display fields. See Attributes. |
locale | enum | no | en, de, es, fr, ru. One document per locale. |
created_at | string (ISO 8601) | no | Used for "Recently added" sort. |
updated_at | string (ISO 8601) | no | Used for change detection in the buffer; recommended. |
external_id rules
Use the CMS primary key. Specifically:
- WooCommerce / WordPress:
post_id(numeric) - PrestaShop:
id_product - Bitrix:
iblock_element_id - Shopify: numeric product ID, not the handle
- Magento:
entity_id, not SKU
If your platform models locale-specific entities as separate rows, append the locale: prod-123:en. Never reuse an external_id across product types or locales — searches will collide.
Categories
Two fields, used together:
categories— full breadcrumb path, top → leaf, human-readable strings.["Shoes", "Running", "Trail"], not["Trail"]. The widget renders the path for filters.category_ids— stable platform IDs for the same path, top → leaf. Used for filter queries that survive category renames.
If a product lives under multiple categories, flatten them: send each path as a separate entry in categories. The order of the array does not matter for ranking; only string identity for facet counts.
| CMS | categories source | category_ids source |
|---|---|---|
| PrestaShop | ps_category_lang.name along the path | ps_category.id_category along the path |
| Bitrix | iblock section names | iblock section IDs |
| WordPress | category taxonomy terms | term IDs |
| Shopify | product_type + collection titles | collection IDs |
| Strapi | reference field, walked recursively | document IDs |
| Sanity | references() walked recursively | _id of category docs |
Variants
The default model is product-level documents. Variant data — size, color, material, variant SKUs, per-variant stock — flattens into attributes. A single product becomes a single document.
This is the right choice for most e-commerce stores: shoppers search "blue running shoes," not "blue running shoes size 9.5 narrow." The widget shows the parent product card; variant selection happens on the product detail page.
When to ship variant-level documents
Build a custom mapper that emits one document per variant when:
- Search must surface individual SKUs (B2B catalogs, parts stores).
- Variants have wildly different titles or images (kit vs single unit).
- Variants live in different categories.
In that case:
external_idis the variant ID, suffixed by parent:prod-123:variant-7.titleincludes the variant attributes:"Blue Running Shoes — Size 9.5 Narrow".image_urlis the variant's image, not the parent's.- Add
attributes.parent_external_idso a deduped "by-product" search can collapse results.
The Shopify reference connector uses product-level documents by default; see Custom Connector to build the variant-level variant.
Prices
Wire format
price and sale_price are major units, decimal numbers. 99.99 USD is sent as 99.99, not 9999. The Connector API converts to BigInt minor units (kopecks-style) inside the ingest pipeline. Sending 9999 will index the price as $9,999.00.
Two-price model
price— the list price the customer would pay without any active promotion.sale_price— the promotional price, if active. Whensale_priceis set, the widget renders both with strikethrough onprice.
When there are many prices
CMS systems with customer groups, B2B tiers, or dynamic pricing have one product but many prices. The simplest convention:
{
"price": 99.99,
"sale_price": 79.99,
"currency": "USD",
"attributes": {
"price_by_group": {
"retail": 99.99,
"wholesale": 79.99,
"vip": 69.99
}
}
}price is the default storefront price. Other tiers go in attributes.price_by_group. The storefront filters or sorts on the right group at query time.
Multi-currency catalogs
Send the customer-facing currency as currency, plus alternatives in attributes.price_by_currency:
{
"currency": "USD",
"price": 99.99,
"attributes": {
"price_by_currency": { "EUR": 92.5, "GBP": 79.99 }
}
}AACsearch indexes the alternative prices as numeric attributes so the storefront can switch currency without re-indexing.
Stock
Two fields that the widget uses to label availability:
availability—"in_stock"|"out_of_stock"|"preorder". Drives the badge on the result card.stock_quantity— total units across all locations. Drives "Only 3 left" hints.
Mapping rules
| CMS / platform | availability derivation |
|---|---|
| PrestaShop | ps_stock_available.quantity > 0 AND out_of_stock policy → in_stock |
| Bitrix | OnPriceUpdate event handler; CATALOG_AVAILABLE = 'Y' → in_stock |
| WooCommerce | _stock_status post meta: instock / outofstock / onbackorder (→ preorder) |
| Shopify | Sum of inventory_quantity across whitelisted locations |
| Strapi | Custom field on the content type, or default to in_stock |
A product with multiple locations is in stock if any whitelisted location has quantity > 0. Use connector config to whitelist locations.
Backorders and pre-orders
If your platform supports backorders or pre-orders, map them to preorder. The widget renders a distinct label. The product remains in search results — useful for "available soon" flows.
Images
image_url is the primary display image, full URL with scheme. The widget renders it on the result card.
For products with multiple images, send the full list in attributes.images:
{
"image_url": "https://cdn.example.com/products/brs-42/main.jpg",
"attributes": {
"images": [
{ "url": "https://cdn.example.com/products/brs-42/main.jpg", "alt": "Front" },
{ "url": "https://cdn.example.com/products/brs-42/side.jpg", "alt": "Side" }
]
}
}Image rules
- Use full URLs (https://...). Relative URLs break when the storefront renders the widget on a CDN edge.
- Use the storefront-sized image, not the original. AACsearch does not resize.
- If the platform serves WebP / AVIF, send the URL of the format the storefront actually shows.
- Empty string is invalid — drop the field if you have no image.
Image fallbacks
The widget renders a placeholder when image_url is missing. If you have product-type-specific placeholders, set them through the widget theme rather than synthesizing a placeholder URL on the connector side.
Locales
AACsearch accepts five locales out of the box: en, de, es, fr, ru. If your CMS has a locale outside this set, send the closest neighbor and store the original code in attributes.language for display:
{
"locale": "en",
"attributes": {
"language": "pt-BR",
"language_label": "Português (Brasil)"
}
}Document split
For platforms with localized catalogs (Polylang, WPML, Shopify Markets, PrestaShop multistore, Strapi i18n):
- One document per locale per entity.
external_idsuffixed with the locale:prod-123:en,prod-123:de.- Title and description in the locale's language.
- Categories in the locale's language too — never the default-locale strings.
Updates to one locale do not affect documents for other locales. The event handler should emit only the locale that changed.
Storefront locale resolution
The widget receives the locale from the storefront — usually from the URL prefix (/en/, /de/) or the lang HTML attribute. The widget sends locale=<code> on every search query, and AACsearch filters documents by the locale field. Documents without a locale value match every locale.
Attributes
Any field that is filterable, facetable, or display-only goes into attributes. Keys are free-form strings; values are primitives or arrays of primitives.
Type inference
AACsearch infers the index type from the first batch ingested under a fresh index:
| Field shape | Indexed as |
|---|---|
"red" (string) | facet, exact match |
42 (number) | numeric, sort + range |
42.5 (number) | float, sort + range |
true (boolean) | filter only |
["red", "blue"] (array) | multi-facet |
[1, 2, 3] (number array) | not supported — flatten or split |
If you change the type of an attribute mid-flight (e.g. send "42" after sending 42), AACsearch rejects the document. Pick a type per attribute and stick to it.
Naming conventions
- Lowercase. Underscores for word separation.
option_color, notOptionColor. - Prefix variant options with
option_:option_color,option_size. The widget renders them under a "Variants" section automatically. - Prefix derived fields with a domain:
price_by_currency,price_by_group,stock_by_location. - Reserve top-level field names (the ones listed above). Don't put
titleintoattributes.
Per-platform reference
| Platform | Mapper file |
|---|---|
| PrestaShop | modules/prestashop/aacsearch/src/Exporter/ |
| Bitrix | modules/bitrix/aac.search/lib/ProductExporter.php |
| WordPress | modules/wordpress/aacsearch-search/src/Mapper/ |
| Shopify | packages/shopify-connector/src/product-mapper.ts |
| Strapi | packages/strapi-plugin/src/server/services/aacsearch.ts |
Read these as canonical examples. New connectors should follow the same field semantics — the inverse mapping (CMS → SyncProductInput) is platform-specific, but the wire format is invariant.
Related pages
- Connector API Lifecycle
- Custom Connector
- Diagnostics — including sync failure examples
- Token Rotation
Custom Connector — Developer Guide
Build a CMS connector against the AACsearch Connector API. Covers token usage, full/delta/delete sync, document shape, mapping rules and required error handling.
Connector Token Rotation
Token lifecycle: creation, scoping, rotation procedure, revocation, and incident response when a token leaks.