Is Guidde - Magically create video documentation safe?
Guidde is high risk. Guidde posts analytics events to app.guidde.com/tk/v1/track for extension and web-app actions, with account properties (user ID, email, name, org ID) plus event data that can include the current page URL.…
Who publishes itGuidde Inc - no other listings under this identity, 1 shared hostname
Guidde Inc - no other listings under this identity, 1 shared hostname
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 1 hostname
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Guidde posts tracked user events and page URLs to its backend
Guidde posts analytics events to app.guidde.com/tk/v1/track for extension and web-app actions, with account properties (user ID, email, name, org ID) plus event data that can include the current page URL.
You use a Guidde extension feature or trigger an extension lifecycle event.
Examples in the source include opening recording UI, welcome-page actions, upload failures, and step-limit events.
Guidde sends an analytics event that can combine your account identity with page context.
The request goes to Guidde's own tracking endpoint under app.guidde.com.
| Field | Value | Why it matters | |
|---|---|---|---|
User ID | user-id: 8f4c9e31-7b6a-4f4d-9c1d-0c2a7d6e5a19 | Links the tracked event to your signed-in Guidde account. | |
Email address | email: alex.rivera@example.com | Connects the event to an address that can identify you directly. | |
Name | name: Alex Rivera | Adds your display name to the analytics record when the account has one. | |
Organization ID | organization-id: org_6b2c91f4 | Associates the event with your workspace or organization inside Guidde. | |
Page URL | https://app.guidde.com/signup?src=extension | Shows which page or Guidde screen was associated with the event. | |
Extension metadata | ext-version: 81.0.0 | Adds the extension ID, version, browser, and environment to help group your event. |
The tracking helper builds the analytics payload and posts it to Guidde
const aZ = () => ({
...oZ(),
...sZ(),
source: "chrome-extension",
"ext-id": chrome.runtime.id,
"ext-version": nZ(),
environment: Li.firebase.projectId
}),
cZ = t => t ? {
...t.customProps || {},
"user-id": t.user.uid,
email: t.user.email,
name: t.user.displayName,
"organization-id": t.roles?.o
} : {},
_g = async () => (await chrome?.storage?.local.get([oa]))[oa] || Li.firebase.authDomain,
fa = async () => `https://${await _g()}`;
let M0 = {},
L0 = {};
const uZ = (t, e = {}) => ds("/tk/v1/track", "POST", {
userProperties: M0,
globalProperties: L0,
eventData: {
event: t,
...e,
timeMs: new Date().getTime()
}
}),
ln = (t, e = {}, n = null) => {
const r = n ? {
url: n.url
} : {},
i = {
...e,
...r
};
return uZ(t, i)
}- app.guidde.com
Guidde backend endpoint that receives /tk/v1/track analytics posts from the extension.
Guidde recording uploads screenshots, URLs, and DOM events
Recording a Guidde flow makes the content script listen for interactions, build a step event with the page URL and DOM details, and ask the background to screenshot.
It uploads via a signed URL and posts the step to Guidde's addStep API.
You start a Guidde recording session on a page.
The recording path is tied to the quick-guide workflow rather than every browsing session.
The extension records step metadata, captures screenshots, and uploads the resulting step to Guidde.
The code path includes both a screenshot upload and an addStep API call.
| Field | Value | Why it matters | |
|---|---|---|---|
Page URL | https://app.guidde.com/welcome | Shows the page where your recorded step happened. | |
Tab title | Welcome to Guidde | Adds the visible page title to the recorded step. | |
DOM node details | tagName: INPUT, type: text, name: email | Describes the clicked or typed-into page element, which can reveal form labels or element identifiers. | |
Window dimensions | innerWidth: 1440, innerHeight: 900 | Adds screen context that helps reconstruct where the step occurred on your page. | |
Screenshot reference | quickguiddeScreenshots/uid_8f4c9e31/playbook_20260712/playbook_20260712_4d9f0c2a.png | Links the step to a captured image of the visible tab during recording. |
The content script turns page interactions into recording events
const nL = (t, e, n, r, i, s = []) => {
let u;
try {
u = {
innerHeight: top?.window?.innerHeight || window.innerHeight,
innerWidth: top?.window.innerWidth || window.innerWidth,
outerHeight: window.outerHeight,
outerWidth: window.outerWidth
}
} catch {
u = {
innerHeight: window?.innerHeight,
innerWidth: window?.innerWidth,
outerHeight: window.outerHeight,
outerWidth: window.outerWidth
}
}
let p = {
x: 0,
y: 0
};
r instanceof MouseEvent && (p = {
x: r.clientX,
y: r.clientY
});
const g = (I, O) => {
p.x = I.clientX + O.getBoundingClientRect()?.x, p.y = I.clientY + O.getBoundingClientRect()?.y
};
let h = !1;
if (self !== top) {
const I = r?.view?.frameElement;
if (I) h = !0, g(r, I);
else {
const O = document?.activeElement,
x = r?.currentTarget?.defaultView?.frameElement;
x ? (h = !0, ql = x, p.x = O?.getBoundingClientRect().x + x.getBoundingClientRect().x, p.y = O?.getBoundingClientRect().y + x.getBoundingClientRect().y) : ql && (h = !0, g(r, ql))
}
} else h = !0;
const b = s?.[0] instanceof Element ? s[0] : null,
S = r?.target instanceof Element ? r.target : null;
return {
screenshot: null,
id: S?.id,
node: CI({
type: e,
stepTextLimit: n,
event: r,
title: i,
isFrameAccessible: h,
clickedItem: S
}),
composedPathNode: CI({
type: e,
stepTextLimit: n,
event: r,
title: i,
isFrameAccessible: h,
clickedItem: b
}),
devicePixelRatio: window.devicePixelRatio,
url: window.location.href,
timeStamp: t,
type: e,
windowDimensions: u,
mousePosition: p
}
},
Vl = ({
port: t,
timeStamp: e,
type: n,
event: r,
composedPath: i,
title: s = ""
}) => {
jM(u => {
const {
useTabUrl: p,
tabUrl: g
} = u, h = SI();
t.postMessage({
action: "CAPTURE_NOW",
uuid: h,
url: p ? g : window.location.href,
type: n
}), t.postMessage({
action: "NEW_EVENT",
uuid: h,
url: p ? g : window.location.href,
QGEvent: nL(e, n, u.stepTextLimit, r, s, i)
})
})
}const Cv = new lG("iframeCS"),
dG = sC((t, e, n, r) => {
Vl({
timeStamp: r,
type: "input",
port: t,
event: e,
composedPath: n
})
}, 500),
fG = 5e3,
Lm = {
timestamp: 0,
target: null
},
Vf = {
mousedown: (t, e) => {
e.composedPath().some(r => r instanceof HTMLElement ? r.id === F_ || r.id === Wl : !1) || (t.postMessage({
action: "COUNT_EVENT",
type: "mousedown",
url: window.location.href
}), rL(Date.now(), t, e, e.composedPath(), !0))
},
click: t => {
t.postMessage({
action: "COUNT_EVENT",
type: "click",
url: window.location.href
})
},
keyup: (t, e) => {
if (e.key === "Enter") {
if (e.isComposing || e.target === Lm.target && Date.now() - Lm.timestamp < fG) return;
Vl({
port: t,
event: e,
timeStamp: Date.now(),
type: "freeText",
title: "Go here",
composedPath: e?.composedPath()
})
}
},
input: (t, e) => {
e.composedPath().some(r => r instanceof HTMLElement ? r.id === F_ || r.id === Wl : !1) || (Lm.timestamp = Date.now(), Lm.target = e.target, dG(t, e, e.composedPath(), Date.now()))
}
}The background script captures screenshots and posts addStep
const Zme = (t, e) => fetch(t, {
method: "PUT",
body: typeof e == "string" ? Jme(e) : e
}).then(n => {
if (!n.ok) throw new Error(`Upload failed with status ${n.status}: ${n.statusText}`)
}),
ege = t => t.replace("storage.googleapis.com", `gstorage.${Li.firebase.authDomain}`),
tge = (t, e = !0) => ds("/c/v1/get-upload-url", "POST", {
filename: t
}).then(n => ({
url: e ? ege(n.url) : n.url
})),
nge = (t, e) => new Promise((n, r) => {
const i = "gs:///" + t;
Ie.info("Getting signed URL", Oe.CreateQgFlow), tge(i).then(({
url: s
}) => {
Ie.info("Signed URL received, uploading file", Oe.CreateQgFlow), Zme(s, e).then(() => {
Ie.info("File uploaded successfully", Oe.CreateQgFlow), n(i)
}).catch(o => {
Ie.error("File upload failed", Oe.CreateQgFlow, {
error: o
}), ln("file_upload_failed", {
storagePath: t
}), Bt(o), r(o)
})
}).catch(s => {
Bt(s), r(s)
})
}),
rge = (t, e) => ds("/c/v1/ext/quickguidde/addStep", "POST", {
playbookId: t,
step: e
}).then(n => (Ie.info("addStep POST request completed successfully", Oe.CreateQgFlow, {
stepId: e.id
}), n)).catch(n => {
Ie.error("addStep POST request failed", Oe.CreateQgFlow, {
stepId: e.id,
error: n
})
})async addMetaData(e, n, r, i, s, o, c) {
Ie.info("Adding metadata", Oe.CreateQgFlow, {
stepWithoutScreenshot: e
});
const [l, d] = await Promise.race([Promise.all([this.getStepElapsedTimeInRecording(e.timeStamp), this.getScreenDimensionFromTab(e.windowDimensions)]), new Promise(_ => setTimeout(() => _([0, null]), 2e3))]);
d && (e.windowDimensions.innerHeight = d.innerHeight, e.windowDimensions.innerWidth = d.innerWidth), e.type === "input" ? (this.aggregatedStep && EG(e.node) !== EG(this.aggregatedStep.eventData.node) && this.processAggregatedStep(), this.aggregatedStep = {
uuid: n,
index: r,
url: i,
favicon: s,
tabTitle: o,
eventData: e,
processed: !1,
...this.captureVideo ? {
stepTimeInRecording: l,
tabId: c
} : {}
}) : (this.processAggregatedStep(), this.data.addEventData({
uuid: n,
index: r,
url: i,
favicon: s,
tabTitle: o,
eventData: e,
processed: !1,
...this.captureVideo ? {
stepTimeInRecording: l,
tabId: c
} : {}
}), this.data.isEventReady(n) && this.addStep(this.data.getItem(n), n))
}
addStep(e, n) {
if (Ie.info("Adding step", Oe.CreateQgFlow), !this.playbookId) throw new Error("Cannot add QG steps - video doesn't exist");
this.data.markItemAsProcessed(n);
const r = {
...e.eventData,
screenshot: e.imageUrl,
id: e.uuid,
favicon: e.favicon,
documentTitle: e.tabTitle,
stepIndex: this.steps.length,
...this.captureVideo ? {
relativeRecordingTime: e.relativeRecordingTime,
tabId: e.tabId
} : {}
},
i = rge(this.playbookId, r);
this.steps.push(i), this.lastStepUrl = e.eventData.url
}captureNow({
uuid: e,
url: n,
type: r,
favicon: i,
tabTitle: s
}) {
Ie.info("Requesting browser engine to capture image", Oe.CreateQgFlow, {
eventType: r
});
const o = this.imageFormat === "jpeg" || this.imageFormat === "png" ? this.imageFormat : "png",
c = this.imageQuality >= 0 && this.imageQuality <= 100 ? this.imageQuality : 50;
(this.imageFormat !== o || this.imageQuality !== c) && (Ie.warn("Invalid capture parameters detected and corrected", Oe.CreateQgFlow, {
originalFormat: this.imageFormat,
safeFormat: o,
originalQuality: this.imageQuality,
safeQuality: c
}), Bt(new Error("Invalid captureVisibleTab parameters detected"))), this.data.addImageUrl(e, "loading", this.stepsIndex, n, i, s, !1, !1), chrome.tabs.captureVisibleTab({
format: o,
quality: c
}, l => {
if (Ie.info("image captured", Oe.CreateQgFlow), chrome.runtime.lastError) {
Ie.info("Runtime had error", Oe.CreateQgFlow, {
error: chrome.runtime.lastError
}), Bt(chrome.runtime.lastError), this.data.addImageUrl(e, null, this.stepsIndex, n, i, s, !0, !0), ln("step_dropped", {
playbookType: "QG",
uuid: e,
url: n,
playbookId: this.playbookId,
lastError: JSON.stringify(chrome.runtime.lastError)
});
return
}
r !== "input" && this.incrementRecPanelCounter();
const {
uid: d
} = Si()?.user, _ = `quickguiddeScreenshots/${d}/${this.playbookId}/${this.playbookId}_${e}.${o}`;
Ie.info("Uploading image to storage", Oe.CreateQgFlow, {
storageRef: _
}), nge(_, l).then(E => {
this.data.addImageUrl(e, E, this.stepsIndex, n, i, s, !1, !1), setTimeout(() => {
const S = this.data.getItem(e);
Ie.info("Image uploaded. Attempting to add a step", Oe.CreateQgFlow), S && !S.processed && this.data.isEventReady(e) && this.addStep(S, e)
}, 500)
}).catch(E => {
ln("screenshot_upload_failed", {
playbookType: "QG",
uuid: e,
url: n,
playbookId: this.playbookId,
storagePath: _,
error: E?.message || String(E)
}), Bt(E, {
context: "screenshot_upload_failed",
uuid: e,
playbookId: this.playbookId,
storageRef: _
}), this.data.addImageUrl(e, null, this.stepsIndex, n, i, s, !0, !0)
})
})
}- app.guidde.com
Guidde API host that receives addStep and signed-upload-url requests.
- gstorage.app.guidde.com
Guidde storage hostname used for screenshot uploads through signed PUT URLs.