API

Release Notes Feed & Webhooks

Public RSS/Atom feed and HMAC-signed webhook subscriptions for Chipp platform release notes

| View as Markdown
1 min read
# release notes # changelog # feed # rss # atom # webhooks # hmac

Chipp publishes an official, maintained programmatic surface for platform release notes — the same content shown on the in-builder What’s New page, with no sign-in required. Read the public RSS/Atom feed, or register a webhook to be notified the moment a new period is published, instead of scraping or polling manually.

Both surfaces read from the same release-notes data as the What’s New page. There is no separate publish step — when a new period appears in the builder UI, the feed and any matching webhook subscriptions update immediately.

ℹ️

Both surfaces are public and unauthenticated. No API key is required to read the feed or subscribe to webhooks.

RSS/Atom feed

plaintext
GET /api/v1/release-notes/feed
GET /api/v1/release-notes/feed?area=integrations

Returns an RSS 2.0 (with Atom namespace) XML document, newest period first. Each <item> maps one release-note period:

FieldSource
titleA human-readable period label, e.g. “Release notes: August 1, 2026”
linkThe public What’s New page URL
guidThe period’s stable id (not a permalink)
pubDateperiodStart
atom:updatedperiodEnd
categoryOne entry per distinct change kind in the period (feature, improvement, fix, other)
description / content:encodedThe rendered release-note markdown body

The optional ?area= query param filters to periods whose items mention that keyword (case-insensitive substring match over each item’s title + body, or the raw markdown for periods with no structured items) — the same area-matching semantics used by webhook subscriptions below. Omit area for the full, unfiltered feed.

The feed is rate-limited per client IP; a 429 response includes a Retry-After header (seconds).

Webhook subscriptions

Register a callback URL to be notified the moment a new release-note period is published, instead of polling the feed.

Create a subscription

plaintext
POST /api/v1/release-notes/subscriptions
Content-Type: application/json

{
  "callbackUrl": "https://your-service.example.com/webhooks/chipp-release-notes",
  "areas": ["integrations"],
  "categories": ["feature", "fix"]
}
FieldRequiredDescription
callbackUrlyesHTTPS endpoint that receives delivery POSTs. Must not point to a private, loopback, or link-local address — validated at subscribe time and again at every delivery.
areasnoCase-insensitive keyword filter. A period matches if any subscribed area appears as a substring in an item’s title or body (or the raw markdown when a period has no structured items). Omit for no area filtering.
categoriesnoOne or more of feature, improvement, fix, other — the structured change-category Chipp already derives from each release note. A period matches if it contains at least one item in the requested categories. Omit for no category filtering.

Response (201 Created):

json
{
  "data": {
    "id": "5b1e6b8a-1c2d-4e3f-9a0b-7c8d9e0f1a2b",
    "callbackUrl": "https://your-service.example.com/webhooks/chipp-release-notes",
    "areas": ["integrations"],
    "categories": ["feature", "fix"],
    "isActive": true,
    "maskedSecret": "whsec_••••••1a2b",
    "secret": "whsec_9f1c...redacted...4d2e",
    "createdAt": "2026-08-01T12:00:00.000Z",
    "updatedAt": "2026-08-01T12:00:00.000Z"
  }
}
⚠️

secret is returned in full exactly once, in the create response. Store it immediately — every later response only shows maskedSecret. There is no way to retrieve the full secret again; delete and recreate the subscription if it’s lost.

Creating a subscription immediately backfills the 5 most recent periods matching your filters, delivered through the same signed-payload path as live notifications — so your system has recent history without waiting for the next release.

List your subscription

There is no Chipp account behind a webhook subscriber, so management is gated by possession of the subscription’s own secret rather than a login:

plaintext
GET /api/v1/release-notes/subscriptions
Authorization: Bearer whsec_9f1c...redacted...4d2e

Returns { "data": [...] } containing the one subscription that secret belongs to (or an empty array if the secret doesn’t match anything).

Delete a subscription

plaintext
DELETE /api/v1/release-notes/subscriptions/{id}
Authorization: Bearer whsec_9f1c...redacted...4d2e

Returns 204 No Content on success. A missing or mismatched secret, or an unknown id, returns 404 — the endpoint deliberately never reveals whether an id exists to a caller who doesn’t hold the matching secret.

Rate limits

Both surfaces are rate-limited per client IP: creating subscriptions and listing/deleting a subscription are each capped per client IP. A 429 response includes a Retry-After header (seconds).

Delivery payload

Every webhook delivery (live or backfill) POSTs this JSON body:

json
{
  "event": "release_notes.period_published",
  "periodStart": "2026-08-01T00:00:00.000Z",
  "periodEnd": "2026-08-02T00:00:00.000Z",
  "markdown": "## New Features\n### Slack integration\n...",
  "categories": ["feature", "fix"],
  "areas": ["Slack integration"],
  "timestamp": "2026-08-02T01:00:03.512Z"
}

categories is the distinct set of structured change-categories present in this period. areas lists this period’s item titles as a hint for client-side filtering — Chipp’s release notes don’t carry a separate “product area” tag today, so this is not a fixed taxonomy.

Verifying signatures

Every delivery includes an X-Chipp-Signature header:

plaintext
X-Chipp-Signature: sha256=8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa

The value is an HMAC-SHA256 hex digest of the raw JSON request body, computed with your subscription’s secret. Verify it before trusting a delivery:

js
import { createHmac, timingSafeEqual } from "node:crypto";

function isValidSignature(rawBody, signatureHeader, secret) {
  const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader);
  return a.length === b.length && timingSafeEqual(a, b);
}

Use the raw request body bytes, not a re-serialized JSON object — re-serializing can reorder keys or change whitespace and will not match the signature.

Retry and redelivery behavior

A delivery attempt that fails with a network error, a timeout, or an HTTP 408/429/5xx response is retried up to twice more with a short backoff (roughly 1s, then 3s), for up to 3 total attempts. A 4xx response other than 408/429 is treated as a permanent rejection and is not retried — fix your endpoint and Chipp will deliver the next period normally.

There is no indefinite retry queue: if all attempts for a period fail, that period is not redelivered later. If you suspect you missed a delivery, recreate your subscription (backfills the last 5 matching periods) or read the RSS/Atom feed directly — it always reflects the full set of currently-published periods, independent of any subscription’s delivery history.

Deliveries are POSTed with a 10 second timeout and Content-Type: application/json. Your endpoint should return a 2xx status quickly; do heavy processing asynchronously after acknowledging receipt.

Deliveries are at-least-once: a period can be delivered more than once (for example, if a subscription’s backfill overlaps a live delivery of the same newly-published period). Treat delivery as idempotent, keying on periodStart + periodEnd, so a duplicate is a no-op on your side.