Best viewed on a desktop - the flow diagrams are wide. On mobile, swipe a diagram to explore it.
BrewedOps
Reusable support-desk blueprint

An automated support desk, built inside GoHighLevel

A customer submits a support form. An opportunity opens in a dedicated Support pipeline and becomes the ticket. Every stage has its own automation, so dragging a card to a stage fires exactly the right actions and the right customer email. Drop your brand, your categories, and your team in - the engine doesn't change.

0Pipeline
0Stages
0Emails
0Workflows
0Manual replies
Built inside
The flow

How it works

A customer submits the Submit a Support Ticket form. That creates or matches a contact, and the Intake router checks whether they already have an open ticket. If they do, the message is added to it. If not, the New sequence generates a ticket number, opens an opportunity in the Support pipeline, sends the received email, and tags the ticket by category. The opportunity is the ticket - its stage is the status, and all ticket data lives on opportunity fields so every ticket is self-contained. Each stage runs its own automation the moment the card lands there.

Open the interactive diagram
The core idea

The opportunity is the ticket

The form writes to contact fields as a capture layer. The New sequence copies them onto opportunity fields, where the real ticket record lives - so a customer who opens a second ticket never overwrites the first.

Gap closed
Per-ticket isolation. If ticket data lived on the contact, a customer opening a 2nd ticket would overwrite the 1st ticket's number, issue, and resolution (GHL matches contacts by email), and the older ticket's emails would reference the newer ticket's data. Putting ticket data on the opportunity makes every ticket independent. The contact fields are only a transient capture layer the New sequence copies onto the opportunity.
Step 1 · Pipeline & stages

One pipeline, five stages

Create a pipeline named Support Tickets with five stages, in order. The stage is the ticket status, and each stage has its own automation that fires the instant a card lands there.

StageMeaningIts automation (on entry)
NewJust arrived (set on form submit)Ticket ID + open opportunity + copy fields + Email 1 + category router
UrgentManual priority flagTag priority-urgent + email alert to the team (SMS optional). No customer email.
In ProgressTeam actively working itEnd the Awaiting Reply timer + clear the urgent flag. Optional "we're on it" email.
Awaiting ReplyWaiting on the customerEmail 2 (needs info), remind at 3 days, auto-close at 5
ResolvedClosedEnd the Awaiting Reply timer + open a 7-day reopen window + stamp Date Resolved + Email 3 + optional CSAT survey
Step 2 · Custom fields

Two field sets: capture & record

The form writes to contact fields. The New sequence copies them onto opportunity fields, where the real ticket record lives - so a customer with multiple tickets never has one ticket overwrite another.

C Contact fields (the form fills these)

FieldTypeMerge key
Request CategoryDropdown{{contact.request_category}}
Support TypeDropdown{{contact.support_type}}
Describe Your IssueMulti-line text{{contact.describe_your_issue}}
Attachment URLText{{contact.attachment_url}}
Support Ticket Number (scratchpad)Text{{contact.support_ticket_number}}

O Opportunity fields (the ticket record)

FieldTypeMerge keyFilled by
Ticket NumberText{{opportunity.ticket_number}}New seq
Ticket CategoryText{{opportunity.ticket_category}}copy
Ticket Support TypeText{{opportunity.ticket_support_type}}copy
Ticket IssueMulti-line text{{opportunity.ticket_issue}}copy
Ticket Attachment URLText{{opportunity.ticket_attachment_url}}copy
Ticket Additional Info NeededMulti-line text{{opportunity.ticket_additional_info_needed}}Agent
Ticket ResolutionMulti-line text{{opportunity.ticket_resolution}}Agent
Ticket Date ResolvedDate{{opportunity.ticket_date_resolved}}Resolved seq (auto)
Merge keys
GHL auto-generates each merge key from the field name (lowercased; spaces and symbols become underscores - a "/" becomes a double underscore). The keys above assume these exact names; after creating the fields, open each one and confirm its real key before using it in emails and workflows.
Tip
Also create two Custom Values: company_name = your business name and support_sla = e.g. "24-48 hours", so the emails stay brand-driven and the SLA changes in one place.
Step 3 · The ticket number

