Skip to main content
Catalog Ingestion lets you bring products into Galactic Core from an outside source. When a merchant’s own system — an ERP, a PIM, another storefront, or a spreadsheet export — is the source of truth for the catalog, you push that data to Tybrite instead of entering products by hand. Send a batch as JSON, XML, or CSV; Tybrite matches each row to your existing catalog by SKU, updates what already exists, and creates what doesn’t.
Dashboard: You can register a feed URL for scheduled sync from your Tybrite dashboard, or push products directly via the API and SDK. Find your signing secret under Integrations → API Keys.
Secret key + request signature required. POST /v1/ingest/products is the one write endpoint where both a secret key (tybrite_sk_*) and a signed request are mandatory. A publishable key returns 403; a missing or invalid signature returns 401.

When it applies

Ingestion is the path for a catalog that lives somewhere else and is mirrored into Galactic Core:
  • A merchant runs their inventory in an ERP/PIM and wants it reflected in their Tybrite store.
  • You’re migrating a catalog from another platform.
  • A nightly export from a back-office system should keep the storefront in sync.
Ingestion is a merchant-side write for keeping a catalog up to date, which is why it sits outside the otherwise storefront-facing API and carries stricter authentication than any other write.

Batch size

A request carries at most 12 products. A larger batch is rejected with 400 and nothing is written, rather than applying part of the batch and leaving the rest to fail item by item. For a large catalog, send a sequence of batches, or configure a scheduled sync, which fetches and imports a whole catalog on a timer and has no such limit.

Selling on a marketplace

Nothing changes. A merchant whose store is part of a marketplace pushes their catalog exactly as any other merchant does, with their own store’s secret key, and their products appear on the marketplace storefront alongside everyone else’s once the store is approved. The marketplace listing is a read over the same catalog — approved stores, aggregated — so there is no separate marketplace feed, no marketplace ingestion endpoint, and nothing extra to configure. An operator key is refused with 403 — ingestion is store-scoped, because a catalog belongs to the merchant who owns it rather than to the marketplace that lists it.

Quick start

Ingestion is a write, so initialize the SDK with a secret key and sign each request. Below, one standalone product and one multi-variant product (its rows share a product_group):
The SDK signs the request for you when it’s configured with your secret key. If you call the endpoint directly (curl, another language, a serverless handler), you must add the signing headers yourself — see Signing a request.

How matching by SKU works

Every row carries a sku — your unique stock-keeping unit. That SKU is the identity Galactic Core matches on, scoped to your store:
  • SKU already exists → the product, its variant, and its stock are updated in place.
  • SKU is new → a product (and variant) is created.
To import only brand-new products and leave existing ones untouched, pass strategy: 'create_only'. Matching SKUs are then skipped (counted in rows_skipped) rather than updated:
The default is upsert_by_sku (update existing, create new).

Variants

Rows that share the same non-empty product_group become variants of a single product — each row still needs its own unique sku. A row with no product_group is a standalone, single-variant product. The first row in a group supplies the product-level fields (name, description, category); each row contributes its own variant_name, price, and stock.

Formats

Declare the format with the Content-Type header or the ?format= query parameter. All three normalize to the same result. The SDK sends JSON; the XML and CSV shapes below are for when you push the raw body yourself.
CSV follows standard quoting rules, so commas and newlines inside quoted fields are handled. Field names are matched leniently (e.g. Product Name maps to name).

Product fields

Your own columns

A column that is not in the table above is matched against the custom fields defined on products for your store, by the field’s label — the name shown in the dashboard, which is what you would have called the column. A match records the value on the product; anything else is ignored, so a feed carrying extra columns behaves exactly as it did before.
Here Fabric and Country of origin land on the product if fields with those labels exist. Values are written after the product itself, and on an update they are refreshed along with everything else, so a feed stays the source of truth for them.
The field has to exist before a feed can fill it. Fields are created in the dashboard under Settings → Custom Fields, not by a feed — a new column does not define a new field.

Response shape

Rows are validated and applied independently — one bad row never fails the whole batch. The response always carries a summary of counts and an errors array describing every rejected row:
status is success when nothing failed, or partial when some rows failed but others imported. If every row fails validation, the request returns 422 and nothing is written.

Summary fields

Per-row errors

Each entry in errors pinpoints exactly what to fix:

Signing a request

The SDK signs for you when initialized with a secret key. If you push the raw body yourself, add three headers. The signature is a base64 HMAC-SHA256 of `${timestamp}.${rawBody}` using your store’s signing secret (the same scheme as orders and payments) — sign the exact bytes you send.
The signature covers the raw body exactly as sent. Re-serializing the JSON (whitespace or key ordering changes) produces a different byte sequence, and verification fails.

Test without a key

Two public, no-key endpoints validate a feed format ahead of any integration work. Both are rate-limited to 60 requests/hour per IP. A valid sample feed to model your own on (?format=json|xml|csv):
The sample includes a single-variant product and a multi-variant product, so you can see exactly how product_group expresses variants. A validation run that writes nothing. It returns the same summary and errors a real import would, plus a normalized_preview showing how Galactic Core interpreted each row:
From the SDK:

Scheduled sync

Instead of pushing, you can have Galactic Core pull a feed for you. Register a feed URL in your dashboard, choose a cadence, and Galactic Core fetches and syncs the catalog on that schedule — matching by SKU exactly like the push API. After each scheduled run, a feed.sync.completed webhook fires with the run’s summary, so your systems can react to the result without polling. Subscribe to it the same way as any other event:
See the Webhooks guide for delivery, signing, and idempotency.

Publish your own catalog (outbound feed)

Ingestion works both ways: a store can also expose its own catalog as a public feed for other systems to pull from — another store’s scheduled sync, a partner, a marketplace, or your own tooling. It is enabled in the dashboard under Integrations → Catalog Sync → Publish my catalog, which produces a public URL in two formats, readable with no API key:
{store} is your store id or short store code.
On a store belonging to an operated platform, the host is that platform’s own. The dashboard shows the URL to copy, and where the store belongs to an agency it reads https://api.theirplatform.com/v1/feeds/… rather than the address above — a feed URL is pasted into Google Merchant Center or a partner’s system, where it outlives the screen it came from. The path, the formats and the token all behave identically. See Running your own platform.
The feed contains only storefront-safe fields (never cost or margins) and includes each product’s rich media gallery and its published specifications:
The feed shape matches what the ingestion endpoints accept, so a store-to-store sync needs no field mapping: one store’s scheduled sync can point straight at another store’s products.json. A token added in the dashboard and appended as ?token=… keeps a feed semi-private.

Response codes


SDK reference

See the IngestionService SDK reference for full method signatures, or the API Reference for the raw endpoints.