Feeds

A product feed is the XML or CSV file ad networks poll to keep their copy of your catalog in sync. Ripllo currently emits a Google Merchant Center-compatible XML feed at a per-merchant URL; CSV and TikTok/Pinterest variants are on the roadmap.

The feed reads from MirroredProduct rows in Ripllo's database, which Storlaunch populates via storlaunch.product.* webhooks. Ripllo is the canonical feed emitter; Storlaunch is the canonical product owner. Keeping them on opposite sides of the partner boundary lets Storlaunch's catalog evolve without coupling to feed emission, and lets Ripllo serve the feed even when Storlaunch is down for maintenance.

Endpoints

Method Path Purpose
GET /api/v1/feeds/config Get feed config
PATCH /api/v1/feeds/config Update feed config
GET /api/v1/feeds/google/:accountId.xml Google Merchant Center feed (XML, no auth)

Get feed config

GET /api/v1/feeds/config

Returns the merchant's feed configuration. Defaults to enabled because the feed URL itself is the opt-in — ad networks won't poll until the merchant submits the URL in their respective dashboards.

Response

{
  "data": {
    "id": "mfc_01HX...",
    "accountId": "acc_01HX...",
    "enabled": true,
    "defaultGoogleProductCategory": "Apparel & Accessories > Clothing",
    "includeUnpublished": false,
    "createdAt": "2026-05-01T10:42:00.000Z",
    "updatedAt": "2026-05-13T10:11:00.000Z"
  },
  "error": null,
  "meta": { "requestId": "...", "timestamp": "..." }
}

If no config row exists yet, the default stub is returned:

{
  "enabled": true,
  "defaultGoogleProductCategory": null,
  "includeUnpublished": false
}

Update feed config

PATCH /api/v1/feeds/config

Upserts the merchant's record. Partial; only the fields you supply are updated.

Request body

Field Type Notes
enabled boolean When false, the public feed endpoint returns 404 feed disabled even with valid accountId.
defaultGoogleProductCategory string (≤200) | null A Google Product Category string applied to every item that doesn't have its own. Lookup tables live at google.com/basepages/producttype/taxonomy.en-US.txt.
includeUnpublished boolean When false (default), only products with available: true are emitted. Set to true for full-catalog inventory feeds.

Response

The full updated MerchantFeedConfig object.

await ripllo.feeds.config.update({
  defaultGoogleProductCategory: 'Apparel & Accessories > Clothing',
  enabled: true,
});

Google Merchant Center feed

GET /api/v1/feeds/google/:accountId.xml

No auth, returns XML. The merchant submits this URL to Google Merchant Center, which polls it every 1–24 hours. The endpoint is intentionally public:

  • The URL itself is the secret. It contains the merchant's accountId, which is opaque from the outside.
  • The data exposed is what the merchant already publishes on their storefront. There's no leak.

Response — success

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
  <channel>
    <title>Storefront acc_01HX...</title>
    <link>https://storlaunch.com/s/acc_01HX...</link>
    <description>Product feed</description>
    <item>
      <g:id>prod_storlaunch_19823</g:id>
      <g:title>Cotton t-shirt &mdash; medium</g:title>
      <g:description>100% organic cotton, garment-dyed. Pre-shrunk.</g:description>
      <g:link>https://storlaunch.com/s/acc_01HX.../products/cotton-tshirt-medium</g:link>
      <g:image_link>https://cdn.storlaunch.com/...</g:image_link>
      <g:availability>in_stock</g:availability>
      <g:price>250000 IDR</g:price>
      <g:brand>ExampleCo</g:brand>
      <g:google_product_category>Apparel &amp; Accessories &gt; Clothing</g:google_product_category>
    </item>
    <!-- … up to 5,000 items, sorted by updatedAt desc … -->
  </channel>
</rss>

The feed is capped at the most recently-updated 5,000 products. For merchants with larger catalogs, paginated feeds are on the roadmap; for now, the cap is documented and stable.

Response — disabled

HTTP/1.1 404 Not Found
Content-Type: text/plain

feed disabled

The merchant flipped enabled: false. The plain-text body is intentional — Google Merchant Center retries on 404 with backoff, which is the right behavior for a temporary disable.

Price formatting

Currency Format Example
IDR <cents> IDR 250000 IDR
Everything else <dollars>.<cc> <UPPERCASE> 25.00 USD

IDR is treated as a zero-decimal currency consistent with the rest of Ripllo — the underlying database stores it in rupiah, no cents. Other currencies are stored in cents and divided by 100 for display.

Field mapping

The feed pulls from the MirroredProduct table, which Storlaunch syncs via webhooks. The mapping is direct:

Feed field Source Notes
g:id externalRef Storlaunch's product ID.
g:title name
g:description description ?? name Falls back to the title if Storlaunch hasn't sent a description.
g:link computed <storefrontBaseUrl>/s/<accountId>/products/<slug>
g:image_link imageUrl Omitted if null.
g:availability available ? 'in_stock' : 'out_of_stock'
g:price priceCents, currency See Price formatting.
g:brand brand Omitted if null.
g:google_product_category googleProductCategory ?? config.defaultGoogleProductCategory Per-product override, else config default. Omitted if both are null.

Storefront base URL

The feed's <link> field is built from STOREFRONT_BASE_URL (defaults to https://storlaunch.com). Storlaunch redirects /s/<accountId> to the canonical slug-based URL because Ripllo doesn't know the merchant's slug — that's a Storlaunch concern. Acceptable for now; will tighten when Ripllo gains a direct-tenant UI of its own.

The merchant feed config object

Field Type Notes
id string mfc_ + ULID.
accountId string Unique — one row per merchant.
enabled boolean Master switch.
defaultGoogleProductCategory string | null Fallback category.
includeUnpublished boolean Include items with available: false.
createdAt, updatedAt ISO 8601

The mirrored product object (read-only)

You can't write to MirroredProduct directly — Storlaunch owns it via webhooks. But the feed renders from it, so understanding the shape helps debug missing items:

Field Type Notes
id string mp_ + ULID.
accountId string Owning merchant.
externalRef string Storlaunch's product ID. Used as g:id.
slug string Used in the canonical URL.
name string
description string | null
priceCents integer Smallest currency unit.
currency string ISO 4217.
available boolean
imageUrl string | null
brand string | null
googleProductCategory string | null Per-product override.
archivedAt ISO 8601 | null Items with archivedAt != null are excluded from the feed regardless of available or includeUnpublished.
updatedAt ISO 8601 Most-recent-first ordering uses this.

Submitting the feed to Google

  1. In Google Merchant Center, Products → Feeds → Add primary feed.
  2. Source: Scheduled fetch.
  3. URL: https://ripllo.com/api/v1/feeds/google/<your-accountId>.xml. Get your accountId from the dashboard URL or GET /api/v1/feeds/config.
  4. Fetch frequency: daily is fine for most catalogs.

For TikTok, Pinterest, and Meta Catalog, point them at the same URL — they all accept Google Merchant feed format, though some fields are ignored.

Events

The feeds resource doesn't emit outbox events. The feed is recomputed on every request; there's no "feed updated" lifecycle to broadcast.

Next

  • Pixels — the event-tracking counterpart to product feeds.
  • Blog — long-form content that powers a different kind of merchant-side SEO.