learn-ship-ecommerce-enabler-with-phoebe / Analyst session 1 of 8
Ship It: Social Commerce Enablers · Analyst session 1 of 8

Seven domains, one enabler estate

An enabler's data does not live in one system. It arrives from three marketplaces, two ad platforms, a content studio, a creator network and a chat inbox, and some of what matters most never arrives at all. Before anyone models anything, the estate needs a map that says, domain by domain, what you can actually get. Tonight you draw that map for Serai Commerce, learn the five words that describe obtainability, find the two chains most of the value hangs on, and write the first version of the estate registry in Python against seven real Olist tables, one of which has an id that is not its grain.

🟢 Foundational Analysts and analytics engineers Python, pandas 45 min
0-3 · Welcome 3-18 · Domains, obtainability, chains 18-40 · Build-along: the estate registry 40-45 · Q&A
Part 0

How the analyst track works

Eight sessions, Python and SQL, on real rows. Tonight is the map. Session 2 conforms three marketplace exports to one schema. Session 3 is the identity gate: which join keys survive the marketplaces' masking, and at which grain. Session 4 writes the per-minute event spec for a live channel, and session 5 turns Stop, Hold, Love, Buy into code. Session 6 follows content through a tagged archive to creator yield. Session 7 writes the metric registry as code. Session 8 is the bench, where the identity ladder and the roadmap sequencer run in your browser on real Olist orders. The stand-in data throughout is the public Olist dataset: real Brazilian marketplace orders, anonymised, which the pages split and mask to look like three marketplace exports. The split and masking are constructed; the orders, and who placed them, are real.

🎫 Clear all three quiz questions to stamp your passport Live = presented in the 45 minutes Self-study = the full depth, after class ★ Build-along = run the code yourself

★ What you walk out with tonight. A seven-domain estate map with an obtainability word on every domain, a registry in Python that declares each table's domain and grain and checks the grain against the data, and the habit that saves the most time on an enabler's estate: asking "can we get it, at what grain" before "what could we do with it".

Part 1 · covers the seven data domains of an enabler

Seven domains, drawn around the work

6 min live

Group the estate by the work that produces it, not by the system it sits in. Serai's work falls into seven domains, and each one has a different owner, a different source and a different answer to the only question that matters on day one: how much of it can we actually have?

SERAI'S ESTATE, BY THE WORK THAT MAKES IT the word under each domain is what you can obtain, not what exists Serai warehouse one conformed schema 1 · Buyers and service partial · identity masked, chat gated 2 · Brands and stores owned · contracts, store health 3 · Catalogue and listings owned · master SKU, listing ids 4 · Orders, fees, settlement full via API · the spine 5 · Ads and traffic spend full · discovery closed 6 · Content and live streams archive owned · results partial 7 · Creators and affiliates partial · attributed GMV only Two domains are fully Serai's own. The one most strategies start with, the buyer, is the least obtainable.
🔍 Click to zoom - domain 1 is red because the buyer is the one thing the marketplaces keep
LiveThe seven, and what each one is for4 min▶
DomainWhat is in itWhere it comes fromThe decision it serves
1 · Buyers and serviceMasked buyer on each order, chat threads, reviewsMarketplace order and chat APIs, review pagesService quality, stated objections before purchase
2 · Brands and storesContracts, fee terms, store health, decoration versionsSerai's own CRM and store auditsWhich store needs attention, what each brand costs to serve
3 · Catalogue and listingsMaster SKU, each platform's listing id, content per listingSerai's catalogue tool, mapped to each platformThe same SKU across platforms, one screen
4 · Orders, fees, settlementOrders, lines, returns, platform fees, payoutsMarketplace order and finance APIsEverything with money in it. The spine
5 · Ads and trafficSpend, creatives, rejections, store trafficAd platform APIs; platform traffic reportsBid, pacing, creative, what to stop spending on
6 · Content and live streamsAsset archive, stream recordings, per-minute eventsSerai's studio; platform live dashboardsWhich content converts, which host sells
7 · Creators and affiliatesCreator ids, attributed and settled GMV, commissionsAffiliate reports, Serai's network rosterWhich creator actually sells, where

