23 "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>."
24 },
25 "reliability": {
26 "status": "reliable",
27 "flags": []
28 },
29 "pitfalls": [
30 {
31 "code": "definitions-then-values",
32 "severity": "warning",
33 "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."
34 },
35 {
36 "code": "meta-key-selection",
37 "severity": "warning",
38 "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."
39 },
40 {
41 "code": "key-is-server-assigned",
42 "severity": "blocker",
43 "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."
44 },
45 {
46 "code": "value-write-is-a-member-patch",
47 "severity": "warning",
48 "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."
53 "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."
54 }
55 ],
56 "mappingGuidance": [
57 "Dependencies: no other modeled record must exist first; custom-field definitions are standalone setup records, although member values consume their server-assigned keys later.",
58 "Create one custom field per approved source user_meta key: human label → name, inferred type → fieldType (TEXT | NUMBER | DATE | URL | SOCIAL), defaultPrivacy PUBLIC | PRIVATE.",
59 "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.",
78 "note": "Verified live: create → 200 (id <anonymized-id>, 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"
79 }
80 ],
81 "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."