---
title: "HootBox | Smart Popup Element & Popup Builder for YOOtheme Pro"
description: "Create smart popups in YOOtheme Pro with any content, flexible triggers, timing, frequency controls and targeting options. Try the live demo first."
url: "https://www.yoozecase.com/?item_id=28"
date: "2026-11-06T13:38:11+00:00"
language: "en-GB"
---

#  HootBox

Popup in da hoot!

**Latest Version**
1.0.1
**Requires**
YOOtheme Pro 5+, Joomla 5+, WordPress 6.5+, PHP 8.1 or newer
**Release date**
02 November 2026
**Made for**
YOOtheme Pro, Joomla, WordPress
**License**
GPL-2.0-or-later
**Price**
€15.00 (incl. 6 months of updates)

 [ 🛒 Get it for Joomla 👍 ](https://yoozecase.lemonsqueezy.com/checkout/buy/dafe534a-e23c-4e8a-85d3-471c792f73b5)

 [ 🛒 Get it for WordPress👍 ](https://yoozecase.lemonsqueezy.com/checkout/buy/ac73ea2f-a9dd-48e4-95a4-1720a40492ee)

 [ Demo page ](https://www.yoozecase.com/hootbox-demo)

The smart popup element for YOOtheme Pro. Place any content, then decide when it appears, how often, and who sees it.

##  What's new in version 1.0.1

- **Fixed:** Joomla: a licence key entered under System, Update Sites is now activated by the plugin itself
- **Fixed:** Joomla: the licence panel says why a key from Update Sites was not accepted
- **Fixed:** WordPress: switch for automatic updates also when HootBox is up to date

 ![Hootbox](https://www.yoozecase.com/files/hootbox.png)

 [](#)

 [](#)

---

##  Description

HootBox is the smart popup element for YOOtheme Pro. You build the popup in the builder like any other section, with text, images, video, a form or a module inside it, and then you decide the part that actually matters: when it appears, how often, and who gets to see it.

A popup does not have to be a box in the middle of the screen. Pick a centred dialog when you need an answer, a bar for an announcement, a corner panel for a quiet hint, a side panel for a form, or a full screen welcome mat. Ten placements in total, each one using the card styles of your theme, so it matches the rest of the site without a line of extra CSS.

Timing is where a smart popup earns its keep. Open on page load after a delay, at a scroll depth, when a chosen element comes into view, on exit intent, after a period of inactivity, or on a click on any link or button you point at. Give it a start and an end date, weekdays and a daily time window, and the Christmas banner disappears on the twenty sixth without anyone logging in.

Then decide how often. Every page load, once per session, once per visitor, or again after a number of minutes, hours or days. Cap the number of views, choose whether a view counts on open, on close or only after a button click, and add a do not show again checkbox for the people who mean it. You decide how long that checkbox holds: the browser session, a number of days or a year. All of it is kept in the visitor's browser: no cookies and nothing sent to our servers. NOTICE.txt in the download describes it, ready to copy into your privacy statement. If your site runs Google Tag Manager, HootBox passes open, close and button clicks to it, so you can count them there.

A closed popup does not have to be gone. Switch on the teaser and a small tab stays in a corner of the screen, so a visitor who closed the offer too fast can open it again on any later page. A button in the popup can also open a second HootBox: ask a short question first and show the form only to the people who say yes.

Show it to guests only or to logged in users only, limit it to phones or desktops, give phones a bottom bar instead of a blocking modal, and match the language of the page, a campaign parameter in the address or the site the visitor came from.

HootBox also knows when to wait. While a cookie banner is on screen, the automatic triggers hold back until the visitor has made a choice, it recognises the consent manager of YOOtheme Pro and the banners of Cookiebot, CookieYes, Complianz, OneTrust and Cookie Consent. With several popups on a page, one waits for the other instead of stacking on top of it, and the priority decides which goes first. A popup placed site-wide in the footer can be limited to /blog or kept away from /checkout with one line per page. A video or map inside the popup loads only when the popup opens and stops when it closes.

Built with Google's guidance on intrusive interstitials in mind. One setting gives phones a bottom bar instead of a blocking popup, and the help text next to each timing field says what is safe. A blocking popup is a real dialog with focus handling and an Escape route, and visitors who ask their system for less motion get no animation at all.

The same element and the same behaviour on Joomla and on WordPress. Each edition has its own licence, so buy the one for the CMS your site runs on. The settings panel in the builder follows the language of your administrator account: English, Dutch, German, French, Spanish or Italian.

##  Feature request

 ### What is the next feature for this plugin?

Submit your idea or vote on existing ones.

  #### Top voted

   Website (leave empty)

'; } html += ''; if (oc.title) { html += '##### ' + escapeHtml(oc.title) + '

'; } if (oc.text) { html += '' + escapeHtml(oc.text).replace(/\\n/g, '
') + '

'; } html += '

'; } // Auto winner(s) - top N from the already vote-sorted features if (mode === 'auto_winner' || mode === 'both') { var n = oc.winnerCount | 0; if (n '; for (var i = 0; i ' + (w.image_url ? '![](https://www.yoozecase.com/'%20+%20escapeHtml(w.image_url)%20+%20')

' : '') + '' + (i + 1) + '' + '' + escapeHtml(w.title) + '' + '' + getIcon(this.cfg.voteIcon) + ' ' + (w.votes | 0) + '' + ''; } html += ''; } } if (html === '') { this.elOutcome.hidden = true; return; } this.elOutcome.innerHTML = html; this.elOutcome.hidden = false; }; /** * Roadmap view (v3.2.0): renders the items as status columns, in the * canonical status order from cfg.statuses. Each column is an 12. inside \* the existing .yc-fb-list , holding its own inner  of items - \* this keeps the delegated click/change handlers on elList working \* unchanged. Unknown statuses fall back into the "open" column so no item \* can silently disappear. Empty columns are skipped when \* cfg.roadmapHideEmpty is set. \*/ Board.prototype.renderRoadmap = function (items) { var stats = (this.cfg.statuses && this.cfg.statuses.length) ? this.cfg.statuses : \['open', 'planned', 'in\_progress', 'shipped', 'declined'\]; var labels = this.cfg.statusLabels || {}; var groups = {}; var i, st; for (i = 0; i ' + '##### ' + '' + escapeHtml(labels\[sv\] || sv) + '' + arr.length + '' + '

    ' + '' + itemsHtml + '
    ' + '
'; } return html; }; Board.prototype.renderItem = function (f, index) { var disabled = this.cfg.isClosed ? 'disabled' : ''; var voted = f.voted ? ' yc-voted' : ''; var descHtml = f.description ? '' + escapeHtml(f.description).replace(/\\n/g, '
') + '

' : ''; var thumbHtml = f.image_url ? '![](https://www.yoozecase.com/'%20+%20escapeHtml(f.image_url)%20+%20')

' : ''; // Status workflow (v3.0.0): badge for any non-default status. The // roadmap view (v3.2.0) skips the badge - the column already // communicates the status, a badge would be redundant noise. var st = (f.status || 'open'); var labels = this.cfg.statusLabels || {}; var statusBadge = (!this.isRoadmap && st && st !== 'open' && labels[st]) ? '' + escapeHtml(labels\[st\]) + '' : ''; // Design themes (v3.1.0): arena / neon / pixel render extra per-item // furniture - rank number, avatar (image or initials), a "leader" pill // on rank 1, and a "votes" label. Classic/colour themes are untouched. var t = this.cfg.theme || 'light'; var themed = (t === 'arena' || t === 'neon' || t === 'pixel'); var rank = (typeof index === 'number') ? (index + 1) : 0; var rankHtml = '', leaderHtml = '', avatarHtml = '', votesLabel = ''; if (themed) { rankHtml = '' + pad2(rank) + ''; if (rank === 1) { leaderHtml = '' + escapeHtml(this.cfg.leaderLabel || 'Leader') + ''; } if (f.image_url) { avatarHtml = '![](https://www.yoozecase.com/'%20+%20escapeHtml(f.image_url)%20+%20')

'; } else { avatarHtml = '' + escapeHtml(initialsOf(f.title)) + '

'; } votesLabel = '' + escapeHtml(this.cfg.votesLabel || 'votes') + ''; } // Moderation overlay: subtle corner buttons, only when canModerate. // Buttons appear on hover/focus (styled in CSS). Even in customizer // preview we render them when canModerate is true so the moderator // can see what visitors with moderation rights will see. var modHtml = ''; if (this.cfg.canModerate) { var parts = []; var hideTitle = escapeHtml(this.t('hideTitle', 'Hide this feature')); var deleteTitle = escapeHtml(this.t('deleteTitle', 'Delete this feature')); if (this.cfg.allowSoftHide) { parts.push( '' ); } if (this.cfg.allowHardDelete) { parts.push( '' ); } if (parts.length || (this.cfg.statuses && this.cfg.statuses.length)) { var sParts = parts.slice(); if (this.cfg.statuses && this.cfg.statuses.length) { var opts = ''; for (var si = 0; si ' + escapeHtml(sl) + ''; } var setStatus = escapeHtml(this.t('setStatus', 'Set status')); sParts.push( '' ); } modHtml = '' + sParts.join('') + '

'; } } var mediaHtml = themed ? avatarHtml : thumbHtml; return ( '32. ' + leaderHtml + rankHtml + '' + mediaHtml + '' + '##### ' + escapeHtml(f.title) + '

    ' + statusBadge + descHtml + '

    ' + modHtml + '
' ); }; /** * Detects whether a JSON response looks like a CSRF/token rejection. * Two cases: * 1. com_ajax-level rejection (Joomla's auto-token-check on POST): top * level message contains the JINVALID_TOKEN_NOTICE text and HTTP * status is typically 403. * 2. Plugin-level rejection from requireToken(): payload.error === "Invalid token". */ Board.prototype.isTokenError = function (status, raw, payload) { if (status === 403) return true; var hay = ''; try { hay = JSON.stringify(raw || {}) + '|' + JSON.stringify(payload || {}); } catch (e) {} return /invalid (security )?token/i.test(hay); }; /** * Fetch a fresh CSRF token from the server. Updates this.cfg.csrfToken so * the next call (and any future calls in this Board's lifetime) use the * correct one. Cache-bust with a timestamp so no upstream CDN caches the * response. */ Board.prototype.fetchFreshToken = function () { var self = this; var url = this.cfg.ajaxUrl + '&task=token&_=' + Date.now(); return fetch(url, { method: 'GET', credentials: 'same-origin', headers: { 'Accept': 'application/json', 'Cache-Control': 'no-cache' } }) .then(function (r) { return r.json(); }) .then(function (data) { var p = self.unwrapAjax(data); if (p && p.ok === true && typeof p.token === 'string' && p.token.length >= 16) { self.cfg.csrfToken = p.token; return p.token; } throw new Error('No fresh token in response'); }); }; /** * Generic POST-with-token helper. Builds FormData from a plain object, * adds the current token, posts. On 403 / token-error response, fetches a * fresh token and retries exactly once. * * @param {string} task The task name (appended to URL and body) * @param {object} fields Key-value form fields (no token) * @return {Promise} Resolves to the unwrapped payload on success. \* Rejects with Error containing .reason on failure. \*/ Board.prototype.postWithToken = function (task, fields) { var self = this; function attempt(token) { var fd = new FormData(); fd.append('task', task); for (var k in fields) { if (Object.prototype.hasOwnProperty.call(fields, k)) { fd.append(k, fields\[k\]); } } fd.append(token, '1'); return fetch(self.cfg.ajaxUrl + '&task=' + encodeURIComponent(task), { method: 'POST', credentials: 'same-origin', body: fd, headers: { 'Accept': 'application/json' } }).then(function (r) { return r.json().then(function (data) { return { status: r.status, raw: data, payload: self.unwrapAjax(data) }; }); }); } return attempt(this.cfg.csrfToken).then(function (res) { // Success path if (res.payload && res.payload.ok === true) { return res.payload; } // Token rejection? Fetch fresh and retry once. if (self.isTokenError(res.status, res.raw, res.payload)) { return self.fetchFreshToken().then(function (fresh) { return attempt(fresh).then(function (res2) { if (res2.payload && res2.payload.ok === true) { return res2.payload; } var err = new Error( (res2.payload && res2.payload.error) || (res2.raw && res2.raw.message) || self.t('requestFailed', 'Request failed.') ); err.payload = res2.payload || null; throw err; }); }); } // Some other error - surface it var err = new Error( (res.payload && res.payload.error) || (res.raw && res.raw.message) || self.t('requestFailed', 'Request failed.') ); err.payload = res.payload || null; throw err; }); }; Board.prototype.handleSubmit = function () { var self = this; if (isInBuilder()) { this.showStatus(this.t('previewSubmit', 'Preview mode - submissions are disabled in the YOOtheme builder. Try this on the published page.'), 'info'); return; } if (this.cfg.isClosed) { this.showStatus(this.cfg.closedMessage || this.t('votingClosed', 'Voting is closed.'), 'info'); return; } var title = (this.elTitle && this.elTitle.value || '').trim(); var desc = (this.elDesc && this.elDesc.value || '').trim(); // Client-side validation: minimum title length. Server re-validates. var minLen = (this.cfg.minTitleLength | 0) || 2; if (title.length for hide/delete, remember parent + sibling for rollback. var li = btn.closest('li'); var parentEl = li ? li.parentNode : null; var nextEl = li ? li.nextSibling : null; if (li && (action === 'hide' || action === 'delete')) { li.remove(); } btn.disabled = true; var task = action === 'hide' ? 'moderate\_hide' : action === 'delete' ? 'moderate\_delete' : action === 'restore' ? 'moderate\_restore' : ''; if (!task) { btn.disabled = false; return; } this.postWithToken(task, { feature\_id: featureId }) .then(function (payload) { // Success - keep the UI as-is (already removed) and refresh // so vote counts, top box, and hidden-features panel reflect // the new state. self.refresh(); if (self.hiddenOpen) { self.hiddenLoaded = false; // force a refetch self.fetchHiddenFeatures(); } var msg = action === 'delete' ? self.t('deleted', 'Feature deleted.') : action === 'hide' ? self.t('hidden', 'Feature hidden.') : action === 'restore' ? self.t('restored', 'Feature restored.') : self.t('done', 'Done.'); self.showStatus(msg, 'success'); setTimeout(function () { self.hideStatus(); }, 3000); }) .catch(function (err) { // Rollback: re-insert the row if we removed it. if (li && parentEl) { if (nextEl) { parentEl.insertBefore(li, nextEl); } else { parentEl.appendChild(li); } } btn.disabled = false; self.showStatus(err && err.message ? err.message : self.t('modFailed', 'Moderation action failed.'), 'error'); }); }; Board.prototype.handleStatusChange = function (featureId, status, sel) { var self = this; if (!featureId || !status) return; sel.disabled = true; this.postWithToken('moderate\_status', { feature\_id: featureId, status: status }) .then(function () { self.refresh(); var label = (self.cfg.statusLabels && self.cfg.statusLabels\[status\]) ? self.cfg.statusLabels\[status\] : status; self.showStatus(self.tf('statusSet', 'Status: %s', label), 'success'); setTimeout(function () { self.hideStatus(); }, 3000); }) .catch(function (err) { sel.disabled = false; self.showStatus(err && err.message ? err.message : self.t('statusFailed', 'Could not set status.'), 'error'); }); }; /\*\* \* Toggle the hidden-features panel open/closed. First open triggers \* an AJAX fetch; subsequent toggles just hide/show the cached list. \*/ Board.prototype.toggleHiddenPanel = function () { if (!this.elHiddenList || !this.elHiddenLabel) return; var arrow = this.elHiddenToggle && this.elHiddenToggle.querySelector('.yc-fb-hidden-arrow'); if (this.hiddenOpen) { this.hiddenOpen = false; this.elHiddenList.hidden = true; this.elHiddenLabel.textContent = this.t('showHidden', 'Show hidden features'); if (arrow) arrow.classList.remove('yc-open'); return; } this.hiddenOpen = true; this.elHiddenList.hidden = false; this.elHiddenLabel.textContent = this.t('hideHidden', 'Hide hidden features'); if (arrow) arrow.classList.add('yc-open'); if (!this.hiddenLoaded) { this.fetchHiddenFeatures(); } }; /\*\* \* Fetch the hidden-features list via the moderator-only endpoint. \* Re-callable to refresh the list after a restore. \*/ Board.prototype.fetchHiddenFeatures = function () { var self = this; if (!this.elHiddenList) return; this.elHiddenList.innerHTML = '2. ' + escapeHtml(this.t('loading', 'Loading...')) + '
'; var url = this.cfg.ajaxUrl + '&task=moderate\_list\_hidden&board\_id=' + encodeURIComponent(this.cfg.boardId); fetch(url, { credentials: 'same-origin', headers: { 'Accept': 'application/json' } }) .then(function (r) { return r.json(); }) .then(function (data) { var payload = self.unwrapAjax(data); if (!payload || payload.ok !== true) { self.elHiddenList.innerHTML = '4. ' + escapeHtml((payload && payload.error) ? payload.error : self.t('hiddenLoadFailed', 'Failed to load hidden features.')) + '
'; return; } self.hiddenLoaded = true; var features = payload.features || \[\]; if (features.length === 0) { self.elHiddenList.innerHTML = '6. ' + escapeHtml(self.t('noHidden', 'No hidden features.')) + '
'; return; } var html = ''; for (var i = 0; i ' + escapeHtml(f.description).replace(/\\n/g, '
') + '' : ''; var restoreTitle = escapeHtml(self.t('restoreTitle', 'Restore this feature')); html += '9. ' + '' + '##### ' + escapeHtml(f.title) + '

    ' + descHtml + '' + escapeHtml(self.tf('votesMeta', '%s votes', f.votes | 0)) + '' + '

    ' + '' + '
'; } self.elHiddenList.innerHTML = html; }) .catch(function () { self.elHiddenList.innerHTML = '11. ' + escapeHtml(self.t('hiddenNetwork', 'Network error while loading hidden features.')) + '
'; }); }; function initAll(scope) { var roots = (scope || document).querySelectorAll('.yc-fb-root'); for (var i = 0; i ---

##  Changelog

---

 [ v1.0.1 02/11/2026 ](#) Fixed- Joomla: your licence key now works in either place you enter it, in the HootBox settings or as Download Key under System, Update Sites. A key that only sits under Update Sites used to give you updates while the licence panel said Not activated, with no daily check and no notice when the subscription ended. The plugin now activates that key by itself the next time you open the administrator. If the key is not accepted, the licence panel says why. A key you activated in the panel always wins.

- Joomla: the help of the licence field mentions Update Sites, in all six languages.
- WordPress: the plugin list shows the switch for automatic updates also when HootBox is up to date. Until now it only appeared while an update was waiting.

 [ v1.0.0 25/10/2026 ](#) Added- First stable release. HootBox has been in public beta since 0.9.0 (5 September 2026) and is now feature complete for 1.0: ten placements with the card styles of your theme, an optional alert header with two buttons, your own builder content inside the popup, six triggers, a period, weekdays and a daily window, frequency per visitor with a do not show again checkbox, a teaser that brings a closed popup back, a button that opens a second HootBox, a queue so popups never stack, waiting for the cookie banner, page rules, phone layouts, and a settings panel in six languages. The same element and the same settings on Joomla 5 and 6 and on WordPress 6.5 and later.

 Changed- When a different licence key is activated and the previous one cannot be released, the panel now says so.
- The help of Close button position says when the X really sits outside the popup, in all six languages.
- From this release on, version numbers follow semantic versioning strictly: a minor release adds features, a patch release only fixes.

 Fixed- A close button set to outside the card only sits above the card when the popup has an overlay and room above it. Without an overlay it showed a white X on a white page, and on a top bar or a top corner it fell above the screen; it now stays inside the card there.
- The keyboard focus ring on buttons and the close button is now white with a dark edge, so it shows on every background. On a primary button in a white card it was invisible.
- Licence panel: a screen reader now hears why an activation failed, and the keyboard focus stays on the button you pressed.
- Licence panel: Deactivate without a stored key no longer reports an error.
- Joomla: activating and deactivating the licence needs permission to edit plugins, not only to see the plugin list.

 [ v0.9.26 25/10/2026 ](#) Changed- A new popup now waits 5 seconds after the page loads instead of 3. Popups you already placed keep the delay they have.

- Activating a different licence key on a site that already has one now activates the new key first and only then releases the old activation. A typo in the new key no longer costs the site its working activation.
- Clearer help in the settings panel for Duration, Layout, Width, Style, Frequency, On phones, Priority, Escape key, In the editor, Show from, Weekdays and Daily from, in all six languages.
- The README describes the four tabs of the settings panel and all five events that HootBox passes to Google Tag Manager.

 [ v0.9.25 25/10/2026 ](#) Changed- Requirements now say YOOtheme Pro 5.0 or later; that is the version HootBox is tested with.

- WordPress: tested up to WordPress 7.1.

 Security- The message of a popup no longer accepts form fields (form, input, button and the like). A message that comes from a field an author fills in can no longer show a fake form. Put a real form in the popup as a builder element.
- WordPress: the licence key sent with the update download is never passed on to a redirect.

 [ v0.9.24 25/10/2026 ](#) Security- Button links: a link with a hidden tab or line break inside "javascript:" is now blocked like any other script link. Before, such a link could get through when a button link came from a field that authors fill in (dynamic content).

- Custom CSS of a popup: a < can no longer end the style block, an @import can no longer slip back in, and CSS with an extra } is not used. The builder says when that happens.
- The licence settings only receive the fields they show, not the whole answer of the licence server.

 [ v0.9.23 25/10/2026 ](#) Changed- WordPress: Dashboard, Updates shows HootBox as tested with your WordPress version when the release was tested with that version.

- The help text of Cookie banner says that a visitor who leaves the banner unanswered does not get the automatic popup. The help text of Popup ID says that a duplicated HootBox needs its own ID.

 Fixed- A licence that expired and was renewed comes back to Active by itself within a day. Until now it stayed expired until you activated it again.
- A short outage of the licence server no longer marks your licence as not valid. The last known status stays and the plugin asks again later.
- Deactivate clears the licence on this site only when the licence server confirms it. During an outage the key stays, so you can try again.
- Pressing Activate again with the same key no longer takes a second activation.
- Two copies of the same HootBox on one page no longer share what visitors closed: ticking one away hid the other too. In the builder, a popup that has the same ID as another one on the page now says so.
- Joomla: pressing Enter in the licence key field activates the key instead of submitting the options form.

 Security- The licence key no longer appears in the HTML of the licence settings, on Joomla and on WordPress. The field shows the last four characters of the stored key; to change the key, paste a new one and press Activate.

 [ v0.9.22 23/10/2026 ](#) Fixed- Joomla: after pressing Activate, the licence panel showed Not activated and The key could not be activated, even when the key had been activated. The panel now shows Active as soon as the licence server accepts the key. Do not press Save while the panel says Not activated: in versions up to 0.9.21 that could empty the licence.

- Joomla: an update of HootBox emptied the Download Key that you entered yourself under System, Update Sites. That key now stays as long as the licence is not activated in HootBox. Activating in HootBox still fills it with the activated key, and deactivating clears it.

 [ v0.9.21 23/10/2026 ](#) Fixed- In Dutch and French the button groups on the Content tab showed in English, because YOOtheme does not translate those two words in every language. They are now called Main button and Second button and are translated in all six languages.

 [ v0.9.20 21/10/2026 ](#) Changed- The settings panel now has four tabs: Content, Layout, Display and Advanced. Display holds everything that was under Triggers and Rules, in the same order. With five tabs the tab bar ran over the HootBox heading in Dutch, French, Spanish and Italian.

 Fixed- In a translated builder, HootBox also changed words in other elements. Source, Advanced, Content and 42 more words that YOOtheme translates itself now keep the translation of YOOtheme, in every element and in the HootBox panel.
- The help texts and the hint in an empty popup name the builder buttons the way YOOtheme shows them in your language, for example Nieuwe lay-out and Bibliotheek in Dutch.

 [ v0.9.19 21/10/2026 ](#) Added- The settings panel of HootBox in the builder now follows the language of your administrator account: English, Dutch, German, French, Spanish or Italian. Labels, options, group headings and the help texts are translated. Hover over a label to read what a setting does.

 Changed- A tidier settings panel. Each button has its own group, the triggers are split by kind (page load, scrolling, leaving and inactivity, cookie banner, click, period, every week), and the options of Do not show again sit together. Shorter labels, so none are cut off in the narrow panel.

 Fixed- The Content tab showed an empty line Popup content without a field. The explanation of how to add content now shows in full under the builder.

## Schema

```json
{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "itemListElement": [
        {
            "@type": "ListItem",
            "position": 1,
            "name": "Home",
            "item": "https://www.yoozecase.com"
        },
        {
            "@type": "ListItem",
            "position": 2,
            "name": "Extensions",
            "item": "https://www.yoozecase.com/extensions"
        },
        {
            "@type": "ListItem",
            "position": 3,
            "name": "HootBox",
            "item": "https://www.yoozecase.com/"
        }
    ]
}
```
