Is Kixie PowerCall safe?

Medium risk

Kixie PowerCall is medium risk. The bundle initializes Mixpanel with full-URL tracking and localStorage persistence. Logged-in paths register account fields, identify the user by userid, and track User Identified with businessId. Observed: a POST to api-js.mixpanel.com.

45Risk
Who publishes it

Kixie - no other listings under this identity, 6 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Declared legal entity
Kixie
Registered address
406 Wilshire Blvd, Santa Monica, CA 90401-1410, US
Registered contact
Jeff Kuei

Shared hosts - 6 hostnames

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.

appsettings.callstats.io
Also called by 4 other listings, including Phone-C
events.callstats.io
Also called by 4 other listings, including Phone-C
stats.callstats.io
Also called by 4 other listings, including Phone-C
auth.callstats.io
Also called by 5 other listings
collector.callstats.io
Also called by 5 other listings, including Phone-C
dashboard.callstats.io
Also called by 5 other listings

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Mixpanel analytics links Kixie activity to account identity

The bundle initializes Mixpanel with full-URL tracking and localStorage persistence.

Logged-in paths register account fields, identify the user by userid, and track User Identified with businessId.

Observed: a POST to api-js.mixpanel.com.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You open or use Kixie PowerCall while logged in.

The extension did this

The extension initializes Mixpanel, registers Kixie account fields, and sends analytics context to Mixpanel.

Captured request
POSThttps://api-js.mixpanel.com

Observed traffic showed Mixpanel current-URL context; no businessId, User Identified event, session-recording payload, or request body was recorded for this capture.

Account and page context in the Mixpanel code paths
  • User identifier
    userid: 482713 (illustrative)

    Lets analytics events be tied back to your Kixie user account.

  • Business account
    businessId: 21594 (illustrative)

    Links the analytics event to the Kixie business account you belong to.

  • Profile fields
    alex.rivera@example.com, role: manager (illustrative)

    Adds your name, email address, role, and trial status to the analytics profile.

  • Current page URL
    https://app.kixie.com/dialpad (illustrative)

    Shows which page was open when analytics context was prepared.

  • Replay identifiers
    replay_id: 018f7c20-2c3a-7b1a-98d1-0a4f2d4e6c19 (illustrative)

    Connects recorded interaction batches to Mixpanel device and user identifiers when session recording is active.

The code that does this

Mixpanel account identification and request configuration

Readable version

Readable account properties and User Identified event

deobfuscated/assets/app-41fd6bad.js
function mixpanelCreateUser(userSettings) {  return {    $name: `${userSettings?.fname} ${userSettings?.lname}`,    $email: userSettings?.email,    userid: userSettings?.userid,    businessId: userSettings?.businessId,    role: userSettings?.role,    inTrial: userSettings?.inTrial  };}function mixpanelRegisterUser(userSettings) {  if (isMixPanelEnabled() && userSettings) {    try {      mixpanel.register(mixpanelCreateUser(userSettings));    } catch (error) {      console.error("userTracking.ts: unable to register user", error);    }  }}function mixpanelRegisterTrialStatus(status = { trialwithcard: 1, paidpilot: 1 }) {  if (isMixPanelEnabled() && status) {    try {      mixpanel.register(status);      mixpanel.people.set(status);    } catch (error) {      console.error("userTracking.ts: unable to register trial status", error);    }  }}function mixpanelSetUser(userSettings) {  const distinctId = userSettings?.userid?.toString?.();  if (isMixPanelEnabled() && distinctId) {    try {      mixpanel.identify(distinctId);      mixpanel.people.set(mixpanelCreateUser(userSettings));      mixpanel.track("User Identified", {        source: "mixpanelSetUser",        businessId: userSettings?.businessId      });    } catch (error) {      console.error("userTracking.ts: unable to set user", error);    }  }}function mixpanelReset() {  if (isMixPanelEnabled()) {    try {      mixpanel.reset();    } catch (error) {      console.error("userTracking.ts: unable to reset Mixpanel", error);    }  }}function initializeMixpanel() {  if (isMixPanelFeatureFlagged()) {    try {      mixPanelInitialized = true;      mixpanel.init(window.processEnv.VITE_APP_MIXPANEL_TOKEN || "", {        debug: true,        track_pageview: "full-url",        persistence: "localStorage",        disable_cookie: true      });    } catch (error) {      console.error("userTracking.ts: unable to initialize Mixpanel", error);    }  }}

