ProductLog
Platform · Widget & Identify SDK

Your changelog, feedback, and surveys — inside your app.

One script tag drops a changelog, feedback, and survey launcher into any web app — isolated in its own Shadow DOM, themed to your brand, with an unread badge. Then call ProductLog.identify() to attribute every vote and post to a real person. Both free.

Free on every plan, including Free Shadow-DOM isolated
Embed

One script tag. No build step.

Paste a single <script> on the page you want the launcher on. It auto-initializes from the data-* attributes after DOMContentLoaded — no bundler, no npm install required. The whole widget lives in its own Shadow DOM, so its styles never leak into your app and your app’s CSS never breaks it.

  • Single IIFE bundle, served content-hashed and cached aggressively
  • Shadow-DOM isolation — zero style collisions either way
  • Reads data-project, data-color, data-position, data-tabs, data-theme
  • Talks only to ProductLog’s public APIs — your pages stay script-light
index.html
<!-- Drop this once, anywhere on the page -->
<script src="https://productlog.dev/widget.js"
        data-project="your-project-slug"
        data-color="#6d28d9"
        data-position="bottom-right"
        data-tabs="changelog,feedback,survey"
        data-theme="auto"></script>
Inside the launcher

Three tabs, one unread badge.

The widget is a floating action button in the corner you choose. Open it for the latest releases, a feedback board, and the active survey — all reading from your public ProductLog.

Changelog tab

Your latest published entries, paginated, with a “view all” link to the full public page.

Feedback tab

Browse boards and posts, upvote, and submit a new post — all through the public feedback API, without leaving your app.

Survey tab

Surface your active survey and collect a response right in context — NPS, CSAT, or a custom question.

Unread badge

The launcher shows a count of changelog entries published since the visitor last opened it — capped at 99+. Opening the tab marks everything seen and clears the badge.

Themed to your brand

Set the accent color, corner, and the visible tabs. The FAB, badge, tabs, links, and buttons all follow your accent automatically.

Light, dark, or auto

Choose light, dark, or auto — auto follows the visitor’s OS prefers-color-scheme.

Configure

Tweak it from the script tag.

Every option is a data-* attribute — pick a corner, choose which tabs appear, set the accent, and lock the theme. Invalid values are simply ignored, so you can never break the embed with a typo.

Positions

bottom-right bottom-left top-right top-left

Tabs (any subset)

changelog feedback survey

Theme

light dark auto
programmatic init (optional)
// Prefer config over data-* attributes? Call init() yourself.
window.ProductLog.init({
  project: "your-project-slug",
  primaryColor: "#6d28d9",
  position: "bottom-left",
  tabs: ["changelog", "feedback"],
  theme: "auto"
});

accentColor is an accepted alias for primaryColor. Omit tabs to show all three.

Identify SDK

Know who’s actually voting.

Anonymous feedback is hard to act on. Call ProductLog.identify() and every vote, post, and survey response attaches to a real person — and you can enrich their profile with plan, MRR, and company.

Attribute & enrich

From anonymous visitor to a profile you can act on.

Pass an externalId (or an email) and ProductLog attributes the current session to that person, upserting an EndUser profile. Layer on plan, mrr, and company to see which accounts are driving the most demand — so you prioritize revenue, not noise.

  • Identity key is externalId (or id), falling back to email
  • Profiles carry plan, mrr (in cents), company, and arbitrary attributes
  • Upserted by externalId then email, and last_seen is refreshed each call
  • Same free SDK that backs the widget — no extra entitlement
after login, in your app
window.ProductLog.identify({
  externalId: "host-user-id",
  email:      "[email protected]",
  name:       "Ada Lovelace",
  plan:       "pro",   // signed-only
  mrr:        4900,    // signed-only (cents)
  company:    "Acme"   // signed-only
}, signature); // HMAC computed on YOUR server
Signed traits

Revenue data your visitors can’t spoof.

Anything sensitive — plan, mrr, company, and custom attributes — is honored only with a valid signature your server computes. The signing key never reaches the browser, so a logged-in visitor can never POST themselves a $10k MRR.

How it works

HMAC over canonical JSON, verified server-side.

On your server, derive the project signing key, canonicalize the traits, and sign them. The browser sends the traits plus that signature; ProductLog re-derives the key, re-canonicalizes, and compares in constant time.

  • Key = HMAC-SHA256(APP_SECRET, "identity:" + projectId) — derived server-side, never shipped to the browser
  • Canonical form: recursive key-sort, then JSON_UNESCAPED_SLASHES | UNICODE
  • Signature is a hex HMAC-SHA256, compared with a constant-time check
  • Identify runs against a PUBLIC_ACCESS endpoint — no API key in the browser

Valid signature

Every trait is trusted, including plan, mrr, and company. The profile is enriched in full.

No signature

Accepted, but downgraded: only id/externalId, email, and name are kept. The signed-only traits are dropped — an anonymous caller can never poison your revenue data.

Invalid signature

Rejected outright with a 401 — never a silent fallback. A tampered payload fails loudly.

Put ProductLog inside your app in two minutes.

The widget and Identify SDK are free on every plan — including Free. Grab your script tag and embed it today.