Recipes / Customer sync for marketing
CRMExport customers and their segments to a mail or loyalty platform, incrementally and without duplicates.
The same flow that feeds our marketing connector.
Segments are the audiences your merchant already maintains: wholesale, newsletter, VIP. One call lists them all, alphabetical, with a customer_count so you can size the sync before running it.
curl -s https://api.storekeeper.me/api/customer-segments -H "Authorization: Bearer $TOKEN"
{
"count": 3,
"total": 3,
"data": [
{ "id": 7, "name": "Groothandel", "description": "B2B bakkerijklanten", "price_list_id": 12, "customer_segment_type_id": 1, "customer_count": 48 },
{ "id": 11, "name": "Nieuwsbrief", "description": null, "price_list_id": null, "customer_segment_type_id": 1, "customer_count": 1893 },
{ "id": 14, "name": "VIP stamgasten", "description": "10% vaste korting", "price_list_id": 19, "customer_segment_type_id": 1, "customer_count": 112 }
]
}
The id is what you feed into /api/customers?segment_id= in the next step. price_list_id tells you a segment also binds custom prices; irrelevant for mailing, useful to know it exists.
Page through the segment members with start/limit (max 500 per page) until you have total rows. q is a word search over name, email and similar fields; combine it with segment_id to spot-check, or leave it off for the full drain.
# page 1, then start=500, start=1000, ... until start >= total
curl -s "https://api.storekeeper.me/api/customers?segment_id=11&start=0&limit=500" \
-H "Authorization: Bearer $TOKEN"
{
"q": "",
"start": 0,
"limit": 500,
"count": 500,
"total": 1893,
"data": [
{ "id": 88412, "name": "Anouk Bakker", "own_id": "", "email": "anouk.bakker@example.nl",
"business_name": "", "contact_person_name": "Anouk Bakker" },
{ "id": 88437, "name": "Modehuis Elegance B.V.", "own_id": "ERP-2041", "email": "inkoop@modehuis-elegance.nl",
"business_name": "Modehuis Elegance B.V.", "contact_person_name": "S. van Dijk" },
{ "id": 88502, "name": "Kassa klant", "own_id": "", "email": "",
"business_name": "", "contact_person_name": "" }
]
}
The row id is the customer's relation_data_id, the same id orders carry. That makes it the join key for everything: you can attach purchase history to a mail profile by matching an order's relation_data_id to this id.
The list rows carry enough for most mail syncs. When you also want phone and address (postal campaigns, country-based segmentation), fetch the detail per customer. Only do this for the rows that survive your email filter; it is one call per customer.
curl -s https://api.storekeeper.me/api/customers/88437 -H "Authorization: Bearer $TOKEN"
{
"id": 88437,
"name": "Modehuis Elegance B.V.",
"own_id": "ERP-2041",
"email": "inkoop@modehuis-elegance.nl",
"business_name": "Modehuis Elegance B.V.",
"contact_person_name": "S. van Dijk",
"phone": "+31612345678",
"address": {
"street": "Herenstraat",
"streetnumber": "24",
"zipcode": "3512 KB",
"city": "Utrecht",
"country_iso2": "NL"
}
}
Three rules keep the sync clean across re-runs:
Filter unusable rows first. Drop rows with an empty email or an obvious placeholder before pushing. A mail platform full of blank guest profiles ruins deliverability stats and costs contact-tier money.
Dedupe on id, never on email. Two family members can share one email address, and one person can appear with two addresses over the years. The id is the identity; email is just an attribute of it.
Store the id as the external key in the mail platform. Every platform has an external-id or custom-field slot. Write the Storekeeper id there and upsert on it, so a re-run updates existing profiles instead of duplicating them, and an email change in Storekeeper propagates instead of forking a second profile.
# per drained row
if row.email is empty -> skip
if seen[row.id] -> skip (already handled this run)
upsert profile where external_id == row.id
set email, name, business_name
Recepten / Klantsynchronisatie voor marketing
CRMExporteer klanten en hun segmenten naar een mail- of loyaliteitsplatform, incrementeel en zonder duplicaten.
Dezelfde flow die onze marketingconnector voedt.
Segmenten zijn de doelgroepen die je winkelier al bijhoudt: groothandel, nieuwsbrief, VIP. Eén aanroep geeft ze allemaal, alfabetisch, met een customer_count zodat je de sync kunt inschatten voordat je hem draait.
curl -s https://api.storekeeper.me/api/customer-segments -H "Authorization: Bearer $TOKEN"
Het id is wat je in de volgende stap in /api/customers?segment_id= stopt. price_list_id vertelt je dat een segment ook eigen prijzen bindt; irrelevant voor mailen, handig om te weten dat het bestaat.
Blader door de segmentleden met start/limit (max 500 per pagina) tot je total rijen hebt. q is een woordzoekopdracht over naam, e-mail en vergelijkbare velden; combineer met segment_id om steekproeven te doen, of laat weg voor de volledige export.
# pagina 1, dan start=500, start=1000, ... tot start >= total
curl -s "https://api.storekeeper.me/api/customers?segment_id=11&start=0&limit=500" \
-H "Authorization: Bearer $TOKEN"
Het rij-id is het relation_data_id van de klant, hetzelfde id dat orders dragen. Dat maakt het de koppelsleutel voor alles: je hangt aankoopgeschiedenis aan een mailprofiel door het relation_data_id van een order aan dit id te matchen.
De lijstrijen zijn genoeg voor de meeste mailsyncs. Wil je ook telefoon en adres (postcampagnes, segmentatie op land), haal dan per klant het detail op. Doe dat alleen voor de rijen die je e-mailfilter overleven; het is één aanroep per klant.
curl -s https://api.storekeeper.me/api/customers/88437 -H "Authorization: Bearer $TOKEN"
Drie regels houden de sync schoon over meerdere runs:
Filter onbruikbare rijen eerst. Gooi rijen met een lege email of een duidelijke placeholder weg voordat je pusht. Een mailplatform vol lege gastprofielen verpest je deliverability-statistieken en kost geld per contact.
Ontdubbel op id, nooit op e-mail. Twee gezinsleden kunnen één e-mailadres delen, en één persoon kan door de jaren heen met twee adressen voorkomen. Het id is de identiteit; e-mail is maar een eigenschap ervan.
Bewaar het id als externe sleutel in het mailplatform. Elk platform heeft een external-id of custom-field. Schrijf daar het Storekeeper-id in en upsert erop, zodat een nieuwe run bestaande profielen bijwerkt in plaats van dupliceert, en een e-mailwijziging in Storekeeper doorstroomt in plaats van een tweede profiel af te splitsen.
# per opgehaalde rij
if row.email is leeg -> overslaan
if seen[row.id] -> overslaan (al gedaan deze run)
upsert profiel where external_id == row.id
zet email, name, business_name