Tags vs Custom Fields vs Custom Values — GHL Data Architecture for Operators
Three data primitives, three jobs. The decision tree for when to use tags, when to use custom fields, and when to use custom values — and the migration cost when you pick wrong.
Three data primitives. Three different jobs. And the single most common mistake in GoHighLevel accounts I audit: using the wrong one for the job.
Tags get used for everything because they’re the easiest to create. Right-click, type a name, done. Three years later the account has 280 tags, half of them duplicates, the workflow logic is unreadable, and the operator can’t figure out why segmentation is broken.
The fix isn’t more discipline. The fix is understanding what each primitive is actually for.
The three primitives
Tags. Boolean state. A contact either has the tag or doesn’t. Use for: lead stage, has-attended-webinar, is-customer, ready-for-followup, opted-into-SMS. The answer is yes or no.
Custom fields. Structured data attached to a contact. Use for: annual revenue, industry, birthday, lead score, billing address, phone numbers, anything that has a value beyond yes/no. The field has a data type (text, number, date, dropdown) and the value persists per-contact.
Custom values. Account-wide constants. Use for: your booking link, your office address, your business signature, your sub-account name. The value is the same for every contact — and changing it in one place updates every workflow and template that references it.
Three primitives. Three completely different jobs. Mix them up and the CRM rots from the inside.
The decision tree
For any piece of information you want to track about a contact:
Is the answer just yes or no? → Tag.
Examples: “Has attended discovery call.” “Is on the waitlist.” “Replied YES to confirmation.” “Refunded a purchase.” “Subscribed to weekly newsletter.” These are state markers. The contact either has the tag or doesn’t.
Is the answer a number, a date, or one of multiple named values? → Custom field.
Examples: “Annual revenue” (number). “Birthday” (date). “Industry” (dropdown: e-commerce, agency, coaching, SaaS, other). “Lead score” (number 0-100). “Last purchase amount” (currency). These have values, not just states.
Is the answer the same for every contact and might change someday? → Custom value.
Examples: “Booking link.” “Office address.” “Brand name.” “Support email.” “Senior-rep signature.” These don’t vary by contact — they vary by account/season/business.
If you can’t decide between the three, go through the tree top to bottom. Tags are first because they’re the most-common need. Custom fields are second because they’re the most-common mistake. Custom values are third because they’re the most-overlooked.
The “tag-as-data” antipattern
The #1 mistake I see in GHL audits: tags being used where custom fields should be.
Wrong:
revenue-100k,revenue-250k,revenue-500k,revenue-1m(four tags for what should be one number field)industry-ecommerce,industry-agency,industry-coaching(three tags for what should be one dropdown field)score-50,score-72,score-95(one tag per possible lead score — eventually you’ll have 100 tags doing one field’s job)
Right:
- One custom field
annual_revenue(number type), populated via form or workflow. - One custom field
industry(dropdown with five options), populated at signup. - One custom field
lead_score(number 0-100), updated by your scoring workflow.
The tag-as-data antipattern accumulates silently. It starts with one workflow that creates a customer-2024 tag when someone buys. Then customer-2025. Then repeat-customer-2024. Within two years the account has 80 tags doing the work of 4 custom fields, every segmentation query is slower than it needs to be, and no operator can remember which tags mean what.
Fix: when a tag’s name has a number, date, or category in it, that’s a custom field crying for help.
The tag bloat ceiling
A healthy GHL account has 30-40 tags. Maybe 50 if you’re running multiple aggressive marketing campaigns.
An unhealthy account has 200+. I’ve audited accounts with 600+ tags. At that scale, nobody knows what’s in there. Workflows trigger off tags nobody remembers creating. Segmentation queries return wrong contacts because three tags mean the same thing.
The signs of tag bloat:
- Duplicate tags with slight spelling differences.
Webinar-attended,webinar attended,webinar_attended— three tags doing one job. - Date-stamped tags that accumulate.
attended-jan-2024,attended-feb-2024, ad infinitum. Use a custom field “last attendance date” instead. - Source tags from every Facebook ad campaign.
fb-ad-summer-2024-v3. Use the UTM custom fields GHL captures automatically. - Tags that nobody can explain. When you ask the operator “what does this tag do?” and they don’t know, delete it.
Audit cadence: quarterly. Export the tag list. Anything with under 10 contacts attached is a deletion candidate. Anything that hasn’t been used as a workflow trigger in 90 days is a deletion candidate. Be ruthless. Documentation is the cost of keeping things.
When custom values are gold
Most operators underuse custom values. They’re the most-overlooked primitive because they don’t show up in the contact record — they’re set once in account settings and referenced inside templates.
Killer use cases:
- Booking link. Set as
{{custom_values.booking_link}}. When you change calendars, swap the value once. Every email, SMS, and workflow that references it updates immediately. Without custom values, you’d be doing find-and-replace across dozens of templates. - Business signature.
Best, Swapnil — Founder, OpSkills. If you ever change your title, you change one place. - Office address / phone. Used in legal-compliance footers, in confirmation emails, in SMS opt-out copy. Change once, update everywhere.
- Service offer name. If you rebrand “Strategy Session” to “Discovery Call” — change the custom value, every template updates.
- Refund-policy URL, terms-URL, privacy-URL. Compliance links that occasionally change. Centralize them.
The rule of three: if a piece of text appears in three or more templates/workflows AND it could conceivably change in the future, make it a custom value.
What goes wrong when you pick wrong
Tag instead of custom field. Your segmentation queries get slower as tag count climbs. Reporting is harder because you can’t group-by-revenue when revenue is split across four tags. Workflow logic becomes “if tag-A or tag-B or tag-C” — fragile and easy to break. Migration later means rewriting every workflow that triggered off those tags.
Custom field instead of tag. Boolean fields (“opted_in_yes_no”) work but they’re harder to query and slower than tags. GHL’s tag-based segmentation is heavily optimized. Force-fitting yes/no into a custom field is a minor sin but it does add friction.
Custom value instead of custom field. This is rare but happens. If you accidentally put per-contact data (“preferred_meeting_time”) into a custom value, you’ve made it global — every contact gets the same value, which defeats the purpose. Custom values are for account-wide constants, not contact-specific data.
Custom field instead of custom value. This is the silent mistake. Operator puts the booking link as a custom field, then has to populate it for every contact via workflow. Works, but creates per-contact overhead for data that’s the same for everyone. Just use a custom value.
The audit process
For an hour-long audit of any GHL account:
Step 1 — Export your tag list. Settings → Tags → Export. Sort by contact count. Any tag with under 10 contacts is a deletion candidate. Any tag whose name contains a date, a number, or a category is a custom-field-in-disguise.
Step 2 — Review your custom fields. Settings → Custom Fields. Any field with under 20% population rate is dead weight. Either delete it or build the workflow that fills it. Empty fields confuse everyone.
Step 3 — Audit your custom values. Settings → Custom Values. List the values that exist. Compare against text strings that repeat in 3+ templates. Anything that should be a custom value but isn’t, fix.
Step 4 — Document. Create a Google Doc listing every tag, custom field, and custom value with a one-line purpose. “What is this and when is it used?” Future operators (and future-you) will thank you. Update quarterly when you audit.
The whole process takes 60 minutes for a typical-size account. The result is a CRM that operators can navigate, workflows that operators can edit confidently, and segmentation queries that return what you actually wanted.
Migration is painful — get it right the first time
The reason this article matters is that the cost of fixing a bad data architecture later is brutal.
Tag-to-custom-field migration. Every workflow that triggers off the tags needs to be rewritten. Every email template that references the tags via conditional content needs rewriting. Reports based on tag-segmentation need rebuilding. For a 12-month-old account with bad architecture, the migration is typically 8-16 hours of operator time.
Custom-field consolidation. If you’ve created five custom fields that should have been one (annual_revenue_2022, annual_revenue_2023, etc.), merging them requires a one-time data migration script, then deleting the old fields, then updating every workflow. 4-8 hours.
Custom-values retrofit. If a piece of text exists in 30 templates and you decide it should now be a custom value, you have to find-and-replace across all 30. GHL doesn’t have a great bulk-edit for this. 1-3 hours.
None of these are catastrophic. All of them are time you’d rather not spend. The fix is to get the foundation right when you set the account up — and audit quarterly to catch drift.
What to do this week
Three concrete actions:
Step 1 — Export your tag list. Look at the count. If it’s over 50, you have tag bloat. If any tag name contains a number, date, or category, you have the tag-as-data antipattern.
Step 2 — Identify your custom-value candidates. Pull up three of your most-used email templates. Find the text that’s repeated across all three. Make a list. Convert each one to a custom value.
Step 3 — Pick one antipattern and fix it. Don’t try to fix everything at once. Pick the worst-offender (probably a tag-as-data set like revenue-tier-A, revenue-tier-B, revenue-tier-C), build the custom field, migrate the workflows, then delete the old tags.
Closing
A CRM is a database with a UI on top. The database has primitives. Use them correctly and the account scales — workflows are readable, segmentation is fast, operators can hand off the account to other operators.
Use them wrong and the account rots. The tags multiply. The custom fields go empty. The custom values stay unused. Three years in, nobody knows what anything is for and everyone is afraid to delete anything.
Tags for state. Custom fields for data. Custom values for constants. That’s the whole framework.
The only hard part is choosing correctly the first time. The audit takes an hour. Do it.
Related reading:
- CRM & Lead Management — Pillar — the broader CRM operations framework
- Lead Scoring Setup in GoHighLevel — what to do once you have a proper score field
- GHL Pipelines That Predict Revenue — stage design that depends on clean data architecture
- GoHighLevel Review After 3 Years — the platform behind these architecture choices
Free PDF · No signup tricks
Free: The GHL Snapshot Library
7 battle-tested GoHighLevel workflows you can steal today. No fluff, no upsell.
Delivered to your inbox in 60 seconds. Unsubscribe anytime.
Keep reading