The grouping is this track's own, chosen because each domain has one owner inside an enabler. Your estate may split one or merge two; what matters is that every table you hold belongs to exactly one, and that the domain says who to ask when it breaks.

Self-studyWhy "by the work" and not "by the source system"3 min▶

A map by source system (Platform A, Platform B, the ad manager, the studio drive) is the natural first draft and the wrong one. It puts the same idea in three places, one per marketplace, and it makes "orders" look like three problems when it is one domain with three feeds. Worse, it hides the question of ownership: nobody at Serai owns "Platform A", but somebody owns the catalogue and somebody owns customer service.

By the work, each domain has one owner and one conformed schema, and the three marketplaces become a column called platform. That column is what makes any comparison across them possible, and session 2 builds it.

Real world

The buyer is the least obtainable domain, on all three. Public integrator documentation reports that Shopee removes buyer names and phone numbers from orders while keeping the address for fulfilment, that Lazada masks buyer name, phone and address in its seller centre and exports from January 2026 and in its Open Platform API from July 2026, and that TikTok Shop desensitises recipient name and phone when its own logistics ship the order. Seller-delivered orders are the exception on the last two. All three are reported, from integrators' public notes, because the seller centres themselves sit behind a login. Session 3 measures what that does to a join.

Part 2 · covers obtainability as a five-word scale

Obtainability, not existence

5 min live

Every domain "exists". The buyer exists, the platform's discovery funnel exists, the minute a viewer left a stream exists. What a roadmap needs is whether you can have it, in what shape, and what it costs to ask. Five words cover every case, and writing one of them on every row of the estate map ends most arguments before they start.

FIVE WORDS, ONE PER ROW OF THE MAP the lower the step, the more a use case built on it is a bet owned full via API partial gated closed yours outright; no permission, no platform can withhold it master SKU table, store contracts, the content archive complete through the platform's API, for your own stores orders, lines, returns, fees, ad spend some of it comes back; the rest stays inside the platform creator attributed GMV, store traffic, live room totals exists, behind a permission you can apply for platform chat access, seller-delivery unmasking exemptions not available, and no workaround the buyer's real name and phone on platform-shipped orders A beautiful use case on a closed row is a two-year detour. The word is cheaper than the discovery.
🔍 Click to zoom - "gated" is the only word whose next step is an email, not an engineering plan
LiveHow to tell partial from full, in one query3 min▶

"Full" and "partial" are easy to claim and easy to check. Take a week of one store's orders from the API and the same week from the seller centre's own summary. If the counts and the GMV agree to the order, the feed is full for that store. If the API returns orders but the seller centre shows traffic, impressions or buyer behaviour the API does not, that behaviour is partial: you can see it on a screen and cannot pipe it anywhere.

Write the test and its date next to the word. A "full" written in January without a test is a hope, and marketplace APIs change their fields every quarter.

Self-studyThis is the playbook's data gate, applied to an enabler2 min▶

The five words are a finer version of the three states taught in the playbook's data gate session (held, held partly, not held). Owned and full are "held"; partial is "held partly"; gated and closed split "not held" into the one you can ask for and the one you cannot. The split matters for an enabler because so much of its estate sits in other companies' systems, and "not held" hides whether the next step is a permission request or a dead end.

Part 3 · covers the two dependency chains

Two chains carry most of the value

4 min live

The domains are not independent projects. Most of what brands will pay for hangs off two chains, and every link in each chain has to exist before the next one means anything. Both chains start in domains Serai fully owns or fully receives, which is the best news on the whole map.

WHAT HAS TO EXIST BEFORE WHAT Orders and lines full via API needs Cross-platform SKU view needs the master SKU mapping needs Regional read one category, four markets the buyer: closed, feeds neither Content archive owned needs Tagged archive a persistent id per asset needs Creator and content yield settled GMV per asset Both chains start on rows Serai owns or fully receives, and neither runs through the buyer.
🔍 Click to zoom - the middle link of each chain is the work nobody budgets for, and the one that unlocks the rest
LiveWhy the middle link is the one to fund3 min▶