A unique ID per ticket

A Recommended - date-time ID (zero dependency)

In the New sequence, build the ticket number from GHL's date and time tags: TKT-{{right_now.date}}-{{right_now.time}}:{{right_now.second}} → TKT-06/17/2026-14:30:52. Unique to the second; no counter needed, and no premium formatter step.

B Optional - sequential (TKT-1001, 1002…)

A Custom Value counter plus a Math Operation step works, but two tickets arriving together can read the same counter value before either writes it back. The date ID has no such race, so it stays the default.

Gotcha
Use date and time, never the separate year / month / day / hour tags. Those are not zero-padded: 3 October at 05:25:44 becomes 2026103-52544, and 13 January and 3 November both become 2026113. {{right_now.date}} (10/03/2026) and {{right_now.time}} (05:41) are padded; the seconds are not, which is why they sit after their own colon.
Step 4 · The support form

The one piece built by hand

A GHL form named Submit a Support Ticket. GHL's form create/clone API is unreliable, so the form is built in the form builder - but every custom field it maps to was created in Step 2, so it's just dragging those fields in.

1
New FormCreate it
  • Sites → Forms → Builder → + Add Form
  • Name it exactly Submit a Support Ticket - the Intake router's trigger matches this name
2
Standard FieldsWho's contacting you
  • First Name + Last Name + Email (required), Phone (optional)
3
Custom FieldsDrag in the existing fields
  • Search each by name and drag it in - dropdown options auto-load from the field
  • Request Category (required) · Support Type (required) · Describe Your Issue (required, multi-line)
  • Attachment URL (optional, for a Loom / Drive / screenshot link)
4
File UploadLet them attach a screenshot
  • Add a File Upload field (optional); allow images / PDF / short video. Stored on the contact, surfaced in the team notification.
5
On-SubmitReassure them
  • Show message: "Thanks - your ticket is in. Our team will respond within {{custom_values.support_sla}}, and a confirmation email is on its way." (or redirect to a thank-you page)
6
Style + EmbedBrand it and ship it
  • Match your brand button color, then Save → Integrate → embed on a /support page or wherever customers reach support

Request Category options (example)

  • Billing & payments
  • Technical issue / bug
  • Account & access
  • Onboarding & setup
  • Product question
  • Other

Swap these for your own products, departments, or service lines - the router reads whatever you put here.

Support Type options (example)

  • Booking or reschedule
  • Payment or billing
  • Login / access
  • Feature or how-to
  • General question
  • Other

A second axis so the team can triage by intent, independent of category.

Step 5 · Email templates

Five branded emails

Subjects and bodies use merge tags so each ticket personalizes itself. Set the From name/email to [Company] Support <support@yourcompany.com> and Reply-To to the same inbox, so customer replies thread back and trigger the reply handler. The first four are below; Email 5 lives with the Intake router in Step 8.

New seq - on submitEmail 1 - Ticket Received · We've got it - Support Ticket #{{opportunity.ticket_number}}

Hi {{contact.first_name}},

Thanks for reaching out. We've received your request and opened Support Ticket #{{opportunity.ticket_number}}.

Our team is on it and will get back to you within {{custom_values.support_sla}}.

Need to add something? Just reply to this email and it'll attach straight to your ticket.

- The {{custom_values.company_name}} Support Team

Awaiting Reply seqEmail 2 - Additional Info Needed · One quick thing on Ticket #{{opportunity.ticket_number}}

Hi {{contact.first_name}},

We're working on Ticket #{{opportunity.ticket_number}} and need one more thing from you to move it forward:

What we need from you
{{opportunity.ticket_additional_info_needed}}

Just reply to this email with that and we'll pick it right back up.

Resolved seqEmail 3 - Resolved · Resolved - Support Ticket #{{opportunity.ticket_number}}

