TBBN.Merchant Platform docs
APIdocs/api/merchant-catalog-feed.md

Merchant Catalog Feed (scheduled feed sync — specified, not yet built)

Status: specified here, not yet implemented. listing-service currently only supports the real-time API path (POST/PUT/DELETE /merchant/listings, see merchant-integration-guide.md step 4). This document specifies the second, feed-based sync path referenced in docs/business/business-model.md's "two adoption paths" principle, so thin-engineering merchants (thrift/consignment shops, classifieds sites) aren't required to write API-calling code to get their catalog into TBBN. See services/listing-service/README.md's "Not yet built" section for the implementation-side status of this spec.

Why a second sync path at all

business-model.md already commits TBBN to two first-class adoption paths — API/SDK for engineering-capable merchants, and widgets/plugins for thin-engineering ones — specifically because excluding either would cut out a large share of the named target market (thrift shops, consignment businesses, classifieds sites). Widgets solve the consumer-facing half of that problem (no negotiation UI to build); they don't solve the catalog sync half — a merchant still needs some way to tell TBBN what they have for sale/trade. Most of that segment already maintains a Google Merchant Center and/or Meta Commerce Manager feed for their own ads/shopping listings. Reusing that same feed shape for TBBN, instead of requiring a bespoke API integration, is the lowest-lift onboarding path available to them.

This is not a novel design — it deliberately mirrors how Google and Meta themselves solve the exact same "some merchants can call an API, most can only export a file" problem, cross-checked against how Amazon and Shopify differ from that pattern (see "Why not just copy Amazon or Shopify," below).

The two sync paths, side by side

Direct API (built)Catalog feed (this doc)
Who it's forMerchants with engineering capacityThin-engineering merchants who already export a Google/Meta-style feed
MechanismPOST/PUT/DELETE /merchant/listings, real-timeTBBN pulls a merchant-hosted feed URL on a schedule
LatencyImmediateUp to the refresh interval, or on-demand via manual trigger
Authsk_live_... API key, per-requestfeedUsername/feedPassword on the feed-source config, sent as HTTP Basic Auth when TBBN fetches the URL
Best forCatalogs that change constantly, or merchants who want full controlCatalogs that change a few times a day, exported from an existing PIM/ecommerce platform

A merchant may use either path, or both (e.g. bulk catalog via feed, high-velocity individual items via the API) — TBBN treats every resulting listing identically once ingested; the sync path is not visible anywhere downstream of ingestion.

Feed formats

XML (RSS 2.0-shaped, TBBN-namespaced) and CSV/TSV, mirroring the format set both Google Merchant Center and Meta Commerce Manager support. Field names deliberately reuse Google's established vocabulary (id, title, price, condition, image_link, availability) rather than inventing new ones — Shopify has no native authenticated feed feature of its own, so its entire third-party feed-app ecosystem (Feedyio, FeedFusion, LitCommerce, etc.) already outputs Google/Meta-shaped feeds. A merchant already running one of those apps can point it at TBBN by changing the destination URL, not by building anything new.

Feed field reference

FieldRequiredModeled onNote
item_idYesGoogle/Meta idUnique key; anchors create/update/delete on re-fetch
category, sub_categoryYesGoogle's google_product_category (standard taxonomy) + product_type (merchant free text)Two-tier split enables cross-catalog matching while preserving the merchant's own organization
brandConditionalGoogle/Meta brandRequired unless the merchant declares a no-brand category (matches Google's conditional rule; Meta's stricter unconditional rule is not adopted here)
conditionYesMeta's unconditional requirementTBBN requires this unconditionally, stricter than Google's conditional rule — barter fairness (the Trade Compatibility Score) depends on condition being present for every item, not just used/refurbished ones
original_priceYesGoogle/Meta priceNumeric + ISO 4217 currency code (e.g. 19.99 USD) — same shape computeTradeValue already expects, no reformatting needed at ingestion
listing_typeYesNo Google/Meta analogClosed enum SALE / TRADE / BOTH — not a boolean, since "either" is a real, common case. Implemented for real on Listing.listingType (Phase 26/30) — a SALE listing is discovery-only and never offer-able; TBBN redirects to product_link instead of orchestrating a trade for it.
wants / trade preferencesNoNo Google/Meta analogStructured array reusing the category/sub_category taxonomy above — kept structured, not free text, so the Wants Engine can match on it the same way it matches API-submitted listings
image_link + additional_image_link[]Yes (primary), No (rest)Google/Meta patternOne required primary image, optional array of secondary images
quantity, availabilityYesGoogle/Meta (kept as two separate fields: an enum status plus an integer count)The trade-session reservation flow needs a real count, not just an in-stock/out-of-stock boolean
product_linkNoGoogle Merchant Center's link fieldURL to the product's page on the merchant's own website — distinct from image_link. Maps to Listing.merchantItemUrl (Phase 30): for a SALE-only listing, Landing-page renders this as the sole outbound CTA in place of an offer/checkout flow — Google-Merchant-product-card behavior exactly. Also lets a "reach-only" merchant (Merchant.tradingEnabled: false, Phase 22) surface a product for discovery without TBBN ever handling its checkout; a trading-enabled merchant can include it too, purely as a reference back to its own listing.

