Deduplicating members
Question: Signup says the phone number is already registered, or the same person appears twice.
What makes a member unique
Section titled “What makes a member unique”Phone number, within one store. That is the identity we enforce.
The same person at two Diller stores is two members, correctly. There is no global customer, and no cross-store deduplication to do.
So: match on phone.
Handling “already registered” on create
Section titled “Handling “already registered” on create”Creating a member whose phone number is already registered at the store
returns 400:
Member with phone number ending in 0001 is already registered for this storeIt is not an error condition — it is information. Find the member and update them instead.
STORE_ID is the id you read from GET /stores at setup.
const ALREADY_REGISTERED = /is already registered for this store/i;
async function createOrUpdateMember(payload) { const res = await dillerFetch(`/api/v2.0/stores/${STORE_ID}/members`, { method: 'POST', body: JSON.stringify(payload), }); if (res.ok) return res.json();
const text = await res.text(); if (res.status !== 400 || !ALREADY_REGISTERED.test(text)) return null;
// Already registered. Find them and update instead. const phone = `${payload.phone.country_code}${payload.phone.number}`; const existing = await findMemberByPhone(phone); if (!existing) return null;
const put = await dillerFetch( `/api/v2.0/stores/${STORE_ID}/members/${existing.id}`, { method: 'PUT', body: JSON.stringify(toUpdateBody(existing, payload)) }, ); return put.ok ? put.json() : null;}findMemberByPhone takes the phone as one string, +4799900001, and
encodes it into the search query.
Build the update body field by field rather than spreading the existing
member into it. A PUT clears address, department_ids and segments
if you leave them out, sets either marketing flag to false if you leave
it out, and rejects segments sent back in the shape GET returns them:
const toSegmentAnswer = (s) => s.type === 'Text' || s.type === 'DateTime' ? { segment_id: s.id, value: s.value } : { segment_id: s.id, selected_options: s.selected_options };
function toUpdateBody(existing, payload) { return { first_name: payload.first_name ?? existing.first_name, last_name: payload.last_name ?? existing.last_name, email: payload.email ?? existing.email, birth_date: payload.birth_date ?? existing.birth_date, gender: payload.gender ?? existing.gender, address: payload.address ?? existing.address, department_ids: payload.department_ids ?? existing.department_ids, segments: payload.segments ?? existing.segments.map(toSegmentAnswer), consent: { ...existing.consent, ...payload.consent }, origin: payload.origin, };}The new signup form’s answers win where it asked the question; everything
it did not ask about is carried over from the existing member. There is no
phone in the body — PUT does not accept one.
Normalise phone numbers before you search
Section titled “Normalise phone numbers before you search”Most “duplicates” are a formatting difference.
99900001 national999 00 001 national, spaced+4799900001 international004799900001 international, 00 prefixAll the same person. Normalise to a single canonical form — international, no spaces — at the edge of your system, before it reaches us or your own database.
import parsePhoneNumber from 'libphonenumber-js';
function canonicalPhone(input, defaultCountry = 'NO') { const p = parsePhoneNumber(input, defaultCountry); if (!p || !p.isValid()) throw new Error(`Invalid phone: ${input}`); return { country_code: `+${p.countryCallingCode}`, number: p.nationalNumber };}We accept +47… and 0047…, and assume the store’s country for a
national-format number. Do not rely on that — send international format
and the ambiguity is gone.
Two tabs, two submissions, one customer. Both search, both miss, both create; one wins and the other is told the number is already registered.
The handler above already covers it: on “already registered”, search again and continue with what you find. Do not surface the conflict to the customer — from their side, nothing went wrong.
Merging genuine duplicates
Section titled “Merging genuine duplicates”If a store already has two members for one person — usually one from the till and one from the webshop, with different numbers — there is no merge endpoint. Consolidation is a manual operation on our side.
What you can do:
- Pick the member with the real history (points, transactions).
- Link that one in your database.
- Ask the merchant to raise a merge request with us for the other.
Prevent the next one by attaching your own customer id as a member identifier, so the link survives a phone number change.
See also: Signup and login, Errors.