Hi {{contact.first_name}},

Good news - Ticket #{{opportunity.ticket_number}} has been resolved.

Resolution
{{opportunity.ticket_resolution}}

If anything still isn't right, just reply to this email and we'll reopen it. Thanks for your patience.

Awaiting Reply seq - after 3 daysEmail 4 - Reminder · Still need a quick reply - Ticket #{{opportunity.ticket_number}}

Hi {{contact.first_name}},

We're holding Ticket #{{opportunity.ticket_number}} open and still waiting on one thing from you:

What we need from you
{{opportunity.ticket_additional_info_needed}}

Reply whenever you have a moment and we'll finish up. If we don't hear back, we'll close the ticket in a couple of days - you can always reply to reopen it.

Reusable HTML shell (Gmail-safe - paste into GHL, swap the copy)

Ticket TKT-06/17/2026-14:30:52 is open. Reply to this email to add details.
Linden Goods Support
Support ticket Received
TKT-06/17/2026-14:30:52
Category Billing & payments
Request type Payment or billing
Expected reply Within 24 hours
         
Received In progress Resolved

Hi Priya,

Thanks for reaching out. Your request is in, and the Linden Goods team will pick it up within 24 hours. Here is what you sent us, for reference.

Your request
I was charged twice for order 48213 on June 16. Can you refund the duplicate charge?
Reply to this email to add details or a screenshot. Your reply attaches to ticket TKT-06/17/2026-14:30:52, so there is no need to open a new one.
Linden Goods Support - you are receiving this because you opened a support ticket.
Sample ticket for a fictional store. The Code tab is the same shell with merge tags - per email, swap the status word, the current step, and the body copy.
EMAIL-SHELL.HTML
<!-- STATUS: swap the pill word per email - Received / Awaiting your reply / Resolved.
     PROGRESS: move the navy bar + bold label to the current step (Received / In progress / Resolved). -->
