Read responses (GET /customers, GET /customers/{customer_id}, and the customer block of every offer) return custom_fields keyed by the field's display label — the same label you see in the Mercura admin UI — rather than the internal column id. Columns with no active definition are omitted. If two custom columns share a label, the one defined first (by display order, then creation time) wins and the other's value is dropped from the response; deduplicate colliding columns in the admin UI. The same label-keying applies to suppliers.
The write path is deliberately asymmetric: on POST /customers send custom_fields keyed by the internal column id (not the label). Only reads are label-keyed.