Overview

Start with the job you need to get done.

Most teams begin by putting the widget in their product. From there you can add audience rules, recurring schedules, AI-assisted drafts, signing, and API automation when you need them. Use the guide that matches your next step.

What Chainlog helps you do

One system to collect product changes, turn them into readable updates, publish them where users already are, and keep the workflow consistent as the stakes rise.

Create a customer-facing source of truth

Start with a hosted changelog or widget so release communication stops living in scattered docs, tickets, and support replies.

Keep updates and docs inside the product

The same runtime can power a launcher, panel, inline widget, or contextual docs surface, so users do not have to leave your app.

Add automation, AI, and proof when needed

Layer in schedules, AI draft workflows, MCP, webhooks, analytics, and signing as communication becomes more operational.

Start from your role

Pick the path that matches the job you own. You do not need to understand the whole product first.

Choose your path

Each guide is organized around a job to be done, not around internal terminology.

Minimal production example

For most teams, this is the fastest safe path: load the widget once, then ask your backend for a short-lived token when the widget needs one.

1. Queue stub + loader

<script>
  window.ChainlogWidget = window.ChainlogWidget || {
    _q: [],
    init: function(config) { this._q.push({ method: "init", config: config }); },
    update: function(config) { this._q.push({ method: "update", config: config }); },
    destroy: function() { this._q.push({ method: "destroy" }); },
    open: function() { this._q.push({ method: "open" }); },
    close: function() { this._q.push({ method: "close" }); },
    toggle: function() { this._q.push({ method: "toggle" }); }
  };
</script>
<script async src="https://chainlog.tech/widget.js"></script>

2. Minimal init call

window.ChainlogWidget.init({
  tenantId: "CHAINLOG_TENANT_ID",
  useTenantDefaults: true,
  tokenProvider: async () => {
    const response = await fetch("/api/widget-token", {
      credentials: "include",
    });

    const { token, expiresInSeconds } = await response.json();
    return { token, expiresInSeconds };
  },
});

Why this shape is the default

Your browser never receives your long-lived widget secret. Your backend creates a short-lived JWT instead, and the widget refreshes it through tokenProvider when needed.

What to read next