<div style="display:none;max-height:0;overflow:hidden;mso-hide:all;font-size:1px;line-height:1px;color:#F4F4ED;opacity:0;">Ticket {{opportunity.ticket_number}} is open. Reply to this email to add details.</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#F4F4ED;">
  <tr>
    <td align="center" style="text-align:center;padding:32px 12px 40px;">
      <!--[if mso]><table role="presentation" width="600" align="center" cellpadding="0" cellspacing="0" border="0"><tr><td><![endif]-->
      <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="max-width:600px;margin:0 auto;text-align:left;background:#FFFFFF;border:1px solid #E6E6DE;border-radius:12px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;">
        <!-- Header: wordmark + mailbox name -->
        <tr>
          <td style="padding:22px 28px;border-bottom:1px solid #EDEDE6;">
            <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
              <tr>
                <td style="font-size:16px;line-height:20px;font-weight:700;letter-spacing:-0.2px;color:#0B1E3F;">{{custom_values.company_name}}</td>
                <td align="right" style="text-align:right;font-size:13px;line-height:20px;color:#5A6479;">Support</td>
              </tr>
            </table>
          </td>
        </tr>
        <!-- Ticket identity -->
        <tr>
          <td style="padding:28px 28px 0;">
            <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
              <tr>
                <td style="font-size:12px;line-height:24px;font-weight:600;letter-spacing:0.8px;text-transform:uppercase;color:#5A6479;">Support ticket</td>
                <td align="right" style="text-align:right;line-height:24px;">
                  <span style="display:inline-block;padding:3px 11px;border-radius:999px;background:#FFF1E6;font-size:12px;line-height:18px;font-weight:600;color:#A84300;white-space:nowrap;"><span style="display:inline-block;width:6px;height:6px;border-radius:3px;background:#FF7A1A;vertical-align:middle;margin:0 6px 2px 0;"></span>Received</span>
                </td>
              </tr>
              <tr>
                <td colspan="2" style="padding-top:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,'Liberation Mono',monospace;font-size:22px;line-height:30px;font-weight:600;letter-spacing:-0.3px;color:#0B1E3F;">{{opportunity.ticket_number}}</td>
              </tr>
            </table>
            <!-- Meta rows -->
            <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin-top:20px;font-size:14px;line-height:20px;">
              <tr>
                <td style="padding:10px 0;border-top:1px solid #EDEDE6;color:#5A6479;">Category</td>
                <td align="right" style="text-align:right;padding:10px 0;border-top:1px solid #EDEDE6;color:#0B1E3F;font-weight:600;">{{opportunity.ticket_category}}</td>
              </tr>
              <tr>
                <td style="padding:10px 0;border-top:1px solid #EDEDE6;color:#5A6479;">Request type</td>
                <td align="right" style="text-align:right;padding:10px 0;border-top:1px solid #EDEDE6;color:#0B1E3F;font-weight:600;">{{opportunity.ticket_support_type}}</td>
              </tr>
              <tr>
                <td style="padding:10px 0;border-top:1px solid #EDEDE6;border-bottom:1px solid #EDEDE6;color:#5A6479;">Expected reply</td>
                <td align="right" style="text-align:right;padding:10px 0;border-top:1px solid #EDEDE6;border-bottom:1px solid #EDEDE6;color:#0B1E3F;font-weight:600;">Within {{custom_values.support_sla}}</td>
              </tr>
            </table>
            <!-- Progress: Received > In progress > Resolved -->
            <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin-top:22px;">
              <tr>
                <td width="32%" style="height:3px;line-height:3px;font-size:0;background:#0B1E3F;border-radius:2px;">&nbsp;</td>
                <td width="2%" style="font-size:0;line-height:3px;">&nbsp;</td>
                <td width="32%" style="height:3px;line-height:3px;font-size:0;background:#E4E4DC;border-radius:2px;">&nbsp;</td>
                <td width="2%" style="font-size:0;line-height:3px;">&nbsp;</td>
                <td width="32%" style="height:3px;line-height:3px;font-size:0;background:#E4E4DC;border-radius:2px;">&nbsp;</td>
              </tr>
              <tr>
                <td style="padding-top:8px;white-space:nowrap;font-size:12px;line-height:16px;font-weight:700;color:#0B1E3F;">Received</td>
                <td></td>
                <td style="padding-top:8px;white-space:nowrap;font-size:12px;line-height:16px;color:#5A6479;">In progress</td>
                <td></td>
                <td style="padding-top:8px;white-space:nowrap;font-size:12px;line-height:16px;color:#5A6479;">Resolved</td>
              </tr>
            </table>
          </td>
        </tr>
        <!-- Body copy slot -->
        <tr>
          <td style="padding:28px 28px 4px;font-size:16px;line-height:26px;color:#243A5E;">
            <!-- EMAIL BODY COPY GOES HERE -->
            <p style="margin:0 0 16px;">Hi {{contact.first_name}},</p>
            <p style="margin:0;">Thanks for reaching out. Your request is in, and the {{custom_values.company_name}} team will pick it up within {{custom_values.support_sla}}. Here is what you sent us, for reference.</p>
          </td>
        </tr>
        <!-- CALLOUT (optional): relabel per email - "What we need from you" + {{opportunity.ticket_additional_info_needed}}, or "Resolution" + {{opportunity.ticket_resolution}}. Delete this row if unused. -->
        <tr>
          <td style="padding:8px 28px 24px;">
            <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#F4F4ED;border-radius:10px;">
              <tr>
                <td style="padding:16px 18px;">
                  <div style="font-size:12px;line-height:16px;font-weight:600;letter-spacing:0.8px;text-transform:uppercase;color:#5A6479;">Your request</div>
                  <div style="padding-top:6px;font-size:15px;line-height:23px;color:#0B1E3F;">{{opportunity.ticket_issue}}</div>
                </td>
              </tr>
            </table>
          </td>
        </tr>
        <!-- Reply hint: a reply threads onto this ticket -->
        <tr>
          <td style="padding:18px 28px 22px;border-top:1px solid #EDEDE6;font-size:14px;line-height:21px;color:#5A6479;">
            <strong style="color:#0B1E3F;font-weight:600;">Reply to this email</strong> to add details or a screenshot. Your reply attaches to ticket <span style="white-space:nowrap;">{{opportunity.ticket_number}}</span>, so there is no need to open a new one.
          </td>
        </tr>
      </table>
      <!-- Footer -->
      <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="max-width:600px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;">
        <tr>
          <td align="center" style="text-align:center;padding:20px 28px 0;font-size:12px;line-height:18px;color:#5A6479;">{{custom_values.company_name}} Support - you are receiving this because you opened a support ticket.</td>
        </tr>
      </table>
      <!--[if mso]></td></tr></table><![endif]-->
    </td>
  </tr>
