This guide explains how to enrich SimplyClub transactions with per-line product attributes — mapping each cart line’s extended_attributes (tags, collections, metafields) onto the Simply line-item fields (Department, CategoryCode, Vendor, …) that drive product-level promotions and reporting.
It is the companion to the Frontend Embedding Guide (which covers setCart) and the Webhook Integration Guide (which covers the order line-item shape). This page focuses only on the attribute → Simply-field layer.
Opt-in, off by default. Mapping does nothing until you explicitly enable it for a POI (
active: true). With it off, yourextended_attributesare accepted and ignored — zero behavior change.
1. What it does
Each cart line item can carry an optional extended_attributes bag. When mapping is active for a POI, the SimplyClub backend reads that bag, resolves a value for each configured field, and copies it onto the Simply transaction line item (TranItem) under the Simply field name.
The result: a single product can tell SimplyClub its department, category, vendor, and classification codes — so club rules and promotions can target “all items in Department 12” or “anything from Vendor X” without SimplyClub having a product catalog.
cart line.extended_attributes ──► (per-POI mapping rules) ──► TranItem Simply fields
{ tags, collections, active === true Department, CategoryCode,
metafields } Vendor, SubDept1…3, PLU…1…3
The same mapping is applied in three contexts, so a line is enriched identically wherever it enters:
- Drop-in session create / update (live cart / benefit calculation)
- Shopify
orders/paidwebhook (transaction close) - Refunds
2. The extended_attributes contract
Your storefront / cart extension is responsible for putting extended_attributes on each line item it sends (via setCart, and forwarded into the order webhook). The shape:
extended_attributes?: {
tags?: { id: string; value: string }[];
collections?: { id: string; value: string }[];
metafields?: { id: string; value: string | number | boolean }[];
}
idis the machine key SimplyClub maps and stores (e.g. a tag ID, a collection ID, or a metafield identifier likecustom.material_composition).valueis the human-readable label — useful for your own debugging; mapping itself keys offid(tags/collections) or matches byid(metafields).
This is the contract this app expects on line objects — it is not a verbatim Shopify Admin API payload. Any storefront that emits this shape works (see §6).
For the surrounding line-item fields (id, sku, price, quantity, …) see the line-item shape in the Webhook Integration Guide and the setCart command in the Technical Documentation — this page does not repeat them.
3. The mappable Simply fields
Nine Simply line-item fields can be filled. Each maps from one source:
| Mapping key | Simply field | Typical use |
|---|---|---|
department | Department | Top-level merchandise department |
category | CategoryCode | Product category |
subDept1 | SubDept1 | Sub-department level 1 |
subDept2 | SubDept2 | Sub-department level 2 |
subDept3 | SubDept3 | Sub-department level 3 |
vendor | Vendor | Supplier / brand |
pluClassification1 | PLUClassificationCode1 | Classification code 1 |
pluClassification2 | PLUClassificationCode2 | Classification code 2 |
pluClassification3 | PLUClassificationCode3 | Classification code 3 |
A field is only written when its rule resolves to a value — unresolved fields are omitted, never set to an empty string.
4. How a value is resolved
Each of the nine fields has a rule:
{ source: 'tags' | 'collections' | 'metafield' | 'none', key?: string }
source | How the value is resolved |
|---|---|
tags | Joins the id of every tag on the line with , (e.g. t1, t2). Resolves to nothing if the line has no tags. |
collections | Joins the id of every collection on the line with , . Entries missing an id are skipped (and logged). |
metafield | Requires a key. Finds the metafield whose id === key and uses its value (coerced to a string). |
none | Field is not mapped. (Default for every field.) |
Tags & collections are bulk by nature. Because they join all entries on the line, point a single Simply field (e.g.
Department) attagsonly when a line carries exactly the tag(s) you want there. For one-value-per-field precision, prefermetafield.
Example
A line item like:
{
"id": 44550337626248,
"sku": "1004",
"extended_attributes": {
"tags": [{ "id": "12", "value": "Womenswear" }],
"metafields": [
{ "id": "custom.vendor_code", "value": "ACME" },
{ "id": "custom.category", "value": "337" }
]
}
}
with this mapping:
{
"department": { "source": "tags" },
"category": { "source": "metafield", "key": "custom.category" },
"vendor": { "source": "metafield", "key": "custom.vendor_code" }
}
produces these fields on the Simply line item:
{ "Department": "12", "CategoryCode": "337", "Vendor": "ACME" }
5. Configuring a POI
Mapping is stored per POI under settings.shopifySimplyAttributeMapping:
{
active: boolean, // master switch — defaults to false
mapping: { /* the 9 rules */ },
discoveredMetafieldKeys: string[] // see §5.2
}
5.1 Management API
These endpoints are vendor-authenticated (Dashboard) and POI-scoped.
GET /api/v1/poi/:poiId/attribute-mapping
PUT /api/v1/poi/:poiId/attribute-mapping
GET /api/v1/poi/:poiId/attribute-mapping/metafield-keys
GET returns the current configuration (with defaults filled in for any unset rule):
{
"storeId": "65f...",
"active": true,
"mapping": {
"department": { "source": "tags" },
"category": { "source": "metafield", "key": "custom.category" },
"subDept1": { "source": "none" },
"subDept2": { "source": "none" },
"subDept3": { "source": "none" },
"vendor": { "source": "metafield", "key": "custom.vendor_code" },
"pluClassification1": { "source": "none" },
"pluClassification2": { "source": "none" },
"pluClassification3": { "source": "none" }
},
"discoveredMetafieldKeys": ["custom.category", "custom.vendor_code"]
}
PUT persists { active, mapping }. The body is validated: active must be a boolean, and any rule with source: "metafield" must include a non-empty key. Unknown / omitted rules default to { source: "none" }. The response uses the standard envelope:
{ "success": true, "data": { "...same shape as GET..." }, "error": null }
curl -X PUT https://dropins.simplyclub.co.il/api/v1/poi/65f.../attribute-mapping \
-H "Authorization: Bearer <vendor-token>" \
-H "Content-Type: application/json" \
-d '{
"active": true,
"mapping": {
"department": { "source": "tags" },
"category": { "source": "metafield", "key": "custom.category" },
"vendor": { "source": "metafield", "key": "custom.vendor_code" }
}
}'
5.2 Metafield-key discovery
You don’t have to know your metafield IDs up front. As carts and orders flow through, the backend records every metafield id it sees into discoveredMetafieldKeys (best-effort, non-blocking). The Dashboard surfaces these as a picklist when you choose source: metafield, so the keys your storefront actually emits become selectable. GET …/attribute-mapping/metafield-keys returns just that list.
6. Channel support
Despite the shopify… naming on the setting and internal types, the mapping engine has no Shopify-specific logic. It works for any storefront that emits the extended_attributes contract through the drop-in session / cart endpoints — custom HTML, WooCommerce, or anything else.
Shopify is simply the one webhook intake wired today. The drop-in session path (used by every channel) already applies the mapping.
Webhook caveat. Shopify’s
orders/paidwebhook frequently dropsextended_attributesfrom its line items. To avoid losing your mapping at transaction close, the backend re-attaches the attributes from the saved cart session (matching lines byvariant_id/sku) before applying the mapping. So attributes set on the cart survive into the paid-order transaction — as long as the session carried them.
7. Checklist before going live
- Storefront emits
extended_attributes(tags/collections/metafields, each{ id, value }) on every relevant line insetCart. - You’ve chosen a
sourcefor each Simply field you need; everything else staysnone. - Every
metafieldrule has a non-emptykeythat matches the metafieldidyour storefront emits. -
activeis set totruefor the POI (PUT…/attribute-mapping). - You ran a test cart and confirmed the expected Simply fields appear (GET
…/attribute-mapping/metafield-keysshows your keys were seen). - For Shopify: confirmed the session carries the attributes so the webhook merge can restore them at
orders/paid.
8. Troubleshooting
| Symptom | Likely cause |
|---|---|
| Fields never appear on the transaction | Mapping is not active for the POI, or no rule points at a populated source. |
A metafield field is always empty | The rule’s key doesn’t match any metafield id on the line, or the storefront isn’t sending that metafield. Check discoveredMetafieldKeys. |
Department has several comma-joined values | source: tags/collections joins all entries on the line. Use metafield for a single value. |
Attributes present on the cart but missing at orders/paid | The Shopify webhook dropped them and the session didn’t carry them (no variant_id/sku match for the merge). Verify the session cart included extended_attributes. |
| PUT rejected with a validation error | active must be boolean; every metafield rule needs a non-empty key. |