The Short Answer
Tags mark a state. Custom fields store a value. Custom values store a constant. If the honest answer to "what is this?" is yes/no, it's a tag. If it's a number, date, or a value that's different for every contact, it's a custom field. If it's the same for every contact and every workflow — a phone number, an office address, a Calendly link — it's a custom value. Mixing these up is the single most common reason a GHL sub-account gets messy within three months of launch.
Tags: A Boolean State on a Contact
A tag either sits on a contact or it doesn't. There's no "how much" or "since when" attached to it — that's the whole point. Tags exist to answer one question fast: is this true about this person right now?
Good tag uses:
lead-source-facebook,lead-source-referral— where they came frombooked-consult,no-show,attended-webinar— did the thing happenopted-into-sms,dnc-email— compliance and consent flagshot-lead,customer,churn-risk— a status you'll filter or trigger on
Tags are also GoHighLevel's most-used automation trigger — a workflow can start the moment a tag is applied or removed, which makes them the connective tissue between separate automations. The pattern that scales: an entry tag starts a workflow, a completion tag marks it finished, and a downstream workflow triggers off that completion tag. Each workflow stays small and testable instead of turning into one 40-step monster.
Custom Fields: Structured Data That Varies Per Contact
A custom field holds a real value — and that value is different for every contact. This is where the tag-as-data mistake shows up constantly: agencies build tags like customer-2024, customer-2025, score-80, score-90, one tag per possible value. The tell is right there in the name — if a tag's name contains a number, a date, or a category, it's a custom field that got turned into ten (or a hundred) separate tags instead of one.
Use a custom field when the answer is one of:
- A number — lead score, annual revenue, deal value
- A date — birthday, contract renewal date, last purchase date
- Free text — company name, job title, notes
- One value from a dropdown — industry, plan tier, service area
Get this split right and custom fields stop being static storage — they become live inputs for if/else branches, personalized merge fields in emails and SMS, and pipeline logic, which opens up automation possibilities a tag-only setup can't reach.
Custom Values: The One Most Guides Skip
Custom values are the piece people forget because they don't live on the contact record at all — they live at the sub-account level. A custom value is a constant you set once — a phone number, a booking link, a support email, a legal disclaimer — and reference everywhere with a merge field like {{custom_values.support_phone}}. Change the phone number once in Settings and it updates in every email, SMS, and funnel that references it, instead of hunting through 30 templates one at a time.
We ran into this exact distinction fixing our own contact form recently — a lead filling the form out twice was landing two separate values instead of one merged contact record, and the fix was routing through GHL's dedicated tags endpoint rather than treating the tag write like a custom-field update. Small difference in the API call, real difference in whether your data stays clean six months in.
Use a custom value when:
- The value is identical for every contact in the sub-account
- It shows up in more than one template, funnel, or workflow
- You want to update it in exactly one place
The Decision Table
| Ask yourself | Use this |
|---|---|
| Is it true or false about this contact? | Tag |
| Does it need to trigger or stop a workflow? | Tag |
| Is it a number, date, or free text that differs per contact? | Custom field |
| Will you branch logic on its specific value (not just presence)? | Custom field |
| Is it the same value for every contact in the sub-account? | Custom value |
| Does it appear in multiple templates or funnels? | Custom value |
Naming Conventions That Prevent the Mess
Structure beats memory. Once a sub-account has 50+ tags, nobody remembers what they mean without a convention:
- Prefix by category:
src-facebook,status-hot,flow-onboarding-complete - Never reuse one tag as a trigger for two unrelated workflows — it will fire both, and debugging which one misbehaved gets slow fast
- Document trigger tags somewhere your team can see — a pinned note in the sub-account or a shared sheet, not just in your head
- Test on one contact before turning a tag-triggered workflow on for everyone
If you're deploying the same tag, field, and value structure to every new client, that's exactly what a GHL snapshot is for — build the naming convention once, then deploy it in minutes instead of rebuilding it by hand per account.
The ZAM88 Verdict
Reach for a tag when:
- You need to trigger, filter, or segment on a yes/no state
- The value has no "how much" — it either applies or it doesn't
Reach for a custom field when:
- Every contact has a different value worth storing and branching on
- You'll personalize a message with it via a merge field
Reach for a custom value when:
- It's one constant used across many templates or funnels
- You want a single place to update it
Get this split right once, and every workflow you build afterward gets simpler instead of more tangled. Get it wrong, and you're the agency explaining to a client why their automation fired for the wrong contacts six months from now.
If your GHL sub-account has grown past the point where the tag list makes sense to anyone but you — book a free strategy call with ZAM88. We audit and rebuild data architecture on live accounts without breaking what's already running. And if you're still deciding whether GoHighLevel is the right CRM for your agency at all, start with our GoHighLevel vs HubSpot breakdown.