Marketplace listings
Publish products to Trendyol, n11, Çiçeksepeti, Hepsiburada, Amazon, eBay, Otto and Allegro — map a category once, answer what it demands, and read the marketplace's verdict back onto the row it came from.
A listing sync puts your products on sale at a marketplace. It is the sixth integration shape, and the only one whose form cannot be filled in before you have connected the account — because the questions are the marketplace’s, not ours.
Everything else in Integrations is configurable from a static declaration: a source declares its settings, a destination declares its columns. A listing cannot be. Which of Trendyol’s ~3,900 categories does this product belong to? Then which of the ~24 attributes that category demands? Then which of the hundreds of values each attribute allows? None of that is knowable until an operator picks a category, so a listing provider is interrogated while the form is being filled in and the answers are stored.
The other half is the writeback. A destination mirrors rows out and hears nothing back. A listing is refused one unit at a time, minutes or hours later, with a reason a person has to read — so where the answer lands is part of the configuration, not an afterthought.
Publishing is manual by default. A publish is an outward, hard-to-undo act at a live marketplace, so a listing sync is created with no schedule and runs when you press Publish now. You can give it one afterwards.
What you need first
- A connected marketplace integration — the same connection its order sync uses. One credential does both jobs.
- A products collection, and a column on it naming your own category.
The ecommerce template’s
product_type(“Apparel”, “Accessories”) is the natural one; any text column works, and the value is matched verbatim. - Optionally a variants collection holding the sellable units. Without one the product row is the unit, which is a perfectly ordinary configuration — see Products without variants.
Setting one up
Integrations → Add sync, and pick the connection whose label ends
· put products on sale. A marketplace normally appears three times — once to
pull orders in, once to push stock and price out, once to list — because they
are three different jobs off one credential.
The form asks for five things:
| Products in | the collection holding your products |
| Category column | the column whose value the category mapping is keyed by |
| Field mapping | your columns → the marketplace’s product fields |
| Variants | optional: where the sellable units live, and their per-unit fields |
| Write the verdict back to | where the marketplace’s answer lands |
The last one is required and the server refuses a sync without it. Without somewhere to put the answer, a batch is published and every verdict is discarded — which reads as “nothing happened” for ever.
The verdict lands on the row the unit came from. If you point the sync at a variants collection, the writeback columns are the variant’s — a unit’s listing status is not a product column. The form follows this automatically the moment you choose a variants collection.
Mapping a category
Once the sync exists, its row grows a Categories button. That panel is where the interrogation happens.
Map a category asks for three things in the order they become answerable:
- Your category value — exactly what the category column holds, e.g.
Apparel. Matched verbatim; a product holding anything else is skipped. - The marketplace category — searched, not browsed. The search matches the
whole path, so
abiyefinds a category whose own name is justTakım. Only the deepest categories are offered: every one of these marketplaces refuses a listing against a parent. - What that category demands — asked only once a category is chosen, because until then there is nothing to ask.
Each attribute is answered one of three ways:
- Pick a value — from the marketplace’s own closed set.
- Read it from a column — the answer differs per unit, so name the column to read it from. This is what a size or a colour wants.
- Type it — free text, offered only where the marketplace accepts it.
An attribute badged splits variants is what tells two units apart. Giving it a fixed answer gives every unit the same size, and the marketplace collapses your variants into one listing. Read it from a column instead — the form says so in place when you pick a fixed answer for one.
A product whose category nobody has mapped is skipped, not published uncategorised, and the run reports how many — that is the single most likely reason a publish looks like it did nothing.
Publishing, and what comes back
Publish now sends one bounded batch and reports what it did: how many units were sent, how many were refused before sending, and how many products sat in unmapped categories.
None of these marketplaces answers a create with a result. They answer with a queue ticket, and the verdict lands minutes or hours later — up to 24 hours at Çiçeksepeti. So a publish records a batch, and a separate sweep asks about open batches until every unit in them has been ruled on.
The sweep runs on its own schedule, not the sync’s. A listing sync defaults to manual, and the scheduler only considers syncs with an interval — so a batch you published by hand would otherwise never be asked about, and every product in it would read “pending” for ever.
The Categories panel lists recent batches with what is still undecided. Per-unit answers land in the columns you mapped:
| Output | What it holds |
|---|---|
| Listing status | pending, accepted or rejected |
| Listing ID | the marketplace’s handle for the listing |
| Rejection reason | verbatim from the marketplace, never parsed |
| Listed at | when it was accepted |
Three of the four marketplaces mint no id of their own and echo your seller code back, so Listing ID is often the same value as your SKU. That is the answer, not a bug.
Products without variants
A workspace that models one sellable unit per product simply leaves the Variants block empty. The product row is then mapped through both column lists — the product’s fields and the per-unit ones — and the verdict lands on the product row.
This is a configuration, not a special case: no provider branches on it, which is why “no variants” costs nothing.
The eight marketplaces
The shape held for all eight, and they agree on almost nothing. What differs is worth knowing before you map one.
Trendyol
- The category tree and a category’s attributes are public — you can browse them before finishing the credentials.
- Needs a brand ID, which is a searchable registry rather than a category attribute. Map it onto a product column.
- Echoes the barcode, so that is the reference; a unit without one is left for the next run.
- Batch results are kept for four hours after completion.
n11
- There is no brand field. The brand is an ordinary category attribute (“Marka”), so it is answered in the mapping form like any other.
- Echoes the stock code. The barcode is optional here and used only to match n11’s own catalog.
- Needs a shipment template name — the one you created under Hesabım → Teslimat Bilgilerim — and refuses the whole task without it.
- Its category tree needs the credential, unlike Trendyol’s.
Çiçeksepeti
- Every attribute is a closed set; free text is refused.
- Attributes that ask the buyer for text at checkout (“Kişiselleştirilebilir Özellik”) are not offered — they are not yours to answer.
- A verdict of
Warningmeans the product listed; the note is usually about pricing law. It is recorded as accepted, with the reason kept. - The description minimum is measured on the plain text, so markup does not count towards it.
- Product creation can take up to 24 hours.
Hepsiburada
- Categories are paged, and an attribute’s values are a second request, so the mapping form takes longer to draw than the others.
- Publishing sends a file. You will not notice; it is mentioned because the batch size behaves differently: one publish covers one category, and products in another category are taken by the next run.
- A verdict of
FAILEDis a technical error at Hepsiburada, not a refusal — where the same word at the other three means the product was rejected. The reason says so, because re-sending is your decision. - Echoes the merchant SKU;
hbSkuis Hepsiburada’s own id and arrives only once the product exists.
Amazon
Amazon is the odd one out on nearly every axis, and all of it is absorbed inside the provider — the mapping form works the same way it does for the other four.
- There is no category tree. Amazon’s categories are product types, a flat vocabulary with no parents, so the picker is a search rather than a walk.
- The attributes come from a schema, and only the answerable ones are shown. A product type’s attributes are published as a JSON Schema, and Amazon describes far more elaborate shapes than one value — nested objects, per-date schedules. Attributes that cannot be answered with a single value are not offered at all, because a field that looks configured and changes nothing is worse than one that is missing.
- Required attributes are listed first. A product type routinely declares two hundred of them and enforces eight.
- The reference is the SKU — and unlike everywhere else, it is not something Amazon echoes back as a courtesy. The SKU is the address: it is in the request path, and every later read is by it.
- A refusal arrives immediately. Amazon is the only one here that answers
the publish itself:
INVALIDcloses that unit on the spot with its reasons. - Acceptance does not.
ACCEPTEDmeans “queued without blocking problems”, not “on sale”. A listing is reported as accepted once Amazon says it is buyable; until then it stays pending, and problems that appear minutes later are read back as a rejection. - Set the currency and the condition on the sync before publishing. Amazon prices carry their currency explicitly and there is nothing sensible to default it to.
- Variants are listed as separate products. Amazon groups variants with a parent SKU and a variation theme rather than by marking an attribute, and that is not built: each of your variants becomes its own listing on its own page. Everything else works — this is the one place to expect less than the other four give you.
eBay
- Connecting is a redirect, not a pasted key — eBay is the first marketplace here that uses OAuth. You paste your App ID, Cert ID and RuName, then press Connect and approve on eBay. The RuName is eBay’s own handle for where to send you back; register this instance’s callback URL against it in eBay’s portal first (the connect dialog shows the URL).
- Set the six listing settings before publishing. An eBay offer names a fulfilment, a payment and a return policy by id, plus a merchant location key — all created by you in Seller Hub. A publish with any of them missing is refused as a whole rather than unit by unit, because every unit would fail the same way.
- The category tree is nested and deep, and eBay says which nodes are leaves itself.
- Aspects say which one is the variant. eBay is the only marketplace here that marks it, so a size or colour aspect is recognisable in the mapping form without you knowing which is which.
- The verdict arrives immediately. There is no batch to watch: a unit is listed or refused by the time the run finishes, and the reason is on the row.
- eBay’s own message is kept verbatim — it names the field, which “Invalid value” would not.
Otto
- Connect with a partner username and password — there is no consent screen and no Connect button here.
- The category picker has two levels: a group, then the categories under it. Map a product to one of the categories; the attributes it demands belong to the group above it.
- Everything the form needs arrives in one walk, so the mapping dialog draws quickly — unlike Hepsiburada’s, which fetches values per attribute.
- Pick the brand from Otto’s own list. It is a searchable registry like Trendyol’s, and brands Otto has retired are not offered because a listing against one is refused.
- Every unit needs a SKU, an EAN and a brand. A row missing one is refused before the batch is sent, with which one it was on the row — Otto would refuse the whole batch and name none of them.
- A product is on sale when Otto reports it ONLINE, and its MOIN is the id written back. Anything else is still pending, and errors come back verbatim.
Allegro
- Connect by redirect, then paste nothing else — Allegro scopes the token to what your application was registered for.
- The category picker walks down instead of searching. Allegro hands its categories over one level at a time and has ~23,000 of them, so the dialog shows a level, you step into it, and a breadcrumb names where you are. Pick a leaf; Allegro says itself which nodes those are.
- Attributes are typed. A dictionary parameter is a closed list unless Allegro marks it as accepting a custom value; the other three types (text, integer, decimal) are values you type. A unit, where there is one, is shown beside the name.
- Variants are not marked here. Allegro models them as separate offers sharing a product rather than by flagging an attribute, so nothing in this form claims to be the varying one.
- The verdict arrives immediately, offer by offer — there is no batch to watch, and a refusal names the offer it was.
Template columns
The ecommerce template’s Variants collection ships the four writeback
columns under Marketplace listing — listing_status, listing_id,
listing_error, listed_at. They are on the variant rather than the product
because a marketplace rules on one unit at a time: one size can be refused for a
missing attribute while its siblings go live.
Beyond the admin
Everything here is reachable from the SDK, GraphQL, MCP and the CLI. The taxonomy reads hang off the connection, not a sync — you browse categories while deciding whether to make a sync at all — while the mapping and the batches hang off the sync, which is what they configure and what they record.
bun backlex integrations categories <integration-id>bun backlex integrations attributes <integration-id> --category <id>bun backlex integrations map <sync-id> --value Apparel --category 3535 \ --attr '338=field:colour' --attr '47=custom:Siyah'bun backlex integrations batches <sync-id>bun backlex integrations sync-run <sync-id>--attr takes one flag with a named half, rather than three flags, because
which of the three answers an attribute takes is decided by the attribute — and
three flags would invite sending two.