</table>
Step 6 · Category routing

Tag the category, ping the team

The form captures Request Category, and the New sequence's router branch reads it: every category gets its own tag so the board stays sortable. The tag is what's category-specific; the owners (your support team) are constant. The customer emails stay shared across all categories.

CategoryTag addedRouting branch
Billing & paymentssupport-billingtag → notify support team
Technical issuesupport-technicaltag → notify support team (+ optional dev escalation)
Account & accesssupport-accesstag → notify support team
Onboarding & setupsupport-onboardingtag → notify support team
Product questionsupport-producttag → notify support team
Other (catch-all)support-generaltag → notify support team (the else so nothing is unrouted)
Why a router, not a pipeline per category
One Support pipeline keeps every ticket on one board the team works from. The category drives the tag (so the board stays sortable + a category-specific step can fire), while the stage drives the customer emails. If a single category later needs heavier automation (e.g. auto-grant access, or a dev-team handoff), split that branch out into its own standalone workflow triggered by its tag - the engine doesn't change.
Step 7 · One automation per stage

Eight workflows, each editable on its own

Every stage is its own workflow, triggered by Pipeline Stage Changed filtered to this pipeline + stage. The New sequence has no trigger of its own - the Intake router (Step 8) owns the form and starts it with Add to Workflow. Drag a card to a stage and that stage's sequence fires - nothing overlaps. One cross-stage reply handler keeps the stages honest, and a small Reopen window timer tells it how long a resolved ticket can still be reopened. Build each as a Draft, then publish after a test ticket runs clean. Click a workflow to expand its steps.

Open the interactive diagram
1
New sequenceNew
Trigger: none - the Intake router starts it with Add to Workflow
1
Remove From WorkflowEnd any old Awaiting Reply timer
  • Workflow: Awaiting Reply sequence
  • Only matters when the last ticket was marked Lost while it waited on the customer - otherwise that old timer would block this ticket's Email 2
2
Update Contact FieldGenerate the ticket number
  • Field: Support Ticket Number
  • Value: TKT-{{right_now.date}}-{{right_now.time}}:{{right_now.second}} → e.g. TKT-06/17/2026-14:30:52 (see Step 3)
3
Create OpportunityOpen the ticket as its own opportunity
  • Pipeline: Support Tickets  |  Stage: New  |  Status: Open
  • Name: {{contact.first_name}} {{contact.last_name}} - {{contact.support_ticket_number}}
  • Workflow settings: "Allow multiple opportunities" = ON, and the sub-account switch below it - see Repeat tickets further down
4
Update Opportunity FieldCopy the form data onto the ticket record
  • Ticket Number = {{contact.support_ticket_number}}
  • Ticket Category = {{contact.request_category}}
  • Ticket Support Type = {{contact.support_type}}
  • Ticket Issue = {{contact.describe_your_issue}}
  • Ticket Attachment URL = {{contact.attachment_url}}
5
Send EmailConfirm receipt to the customer
  • Template: 01 - Ticket Received
  • From + Reply-To: support@yourcompany.com (so replies thread back and fire the reply handler)
