Skip to content

Storefront telemetry

What STOQ's storefront analytics pixel collects, where it sends it, what its public insert-only key can and cannot do, how long data is kept, and when the pixel runs.

Last updated

STOQ (Restock Rocket) is a Shopify app for preorders and back-in-stock alerts. On stores that use STOQ, a Shopify web pixel sends storefront events to STOQ’s analytics database. We use them to see how the preorder and “Notify me” buttons perform, and to find storefront bugs.

This page lists exactly what is sent, what the pixel’s database key can do, and how long data is kept.

The key in the pixel is public and insert-only

Section titled “The key in the pixel is public and insert-only”

The pixel sends events straight to STOQ’s analytics database (ClickHouse Cloud, AWS us-east-1) as the database user stoq_public_insert_only. The user name and key are in the pixel’s code on purpose, the same way an analytics write key (Segment, PostHog) is. This is what that user can do:

SHOW GRANTS FOR stoq_public_insert_only
GRANT INSERT ON stoq_analytics.stoq_events TO stoq_public_insert_only
  • Insert only, one table. The single permission is INSERT on the stoq_events table. It has no other tables, no ALTER or DROP, and no roles.
  • No reads. It cannot run SELECT queries at all, so it cannot read any row, including your store’s.
  • Locked settings. Its query settings are fixed and the user cannot change them. A request that tries to change a setting is refused.
  • Rate limits. 240 inserts per minute and 6,000 per hour, per IP address.
  • Rotation. We can replace the key at any time with a pixel release.

Check it yourself. Using the user name and key from the pixel’s code, ask the database for its own grants. The answer is the single GRANT INSERT line above. The refusals you will see are ACCESS_DENIED for a DROP or an insert into any other table, SETTING_CONSTRAINT_VIOLATION for a setting change, and TOO_BIG_AST for a SELECT.

curl -sS "<STOQ_ANALYTICS_URL>" \
-H "X-ClickHouse-User: stoq_public_insert_only" \
-H "X-ClickHouse-Key: <PUBLIC_INSERT_ONLY_WRITE_KEY from the pixel>" \
--data-binary "SHOW GRANTS"

<STOQ_ANALYTICS_URL> and the key are in the pixel’s source, which Shopify serves to the browser on every storefront page.

Anyone who copies the key can send rows to the table, and a row can name any store. They cannot read or change existing rows. That adds noise to STOQ’s internal analytics. It does not expose anyone’s data. The per-IP limits above cap how much one source can send.

Each event is one row in stoq_events, with these fields:

Field What it holds
shopify_domain Your store’s myshopify.com domain
event_name Which event it is (see below)
occurred_at When it happened
session_id An id that ties one visitor’s events together (see below)
variant_id, product_id The product and variant involved, when there is one
page_type Product, collection, cart and so on
page_url The page address, trimmed as described below
market_id, country_code The storefront market and country code, such as US
channel notify_me, for Notify me button events
cart_token, checkout_token Shopify’s cart id (without its secret key) and checkout id, to join an order to the visit that led to it
app_version, app_rendered_at The STOQ theme app extension version and when it rendered on the page
properties Details specific to the event, for example quantity, price and currency on an add to cart
extension_details Context about how STOQ ran on the page, such as timings and the pixel build (pixel_version)

Events:

  • Shopify’s own product_added_to_cart and checkout_started. For a checkout, properties holds the line items (product and variant ids, quantity, product title), total price, currency and order id.
  • STOQ’s own button events: button_shown, button_clicked, signup_completed (a Notify me signup finished), preorder_added_to_cart, and accelerated_checkout_clicked, accelerated_checkout_blocked and accelerated_checkout_held for express checkout buttons.
  • cart_sweep_changed: sent only when STOQ adjusts a preorder item in the cart. It records which lines changed and why.
  • storefront_query_error and js_error: a failed STOQ read from the storefront, or an error thrown by STOQ’s own storefront code. These hold an error class, a trimmed message and a stack trace limited to STOQ’s code.

Not collected: names, email addresses, phone numbers, street addresses, payment details, or anything typed into a form. The Notify me signup event says that a signup happened. It does not contain the email address or phone number entered.

page_url is the page’s origin and path, for example https://your-store.com/products/example. The query string and #fragment are dropped, because they can carry things like emails and ad click ids. Only these parameters are kept, because they name a channel rather than a person:

Parameter Kept as
utm_source, utm_medium The value, if it is a short plain token (letters, digits, ., _, -; up to 64 characters).
utm_term The value, only if it is a UUID. Any other value is dropped, because an ad’s utm_term can hold a shopper’s search words.
fundingSource, edge_redirect, skip_shop_pay The value, if it is a short plain token.
cart_link_id The name only, with no value.

Every other parameter is removed. The same trimming applies to web addresses that appear inside properties and extension_details. A cart’s ?key= secret is removed everywhere it appears, including from cart_token.

session_id is Shopify’s own visitor id. The pixel uses the first of these that is available:

  1. Shopify’s visitor cookie (_shopify_y).
  2. The client id Shopify’s pixel API provides.
  3. Only if neither exists, a random id that STOQ creates for that browser session. It starts with g-.

It is used only to tie one visitor’s events together.

The pixel declares analytics as a required purpose in its Shopify configuration, so Shopify does not load it for visitors who have declined analytics under the store’s consent settings. The pixel also follows Shopify’s customer privacy API itself:

  • While analytics processing is not allowed, it sends nothing and stores nothing.
  • If a visitor withdraws consent, it discards unsent events and the context it saved in the browser, and forgets the g- id it created. Events dropped while consent was missing are not sent later.

Each event is deleted automatically 6 months after it was recorded. The database enforces this with a table TTL.

For questions about this page, or to report a security problem with the pixel, email support@stoqapp.com.

Was this page helpful?

Still stuck? Ask Quinn or chat with support