Cookbook
Use recipes when you know the job, not the subsystem.
These guides are intentionally short. Each one starts with the problem, shows the smallest useful implementation, and calls out the one or two gotchas that actually matter in production.
Use this page when you already know the workflow
Cookbook is for teams who do not want a subsystem tour. Each recipe starts from the job, shows the smallest useful implementation, and points out the few things that actually break in production.
Best for implementation teams
Use this when you know what you are trying to ship and want the shortest path from idea to working example.
Best for repeatable workflows
These recipes are useful when the same pattern will show up across multiple product areas or releases.
Not a replacement for reference docs
If you need exact runtime fields or API behavior, jump back to Widget runtime or API and automation after the recipe gets you oriented.
Open release notes from a custom button
Use this when your app already has a nav, command bar, or help surface and should own the trigger UI.
<button type="button" onclick="window.ChainlogWidget.toggle()">
Release notes
</button>
<script>
window.ChainlogWidget.init({
tenantId: "CHAINLOG_TENANT_ID",
useTenantDefaults: true,
tokenProvider: async () => fetch("/api/widget-token", { credentials: "include" }).then((r) => r.json()),
mode: "panel",
showTrigger: false,
launcherLabel: "Updates",
});
</script>Expected result: the widget stays hidden until your button calls ChainlogWidget.toggle(). Keep showTrigger: false so you do not end up with two launchers.
Troubleshooting: if nothing opens, verify the runtime script loaded and the token route returned a JWT rather than the widget credential secret.
Render an inline changelog inside your product
Use this when release notes belong on a settings, onboarding, billing, or admin page instead of a floating launcher.
<div id="release-notes"></div>
<script>
window.ChainlogWidget.init({
tenantId: "CHAINLOG_TENANT_ID",
useTenantDefaults: true,
tokenProvider: async () => {
const response = await fetch("/api/widget-token", { credentials: "include" });
return response.json();
},
mode: "inline",
target: "#release-notes",
title: "Release notes",
subtitle: "What changed recently",
autoHeight: true,
});
</script>Expected result: the iframe mounts into #release-notes and grows with content because autoHeight is enabled.
Variation: add fixed height or explicit width only if your layout truly requires it. The inline default is usually the right starting point.
Show updates for a specific product area
Use this when a page like billing, reporting, or admin should prioritize updates that match its own module instead of showing every release note equally.
window.ChainlogWidget.init({
tenantId: "CHAINLOG_TENANT_ID",
useTenantDefaults: true,
tokenProvider,
mode: "panel",
contextModule: "billing",
});Expected result: widget entry reads prioritize entries whose moduleKey matches billing or whose resurface triggers include billing.
Publish from CI or a release pipeline
Use this when a deployment pipeline or release bot should create customer-facing entries without opening the dashboard.
curl -X POST "https://chainlog.tech/api/v1/entries" \
-H "Authorization: Bearer cl_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"title": "Billing exports are faster",
"slug": "billing-exports-are-faster",
"summary": "CSV exports now stream in the background.",
"body": "## What changed\nLarge exports now process asynchronously.",
"updateType": "improvement",
"deliveryType": "in_app_widget",
"changeCategory": "product_release",
"platforms": ["web"],
"moduleKey": "billing",
"status": "published"
}'Expected result: the pipeline creates or publishes an entry with structured metadata that downstream feeds, widgets, and webhooks can all reuse.
Production default
Start by creating drafts from automation and keep final publish behind a human review step unless your release process is already highly structured.
Consume publish webhooks
Use this when another system should react as soon as a release note becomes visible.
- Subscribe to
entry.publishedin Dashboard → Webhooks. - Verify the raw body with the shared HMAC secret before parsing.
- Use
X-Webhook-Idto deduplicate deliveries on your side if needed.
Use a customer domain for the hosted changelog
Use this when public release notes should live on your brand domain instead of the shared app domain.
Current state
Treat this as a semi-manual setup today. The domain can be verified and used, but the onboarding flow is not yet a polished self-serve path.
- Create a customer-facing subdomain such as
updates.example.com. - Point its CNAME to
cname.vercel-dns.com. - Add the same domain in Chainlog settings and verify it after DNS and SSL are active.
- Confirm changelog links now resolve through the verified custom domain.
Troubleshooting: if verification stalls, confirm the DNS record is live and the hostname is also present in the project domain configuration.
What to read next