6
If / Else - Category routerTag the category and ping the team
  • Condition on {{contact.request_category}} - one branch per category, each does Add Contact Tag + Internal Notification to the support team
  • Billing → support-billing · Technical → support-technical · Access → support-access …
  • Else → support-general so nothing is unrouted
  • Notification subject: New ticket {{opportunity.ticket_number}} - {{opportunity.ticket_category}} / {{opportunity.ticket_support_type}}

The only stage a ticket enters automatically. Build order matters: release → number → opportunity → copy fields → email → router. Everything below is manual-drag triggered. GHL note: turn on Allow Re-Entry - a returning customer enters this workflow once per ticket.

2
Urgent sequenceUrgent
Trigger: Pipeline Stage Changed - Pipeline "Support Tickets", Stage = Urgent
1
Add Contact TagFlag it
  • Tag: priority-urgent
2
Internal Notification - EmailAlert the team
  • Subject: URGENT - Ticket {{opportunity.ticket_number}} ({{opportunity.ticket_category}})
3
Internal Notification - SMS - optionalSame alert, second channel
  • Body: URGENT ticket {{opportunity.ticket_number}} needs eyes now.
  • Off by default - add it once the team's phones are set up to receive internal SMS

No customer email - Urgent is internal priority only. Agent SOP: drag any New / In Progress ticket here to escalate. No task either: the Support Tickets board is the work queue, and a task per stage entry stacks duplicates every time a ticket comes back.

3
In Progress sequenceIn Progress
Trigger: Pipeline Stage Changed - Pipeline "Support Tickets", Stage = In Progress
1
Remove From WorkflowEnd the Awaiting Reply timer
  • Workflow: Awaiting Reply sequence - the customer answered, so the reminder and auto-close no longer apply
