The whole integration fits on this page. If something here doesn't match what the product does, that's a bug. Tell me at [email protected].
Add a mount where comments should appear and the script tag before </body>. Your site key is on the Websites page of the dashboard. That is the entire install: the widget finds its mount, fetches the thread on load, and themes itself.
<div id="lipwalk-comments" data-page-id="my-post-42"></div>
<script
src="https://js.lipwalk.com/embed.js"
data-lipwalk-site-key="YOUR_SITE_KEY"
async
></script>Works anywhere HTML works: hand-written pages, WordPress, Ghost, Next.js, Astro, Webflow. Framework-specific snippets are on the integrations page.
data-page-id is the identity of a thread. Set it to something that never changes, a CMS post ID or a slug you commit to, and the conversation survives URL redesigns, domain moves, and tracking parameters.
If you omit it, Lipwalk falls back to the canonicalized URL: it strips every known tracking parameter (utm_*, fbclid, gclid, and dozens more), normalizes protocol, ports, and trailing slashes, and honors per-site "meaningful query parameters" you configure. And if a URL change ever does orphan a thread, the dashboard has a merge tool that folds one thread into another: comments move, reactions and ratings deduplicate, and the old URL keeps resolving.
Two optional mounts, same script tag. Each renders only if its element exists on the page.
<!-- Star ratings: place near the content being rated -->
<div id="lipwalk-rating" data-page-id="my-post-42"></div>
<!-- Reactions bar (paid plans) -->
<div id="lipwalk-reactions" data-page-id="my-post-42"></div>Ratings store every vote as a normalized fraction, so switching a site between 3, 5, and 10-star scales (paid plans) never corrupts history: a 4/5 becomes an 8/10, not a 4/10. When a page has enough ratings, the widget injects AggregateRating JSON-LD so stars can appear in search results.
Reactions are configured per site in the dashboard: pick from industry packs or define your own (up to 6 active). Renaming a reaction keeps its counts; merging two reactions deduplicates per visitor so nobody is counted twice.
The widget ships zero inline styles and inherits your page's font by default, so it already half-blends in. Everything else is plain CSS: every knob is a CSS variable on the mounts, and every element uses a low-specificity .lipwalk-* class your stylesheet wins against without !important.
#lipwalk-comments {
--lw-accent: #0ea5e9; /* buttons, stars, active states */
--lw-font: inherit; /* default: your site's font */
--lw-text: #222; /* body text */
--lw-muted: #667; /* secondary text */
--lw-border: #ddd; /* inputs, dividers */
--lw-radius: 4px; /* corners everywhere */
}
/* or target anything directly */
.lipwalk-comment-body { font-size: 16px; }
.lipwalk-btn { text-transform: uppercase; }The accent set on the dashboard's Appearance page applies automatically on first paint; the color scheme follows it too (dark, light, or auto via prefers-color-scheme).
Four options, all per-site toggles, every one enforced on the Lipwalk servers, not just hidden in the UI:
If your site already has logins (a membership site, a store, a custom app), your users should not need a second account to comment. With Site SSO your server vouches for the logged-in user by signing a short-lived token, and the widget signs them in automatically. Available on Business and above.
Setup: generate an SSO secret in the dashboard under your website's settings, keep it server-side, and sign an HS256 JWT with these claims:
{
"sub": "42", // required: stable user id in YOUR system
"name": "Ada Lovelace", // required: display name
"email": "[email protected]", // optional: stored only as a hash
"avatar": "https://...", // optional: avatar URL
"exp": 1718000000 // required: keep it short (minutes)
}Hand the token to the page before the embed script and the widget does the rest:
<script>window.LIPWALK_SSO_TOKEN = "<signed JWT>";</script>WordPress: the Lipwalk plugin does all of this for you. Paste the secret into Settings > Lipwalk and every logged-in WordPress user (including WooCommerce customers and members from MemberPress, Paid Memberships Pro, BuddyPress, and any other plugin that uses WordPress accounts) comments under their site identity.
Node and Next.js: the SDK ships a signer; call it server-side and print the token into the page:
import { signSsoToken } from "@lipwalk/comments/sso";
const token = signSsoToken({
secret: process.env.LIPWALK_SSO_SECRET!,
user: { id: user.id, name: user.name, email: user.email },
});Any other stack works the same way: every language has an HS256 JWT library. Sign on the server, never expose the secret to the browser, and rotate the secret in the dashboard to invalidate all outstanding tokens at once. When a visitor signs out of your site the widget drops the piggybacked session on the next page load.
Every comment passes through layers, cheapest first: heuristics (links, caps, patterns), then a quantized Toxic-BERT model running on Lipwalk servers (your readers' words are not sent to a third-party AI API for routine scoring), then optional cloud escalation for genuinely borderline content. Image attachments are scored the same way before they publish.
The combined risk score decides: below 0.3 publishes, 0.3–0.7 queues for human review (your moderators get an email), above 0.9 rejects outright. You can also hold everything for manual approval per site, block users, and bulk-moderate from the dashboard. Approved authors are notified their comment went live, with one-click unsubscribe.
Growth and up. Replay records DOM events (clicks, scrolls, mutations) not video, with input fields masked. The recorder is a separate script that loads only when a session is actually being sampled, so readers who aren't recorded never download it.
It honors Do-Not-Track automatically, and you can opt out any page:
<html data-lipwalk-no-replay>Recordings expire on your plan's schedule (30/90/180 days) and the dashboard renders click-density heatmaps, scroll-depth curves, and engagement zones aggregated across sessions.
Webhooks (Growth and up) deliver JSON to your endpoint for the events you subscribe to:
comment.created comment.updated comment.deleted
comment.approved comment.flagged comment.voted
rating.created rating.updated rating.deleted
commenter.blocked thread.created moderation.pendingEvery delivery is signed. Verify it like this:
// Headers on every delivery
// X-Lipwalk-Event: comment.created
// X-Lipwalk-Delivery: <unique id>
// X-Lipwalk-Timestamp: <unix seconds>
// X-Lipwalk-Signature: t=<timestamp>,v1=<hmac>
const expected = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
// compare with the v1 value using a constant-time comparisonFailed deliveries retry with backoff, and every attempt (status code, response time, error) is visible in the dashboard. API keys (created under Settings, shown once, scoped) authenticate server-to-server access for moderation and exports. A TypeScript SDK wraps the same REST endpoints.
Lipwalk ships an MCP server, @lipwalk/mcp, so any MCP-capable agent (Claude Code, Claude Desktop, a scheduled bot) can connect to a site's comments. The agent reads what readers said since its last check, sees each comment with full thread context and a summary of the page's conversation, replies in-thread, and moderates by your own policy. Two keys, two jobs: the public site key only renders the widget and is locked to your domain; an agent key is the secret your agent uses.
Setup. In the dashboard, go to Settings > API keys and create an agent key with the scopes comments:read, comments:write, and comments:moderate. The lw_... secret is shown once, so copy it then. Then add the server to your agent:
# Claude Code
claude mcp add lipwalk --env LIPWALK_API_KEY=lw_... -- npx -y @lipwalk/mcp
# Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"lipwalk": {
"command": "npx",
"args": ["-y", "@lipwalk/mcp"],
"env": { "LIPWALK_API_KEY": "lw_..." }
}
}
}Two environment variables: LIPWALK_API_KEY (required, the agent key) and LIPWALK_API_URL (optional, defaults to https://api.lipwalk.com).
What an agent can do: read comments since the last check; see each comment with its full thread context and a summary of the page's conversation; reply in-thread under the agent's own identity; and moderate. Replies never reappear in the agent's own inbox, so an agent can't loop on its own output. The same endpoints are plain REST under /api/v1 if you'd rather call them directly. The agent key is scoped, rate-limited per key, and revocable or rotatable any time from the dashboard.
Readers don't need to email anyone to exercise their rights. Signed-in commenters can export everything Lipwalk holds about them as JSON, and delete their identity and content with a confirmed two-step request, both self-serve, straight from the widget. Deletion anonymizes immediately.
Details, retention windows, and the full data inventory live in the privacy policy.