feed_username/feed_password live on the feed-source config (the merchant→feed-URL mapping, alongside merchant_settings), never on the item schema itself — this mirrors how both Google and Meta separate transport auth from the product-data payload, and keeps credentials out of every individual item record.

Authentication: separate credential fields, not a URL-embedded secret

TBBN's feed puller sends Authorization: Basic using a feedUsername/feedPassword pair stored on the merchant's feed-source config — never credentials embedded directly in the feed URL string. This is the same shape both Google (DatafeedFetchSchedule.fetchUrl/username/password on the Content API) and Meta ("if password protected, provide username/password" in Commerce Manager's scheduled-feed setup) use, and for the same reason: URL-embedded credentials are routinely dropped or mishandled by HTTP clients on redirect, and they leak into server access logs. feedUrl and the credential pair are three separate fields, full stop.

Refresh cadence and the manual trigger

24-hour scheduled pull, plus an on-demand manual "fetch now" trigger — gated behind the merchant's production API key the same way every other write path is credential-gated. This matches how both reference platforms actually behave, not just how they're marketed:

  • Google's own scheduled-fetch UI only offers daily/weekly/monthly granularity — there is no hourly option — plus a manual "Fetch now" button and a fetchnow-equivalent API method. Google explicitly advises against calling it more than once a day; merchants who need tighter latency are pointed at the real-time Products API instead.
  • Meta's Commerce Manager offers hourly/daily/weekly scheduling plus its own "Fetch Now" button, and separately offers a Catalog Batch API for merchants who need to update more often than once an hour.

TBBN's 24-hour default plus manual trigger is therefore the direct baseline case both platforms converge on; a merchant whose catalog changes faster than that should use the direct API path (above) for those items instead of raising the feed's refresh frequency — same advice Google gives its own merchants, for the same reason (a tight polling loop against a merchant-hosted file is a worse experience for everyone than a push call TBBN can process immediately).

Why not just copy Amazon or Shopify

Both were checked as contrast cases, not as alternative designs to adopt:

  • Amazon SP-API is push-only over OAuth2 to an Amazon-owned endpoint; there's no merchant-hosted pull-URL concept at all, and its legacy XML feed types are being retired in favor of a JSON-only feed format. Copying this would mean no low-lift path for merchants who only have a static feed export to offer — precisely the segment this design exists to serve.
  • Shopify has no native authenticated pull-feed feature of its own; its ecosystem of third-party feed apps generating Google/Meta-shaped feeds is exactly why this spec reuses Google's field names in the first place (see "Feed formats," above).

Net effect: pull + HTTP Basic Auth + scheduled/manual refresh is the dominant pattern among the platforms merchants already use to reach shoppers, not a TBBN invention — adopting it is what makes "point your existing feed at a new URL" actually true for most of the target segment.

Open items

  • Uncertain, flagged rather than assumed: Meta's exact wording for its username/password fields and its claimed hourly-schedule granularity are corroborated from third-party feed-vendor documentation, not fetched directly from Meta's own (JS-rendered) help center — treat as reasonably confident, not verified primary-source fact.
  • Whether a "sub-hourly" Meta refresh option genuinely exists is a single, unconfirmed source — do not build a TBBN feature around matching it without re-verifying first.
  • Implementation is not scoped to a specific phase yet — see services/listing-service/README.md for the current "Not yet built" note. Building it means: a feed-source config table (feed URL + credentials, keyed to merchant_settings), an XML/CSV parser producing the same normalized listing shape POST /merchant/listings already validates against, a scheduler (24h default, configurable), and the manual-trigger endpoint. No new ingestion logic should diverge from the validation @tbbn/core-listings already enforces for the direct API path — a fed-in listing and an API-submitted listing must be indistinguishable to every downstream consumer.