Skip to main content

Publish a changelog from Python

Publish release notes from code, a CLI, or an AI coding agent. Entries are Markdown, can be scheduled, and are served on a hosted page at launch.nordva.dev/log/<your-slug> or on your own domain.

Keys

Publishing uses a secret key on your server. The public feed needs no key. Subscribing from a browser uses a publishable key. Create both under Dashboard → API keys.

Publishable key pasted into the page that embeds the form
NORDVA_SECRET_KEY=nv_live_…

01Server: Python

changelog-publish.py

import os
import httpx

res = httpx.post(
    "https://api.nordva.dev/v1/changelog/entries",
    headers={"Authorization": f"Bearer {os.environ['NORDVA_SECRET_KEY']}"},
    json={"title": "v1.4 — CSV export", "body_markdown": "## What changed\n\nExports now stream.", "category": "feature", "version": "1.4.0", "notify_subscribers": True},
)
if res.status_code >= 400:
    err = res.json()["error"]
    raise RuntimeError(f"{err['code']}: {err['message']}")
data = res.json()["data"]
print(data["public_url"])

Add an Idempotency-Key header when publishing from CI so a retried job cannot create a duplicate entry.

02Zero-code alternative: hosted widget

any HTML page

<script src="https://cdn.nordva.dev/v1/changelog.js"
  data-key="nv_pub_live_…"
  data-theme="auto"
  data-limit="5"
  data-show-badge="true"></script>
<nordva-changelog></nordva-changelog>

03Verify from a terminal

curl -s https://api.nordva.dev/public/changelog/your-project-slug | jq '.data[0]'

Behaviour worth knowing

  • Every response is { data, error, meta }. On failure error.code is a stable string such as VALIDATION_ERROR, PLAN_LIMIT_REACHED or RATE_LIMITED, with a remediation message.
  • POST and PATCH requests accept an Idempotency-Key header; the same key with the same body returns the original response for 24 hours.
  • Rate limits per key: 30 requests a minute on Free, 120 on Indie, 500 on Builder. A 429 carries Retry-After.
  • Browser calls with a publishable key must come from an origin registered on the project, otherwise the API answers ORIGIN_NOT_ALLOWED.