Multi-channel product feed generation, per-product channel selection, feed format specifications, data quality rules, and a repeatable onboarding playbook for every new site.
Product distribution for automotive and off-road e-commerce spans four major channel categories. Not every product belongs on every channel. A chassis frame cannot be sold on Amazon with free two-day shipping, but a bolt-on air shock kit absolutely can. This section maps the full distribution landscape and defines a phased rollout strategy that prioritizes high-impact channels first.
Distribution channels fall into four categories based on how they connect products to buyers. Each category has distinct feed requirements, audience behavior, and cost structures.
| Category | How It Works | Cost Model | Best For |
|---|---|---|---|
| Search/Shopping Engines | Products appear in search results and shopping tabs | CPC (cost-per-click) | High-intent buyers actively searching for specific parts |
| Social Commerce | Products displayed in social feeds, shops, and pins | CPC/CPM (impression or click) | Discovery, lifestyle branding, visual products |
| Marketplaces | Products listed on third-party storefronts | Commission per sale (8-15%) | Commodity parts, accessories, high-volume items |
| Affiliate Networks | Publishers promote products for a commission | CPA (cost-per-acquisition) | Niche audiences, off-road forums, build blogs |
The following table defines every distribution channel in the system. Each channel has a unique key used in the database, a human-readable label, and an assigned implementation phase.
| Key | Label | Category | Feed Format | Phase |
|---|---|---|---|---|
google | Google Shopping | Search/Shopping | XML (Atom) | 1 |
bing | Bing Shopping | Search/Shopping | XML (Atom) | 1 |
meta | Meta (Facebook/Instagram) | Social Commerce | CSV / TSV | 1 |
pinterest | Pinterest Shopping | Social Commerce | CSV / TSV | 1 |
youtube | YouTube Shopping | Social Commerce | XML (via Google Merchant) | 2 |
tiktok | TikTok Shop | Social Commerce | CSV / API | 2 |
ebay | eBay Motors | Marketplace | CSV / API | 2 |
amazon | Amazon Marketplace | Marketplace | Flat File (XLSX/TSV) | 2 |
walmart | Walmart Marketplace | Marketplace | API / Flat File | 2 |
shareasale | ShareASale | Affiliate Network | CSV / XML | 2 |
Phase 1 channels drive traffic back to your own website. You own the customer relationship, collect the email, and control the checkout experience. These channels require product feeds but do not handle fulfillment or take a sales commission. Implementation cost is low: create the feed files, submit them to each platform, and monitor for errors.
Phase 2 channels place products on third-party storefronts where the platform controls checkout, payment processing, and often fulfillment expectations. Commission rates range from 8% to 15%. These channels require more operational overhead: inventory sync, order management, returns processing, and platform-specific compliance. Deploy Phase 2 only after Phase 1 feeds are stable and generating consistent traffic.
Not every product belongs on every channel. Use this decision tree to determine which channels apply.
| Product Type | Bing | Meta | eBay | Amazon | ||
|---|---|---|---|---|---|---|
| Complete chassis | Yes | Yes | Yes | Yes | No | No |
| Modular chassis kits | Yes | Yes | Yes | Yes | Maybe | No |
| Suspension components | Yes | Yes | Yes | Yes | Yes | Yes |
| Air shock kits | Yes | Yes | Yes | Yes | Yes | Yes |
| Bolt-on accessories | Yes | Yes | Yes | Yes | Yes | Yes |
| Turnkey builds | Yes | Yes | Yes | Yes | No | No |
| Custom fabrication | No | No | Yes | Yes | No | No |
Each distribution channel requires product data in a specific format. While Google and Bing use similar XML schemas, social platforms prefer CSV/TSV flat files. This section provides the complete field mapping for every Phase 1 format, plus notes on Phase 2 marketplace differences.
Google Shopping feeds use an Atom XML format with the g: namespace for Google-specific fields. This is the most comprehensive feed format and serves as the foundation for Bing and YouTube Shopping feeds.
| Field | Required | Max Length | Notes |
|---|---|---|---|
g:id | Yes | 50 chars | Unique product identifier; must match across feed updates |
g:title | Yes | 150 chars | ~70 chars displayed in ads; front-load keywords |
g:description | Yes | 5,000 chars | Plain text only; no HTML tags |
g:link | Yes | — | Absolute URL to product page; must match verified domain |
g:image_link | Yes | — | Absolute URL; min 800x800px; no watermarks or text overlays |
g:additional_image_link | No | — | Up to 10 additional images; same quality requirements |
g:price | Yes | — | Format: 99.99 USD; must match landing page price exactly |
g:sale_price | No | — | Active sale price; original price shown with strikethrough |
g:availability | Yes | — | in_stock, out_of_stock, preorder, backorder |
g:brand | Yes | 70 chars | Required for all branded products |
g:gtin | Conditional | 50 chars | UPC/EAN/ISBN; required if manufacturer-assigned |
g:mpn | Conditional | 70 chars | Required when GTIN not available; manufacturer part number |
g:identifier_exists | Conditional | — | Set to false for custom/handmade products without GTIN or MPN |
g:condition | Yes | — | new, refurbished, used |
g:google_product_category | Recommended | — | Google taxonomy ID or full path; e.g., Vehicles & Parts > Vehicle Parts & Accessories |
g:product_type | Recommended | 750 chars | Your own category hierarchy; e.g., Chassis > Modular Kits > Front Clip |
g:shipping | Recommended | — | Country, service, price; or configure account-level shipping |
g:shipping_weight | Recommended | — | Format: 45.5 lb; critical for freight items |
g:shipping_length | No | — | Format: 96 in; required for oversized items |
g:shipping_width | No | — | Format: 48 in |
g:shipping_height | No | — | Format: 12 in |
g:custom_label_0 | No | 100 chars | Campaign segmentation; e.g., best-seller, clearance |
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:g="http://base.google.com/ns/1.0">
<title>Patriot Chassis Product Feed</title>
<link href="https://patriotchassis.com" rel="alternate"/>
<entry>
<g:id>PC-AIR-001</g:id>
<g:title>Patriot Chassis Air Shock Kit - GM 10-Bolt</g:title>
<g:description>Bolt-on air shock conversion kit...</g:description>
<g:link>https://patriotchassis.com/product.html?id=PC-AIR-001</g:link>
<g:image_link>https://patriotchassis.com/images/products/air-shock-kit-gm.jpg</g:image_link>
<g:price>849.99 USD</g:price>
<g:availability>in_stock</g:availability>
<g:brand>Patriot Chassis</g:brand>
<g:mpn>PC-AIR-001</g:mpn>
<g:identifier_exists>false</g:identifier_exists>
<g:condition>new</g:condition>
<g:google_product_category>Vehicles & Parts > Vehicle Parts & Accessories > Motor Vehicle Parts > Motor Vehicle Suspension Parts</g:google_product_category>
<g:product_type>Suspension > Air Shock Kits</g:product_type>
<g:shipping_weight>22.5 lb</g:shipping_weight>
</entry>
</feed>
Meta Commerce Manager accepts product data as CSV or TSV files. The format is simpler than Google XML but has strict column naming requirements. Column headers must match exactly.
| Column | Required | Notes |
|---|---|---|
id | Yes | Unique identifier; max 100 chars |
title | Yes | Max 200 chars; no ALL CAPS |
description | Yes | Max 9,999 chars; plain text |
availability | Yes | in stock, out of stock, preorder (space-separated, lowercase) |
condition | Yes | new, refurbished, used |
price | Yes | Format: 849.99 USD |
link | Yes | Product page URL; must use HTTPS |
image_link | Yes | Min 600x600px; 1024x1024px recommended; no text overlays |
brand | Yes | Brand name |
google_product_category | Recommended | Google taxonomy accepted by Meta |
sale_price | No | Active sale price |
sale_price_effective_date | No | ISO 8601 date range |
additional_image_link | No | Comma-separated URLs; up to 20 additional images |
product_type | No | Your own category path |
shipping | No | Format: US::Standard:9.99 USD |
custom_label_0 | No | For ad campaign segmentation |
in stock not in_stock) and requires HTTPS for all URLs. The feed generator must transform these fields when producing Meta-format output from the same source data.Pinterest accepts CSV/TSV feeds through its Catalogs feature. The format closely mirrors Google's field naming but adds Pinterest-specific fields for rich pin display.
| Column | Required | Notes |
|---|---|---|
id | Yes | Unique product identifier |
title | Yes | Max 500 chars; descriptive and keyword-rich |
description | Yes | Max 10,000 chars |
link | Yes | Product URL; must be on verified domain |
image_link | Yes | Min 800x800px; 1000x1500px (2:3) preferred for pin display |
price | Yes | Format: 849.99 USD |
availability | Yes | in stock, out of stock, preorder |
brand | No | Recommended for brand attribution |
google_product_category | Recommended | Google taxonomy; used for discovery categorization |
product_type | No | Your category hierarchy |
condition | No | new, refurbished, used |
additional_image_link | No | Up to 10 additional images |
sale_price | No | Shows original + sale price on pin |
shipping | No | Free shipping badge if 0 USD |
Microsoft Advertising accepts Google Shopping feeds directly with minimal modification. Bing processes the same Atom XML format and recognizes the g: namespace fields. The primary differences are operational, not format-based.
| Aspect | Bing | |
|---|---|---|
| Feed format | Atom XML | Same Atom XML accepted directly |
| Merchant ID | Numeric (Google Merchant Center) | Microsoft Merchant Center store ID |
| Processing time | ~30 minutes | Up to 3 business days for new feeds |
| Image minimum | 800x800px recommended | 220x220px minimum (800x800 recommended) |
| UET Tag | Not required (uses Google Tag) | Required on all pages; min 50 events for verification |
| Auto-import | N/A | Can auto-import from Google Merchant Center |
A single "publish to all channels" toggle is inadequate for automotive and off-road e-commerce. A complete rolling chassis cannot be listed on Amazon — it requires freight shipping coordination, has no UPC, and needs customer consultation before purchase. Meanwhile, a bolt-on air shock kit is a perfect Amazon product: fixed price, standard box, UPC-assignable, and shippable via common carriers. Per-product channel selection ensures each product appears only on channels where it can actually be sold and fulfilled.
Automotive products span an enormous range of complexity, price, and fulfillment requirements. Listing a $45,000 turnkey build on Amazon would generate support tickets, bogus orders, and immediate policy violations. Listing a $12 bolt pack only on Google Shopping wastes marketplace potential. Per-product channel assignment prevents three categories of problems:
The following matrix maps product characteristics to eligible channels. Products must meet ALL criteria in a column to be eligible for that channel.
| Criteria | Google/Bing | Meta/Pinterest | eBay/Amazon | Affiliate |
|---|---|---|---|---|
| Has fixed price (not quote-based) | Yes | Recommended | Required | Yes |
| Has SKU/MPN | Required | Required | Required | Required |
| Shippable via standard carrier | Not required | Not required | Required | Not required |
| Under marketplace weight limit | N/A | N/A | Required (150 lb typical) | N/A |
Has GTIN/UPC or can set identifier_exists=false | Yes | Yes | GTIN preferred | N/A |
| Product images meet platform spec | 800x800 | 600x600 | 1000x1000 | Any |
Each product's channel assignments are stored in the portal_product_channels table. This table links a product to its enabled channels and stores per-channel overrides.
portal_product_channels
├── id (INT AUTO_INCREMENT PK)
├── product_id (INT FK → portal_products.id)
├── channel_id (INT FK → portal_feed_channels.id)
├── enabled (TINYINT(1), default 1)
├── custom_title (VARCHAR(255), nullable — override for this channel)
├── custom_description (TEXT, nullable — override for this channel)
├── custom_category (VARCHAR(255), nullable — platform-specific category)
└── UNIQUE(product_id, channel_id)
Some products need different titles, descriptions, or categories depending on the platform. Override fields allow customization without duplicating the entire product record.
| Scenario | Override Field | Example |
|---|---|---|
| Google title too long for display | custom_title | Shorten "Patriot Chassis Heavy-Duty Adjustable Air Shock Conversion Kit for GM 10-Bolt Rear Axle" to "Patriot Air Shock Kit - GM 10-Bolt Rear" |
| Pinterest needs lifestyle-focused description | custom_description | Replace spec-heavy description with build inspiration copy |
| Amazon requires specific category path | custom_category | "Automotive > Replacement Parts > Shocks, Struts & Suspension > Shocks & Struts" |
| Pinterest prefers vertical image | custom_image_url | Use 1000x1500px lifestyle photo instead of square product shot |
Feed generation transforms product data stored in the portal database into platform-specific output files. The system supports multiple sites, multiple channels, and per-product channel selection through a single generation pipeline.
The feed generation pipeline reads from three primary tables and produces output files for each enabled channel per site.
portal_products (master product data)
│
├── portal_product_channels (per-product channel assignments + overrides)
│
└── portal_sites (site configuration: domain, feed URLs, brand info)
│
▼
Feed Generator (PHP)
│
├── google-feed.xml (Google Shopping + Bing)
├── meta-feed.csv (Facebook / Instagram)
├── pinterest-feed.csv (Pinterest Catalogs)
└── generation-log.json (timestamp, counts, errors)
The portal manages products across multiple sites. Each site has its own domain, brand name, and product catalog. The feed generator produces separate feed files for each site because each platform merchant account is tied to a specific domain.
| Site | Domain | Feed Base Path |
|---|---|---|
| Patriot Chassis | patriotchassis.com | /feeds/patriotchassis/ |
| Advanced Offroad | advancedoffroadtrailer.com | /feeds/advancedoffroad/ |
| The High Road Mfg | thehighroadmfg.com | /feeds/highroad/ |
| Phillips Ranch | phillipsranch.com | /feeds/phillipsranch/ |
| Pit Innovations | pitinnovations.com | /feeds/pitinnovations/ |
Feed files follow a strict naming convention for predictable URLs and automated submission.
/{site-slug}/feeds/
├── google-feed.xml ← Google Shopping / Bing Shopping
├── meta-feed.csv ← Facebook / Instagram Commerce
├── pinterest-feed.csv ← Pinterest Catalogs
├── feed-log.json ← Generation metadata
└── archive/
├── google-feed-2026-02-25.xml ← Daily archive
└── meta-feed-2026-02-25.csv
Each feed is accessible at a predictable URL for platform submission:
https://{domain}/feeds/google-feed.xml
https://{domain}/feeds/meta-feed.csv
https://{domain}/feeds/pinterest-feed.csv
The feed generator follows a strict sequence to ensure data integrity and traceability.
status = 'approved' AND site_id matches AND channel is enabled in portal_product_channelscustom_title, custom_description, custom_category, custom_image_url; use override if not NULL, otherwise use base product fieldin_stock for Google vs in stock for Meta)archive/ directory with a date suffixEvery feed generation run produces a log entry in feed-log.json for debugging and monitoring.
{
"generated_at": "2026-02-25T14:30:00-07:00",
"site": "patriotchassis",
"feeds": {
"google": { "products": 67, "skipped": 3, "file_size": "48.2 KB" },
"meta": { "products": 64, "skipped": 6, "file_size": "22.1 KB" },
"pinterest": { "products": 58, "skipped": 12, "file_size": "19.8 KB" }
},
"errors": [
{ "product_id": 42, "channel": "google", "reason": "Missing image_link" },
{ "product_id": 71, "channel": "meta", "reason": "Price is 0.00" }
]
}
Feed quality directly determines product visibility. Google Merchant Center, Meta Commerce Manager, and Pinterest Catalogs all run automated validation on submitted feeds. Products with missing required fields, invalid images, or price mismatches are disapproved — often silently. This section defines the minimum quality bar for every feed field across all platforms.
| Field | Bing | Meta | ||
|---|---|---|---|---|
id | Required | Required | Required | Required |
title | Required | Required | Required | Required |
description | Required | Required | Required | Required |
link | Required | Required | Required | Required |
image_link | Required | Required | Required | Required |
price | Required | Required | Required | Required |
availability | Required | Required | Required | Required |
brand | Required | Required | Required | Recommended |
condition | Required | Required | Required | Optional |
gtin or mpn | Conditional | Conditional | Optional | Optional |
google_product_category | Recommended | Recommended | Recommended | Recommended |
shipping_weight | Recommended | Recommended | Optional | Optional |
Image quality is the single most common reason for feed disapprovals. Every platform has specific minimum dimensions, format requirements, and content restrictions.
| Specification | Bing | Meta | ||
|---|---|---|---|---|
| Minimum dimensions | 800 x 800 px | 220 x 220 px | 600 x 600 px | 800 x 800 px |
| Recommended | 1200 x 1200 px | 800 x 800 px | 1024 x 1024 px | 1000 x 1500 px |
| Max file size | 16 MB | 16 MB | 8 MB | 10 MB |
| Aspect ratio | Square preferred | Square preferred | 1:1 in shops | 2:3 vertical preferred |
| Formats | JPEG, PNG, WebP | JPEG, PNG | JPEG, PNG | JPEG, PNG |
| Background | White recommended | White recommended | No requirement | No requirement |
| No text/watermarks | Prohibited | Prohibited | Limited (<20% image area) | Limited |
| No placeholder images | Prohibited | Prohibited | Prohibited | Prohibited |
Product identifiers (GTIN, MPN, brand) tell platforms whether your product matches known catalog entries. Incorrect identifier handling is a leading cause of silent disapprovals.
| Scenario | GTIN | MPN | identifier_exists |
|---|---|---|---|
| Reselling a manufacturer's product with UPC | Required | Recommended | Omit (defaults true) |
| Selling a product you manufacture (has MPN, no UPC) | Omit | Required | Omit (defaults true) |
| Custom/handmade product (no UPC, no established MPN) | Omit | Omit | Set to false |
| Bundle or kit with mixed components | Omit | Create a kit MPN | Omit |
identifier_exists to false for these products. However, off-the-shelf components (shocks, springs, hardware kits) from major manufacturers DO have UPCs — you must provide them. Google will cross-reference your brand + MPN against its catalog and flag mismatches.| Rule | Requirement |
|---|---|
| Format | {amount} {currency} — e.g., 849.99 USD |
| Decimal places | Always 2 decimal places, even for whole dollar amounts (100.00 USD) |
| Currency code | ISO 4217 (USD, CAD, EUR, etc.) |
| No symbols | Do not include $, ,, or other formatting characters |
| Landing page match | Feed price must exactly match the price displayed on the product page |
| Zero prices | Never submit 0.00 USD — skip the product or use a minimum price |
| Sale prices | Must be lower than the regular price; include sale_price_effective_date |
Each platform uses slightly different availability values. The feed generator must translate the portal's internal status to the correct platform format.
| Portal Status | Google/Bing Value | Meta Value | Pinterest Value |
|---|---|---|---|
| In Stock | in_stock | in stock | in stock |
| Out of Stock | out_of_stock | out of stock | out of stock |
| Pre-Order | preorder | preorder | preorder |
| Backorder | backorder | available for order | in stock |
| Discontinued | Exclude from all feeds | ||
Automotive aftermarket e-commerce has industry-specific data standards, fitment requirements, and marketplace conventions that do not apply to general retail. This section covers the standards and integrations that matter for chassis, suspension, and off-road product distribution.
ACES is the industry standard for expressing vehicle fitment data — which parts fit which vehicles. It maps products to Year/Make/Model/Submodel (YMMS) combinations using standardized vehicle configuration codes maintained by the Auto Care Association.
vehicle_compatibility structured data (emerging)| Field | Description | Example |
|---|---|---|
BaseVehicleID | Standardized Year/Make/Model code | 32987 (2015 Chevrolet Silverado 1500) |
SubModelID | Trim/submodel identifier | 1 (LT) |
PartTerminologyID | What the part is (standardized category) | 12680 (Shock Absorber) |
Position | Where it mounts | Rear |
Qualifier | Additional fitment notes | 4WD Only |
PIES is the companion standard to ACES. While ACES defines fitment (what vehicles a part fits), PIES defines the product itself — attributes, descriptions, images, dimensions, and marketing content in a standardized format.
| Category | Fields | Relevance |
|---|---|---|
| Descriptions | Short (80 char), Medium (200), Long (2000), Marketing | Maps to feed title/description fields |
| Dimensions | Length, Width, Height, Weight (product and package) | Required for shipping calculations |
| Digital Assets | Images (6 types), PDFs, Videos, 360-degree | Maps to feed image fields |
| Attributes | Material, finish, color, warranty, country of origin | Enhances filtering and search |
| Packaging | UPC, package dimensions, quantity per pack | Required for marketplace fulfillment |
eBay Motors uses its own vehicle compatibility format derived from ACES. Sellers provide fitment data as a compatibility list attached to each listing. This powers the "This item fits your vehicle" badge that dramatically increases conversion rates.
Year | Make | Model | Trim | Engine | Notes
2014 | Chevrolet | Silverado 1500 | LT | 5.3L V8 | 4WD Only
2015 | Chevrolet | Silverado 1500 | LT | 5.3L V8 | 4WD Only
2015 | GMC | Sierra 1500 | SLE | 5.3L V8 | 4WD Only
2016 | Chevrolet | Silverado 1500 | All | 5.3L V8 | 2WD & 4WD
For products without ACES data, build compatibility lists manually from your fitment knowledge. Even a basic list covering the most common applications significantly improves search ranking within eBay Motors. Products with compatibility data receive priority placement in "Parts that fit" search results.
Amazon Automotive uses a proprietary fitment system called "Part Finder" that lets buyers enter their vehicle details and see only compatible parts. To participate, sellers must submit fitment data through Amazon's Part Finder template — a flat file mapping each product to Year/Make/Model/Submodel/Engine combinations.
Chassis frames, roll cages, and complete suspension kits are oversized freight items. Accurate shipping dimensions are not optional — they directly affect feed approval, shipping cost calculations, and marketplace compliance.
| Product Category | Typical Dimensions | Typical Weight | Shipping Class |
|---|---|---|---|
| Complete chassis | 120" x 48" x 18" | 800-1,500 lb | LTL Freight |
| Modular chassis section | 72" x 36" x 12" | 150-400 lb | LTL Freight |
| Suspension kit (complete) | 48" x 24" x 18" | 80-200 lb | Oversized parcel / LTL |
| Air shock kit | 24" x 12" x 8" | 15-30 lb | Standard parcel |
| Bolt-on accessory | 18" x 12" x 6" | 5-20 lb | Standard parcel |
| Hardware kit | 8" x 6" x 4" | 1-5 lb | Standard parcel |
shipping_weight, shipping_length, shipping_width, and shipping_height to estimate shipping costs. If these fields are missing for heavy items, Google may show inaccurate shipping estimates that either deter buyers (too high) or create losses (too low). Always provide accurate dimensions in your feed for items over 20 lb.Submitting feeds is only the beginning. Every platform runs ongoing validation against submitted product data and will disapprove individual products or suspend entire feeds when issues are detected. Monitoring is not a launch task — it is an ongoing operational responsibility.
Google Merchant Center provides the most detailed diagnostics of any platform. It categorizes issues by severity and provides specific fix instructions for each disapproved product.
| Disapproval | Cause | Fix |
|---|---|---|
| Missing GTIN | Product has manufacturer UPC but feed omits it | Add GTIN or set identifier_exists=false for custom products |
| Price mismatch | Feed price differs from landing page price | Regenerate feed after price update; ensure feed and page use same data source |
| Image too small | Main image under 800x800px | Replace with minimum 1200x1200px image |
| Promotional overlay on image | "SALE" or "FREE SHIPPING" text on product image | Remove text overlays; use clean product photos only |
| Automatic item disapproval (policy) | Product violates Google Shopping policies | Review Shopping policies; common for weapons-adjacent products |
| Landing page not found (404) | Product URL is broken or page is not yet published | Fix URL or exclude product until page is live |
| Mismatched availability | Feed says "in_stock" but page shows "out of stock" | Sync availability between database, feed, and product page |
| Missing shipping information | No shipping configured at account or product level | Add g:shipping to feed or configure account-level shipping in Merchant Center |
| Level | Impact | Response Time |
|---|---|---|
| Account suspension | All products removed from Shopping | Immediate — fix within 24 hours, request review |
| Item disapproval | Individual product removed | Fix within 1 week |
| Warning | Product may be demoted or disapproved soon | Fix within 2 weeks |
| Suggestion | Optimization opportunity | Address during next feed update cycle |
Meta's validation is less granular than Google's but equally strict on required fields. The Commerce Manager dashboard shows feed health with a percentage score and categorizes issues by type.
in stock (space-separated), not in_stock (underscore)id value across the entire feedPinterest Catalogs provides a feed health dashboard showing processing status, active pins, and errors. Pinterest is generally more lenient than Google or Meta but still requires core fields.
$849.99 is invalid; 849.99 USD is correctFeed freshness matters. Platforms penalize stale feeds and reward frequent updates. The following schedule balances server load with data freshness.
| Trigger | Frequency | Reason |
|---|---|---|
| Price changes | Within 1 hour | Price mismatch between feed and landing page causes disapproval |
| Availability changes | Within 1 hour | Showing "in stock" for unavailable products violates all platform policies |
| New product added | Next scheduled run | New products appear in feeds on the next generation cycle |
| Routine regeneration | Daily (2:00 AM) | Catches any data drift and signals freshness to platforms |
| Full catalog rebuild | Weekly (Sunday 3:00 AM) | Rebuilds all feeds from scratch; catches orphaned entries |
Content API for Shopping for real-time price and availability updates on high-velocity products. For most automotive parts (stable pricing, limited inventory churn), daily feed fetches are sufficient.This checklist covers every step from initial setup through ongoing maintenance. Complete each section in order. Do not submit feeds to platforms until all pre-launch items are verified.
identifier_exists=falseamount currency format (e.g., 849.99 USD) with 2 decimal placesfeed-log.json) writes successfully with zero errorsViewContent, AddToCart, Purchase eventsEach platform needs to know where to fetch your feed. Submit the following URLs after your feed generator is deployed and producing valid output.
| Platform | Feed URL | Fetch Schedule |
|---|---|---|
| Google Merchant Center | https://{domain}/feeds/google-feed.xml | Daily (set in Merchant Center) |
| Bing Merchant Center | https://{domain}/feeds/google-feed.xml | Daily (or auto-import from Google) |
| Meta Commerce Manager | https://{domain}/feeds/meta-feed.csv | Daily (set in Catalog settings) |
| Pinterest Catalogs | https://{domain}/feeds/pinterest-feed.csv | Daily (set in Catalog source) |
This section is the repeatable playbook for bringing a brand-new site into the feed distribution system. Every site — whether it sells chassis, trailers, ranch equipment, or fabrication tools — follows the same sequence. The system is designed so that a new site can go from zero to live feeds in a single working session once its products are in the portal.
portal_sites table with a valid domain, slug, and name. All 5 DominionPort sites are pre-seeded. If adding a 6th+ site, insert the row first.During the Build Process Discovery phase (Question 9b), the client selects which distribution channels apply to their business. The Build Process form (/build-process/index.html) presents all 10 channels organized by category:
| Category | Channels | Default State |
|---|---|---|
| Search / Shopping Engines | Google Shopping, Bing / Microsoft Shopping | Checked (recommended for all sites) |
| Social Commerce | Facebook & Instagram (Meta), Pinterest, YouTube Shopping, TikTok Shop | Meta checked; others unchecked |
| Marketplaces | eBay Motors, Amazon Automotive, Walmart | All unchecked (requires evaluation) |
| Affiliate | ShareASale | Unchecked |
The client's channel selections during Discovery determine which channels get enabled at the site level in Step 3. Phase 2 channels (YouTube, TikTok, eBay, Amazon, Walmart, ShareASale) can be selected during Discovery for planning purposes — they appear dimmed in the portal UI until implementation is ready.
Products must pass through the portal workflow before they can appear in feeds. The feed generator only includes products with status = 'approved' or status = 'live'. The minimum viable product record for feed eligibility requires:
| Field | Portal Tab | Required For Feeds | Notes |
|---|---|---|---|
| Name / Title | Overview | Yes | Under 150 chars; front-load keywords |
| Short Description | Description | Yes (50+ chars) | If under 50 chars, generator falls back to full description |
| Full Description | Description | Recommended | Used as fallback; also shown on product page |
| SKU | Overview | Yes | Used as feed id; must be unique per site |
| Brand | Overview | Yes | Falls back to site name if empty |
| Category | Overview | Recommended | Maps to google_product_category and product_type |
| Price (fixed, in cents) | Pricing | Yes | Quote-based products are excluded from all feeds |
| Condition | Overview | Yes | Defaults to new if empty |
| Availability | Overview | Yes | Defaults to in_stock if empty |
| At least 1 image | Images | Yes | Products with no images are silently excluded from all feeds |
| GTIN/UPC | Overview | Conditional | Required for resold products; omit for custom fabrication |
| MPN | Overview | Conditional | Required when no GTIN; identifier_exists=false if neither |
| Weight (lbs) | Specs | Recommended | Required for items over 20 lb; omitted from feed if empty |
approved or live, (b) it has a fixed price, (c) it is marked active = 1, AND (d) it has at least one image. All four conditions must be met. The feed generator enforces this in the SQL query and the image-skip logic — there is no manual override.After products are loaded, enable the channels selected during Discovery at the site level via the Hub Feeds dashboard (/hub/feeds.html).
merchant_id — Google Merchant ID, Meta Catalog ID, etc.feed_url — the public URL where the generated feed will be servedconfig — channel-specific JSON (rare for Phase 1)portal_site_channelsUse the Distribution tab (10th tab) in the Product Editor for per-product channel assignment.
custom_title — platform-optimized title (e.g., shorter for Google, lifestyle-focused for Pinterest)custom_description — channel-specific description overridecustom_category — platform-specific taxonomy pathportal_product_channelsFeed files must be accessible at public URLs on the site's production domain for platforms to fetch them. Deploy using the standard DevHub → Production pipeline.
# Feed output directory structure on production:
/{site-docroot}/feeds/
├── google-merchant.xml
├── bing-shopping.xml
├── facebook-catalog.csv
└── pinterest-catalog.csv
# URLs submitted to platforms:
https://{domain}/feeds/google-merchant.xml
https://{domain}/feeds/bing-shopping.xml
https://{domain}/feeds/facebook-catalog.csv
https://{domain}/feeds/pinterest-catalog.csv
For each enabled channel, create the platform merchant account and submit the feed URL. See Section 8.2 for the detailed checklist per platform. The sequence is always:
portal_sites with correct domain, slug, and nameapproved or live statusProduct data in the portal database often contains HTML entities, formatting artifacts, and inconsistent whitespace that would pollute feed output. The feed generator applies automatic sanitization to every text field before writing it to any feed format. These rules are enforced in code and documented here so they are consistently applied across all sites.
Every text field (title, description, category, brand) passes through the sanitize_for_feed() function before being written to any feed output. The pipeline runs in this exact order:
<strong>, <em>, <br>, etc.). Feed platforms require plain text and will reject or misrender HTML.| Entity | Character | Common Source |
|---|---|---|
″ | ″ (double prime / inch mark) | WordPress smart quotes on measurements like 4″ |
’ | ’ (right single quote / apostrophe) | WordPress smart apostrophes in possessives |
“ / ” | “ / ” (smart double quotes) | WordPress content editor |
| (non-breaking space) | Rich text editors, copy-paste from Word |
& | & | Double-encoded ampersands |
– | – (en dash) | WordPress auto-typography |
— | — (em dash) | WordPress auto-typography |
″ as literal text rather than an inch mark, hurting relevance matching, and (2) platform validation may flag descriptions containing HTML as malformed. The sanitizer runs automatically — no manual cleanup needed per product.Each platform has minimum and maximum description length requirements. Descriptions that are too short get flagged or reduce product visibility. The feed generator enforces these rules automatically:
| Rule | Threshold | Generator Behavior |
|---|---|---|
| Short description under minimum | < 50 characters | Falls back to full_description (truncated to 5,000 chars) |
| Full description available | Any length | Used as fallback; sanitized and truncated |
| Custom channel description set | Any length | Takes priority over both short and full descriptions |
| No description at all | 0 characters | Product still included but may be disapproved by platform |
1. custom_description (per-channel override from Distribution tab)
↓ if NULL
2. short_description (if ≥ 50 characters after sanitization)
↓ if under 50 chars
3. full_description (truncated to 5,000 characters)
↓ if also empty
4. Empty string (product may be disapproved)
| Platform | Minimum | Recommended | Maximum |
|---|---|---|---|
| Google Shopping | None (but penalizes short) | 150–500 chars | 5,000 chars |
| Bing Shopping | None | 150–500 chars | 10,000 chars |
| Meta Commerce | None (flags < 30 chars) | 100–500 chars | 9,999 chars |
| None | 100–500 chars | 10,000 chars |
The feed generator enforces image requirements at the product level. These rules ensure no feed entry is submitted without the required image data.
| Rule | Generator Behavior |
|---|---|
| Product has no images at all | Product excluded from all feeds (silently skipped) |
| Product has images but no primary flag | Falls back to hero role, then lowest sort_order |
Image path is relative (e.g., images/product.jpg) | Prepends https://{site_domain}/ automatically |
| Image path is already absolute URL | Used as-is |
| Additional images available | Up to 10 included per product in all feed formats |
# Relative path in database:
images/gallery/air-shock-hero.jpg
# Constructed feed URL:
https://patriotchassis.com/images/gallery/air-shock-hero.jpg
# Resolution priority for primary image:
1. is_primary = 1 flag on portal_product_images row
↓ if no primary flag set
2. role = 'hero' on any image row
↓ if no hero role
3. Lowest sort_order value (first image uploaded)
| Condition | Generator Behavior |
|---|---|
price_type != 'fixed' | Product excluded (quote-based items cannot have feed prices) |
price_cents IS NULL | Product excluded |
price_cents = 0 | Product included but will likely be disapproved — platforms reject $0 products |
| Valid price | Formatted as XX.XX USD (2 decimal places, ISO currency code, no $ symbol) |
# Identifier resolution for each product:
IF gtin IS NOT NULL → output g:gtin
IF mpn IS NOT NULL → output g:mpn
IF both NULL → output g:identifier_exists = false
# Brand fallback:
IF brand IS NOT NULL → use brand
ELSE → use site_name from portal_sites
ELSE → "Unknown"
identifier_exists=false is the correct approach — not inventing fake identifiers. Google and Bing explicitly support this for custom/handmade products. However, if you later obtain UPCs (e.g., from GS1 for high-volume shippable items), adding them will improve ranking.Run this query against any site to identify feed-readiness gaps before generating feeds. Replace ? with the site_id.
SELECT
COUNT(*) as total_products,
SUM(CASE WHEN status IN ('approved','live') THEN 1 ELSE 0 END) as feed_eligible,
SUM(CASE WHEN price_type = 'fixed' AND price_cents IS NOT NULL THEN 1 ELSE 0 END) as has_price,
SUM(CASE WHEN gtin IS NOT NULL AND gtin != '' THEN 1 ELSE 0 END) as has_gtin,
SUM(CASE WHEN mpn IS NOT NULL AND mpn != '' THEN 1 ELSE 0 END) as has_mpn,
SUM(CASE WHEN weight_lbs IS NOT NULL THEN 1 ELSE 0 END) as has_weight,
SUM(CASE WHEN LENGTH(short_description) >= 50 THEN 1 ELSE 0 END) as desc_adequate,
SUM(CASE WHEN LENGTH(short_description) < 50 THEN 1 ELSE 0 END) as desc_thin
FROM portal_products
WHERE site_id = ? AND active = 1;
-- Image coverage:
SELECT p.id, p.name, COUNT(i.id) as image_count
FROM portal_products p
LEFT JOIN portal_product_images i ON i.product_id = p.id
WHERE p.site_id = ? AND p.active = 1
GROUP BY p.id
HAVING image_count = 0;
| Field | Google XML | Meta CSV | Pinterest CSV | Bing XML |
|---|---|---|---|---|
| Product ID | g:id | id | id | g:id |
| Title | g:title | title | title | g:title |
| Description | g:description | description | description | g:description |
| Product URL | g:link | link | link | g:link |
| Image URL | g:image_link | image_link | image_link | g:image_link |
| Price | g:price | price | price | g:price |
| Availability | g:availability | availability | availability | g:availability |
| Brand | g:brand | brand | brand | g:brand |
| Condition | g:condition | condition | condition | g:condition |
| Category | g:google_product_category | google_product_category | google_product_category | g:google_product_category |
| In Stock Value | in_stock | in stock | in stock | in_stock |
| Platform | Minimum | Recommended | Preferred Ratio |
|---|---|---|---|
| Google Shopping | 800 x 800 | 1200 x 1200 | 1:1 (square) |
| Bing Shopping | 220 x 220 | 800 x 800 | 1:1 (square) |
| Meta Commerce | 600 x 600 | 1024 x 1024 | 1:1 (square) |
| 800 x 800 | 1000 x 1500 | 2:3 (vertical) | |
| Amazon | 1000 x 1000 | 2000 x 2000 | 1:1 (square) |
| eBay | 500 x 500 | 1600 x 1600 | 1:1 (square) |
All feed distribution tables live in the jj_wordpress database (or the site-specific database for the current DevHub user) alongside the other portal_ tables. The schema is designed for multi-site operation — every table references portal_sites.id or portal_products.id as the site/product foreign key.
Seeded with 10 channels. Rarely modified after initial creation. Shared across all sites.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | Referenced by all other feed tables |
channel_key | VARCHAR(50) UNIQUE | google-shopping, bing-shopping, facebook-instagram, pinterest, etc. |
label | VARCHAR(100) | Display name: "Google Shopping", "Facebook & Instagram" |
category | ENUM('search','social','marketplace','affiliate') | Groups channels in UI |
feed_format | VARCHAR(50) | google-xml, bing-xml, meta-csv, pinterest-csv, custom |
format_shared_with | VARCHAR(50) NULL | channel_key this mirrors (e.g., bing → google) |
icon | VARCHAR(10) NULL | Emoji for UI display |
base_url | VARCHAR(255) NULL | Platform dashboard URL for quick access |
phase | TINYINT DEFAULT 1 | 1 = built now, 2 = future (dimmed in UI) |
active | TINYINT(1) DEFAULT 1 | Soft-disable a channel system-wide |
sort_order | INT DEFAULT 0 | Display order within category |
created_at | DATETIME |
Which channels are enabled for each site, plus platform-specific credentials.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | |
site_id | INT FK → portal_sites | |
channel_id | INT FK → portal_feed_channels | |
enabled | TINYINT(1) DEFAULT 1 | Toggle this channel on/off for the site |
merchant_id | VARCHAR(100) NULL | Google Merchant ID, Meta Catalog ID, etc. |
feed_url | VARCHAR(255) NULL | Public URL where feed is served |
config | JSON NULL | Channel-specific config (rare for Phase 1) |
| UNIQUE(site_id, channel_id) | ||
The checkbox data — which products go on which channels, with optional per-channel overrides.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | |
product_id | INT FK → portal_products | |
channel_id | INT FK → portal_feed_channels | |
enabled | TINYINT(1) DEFAULT 1 | Toggle this product on/off for this channel |
custom_title | VARCHAR(255) NULL | Override title for this channel |
custom_description | TEXT NULL | Override description for this channel |
custom_category | VARCHAR(255) NULL | Platform-specific category path |
| UNIQUE(product_id, channel_id) | ||
Every feed generation event is logged here for audit trail and monitoring.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | |
site_id | INT FK → portal_sites | |
channel_id | INT FK → portal_feed_channels | |
feed_format | VARCHAR(50) | e.g., google-xml, meta-csv |
filename | VARCHAR(255) | Output file path |
product_count | INT DEFAULT 0 | Products actually included (after image/validation skip) |
file_size_bytes | INT NULL | |
status | ENUM('generating','success','error') | |
error_message | TEXT NULL | |
generated_by | INT NULL FK → portal_users | |
started_at | DATETIME | |
completed_at | DATETIME NULL |
| channel_key | label | category | feed_format | phase |
|--------------------|-----------------------------|-------------|---------------|-------|
| google-shopping | Google Shopping | search | google-xml | 1 |
| bing-shopping | Bing / Microsoft Shopping | search | bing-xml | 1 |
| facebook-instagram | Facebook & Instagram | social | meta-csv | 1 |
| pinterest | Pinterest | social | pinterest-csv | 1 |
| youtube-shopping | YouTube Shopping | social | google-xml | 2 |
| tiktok-shop | TikTok Shop | social | google-xml | 2 |
| ebay-motors | eBay Motors | marketplace | custom | 2 |
| amazon-auto | Amazon Automotive | marketplace | custom | 2 |
| walmart | Walmart Marketplace | marketplace | custom | 2 |
| shareasale | ShareASale | affiliate | custom | 2 |
portal_product_section_status.section ENUM — 'distribution' added as 10th sectionportal_permissions — manage_feeds permission added (category: admin, sort_order: 5)products.php line 83 — section divisor changed from /9 to /10 for completion percentageAll feed distribution APIs follow the same patterns as other portal APIs: JSON request/response, same database config (portal/api/config.php), same error format.
File: /portal/api/product-channels.php
| Method | Parameters | Description |
|---|---|---|
GET | ?product_id=N | Returns all channels with enabled state for this product (LEFT JOINs portal_product_channels on portal_feed_channels) |
PUT | ?product_id=N | Bulk save channel assignments. Body: { channels: [{channel_id, enabled, custom_title, custom_description, custom_category}] }. Uses INSERT ON DUPLICATE KEY UPDATE. Auto-updates distribution section status. |
GET | ?site_id=N&summary=1 | Returns product count per channel for a site (for dashboard stats) |
File: /hub/api/feed-channels.php
| Method | Parameters | Description |
|---|---|---|
GET | (none) | Returns all channels from portal_feed_channels |
GET | ?site_id=N | Returns channels with site-specific enabled state, product counts, and last-generated timestamps |
PUT | ?site_id=N | Bulk update site-channel config. Body: { channels: [{channel_id, enabled, merchant_id, feed_url, config}] } |
File: /hub/api/feed-generate.php
| Method | Parameters | Description |
|---|---|---|
POST | Body: { site_id, channel_id } | Triggers feed generation for a specific channel+site. Validates channel phase and site existence. Determines output directory from site slug. Logs to portal_feed_generations. |
GET | ?site_id=N | Returns latest generation status per channel for dashboard |
File: /hub/api/feed-history.php
| Method | Parameters | Description |
|---|---|---|
GET | ?site_id=N | Paginated generation history (most recent first) |
GET | ?site_id=N&latest=1 | Latest generation per channel (for dashboard stat cards) |
File: /hub/api/feed-generator.php — included by feed-generate.php, never called directly via HTTP.
| Function | Output File | Channels |
|---|---|---|
generate_google_xml($site_id, $channel_id, $output_dir) | google-merchant.xml | Google Shopping |
generate_bing_xml($site_id, $channel_id, $output_dir) | bing-shopping.xml | Bing / Microsoft Shopping |
generate_meta_csv($site_id, $channel_id, $output_dir) | facebook-catalog.csv | Facebook & Instagram |
generate_pinterest_csv($site_id, $channel_id, $output_dir) | pinterest-catalog.csv |
| Function | Purpose |
|---|---|
sanitize_for_feed($text) | Strip HTML, decode entities, normalize whitespace |
get_channel_products($site_id, $channel_id) | Query eligible products with channel overrides and site info |
get_product_title($product) | custom_title → product name (sanitized) |
get_product_description($product) | custom_description → short_description (if ≥ 50 chars) → full_description (sanitized, truncated to 5000) |
get_product_category($product) | custom_category → product category (sanitized) |
get_product_image_url($product) | is_primary → hero role → lowest sort_order |
get_additional_images($product_id) | Up to 10 non-primary images |
build_image_url($product, $file_path) | Resolve relative path to absolute URL using site domain |
format_price_feed($cents) | Convert cents to XX.XX USD |
map_availability_google($availability) | Portal status → Google/Bing format |
map_availability_meta($availability) | Portal status → Meta/Pinterest format |
log_feed_generation(...) | Write entry to portal_feed_generations |
The Hub API directory uses a deny-by-default .htaccess. The following feed-related files are explicitly allowed:
<Files "feed-channels.php">
Require all granted
</Files>
<Files "feed-generate.php">
Require all granted
</Files>
<Files "feed-history.php">
Require all granted
</Files>
feed-generator.php is intentionally NOT in the allowlist — it is an internal include file and must never be accessible via HTTP.
DominionPort Product Distribution & Feed Management
Version 1.1 — February 2026
This document serves as the binding protocol for all product distribution and feed management across all DominionPort sites.