Readable Mixpanel request and session-recording properties

deobfuscated/assets/app-41fd6bad.js
const DEFAULT_API_ROUTES = { track: "track/", engage: "engage/", groups: "groups/", record: "record/" };const DEFAULT_CONFIG = {  api_host: "https://api-js.mixpanel.com",  api_routes: DEFAULT_API_ROUTES,  api_method: "POST",  api_transport: "XHR",  api_payload_format: PAYLOAD_TYPE_BASE64,  app_host: "https://mixpanel.com",  cdn: "https://cdn.mxpnl.com",  track_pageview: false,  persistence: "cookie",  disable_cookie: false,  batch_requests: true,  record_sessions_percent: 0,  recorder_src: "https://cdn.mxpnl.com/libs/mixpanel-recorder.min.js"};SessionRecording.prototype._sendRequest = function(replayId, properties, body, callback) {  const onResponse = _.bind(function(response, responseBody) {    if (response.status === 200 && this.replayId === replayId) {      this.seqNo++;      this.batchStartUrl = _.info.currentUrl();    }    callback({      status: 0,      httpStatusCode: response.status,      responseBody,      retryAfter: response.headers.get("Retry-After")    });  }, this);  win.fetch(this.getConfig("api_host") + "/" + this.getConfig("api_routes").record + "?" + new URLSearchParams(properties), {    method: "POST",    headers: {      Authorization: "Basic " + btoa(this.getConfig("token") + ":"),      "Content-Type": "application/octet-stream"    },    body  }).then(function(response) {    response.json().then(function(responseBody) {      onResponse(response, responseBody);    }).catch(function(error) {      callback({ error });    });  }).catch(function(error) {    callback({ error, httpStatusCode: 0 });  });};SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function(events, _, callback) {  const eventCount = events.length;  if (eventCount > 0) {    var replayId = this.replayId;    var batchStart = events[0].timestamp;    if (this.seqNo === 0 || !this.replayStartTime) {      if (this.seqNo !== 0) this.reportError("Replay start time not set but seqNo is not 0. Using current batch start time as a fallback.");      this.replayStartTime = batchStart;    }    var replayLength = events[eventCount - 1].timestamp - this.replayStartTime;    var properties = {      $current_url: this.batchStartUrl,      $lib_version: Config.LIB_VERSION,      batch_start_time: batchStart / 1000,      distinct_id: String(this._mixpanel.get_distinct_id()),      mp_lib: "web",      replay_id: replayId,      replay_length_ms: replayLength,      replay_start_time: this.replayStartTime / 1000,      replay_start_url: this.replayStartUrl,      seq: this.seqNo    };    var encodedEvents = _.JSONEncode(events);    var deviceId = this._mixpanel.get_property("$device_id");    if (deviceId) properties.$device_id = deviceId;    var userId = this._mixpanel.get_property("$user_id");    if (userId) properties.$user_id = userId;    if (CompressionStream) {      var stream = new Blob([encodedEvents], { type: "application/json" }).stream();      var gzip = stream.pipeThrough(new CompressionStream("gzip"));      new Response(gzip).blob().then(_.bind(function(blob) {        properties.format = "gzip";        this._sendRequest(replayId, properties, blob, callback);      }, this));    } else {      properties.format = "body";      this._sendRequest(replayId, properties, encodedEvents, callback);    }  }});
External analytics destinations in the bundled Mixpanel library
    • api-js.mixpanel.com

    Mixpanel analytics API host that received the observed POST and is configured for track, engage, groups, and record routes.

    • mixpanel.com

    Mixpanel application host configured by the bundled analytics library.

    • cdn.mxpnl.com

    Mixpanel CDN host configured for the session-recording loader.

Updated 30 September 2026bmdpeakaalmaaflnkckhjcojpemlkjig