Orders arrive complete; the regional read is what brands entering a new market will pay for. Between them sits the master SKU mapping: one Serai id per product, mapped to each platform's listing id. Nobody asks for it by name and nothing works without it. Session 2 builds it, session 3 measures what happens when a few percent of listings are missing from it.

On the content chain the middle link is the tag: a persistent id on every asset Serai produces, carried into the listing or the stream, so a later order can be joined back to the asset that sold it. The platforms do not hand back content-level performance in a form you can pipe; the tag is how an enabler builds its own. Session 6 follows it end to end.

Self-studyA chain that does not exist: the single customer view3 min▶

Most enabler strategies draw a third chain: buyers across platforms to one customer, to lifetime value, to cross-platform retargeting. It starts on a closed row. Without a shared identifier and without the raw phone, there is no join key, and the leader track's session 4 explains why that makes the single customer view impossible rather than hard. The bench in session 8 measures it: on real orders, no combination of the keys the exports leave lifts the true match rate past 23.3 percent at customer grain, while Serai's own store id scores 100 percent at store grain.

Write it on the map anyway, marked closed. The most useful line in an estate map is sometimes the one that stops two years of work before it starts.

Build-along

The estate registry, on seven real tables

★ 22 min · everyone builds

Olist ships seven core tables. Place each one in a domain, declare its grain, and let the data check the declaration. Every number printed below is what the code prints on the public dataset; if yours differs, check the file version first.

SEVEN OLIST TABLES, SEVEN DOMAINS 1 buyers 2 stores 3 catalogue 4 orders 5 ads 6 content 7 creators customers reviews sellers products orders items payments no table no table no table id is not the grain Four domains filled, three empty. The stand-in shows the spine and the buyer, and none of the social half.
🔍 Click to zoom - the empty columns are why sessions 4 to 6 use constructed data, labelled as such

Get the data and count the tables. The no-login route is kagglehub. Run from a scratch folder, not inside a repo, so the CSVs do not end up committed.

★ Step 1 · seven tablesimport kagglehub, pandas as pd path = kagglehub.dataset_download("olistbr/brazilian-ecommerce") T = {"orders": "olist_orders_dataset.csv", "items": "olist_order_items_dataset.csv", "payments": "olist_order_payments_dataset.csv", "reviews": "olist_order_reviews_dataset.csv", "customers": "olist_customers_dataset.csv", "sellers": "olist_sellers_dataset.csv", "products": "olist_products_dataset.csv"} df = {k: pd.read_csv(f"{path}/{v}") for k, v in T.items()} for k, d in df.items(): print(f"{k:<10} {len(d):>7} rows") # orders 99441 · items 112650 · payments 103886 · reviews 99224 # customers 99441 · sellers 3095 · products 32951

Write the registry: domain, grain, obtainability, owner. One dictionary, in one file, read by everything later. The grain is a list of columns that should identify a row; write down what you believe before you look.

★ Step 2 · the estate registryESTATE = { "orders": {"domain": 4, "grain": ["order_id"], "obtain": "full", "owner": "data eng"}, "items": {"domain": 4, "grain": ["order_id", "order_item_id"], "obtain": "full", "owner": "data eng"}, "payments": {"domain": 4, "grain": ["order_id", "payment_sequential"], "obtain": "full", "owner": "finance"}, "reviews": {"domain": 1, "grain": ["review_id"], "obtain": "partial", "owner": "service"}, "customers": {"domain": 1, "grain": ["customer_id"], "obtain": "partial", "owner": "data eng"}, "sellers": {"domain": 2, "grain": ["seller_id"], "obtain": "owned", "owner": "brands"}, "products": {"domain": 3, "grain": ["product_id"], "obtain": "owned", "owner": "catalogue"}, }

Let the data check every grain. A declared grain that has duplicates is a wrong belief, and every join built on it will fan out silently.

★ Step 3 · the grain checkfor name, t in ESTATE.items(): dup = df[name].duplicated(t["grain"]).sum() print(f"{name:<10} {'+'.join(t['grain']):<32} duplicates {dup}") # orders 0 · items 0 · payments 0 · customers 0 · sellers 0 · products 0 # reviews review_id duplicates 814

