開発者向けドキュメント
最終更新日 2026年9月26日。
Changeloopがあなたのために公開するものはすべて、HTTPS上のシンプルなJSONです。インストールするSDKも、ローテーションするAPIキーも、サインインの手順もありません。以下の2つのフィードは、あなたのフィードIDをキーとした匿名の公開読み取りです。このページのどの例でも、YOUR_PUBLIC_IDを自分のものに置き換えてください。
始める前に知っておくべきこと:あなたの公開フィードIDはアプリ自体にあります。サインインして設定を開くと、デフォルトで表示される「公開フィード」セクションに、changelog.jsonとroadmap.jsonへのすぐに使えるリンク、ホスト型フィードページへのリンク、下記のウィジェットのスニペットとともに表示され、それぞれに独自のコピーボタンがあります。
はじめに
登録から自分のサイトにchangelogを載せるまで、5つのステップで進めます。アプリの「Get started」ページが順番に案内し、終わったステップにはチェックが付きます。
- ソースを接続します。GitHubリポジトリ、GitLabプロジェクト、Bitbucketリポジトリのいずれかです。
- エントリを書く言語を選びます。
- 必要に応じてタグを作成し、読者がプロダクトの領域ごとに絞り込めるようにします。
- 最初のエントリを公開します。マージされた変更は下書きとしてレビューボックスに届きます。承認するか、そのリポジトリの自動公開をオンにしてください。
- サイトに載せます。ホストされたページにリンクする、ウィジェットを貼り付ける、または自分のページでJSONフィードを表示します。
約10行のReactで作るあなたのchangelog
これをコンポーネントに貼り付けるだけで、動作するchangelogが完成します。それ以上追加する必要はありません。
import { useEffect, useState } from 'react';
const FEED = 'https://api.changeloop.dev/v1/public/YOUR_PUBLIC_ID/changelog.json';
export function Changelog() {
const [entries, setEntries] = useState([]);
useEffect(() => {
fetch(FEED).then((r) => r.json()).then((feed) => setEntries(feed.data));
}, []);
return <ul>{entries.map((e) => <li key={e.id}><b>{e.title}</b><p>{e.mdContent}</p></li>)}</ul>;
}
mdContentは、当社がテキストとして作成したmarkdownです。フォーマット済みの出力をレンダリングしたい場合は、代わりにhtmlContentを使用してください。これはサーバー側で当社独自のサニタイザーによって、許可されたタグと属性の固定リストから構築されており、これらのレスポンスの中でマークアップとして挿入することを意図した唯一の値です。それ以外はすべてテキストであり、公開リポジトリから作成されたエントリは、そこでプルリクエストを開ける誰にでも影響を受ける可能性があるため、それに応じて扱ってください。
changelogフィード
GET/v1/public/YOUR_PUBLIC_ID/changelog.json公開されたエントリを最新のものから順に、同一のタイムスタンプの場合は最新のIDで同点を解消します。
クエリパラメータ
- reposは、acme/web,acme/apiのようにカンマ区切りのリポジトリ完全名のリストを受け取ります。それらのリポジトリからのエントリのみが返されます。指定しなければすべて取得します。
- limitは、1ページあたりに欲しいエントリの数です。デフォルトは20で、50を超える値は50に制限され、正の数として解釈できない値は失敗する代わりに20に戻ります。
- cursorは不透明です。前回のレスポンスのnextCursorの値を取得し、そのまま返してください。デコードできないカーソルは、カーソルがないものとして扱われるため、エラーの代わりに最初のページが再度返されます。
レスポンス
{
"data": [
{
"id": "66b0c1f2e4a9d1c3b5a70011",
"title": "Saved views on the inbox",
"mdContent": "You can now pin a filter and come back to it.",
"htmlContent": "<p>You can now pin a filter and come back to it.</p>",
"repoFullName": "acme/web",
"category": "feature",
"tags": ["Inbox"],
"learnMoreUrl": "https://acme.example/docs/saved-views",
"publishedAt": "2026-08-06T09:12:44.000Z"
}
],
"nextCursor": null,
"tagColors": { "Inbox": "#4f46e5" }
}
各エントリは同じ9つのキーを持ちます:id、title、mdContent、htmlContent、repoFullName、category、tags、learnMoreUrl、publishedAt。categoryはfeature、fix、internalのいずれかで、作成者が設定しなかった場合はnullになります。publishedAtはISO 8601形式の文字列で、htmlContentは作成者を一度も経由していないエントリでは空文字列になります。tagsはあなた自身のプロダクト領域名の配列で、割り当てがない場合は空になります。learnMoreUrlはレビュアーが追加しない限りnullで、各タグを描画する色はエントリではなくレスポンスのtagColorsマップから取得されるため、語彙から削除したタグは単に色なしでレンダリングされます。末尾に到達するとnextCursorはnullになります。
不明なフィードIDは{"error":"not_found"}を伴う404を返し、不正な形式のものも同様です。この2つは意図的に区別できないため、このエンドポイントを使ってどのIDが存在するかを調べることはできません。
roadmapフィード
GET/v1/public/YOUR_PUBLIC_ID/roadmap.jsonあなたのチームがすでに手動で管理している同じ3つのカラム。
{
"columns": [
{ "column": "planned", "items": [], "hasMore": false },
{
"column": "building",
"items": [
{
"id": "66b0c1f2e4a9d1c3b5a70042",
"column": "building",
"publicTitle": "Slack notifications",
"publicDescription": "Post each published entry to a channel you pick.",
"publishedAt": "2026-08-05T16:20:01.000Z"
}
],
"hasMore": false
},
{ "column": "shipped", "items": [], "hasMore": false }
]
}
columnsはカラム名でキー付けされたオブジェクトではなく配列であり、その順序は契約の一部です:planned、次にbuilding、次にshipped。空のものも含め、3つとも常に存在するため、「そのカラムが存在しない」と「まだ何も入っていない」を区別する必要は一切ありません。受け取った順序でレンダリングすれば、当社が構築する他のあらゆる表面と一致します。
アイテムはちょうど5つのキーを持ちます:id、column、publicTitle、publicDescription、publishedAt。publicDescriptionは常に文字列で、空になることはあってもnullになることはありません。アイテムの元になったissueについての情報はここでは一切公開されません。リポジトリもissue番号も含まれず、これは意図的なものであり、後で埋める予定の見落としではありません。
このエンドポイントはクエリパラメータを一切受け付けません。カーソルもlimitもリポジトリフィルタもありません。roadmapは無限に成長するログではなく、人がキュレーションする小さなボードだからです。各カラムは最大50件のアイテムを返し、それ以上あった場合はhasMoreを設定します。hasMoreは情報提供のみが目的です:それに従うカーソルは存在しないため、それを中心にページネーションを構築しないでください。
publicTitleとpublicDescriptionは、issueのタイトルと本文から作成された平文であり、公開リポジトリではissueを開ける誰にでも影響を受ける可能性があります。HTMLサニタイズの保証は一切なく、htmlContentの例外でもありません。テキストとしてレンダリングしてください。
埋め込みウィジェット
何も構築したくない場合は、この2行を挿入してください。ウィジェットはshadow root内にレンダリングされるカスタム要素なので、あなたのスタイルを継承することも、そこに漏れることもありません。
<script src="https://api.changeloop.dev/widget.js" defer></script>
<changelogapp-widget
data-public-id="YOUR_PUBLIC_ID"
data-api="https://api.changeloop.dev"></changelogapp-widget>
両方の属性が必須です。data-public-idはあなたのフィードID、data-apiはウィジェットがデータを取得するオリジンです。どちらかが欠けていると、要素はコンソールにエラーを書き込み、何もレンダリングしません。ウィジェットがあるべき場所に空白が見える場合、最初に確認すべきことです。
要素に data-theme="dark" を付けるとダークで描画されます。ページ側で実行時に切り替えることもできます。さらに細かくスタイルを調整したい場合、ウィジェットは CSS カスタムプロパティ(--changelogapp-text、--changelogapp-bg、--changelogapp-accent など)と ::part() 名を公開しており、あなた自身のスタイルシートで設定できます。両方のテーマは、設定の「公開フィード」でライブプレビューできます。
data-repos を追加すると、一部のリポジトリだけを表示できます。たとえば複数のプロダクトが 1 つのアカウントを共有している場合に、あるプロダクトのサイトにそのプロダクトの changelog だけを出す、といった使い方です。値は owner/repo 形式のフルネームをカンマで区切ったリストです。所有者を省いた名前は何にも一致せず、エラーなしで空のフィードが表示されます。対象になるのは最大 10 リポジトリです。絞り込んだウィジェットには Updates と Feedback のタブだけが表示されます。ロードマップにはリポジトリ単位の表示がないためで、フィードバックは引き続きチームのフィードバック送信先に登録されます。設定の「公開フィード」には、この属性を自動で書き出すセレクターがあります。
次の順序で3つのタブをレンダリングします:Updates、Roadmap、Feedback。最初の2つは上記のフィードを読み取ります。3つ目は下記のエンドポイントに送信し、各送信のIDをlocalStorageに保存するため、訪問者は戻ってきて送信したものがどうなったかを確認できます。
スクリプトはバージョン付きで提供されます。/widget.jsは常に最新のビルドを提供し、1時間キャッシュされるため、あなたが何もしなくてもリリースは訪問者に届きます。/widget-vN.jsは1つのビルドを固定します:バージョン番号が一度提供されると、そのバイトは二度と変わらず、1年間キャッシュされます。変更を意図的に採用したい場合は固定してください。
1ページにつきウィジェットスクリプトを正確に1つ読み込む
2つのURLは代替であって、レイヤーではありません。どちらも同じカスタム要素名を登録し、ブラウザはドキュメントごとに名前を一度しか登録できないため、先に実行されたスクリプトがそのページの生存期間中は勝ち、2つ目は無効になります。したがって、/widget.jsと/widget-v5.jsの両方を含むページは、ブラウザが先に実行した方をレンダリングします。これはあなたが制御できるものではなく、既存の/widget.jsの隣にバージョン固定のために/widget-v5.jsを追加しても何も起こりません。
これが起こると、ウィジェットは両方のビルドを名指ししてコンソールに警告を書き込むため、推測に頼る必要はありません。それは警告以上のことはできません:2つ目のコピーが実行される頃には、1つ目がすでにその名前を確保しているからです。修正方法は常に、別のスクリプトを追加するのではなくスクリプトタグを置き換えることです。タグマネージャーや部分テンプレートがあなたの代わりに挿入する場合も同様です。継続的なビルドから固定されたビルドに移行するには、srcを変更してください。
ホスト型フィードページ
https://feed.changeloop.dev/feed/YOUR_PUBLIC_ID当社はその住所にシンプルなページもホストしています:あなたのchangelogとroadmapボードが、上記と同じ2つのフィードからレンダリングされます。サインインもあなた側での設定も不要です。ループが閉じたときに人々を送り返す先でもあります:GitHub issueに残すShippedコメントはここにリンクし、上記の送信検索からのshippedEntry.linkも同様に、両方とも配信されたエントリの独自の#entry-IDアンカーにたどり着きます。これは、その後別のページに移動していてもエントリを見つけます。
これを統合ではなくフォールバックとして扱ってください。あなたのサイトにこれを配置して、当社ではなくあなたの製品のように見せる方法は、changelogフィードとウィジェットのままです。このページは、まだそれを行っていない場合や、他に何を構築していてもここを指すループを閉じるリンクのためのものです。
独自ドメイン
DNS や証明書を変更せずに、ホスト型ページを自分のアドレスから配信できます。設定の「カスタムドメイン」で、読者に見せる公開アドレス(例:https://example.com/changelog)を貼り付け、サイト上のそのパスを、そこに表示されるプロキシ先に向けてください。ルールひとつでページ、アセット、データ、フィードのすべてをカバーします。「ドメインを確認」は当社側からあなたのアドレスを取得し、プロキシが正しいか、正しくなければ何を変えるべきかを教えます。
MCPサーバー
POSThttps://api.changeloop.dev/mcpClaude Code、ChatGPT、またはModel Context Protocolに対応する他のエージェントで作業している場合は、あなたのchangelogに直接接続できます。エージェントは、レビュー待ちのものを確認し、文言を編集し、エディタを離れることなく公開できます。これはウェブアプリと同じレビューゲートです:何かが承認するまで、何も公開されません。
Claude Codeの接続
まずAPIキーを作成し(設定、APIキー)、その後ヘッダーにキーを含めてサーバーを追加します:
claude mcp add --transport http changeloop \
https://api.changeloop.dev/mcp \
--header "Authorization: Bearer clapi_YOUR_KEY"
代わりにJSON設定を読み取るクライアントの場合、同じことは次のようになります:
{
"mcpServers": {
"changeloop": {
"type": "http",
"url": "https://api.changeloop.dev/mcp",
"headers": { "Authorization": "Bearer clapi_YOUR_KEY" }
}
}
}
まだOAuthフローはありません。認証はヘッダー内のAPIキーであり、上記の2つのコマンドが行っていることです。設定でそのキーを取り消すと、次のリクエストでエージェントが切断されます。
エージェントができること
7つのツールがあり、リストは意図的に短くしています。この製品ができる他のことはすべて、同じキーでREST API経由で利用できます。エージェントに公開される各ツールは、それを呼び出すよう説得され得るもう1つの手段です。
- list_pending_entries、list_published_entries、get_entry - あなたのエントリを読み取ります。保留中のものは公開されていません。
- update_entry - エントリのタイトルまたはmarkdown本文を変更します。フィードが提供するHTMLは、あなたのmarkdownから当社のサニタイザーによって再レンダリングされます。エージェントはHTMLを提供できません。
- approve_entry - 公開します。これは公開かつ即時であり、GitHub上のリンクされたフィードバックに通知します。保留中のエントリのみ承認できます。
- discard_entry - エントリをchangelogから除外します。ウェブアプリから元に戻せます。
- get_changelog_info - あなたのフィードIDとchangelogが提供されているアドレス。
できないこと
すべてのツールはキーが属するチームに限定されており、どれも引数としてチームを受け取らないため、何かが試みても他のチームを指すものは何もありません。サーバーはブラウザセッションを受け付けず、キーのみを受け付けます。リクエストは意図的に認証情報を添付する必要があります。また、キーはキーを管理することも、データエクスポートをダウンロードすることもできないため、この方法で接続されたエージェントは、自分自身に2つ目の認証情報を発行することも、1回の呼び出しであなたのデータを引き出すこともできません。
APIキー
上記はすべて匿名であり、認証情報を必要としません。認証されたAPI - あなたの設定、あなたのレビューボックス - は別の表面であり、サインイン済みのブラウザセッションまたはAPIキーのいずれかを受け付けます。キーはスクリプトとエージェントのためのものです:キーボードに人がいない状態であなたのchangelogに到達する必要のあるものすべて。
Authorization: Bearer clapi_YOUR_KEYアプリの「設定」の「APIキー」タブで作成します。キーは作成した瞬間に一度だけ表示され、二度と表示されません。当社はそのハッシュのみを保存するため、二度目にそれをあなたに表示できる画面はどこにもありません。紛失した場合は、それを取り消して別のキーを作成してください。
キーができることとできないこと
キーは、それが作成された単一のチームに限定された、サインインと同じアクセス権を持ちますが、2つの意図的な例外があります。APIキーを管理することも、データエクスポートをダウンロードすることもできません。どちらも実際のサインインが必要です。これにより、漏洩したキーが自分自身の代替品を発行することも、それをブロックするために使うキーを取り消すことも、1回のリクエストであなたのチームのデータを引き出すこともできません。
取り消し
取り消しは次のリクエストで有効になります。取り消されたキーは不明なキーとまったく同じように401を返し、有効なセッションをまだ保持しているブラウザからでも401を返し続けます。Authorizationヘッダーを含むリクエストは、cookieリクエストとして黙って再試行されることは決してないためです。取り消されたキーは、取り消された日付と最後に使用された日付とともにリストに残ります。これは、漏洩したキーがどこに到達したかを解明する際に必要なものです。
プランと上限
無料プランは月に 20 件のマージ済み変更を記事化し、1 日あたりの確認するマージ数、トリアージするフィードバック数、下書きするロードマップカード数、代替バージョン数をそれぞれ 50 に制限します。チームプランに固定の上限はありません。設定の「プランと使用量」は、製品自身が数える通りに各予算を表示し、それぞれがリセットされる時刻も、何かが拒否される前に示します。上限を超えて届いた作業は失われず保留されます。クォータ超過のエントリは受信箱で待機し、拒否されたロードマップの下書きは期間が切り替わればリトライできます。
GitLabとBitbucket
GitLabプロジェクトまたはBitbucketリポジトリは、GitHubリポジトリと同じ方法であなたのchangelogにフィードできます:設定のGitLab、または設定のBitbucketで接続し、当社が提供するwebhookを追加する(bitbucket.orgでは、BitbucketのページにそのボタンがあればConnect with Bitbucketに追加を任せる)と、あなたが指定したブランチにマージされたすべての変更が、同じ方法で書かれ、同じ人間によるレビューを経る、レビューボックス内の下書きエントリになります。エントリは、マージされたプルリクエストまたはマージリクエストから作成されます。GitHubとBitbucketでは、設定のWhat creates draftsでプッシュモードを選んだ場合はプッシュからも作成されます。GitLabプロジェクトはマージリクエストからのみ下書きを作成します。
プロジェクトの接続
GitLabプロジェクトは設定のGitLabで、Bitbucketリポジトリは設定のBitbucketで接続します。パス(GitLabではacme/webのようなグループとプロジェクト、Bitbucketではacme/appのようなワークスペースとリポジトリ)を入力すると、当社はwebhookアドレスとシークレットを返します。両方を相手側のwebhook設定に貼り付けてください:GitLabではMerge request eventsにチェックを入れ、BitbucketではMerged pull requestトリガーとPush repositoryトリガーの両方にチェックを入れてください。セルフホスト型のインスタンスはhttps経由で動作します。シークレットはその瞬間に一度だけ表示されます。紛失した場合は、プロジェクトを削除して再度接続してください。bitbucket.orgの場合、BitbucketのページにConnect with Bitbucketボタンが表示されていれば、貼り付けを省略できます:そのボタンを押してアクセスを一度許可すると、当社がリポジトリのメインブランチを読み取り、webhookを追加します。リポジトリの管理者権限が必要です。セルフホスト型のBitbucketの場合、または手動で貼り付けたい場合は、Set it up by handを選ぶと、上記のとおりアドレスとシークレットが表示されます。Bitbucketのリポジトリを削除して再接続する場合は、Bitbucket側の古いwebhookもRepository settings、次にWebhooksから削除してください。プロジェクトを接続した後は、そのプロジェクトの行でブランチを変更したり、自動公開をオンにしたりできます。配信が無視された場合は、その行に理由が表示されます。
なぜBitbucketはブランチを尋ね、GitLabは尋ねないのか
GitLabは、あなたのプロジェクトがデフォルトとして扱っているブランチを当社に伝えるため、フィールドを空白のままにして、まさにそれを意味することができます。Bitbucketはデフォルトのブランチを一切送信しないため、空白のままにすることを許してしまうと、比較対象がなくなり、あなたのwebhookは完璧にインストールされているように見えながら、一件もエントリを生成しないままになってしまいます。当社は、それが起こるのを許すよりも、一つの質問をすることを選びます。Connect with Bitbucketを使うと、アクセスを許可した時点で当社がBitbucketにメインブランチを問い合わせるため、入力する必要はありません。
まだカバーしていないこと
changelogエントリだけであり、それ以外はありません。あなたのためにissueを提出するフィードバックウィジェット、修正が配信されたときにそのissueに投稿される返信、issueラベルによって動く公開roadmap、レビューボックス内のソースプレビューは、今日はすべてGitHub専用です。
理由は、覆い隠すよりも述べることを選ぶものです。これらそれぞれが、当社が保持する、あなたのプロジェクトへの書き込み権限を持つアクセストークンを必要とします。changelogエントリはどちらも必要としません。それらが書かれる元になるものはすべてwebhook自体に届くため、GitLabやBitbucketをwebhookで接続しても、当社に認証情報が渡されることも、あなたのコードの読み取りが行われることもありません。Connect with Bitbucketは唯一の例外です。Bitbucketは1回のリクエストに限り、リポジトリとプルリクエストの読み取り、およびwebhookの管理ができるトークンを当社に貸与します。当社はそれをメインブランチの読み取りとwebhookの追加にのみ使用し、その後破棄します。何も保存されません。当社は、機能リストを完成させるためにトークンを要求するよりも、あなたに何のコストもかからない部分を提供することを選びます。
エントリの他のバージョン
1つの変更は通常、複数回説明する必要があります:changelogで顧客に、それについての質問に答える人に、そして誰も4段落を読まないチャンネルで。レビューボックスから、エントリを承認する前に、2つの追加バージョンのうちの1つを作成できます。
アナウンスバージョンは1~2行で、エントリを承認したときに全文の代わりにSlackに投稿されるものです。サポートノートは社内向けブリーフィングです:何が変わったか、顧客が何に気づくか、サポート担当者がほぼそのまま言えるような一文。どちらも、使用される前に書き直せる下書きであり、どちらも削除できます。
どちらも公開されません
これらのバージョンは、あなたのchangelogページ、どのフィード、ウィジェット、それらを提供するAPIにも一切表示されません。特にサポートノートは、あなたの会社内の人々のために書かれており、エントリ自体よりも率直な場合があります。それが存在する唯一の場所は、あなたのレビューボックスと、使用している場合はあなた自身のコピーです。
何から書かれるか
常にエントリから書かれ、プルリクエストからは決して書かれません。これは意図的なものです:エントリはすでに、セキュリティ修正を曖昧に保つルールと、あなた自身のレビューを経ています。そこから書き直されたバージョンは、あなたが削除した詳細を再び持ち込むことはできません。その詳細はモデルに与えられたものの中にないからです。
Slackでのアナウンス
エントリを承認すると、公開されるのと同じ瞬間にSlackチャンネルに投稿できます。設定の「Slack」タブで接続してください:自分のワークスペースで受信webhookを作成し、チャンネルを選び、URLを貼り付けます。そのwebhook以外は、あなた側で何もインストールされず、当社があなたのワークスペースへのアクセスを求めることは一切ありません。
メッセージには、エントリのタイトル、あなたが承認したままのテキスト、そのカテゴリとタグ、そしてあなたのchangelog上のエントリへのリンクが含まれます。Markdownは、Slackが実際にレンダリングするものに変換されるため、エントリが独自のアスタリスクを表示したまま届くことはありません。
webhookのURLは認証情報です
そのURLを持つ人は誰でもチャンネルに投稿できるため、当社はそれをパスワードのように扱います:保存された後、あなた自身のデータエクスポートを含め、どの画面もどのAPIレスポンスも二度とそれを表示しません。その後見えるのはマスクであり、2つのwebhookを区別するには十分ですが、他の誰にとっても役に立ちません。当社はhooks.slack.comのアドレスのみを受け付けるため、誤入力または置き換えられたURLは取得される代わりに拒否されます。
動作しなくなるとき
Slackでアプリを削除したり、チャンネルをアーカイブしたりすると、webhookは永久に動作しなくなります。当社は最初の拒否されたメッセージでそれに気づき、アナウンスをオフにし、理由と日付とともにSlackタブでそれを示します。静かに再試行を続けないのは意図的なものです:誰もアナウンスしなかったchangelogは、誰も読まなかったものとまったく同じに見えます。これは伝える価値のある違いです。
一時停止
一時停止はアナウンスを停止しwebhookを保持するため、再開はSlackを通じてもう一度手続きするのではなく、1回のクリックで済みます。切断はURLを完全に削除します。いずれにせよ、公開自体には影響ありません。Slackはあなたのchangelogが投稿するチャンネルであり、待機するゲートでは決してありません。何かを承認したときにSlackに到達できない場合でも、エントリは公開され、アナウンスは自動的に再試行されます。
RSSとJSON Feed
GEThttps://api.changeloop.dev/v1/public/YOUR_PUBLIC_ID/rss.xmlGEThttps://api.changeloop.dev/v1/public/YOUR_PUBLIC_ID/feed.json同じ公開エントリを、読者が理解する2つの形式:RSS 2.0とJSON Feed 1.1で購読可能なフィードとして提供します。どちらもchangelogフィードと同じrepos、category、tagフィルターを受け付け、同じCache-ControlとETagを持ちます。どちらもページネーションしません:読者はフィードの先頭をポーリングするため、これらはカーソルなしで最新のエントリのみを返します。
エントリのテキストはサニタイズされたHTMLで、RSSではCDATAに包まれ、JSON Feedではcontent_htmlとして提供されます。JSON Feedはさらに、名前空間付きの_changelogapp拡張の下にあなたのタグの色を含みますが、RSSは含みません。それらを描画する読者がいないためです。
ホスト型ページは両方をrel="alternate"リンクとして宣伝するため、そこにたどり着いたブラウザや読者は、パスを教えられなくても購読できます。
単独のエントリ
GEThttps://api.changeloop.dev/v1/public/YOUR_PUBLIC_ID/entries/ENTRY_ID1つの公開エントリを返します。これは、changelogフィードがそのdata配列で運ぶのと同じオブジェクトです。これは、フィード内の永続リンクが指す先であり、IDがあってそれを見つけるためにフィードをページネーションしたくない場合に便利です。不明なID、または公開されていないエントリに属するIDは、他の不明なIDと同じ本文で404を返します。
markdownフィード
GEThttps://api.changeloop.dev/v1/public/YOUR_PUBLIC_ID/changelog.md同じ公開エントリを、text/markdownとして提供されるプレーンなmarkdownとして提供します。これはブラウザではない読者のために存在します:「この製品に最近何が変わったか」に答えるLLMやエージェントは、RSSをパースしたりJSONをたどったりすることなくテキストを取得します。changelogフィードと同じrepos、category、tagフィルターを受け付け、同じCache-ControlとETagを持ち、他の2つとまったく同じように条件付きリクエストに304で応答します。
各エントリはセクションです:見出しとしてのタイトル、次に日付・カテゴリ・任意のタグを含む1行、次に書かれたままのエントリテキスト、次にエントリにあればLearn moreリンク、次にその永続リンク。ドキュメントはあなたのフィードのタイトルと説明で始まり、ホスト型ページへのリンクで戻ります。まだ何も公開されていない場合、空の本文を返す代わりに一文でそれを述べるため、読者はそれを失敗した取得と区別できます。
ホスト型ページは、RSSとJSON Feedのリンクとともに、type text/markdownのrel="alternate"リンクとしてそれを宣伝するため、HTMLを取得したエージェントは、パスを教えられなくてもそれを見つけられます。
提供されるものは、サニタイズされたHTMLではなく、当社が作成しあなたが承認したmarkdownです。これは、不活性なmarkdownとして安全であり、それがこのレスポンスが決してtext/htmlにならない理由です。自分でレンダリングする場合は、他の信頼できないmarkdownをエスケープするのと同じようにエスケープしてください:公開リポジトリから作成されたエントリは、そこでプルリクエストを開ける誰にでも影響を受ける可能性があります。
あなた自身のサイトからフィードバックを収集する
テストする前にオリジンを追加してください
これは、製品内で書き込みを行う唯一のエンドポイントであるため、どこからでもリクエストを受け付けるわけではありません。ブラウザのOriginヘッダーをチーム単位の許可リストと照合し、そのリストは空から始まります。空とはすべて拒否することを意味し、すべて許可することではありません。埋め込んでいるオリジンを追加するまで、すべての送信は{"error":"origin_not_allowed"}を伴う403を返し、あなたの受信箱には何も届きません。フォームが正しく見えるのにまだ失敗する場合、ほとんど常にこれが原因です。認証済みのPATCHで/v1/settings/feedに{"allowedOrigins": ["https://your-site.example"]}を送ってリストを設定し、同じパスへのGETで読み返してください。これはあなたのpublicId、allowedOrigins、そしてあなたの購読者がフィードリーダーで見るfeedTitleとfeedDescriptionで応答します。当社は各オリジンをブラウザが送信する正確な形式で保存するため、末尾のスラッシュや明示的なデフォルトポートは問題になりません。
POST/v1/public/YOUR_PUBLIC_ID/feedbackPOST https://api.changeloop.dev/v1/public/YOUR_PUBLIC_ID/feedback
Content-Type: application/json
Origin: https://your-site.example
{ "email": "someone@example.com", "message": "Dark mode, please." }
202 Accepted
{ "publicSubmissionId": "0ZbQ8yqk3n7T1sVJ4mWpLd2rXfEuGh6A" }
emailはメールアドレスのように見え、254文字以下である必要があります。messageは空でなく、文字数ではなくUTF-8バイトで測定して2KB以下である必要があります。JSON本文全体は8KBに制限されています。もう1つフィールドがあります、website:これはハニーポットなので、省略するか、当社のウィジェットが行うように隠しフィールドとしてレンダリングする場合は空で送信してください。
これで何かをデバッグする前に、ハニーポットを理解しておく価値があります。websiteに何か記入されて届いた場合、当社は完全に普通に見える送信IDとともに202を返し、その後何もしません。捕まったことを知ったボットは単に別の方法で再試行するからです。これはボットにとって正しい応答であり、あなたにとっては紛らわしいものです。あなた自身のフォームにブラウザが自動入力しそうなwebsiteという名前のフィールドがある場合は、名前を変更するか削除してください。受け付けられたように見えて決して現れない送信は、ほとんど常にこれです。
当社が受け付けた送信は、publicSubmissionIdとともに202を返します。それを送信者に返し、可能であれば保存してください:それは、その後何が起こったかを送信者が知る唯一の方法です。
失敗モードは、形式が間違っている場合はinvalid_emailまたはinvalid_messageを伴う400、形式は正しいが大きすぎる場合はemail_too_largeまたはmessage_too_largeを伴う413、1つのアドレスから1つのフィードへの分あたり5回または時間あたり30回を超える送信の場合はrate_limitedを伴う429、origin_not_allowedを伴う403、そして認識できないフィードIDの場合はnot_foundを伴う404です。
また、送信がどれだけの後続作業をトリガーできるかについて、チームごとの1日の上限もあります。それを超えても、届いたものはすべて引き続き受け付けて保存しますが、単に何かを自分自身で開くのではなく、チームの誰かが見るのを待つだけです。
1件の送信の確認
GET/v1/public/YOUR_PUBLIC_ID/feedback/PUBLIC_SUBMISSION_IDstatusで応答し、その送信に対するissueが存在すればgithubIssueUrlを、作業が完了すればタイトルとリンクを持つshippedEntryを追加で返します。送信者のメールアドレスは、このルートのために当社のデータベースから読み取られることは一切なく、まして返されることもありません。これにより、誰でも見ることができるページでレンダリングしても安全なレスポンスになります。IDはそれ自体が認証情報のすべてであるため、そのように扱ってください。1分あたり20リクエスト、1時間あたり200リクエスト、アドレスとフィードごとに制限されています。
キャッシュ、CORS、条件付きリクエスト
両方のフィードは、強力なETagとともにCache-Control: public, max-age=60, stale-while-revalidate=300を送信します。そのETagをIf-None-Matchとして送り返すと、変更のないフィードは本文なしで304を返します。どのレスポンスフィールドも壁時計の値を持たないため、変更されていないデータを再レンダリングしてもETagは安定したままであり、それがこの304を信頼できるものにしています。
両方のフィードと送信検索は匿名の読み取りであり、Access-Control-Allow-Origin: *で応答するため、どのオリジンからでも、curlからでも、ビルドステップからでも呼び出せます。フィードバックのPOSTは例外です:オリジンが許可されているかにかかわらず、ワイルドカードではなく、あなた自身の許可されたオリジンとVary: Originで応答します。ブラウザはこれに対してプリフライトを行い、プリフライトは常に204で応答するため、あなたの設定を探るために使用することはできません。