2
Remove Contact TagClear stale state
  • Remove priority-urgent (it's now actively worked, not just flagged)
3
Send Email - optional"We're on it" touch
  • Off by default to avoid over-emailing. Turn on only if tickets tend to sit before pickup.

This is also where the Reply handler and the Intake router land a ticket the customer just answered, or a reopened one - and both already notify the team. GHL note: turn on Allow Re-Entry in this workflow's settings - a ticket can enter In Progress more than once (after a reopen or a reply), and with re-entry off the second entry won't fire.

4
Awaiting Reply sequenceAwaiting Reply
Trigger: Pipeline Stage Changed - Pipeline "Support Tickets", Stage = Awaiting Reply
1
Send EmailAsk for the missing info
  • Template: 02 - Additional Info Needed - pulls {{opportunity.ticket_additional_info_needed}}
2
WaitGive them 3 days
  • Wait: 3 days
  • If the customer answers first, the In Progress sequence removes them from this workflow, so the rest never runs
3
If / ElseOnly remind if still silent
  • If still Awaiting Reply → Send Email 04 - Reminder; else do nothing
4
WaitGive 2 more days
  • Wait: 2 days, then the same stage check
5
If / Else - auto-closeNo response = close politely
  • If still Awaiting Reply → set Ticket Resolution = Closed - no response. Reply to reopen.
  • then move Stage to Resolved (hands off to the Resolved sequence)

GHL note: a contact still inside this workflow's wait is skipped when a ticket enters Awaiting Reply again - Allow Re-Entry only applies once the earlier run has ended. Ask a customer for more info twice in five days and the second Email 2 silently never sends. That is why every way out of this stage (In Progress, Resolved, and a brand-new ticket) starts with Remove From Workflow: Awaiting Reply sequence. The If/Else checks stay as a second guard.

5
Resolved sequenceResolved
Trigger: Pipeline Stage Changed - Pipeline "Support Tickets", Stage = Resolved
1
Remove From WorkflowEnd the Awaiting Reply timer
  • Workflow: Awaiting Reply sequence - a ticket resolved straight from Awaiting Reply must not get a reminder
2
Remove From Workflow + Add Tag + Add to WorkflowOpen the 7-day reopen window
  • Remove from Reopen window, add tag support-reopen-window, then Add to Workflow Reopen window
  • Remove-then-add restarts the 7 days if a ticket is resolved again
3
Update Opportunity FieldStamp the close date
  • Ticket Date Resolved = {{right_now.middle_endian_date}} (today's date, for reporting)
4
Send EmailTell the customer it's done
  • Template: 03 - Resolved - pulls {{opportunity.ticket_resolution}}
5
Wait + Send - optionalAsk how it went
  • Wait 1 day → one-question CSAT. Off by default; turn on once the core flow is proven.

This stage also receives auto-closed tickets from the Awaiting Reply timeout (resolution already filled with the "no response" text).

+
Reply handlercross-stage
Trigger: Customer Replied (inbound) - add it twice: Email + SMS
1
Find OpportunityFind the ticket this reply belongs to
  • Matching values: Pipeline = Support Tickets  |  Opportunity to be found: Latest
  • Not Found → end. The contact has never had a ticket, so this is a normal sales reply
  • Found → that card becomes the workflow's opportunity, so the moves below hit the ticket, never a sales card
2
If / ElseBranch on the ticket's current stage
  • If Awaiting Reply → move to In Progress + notify: Customer replied with the info
  • Else-if Resolved and the contact has tag support-reopen-window → remove the tag, Remove from Reopen window, move to In Progress + notify: Ticket reopened
  • Else-if Resolved longer ago, or the ticket is Lost / Won → end. The reply isn't about a live ticket
  • Else (New, Urgent, In Progress) → notify only: New reply on the ticket

Why the lookup: Customer Replied knows the contact, not the ticket. One open ticket per customer (Step 8) is what makes "latest in Support Tickets" always the right card. The 7-day window stops a reply to a sales email months later from reopening an old ticket - past that, the customer submits the form and gets a fresh one. GHL note: don't gate this on a date condition. An If/Else "in the last 7 days" on an opportunity date field saves fine but never evaluates true; the tag is checked reliably.

+
Reopen windowResolved
Trigger: none - the Resolved sequence starts it with Add to Workflow
1
WaitKeep the window open
  • Wait: 7 days
2
Remove Contact TagClose it
  • Remove support-reopen-window - a reply after this ends quietly

Why its own workflow: the wait can't sit inside the Resolved sequence. A contact still waiting there would be skipped the next time one of their tickets is resolved - no Email 3. Turn on Allow Re-Entry.

Step 8 · The front door

One open ticket per customer

Every form submission goes through the Intake router first. It looks the customer up in the Support pipeline itself: no open ticket, a new one opens; an open ticket already there, the new message is added to it - no second card, nothing thrown away, and the customer hears back straight away.

Why one
Customer Replied knows the contact, not the ticket. The reply handler has to look the ticket up, and "the latest card in Support Tickets" is only the right card when a customer has one open ticket. The priority-urgent tag lives on the contact too. One open ticket per customer keeps both honest.
Nothing to unlock
The router reads the pipeline, not a flag. Resolve a ticket, mark it Lost, or delete the card, and the customer's next submission opens a fresh ticket on its own. There is no tag to clear and no way to lock a customer out.
Open the interactive diagram
+
Intake routerfront door
Trigger: Form Submitted - Form is "Submit a Support Ticket"
1
Find OpportunityLook for this customer's latest ticket
  • Matching values: Pipeline = Support Tickets  |  Opportunity to be found: Latest
  • Two branches come out of it: Opportunity Found and Opportunity Not Found. The found card becomes the workflow's opportunity, so the steps below read and move that ticket
2
Not Found - Add to WorkflowFirst ticket: open it
  • Workflow: NEW sequence. One step, no hand-off tag to set or clear
3
Found - If / ElseIs that ticket still open?
  • Closed when Opportunity Status is not Open (Lost, Won, Abandoned) or Pipeline Stage is Resolved → Add to Workflow NEW sequence
  • Open otherwise → continue with steps 4-7
  • Check both: Resolved is a stage, so a resolved ticket still has status Open
4
Add NotePut the new message on the ticket
  • Follow-up on {{opportunity.ticket_number}} ({{contact.request_category}} / {{contact.support_type}}): {{contact.describe_your_issue}} {{contact.attachment_url}}
  • The note lands on the customer's timeline, which the agent sees from the ticket card
5
Internal NotificationTell whoever is working it
  • To: the assigned user (fall back to the support team). Subject: Follow-up on open ticket {{opportunity.ticket_number}}
6
Send EmailClose the loop with the customer
  • Template: 05 - Added to Your Ticket (below)
7
If / Else - Update OpportunityThey answered our question
  • If stage is Awaiting Reply → move the opportunity to In Progress. That ends the reminder timer, exactly like an email reply

Why a router in front of the New sequence? The New sequence stays one straight path that only ever opens tickets. The router owns the form trigger and decides; Add to Workflow hands off in a single step, so there's no hand-off tag to get stuck. Test once: submit twice from the same email and confirm Email 5 shows the first ticket's number.

T What the router decides

Latest Support ticketRouter does
NoneOpens a new ticket (New sequence)
New Urgent In ProgressAdds the message to it, pings the owner, sends Email 5
Awaiting ReplySame, and moves it to In Progress - the customer just gave us what we asked for
Resolved, or marked Lost / Won / AbandonedOpens a new ticket
Intake router - open ticketEmail 5 - Added to Your Ticket · Added to your open ticket - #{{opportunity.ticket_number}}

Hi {{contact.first_name}},

Thanks for the extra detail. You already have an open ticket with us - #{{opportunity.ticket_number}} - so we've added this message to it instead of opening a new one.

The same team is on it, with everything you've sent in one place. Want to add more? Just reply to this email.

- The {{custom_values.company_name}} Support Team

Important setting

Repeat tickets

Every ticket is its own opportunity, so a returning customer ends up with several opportunities in the same Support pipeline. GHL blocks that by default, and it fails quietly:

Edge cases

All covered

CaseHandled by
Same customer opens 2+ ticketsTicket data on the opportunity = each ticket independent (no overwrite)
Customer submits again while a ticket is openIntake router adds the message to the open ticket, pings the owner, and sends Email 5 - no second card, nothing lost
Ticket marked Lost, Won, or deletedThe router reads the pipeline, so the next submission opens a fresh ticket - nothing to unlock
Customer replies to a sales email months after a ticket closedReply handler only reopens tickets resolved in the last 7 days
Double-click: two submissions in the same secondBoth can land before the first card exists and open two tickets. Rare - mark the duplicate Lost
Ticket is about a specific categoryThe New sequence's router tags the category + notifies the team
Customer replies during Awaiting ReplyReply handler moves it back to In Progress + notifies
Customer replies after ResolvedReply handler reopens to In Progress + notifies
Customer never replies (stuck)Awaiting Reply reminds at 3 days, auto-closes at 5
Agent asks for more info a second time within 5 daysIn Progress and Resolved end the old Awaiting Reply run first, so the second Email 2 always sends
No extra info needed (straight to fix)Agent fills Resolution, drags to Resolved; Awaiting Reply is optional
File / screenshot attachedForm file field stored on contact; link surfaced in the team notification
Ticket # uniquenessPadded date + time to the second; no two moments produce the same number
Make it yours

Customize & extend

Quick swaps

  • Category list + tags in the router to match your products / departments
  • SLA in support_sla (same-day, 24-48 hrs)
  • Support Type options as your offer mix changes
  • Voice of the five emails to match your brand

Optional add-ons

  • A CSAT one-question survey after Resolved
  • A self-serve deflection (link a help center before a ticket opens)
  • SLA-breach alert if New / In Progress sits unworked past X hours
  • A /support page embedding the form
Ship it

Build checklist

The mindset

The stage is the status. The opportunity is the ticket. Nobody writes a reply by hand.

Swap the brand, the categories, and the team - the engine stays exactly the same.