Fix the one that failed, and write down why. 814 review ids repeat. Look before you drop: 789 review ids are attached to more than one order, so a review is one piece of text filed against several orders. The honest grain is the pair.

★ Step 4 · the real grain of reviewsr = df["reviews"] print(r.groupby("review_id")["order_id"].nunique().gt(1).sum()) # 789 print(r.duplicated(["review_id", "order_id"]).sum()) # 0 ESTATE["reviews"]["grain"] = ["review_id", "order_id"] ESTATE["reviews"]["note"] = "one review text can be filed against several orders"

Print the coverage by domain, including the empty ones. The empty domains belong in the output. A registry that lists only what you have makes the gaps invisible, and the gaps are where the roadmap's risks live.

★ Step 5 · what the estate coversDOMAINS = {1: "buyers and service", 2: "brands and stores", 3: "catalogue and listings", 4: "orders, fees, settlement", 5: "ads and traffic", 6: "content and live", 7: "creators and affiliates"} for n, label in DOMAINS.items(): tables = [k for k, t in ESTATE.items() if t["domain"] == n] print(n, label, tables or "NONE") # 1 [reviews, customers] · 2 [sellers] · 3 [products] · 4 [orders, items, payments] # 5 NONE · 6 NONE · 7 NONE

Keep the registry in one file and import it. Session 2 reads it to conform the exports, session 7 extends it into the metric registry. Two scripts with their own copy of the grain are two sources of truth, and they will disagree the week somebody fixes one.

Homework

Before session 2

30 min
  1. Write the seven-domain map for an estate you work on. Put one obtainability word on every row, and a date and a test next to every "full".
  2. Run the grain check on three tables you use every week. Most people find at least one declared key with duplicates; bring the count and the reason.
  3. Find the master product id in your estate, if there is one. If each marketplace's listing id is used directly in reports, write down how many reports would break if a listing were re-created.
  4. Mark the closed rows on your map and write, next to each, the use case somebody has already proposed on top of it.
Check yourself

Three questions

Clear all three and Analyst session 1 gets stamped in your passport.

1 · Why group the estate by the work that produces the data rather than by source system?

By source, "orders" is three problems; by the work, it is one domain with three feeds and a platform column. That column is what makes any cross-platform comparison possible, and the owner is who you call when it breaks.

2 · Chat access is available on one marketplace only if you apply for it. Which word goes on that row?

Gated and closed are both "not held" today, and they call for opposite next steps. Gated means ask in week one. Closed means stop, and write it down so nobody spends a year on it.

3 · The grain check reports 814 duplicate review ids. What is the right move?

A duplicate on a declared grain is a wrong belief about the data, not dirt. Dropping rows would lose real order-to-review links; the pair has zero duplicates, so the grain is the pair, with the reason written next to it.

Sources covered

What this session teaches, and where it came from

✓The seven data domains of an enablerThis track's own grouping, by the work and its owner; Serai's sources and owners are constructed
✓Obtainability as five wordsOwned, full via API, partial, gated, closed; a finer cut of the playbook's data gate, linked rather than re-taught
✓The two dependency chainsOrders to SKU view to regional read; content to tagged archive to creator yield
✓Olist's seven tables and their grainsBrazilian E-Commerce Public Dataset by Olist, Kaggle, CC BY-NC-SA 4.0; every count, including the 814 duplicate review ids, measured in this build
◐Marketplace buyer maskingReported, from public integrator documentation; the seller centres are behind a login. Measured on the bench in session 8

Every fact on this page, and its verification tier, is recorded in the course's source map.

Analyst session 1 cheat sheet · pin this

By the workSeven domains, one owner each; the marketplace is a column, not a folder.
Five wordsOwned, full via API, partial, gated, closed. One per row, with a dated test.
Gated is an emailClosed is a stop. Both are "not held"; they need opposite next steps.
Two chainsOrders to SKU view to regional read; content to tagged archive to creator yield.
Fund the middle linkThe master SKU mapping and the asset tag. Nobody asks for them; nothing works without them.
Check the grainDeclare it, then let the data disagree. Olist reviews: the grain is the pair.