22 "decision": "Two-step: (1) define the field with { field: { name, fieldType, defaultPrivacy } } and capture the SERVER-ASSIGNED key from the response; (2) write per-member values with PATCH /members/v1/members/{memberId} { member: { contact: { customFields: { \"<key>\": { value } } } } }. Delete needs the revision as a query param: DELETE /members/v1/custom-fields/{id}?revision=<n>."
23 },
24 "reliability": {
25 "status": "reliable",
26 "flags": []
27 },
28 "pitfalls": [
29 {
30 "code": "definitions-then-values",
31 "severity": "warning",
32 "summary": "This surface defines the profile field (name, fieldType, defaultPrivacy); per-member values are written on the member profile. Import definitions before members or the value writes have no field to land in."
33 },
34 {
35 "code": "meta-key-selection",
36 "severity": "warning",
37 "summary": "WordPress user_meta is a mixed bag of plugin bookkeeping and real profile data; import only keys the mapping review approves, or the member profile schema fills with junk fields."
38 },
39 {
40 "code": "key-is-server-assigned",
41 "severity": "blocker",
42 "summary": "CORRECTED 2026-08-16 (live): `key` is READ-ONLY and server-derived — creating a field named \"ZZZ Probe WP Meta\" returned key `custom.zzz-probe-wp-meta-iveokecsuhrarwyqq` (slugified name + random suffix). You cannot set the source meta key as the Wix key, and you cannot reconstruct it. Capture `field.key` from the create response into the crosswalk; every value write addresses that key. A codegen path that derives the key from the source meta key writes into a field that does not exist."
43 },
44 {
45 "code": "value-write-is-a-member-patch",
46 "severity": "warning",
47 "summary": "VERIFIED LIVE 2026-08-16: values land at member.contact.customFields[key].value via PATCH /members/v1/members/{id} — not on this surface, and not under member.profile. The patch succeeded with no revision field on the member payload; the readback returns { name, value } per key."
52 "summary": "VERIFIED LIVE 2026-08-16: DELETE /members/v1/custom-fields/{id} with no query string → 400 `revision must not be empty`. Pass the current revision as a query param (`?revision=1`) — rollback/cleanup code must read the field first."
53 }
54 ],
55 "mappingGuidance": [
56 "Create one custom field per approved source user_meta key: human label → name, inferred type → fieldType (TEXT | NUMBER | DATE | URL | SOCIAL), defaultPrivacy PUBLIC | PRIVATE.",
57 "Record the SERVER-ASSIGNED field key from the create response in the crosswalk (source meta key → Wix `field.key`); the member import addresses values by that key, never by the source key.",
75 "path": "migrations/probe-run-20260812/config/wix.env (target site 00000000-0000-0000-0000-000000000000)",
76 "note": "KB verification probe 2026-08-16, safe mode on / site muted: create → 200 (id 32611f4f…, server key custom.zzz-probe-wp-meta-iveokecsuhrarwyqq, fieldOrigin CUSTOM, appliesTo ALL_MEMBERS, section GENERAL); value set on a probe member via member PATCH and read back; DELETE without revision → 400, DELETE ?revision=1 → 200; list-custom-fields empty afterwards"
77 }
78 ],
79 "notes": "Authored 2026-08-11 from the docs-survey gap (member Custom Fields). Classified setup-config: it defines profile schema (the member-side analog of contact extended fields); per-member values ride on members/member. Promoted to verified-live 2026-08-16; the probe corrected the original mappingGuidance claim that the source meta key becomes the Wix field key — the key is server-assigned."