A support widget that already knows who it is talking to
- Published on
- • 4 mins read•--- views
A shop with its own help desk still has a gap: the shop and the desk do not know about each other. A customer with a question leaves the site, finds the desk separately, and opens a ticket as a stranger, retyping the name, email and phone the shop already has on file. The agent then spends two replies working out who they are talking to and which order this is about.
The vendor's answer is a snippet you paste into the theme. That closes half the gap and opens three new problems.
Problem one: the snippet is in the wrong place
Code in a theme template dies with the theme. A theme update, a switch to a child theme, a handover to a new developer, and tracking or support silently disappears. Nobody notices, because nothing errors. The widget simply is not there any more.
Worse, every setting is now a code edit. Colour, corner, language, which fields the form requires: all of them live in markup, so changing one means a deploy and a developer.
Move it into a plugin with a settings screen. This is unglamorous and it is the whole fix for this problem.
Problem two: identity has to be handed over
A widget that knows nothing produces a ticket that starts from zero. Passing the signed-in customer's details costs almost nothing:
$visitor = $user
? ['visitor_name' => $user->getName(),
'visitor_email' => $user->getEmail(),
'visitor_phone' => $user->getPhone()]
: [];
return [...$settings, ...$visitor];
Note the empty array. With no session the widget still renders, just without visitor data. This matters more than it sounds: an anonymous visitor is the one most likely to have a pre-sales question, and a widget that errors or hides for logged-out users removes support exactly where it is most valuable.
Problem three: a site-wide widget costs a query per page
Resolving the user and their meta on every render adds a database round trip to every page on the site, for a widget most visitors never open.
Cache it per user:
$cached = wp_cache_get('freescout_user_' . $user_id);
if ($cached) return unserialize($cached);
$dto = $this->loadFromDb($user_id);
if ($dto) wp_cache_set('freescout_user_' . $user_id, serialize($dto));
return $dto;
The key must include the user id. A shared key is how one customer's name ends up in another customer's chat widget, and that is a data leak rather than a performance bug. Whenever you cache something derived from who is asking, the identity belongs in the key. This is the single easiest way to turn a caching change into an incident.
Placement is a choice, not a default
Footer injection puts the widget on every page. A shortcode puts it on one. Both are legitimate: a shop wants it everywhere, a documentation site might want it only on the contact page.
Support both, and let them share the same identity code. The temptation is to build the site-wide version first and add the shortcode later by copying the render function, at which point you have two widgets that drift apart.
What to check before calling it done
Open the shop signed in and confirm the form starts pre-filled. Then open it in a private window and confirm the widget still appears, empty and working. The second check is the one people skip, and it is the one that catches the null-user crash.
Then look at what the widget is actually sending. It ships whatever you handed it to a third-party service, and "whatever you handed it" tends to grow. A phone number is reasonable for a support desk. An order history, an internal customer id or a loyalty tier probably went in because it was available, not because support needed it.
Open for contract collaboration
I am available for contract-based collaboration. If you have an interesting project idea, schedule a call via Calendly.
Schedule a 30-min call