開発者向けの手順 · RESTとMCP
ナレッジベースを同期する
既存のシステムで執筆を続けながら、変更をSonnyに送れます。この手順では「請求」カテゴリーを作成し、記事を下書きとして同期してから、認証済みの顧客オーディエンス向けに公開します。
1. チャネルを選んでキーを作成する
- チャネル → あなたのチャネル を開きます。ダッシュボードのURLの /app/sources/ の後にあるIDをコピーします。APIではこのチャネルIDを sourceId と呼びます。ウィジェットの siteId やヘルプセンターの公開スラッグとは異なります。
- 設定 → 開発者 → APIキー → キーを作成 で、kb:read と kb:write を付与します。チャネルへのアクセスを、同期するチャネルに限定します。一度だけ表示されるキーを、サーバーのシークレットマネージャーに保存してください。
- 以下の変数をバックエンドの環境に設定します。これらの例ではcurlを使っています。大文字のIDは、Sonnyから返された値に置き換えてください。公開中のヘルプセンターを同期する前に、テスト用のチャネルで実行してください。
# Load SONNY_API_KEY from your server's secret manager first.
# SOURCE_ID is the ID in the channel dashboard URL: /app/sources/SOURCE_ID
export SOURCE_ID="YOUR_SOURCE_ID"
export BASE="https://www.usesonny.com/api/v1/sources/$SOURCE_ID"任意の確認方法:GET /api/v1/sources とMCPツールの list_sources は、アクセス可能なチャネルを返し、conversations:read が必要です。チャネルIDがすでにわかっていれば、このスコープは不要です。ドメインの接続は引き続きダッシュボードで行います。
APIキーの詳しい設定2. カテゴリーを作成または更新する
URLには、元のシステムの固定のIDを使います。同じ外部IDを再度送ると、重複を作らずに既存のカテゴリーが更新されます。最初の作成時に、このチャネルのヘルプセンターが自動で初期化されます。
curl --fail-with-body -X PUT "$BASE/categories/by-external-id/billing" \
-H "Authorization: Bearer $SONNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Billing","position":0}'作成時は201、更新時は200が返され、カテゴリーは data に含まれます。Sonnyの内部カテゴリーIDが必要な場合は data.id を保存してください。外部IDはURLエンコードし、タイトルが変わっても固定のままにしてください。
3. 記事を下書きとして同期する
curl --fail-with-body -X PUT "$BASE/articles/by-external-id/billing-guide" \
-H "Authorization: Bearer $SONNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Manage billing","body":"## Find your invoices\n\nOpen **Settings → Billing** in your account.","bodyFormat":"markdown","categoryExternalId":"billing","status":"draft"}'レスポンスには data.id と、サニタイズされたHTMLが data.body に含まれます。チャネル → あなたのチャネル → ヘルプセンターで記事を開き、書式を確認してください。MarkdownはHTMLに変換されます。bodyFormat: "html"(デフォルト)で生のHTMLも受け付けます。記事のタイトルは本文の見出しとは別です。
手順2のカテゴリーを参照するには categoryExternalId を、SonnyのIDを使う場合は categoryId を使います。指定するのはどちらか一方だけです。記事を未分類にするには、どちらかを null に設定します。アップサートには必ずタイトルと本文が必要です。部分的な編集にはPATCHを使えます。status を省略すると、新しい記事はデフォルトで下書きになります。
4. アクセスを設定してから公開する
この非公開ヘルプセンターの例では、まず認証済みの顧客ログインを接続します。オーディエンスを作成し、返された data.id を保存します。
curl --fail-with-body -X POST "$BASE/audiences" \
-H "Authorization: Bearer $SONNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Pro customers","rules":{"match":"all","conditions":[{"trait":"plan","op":"in","value":["pro","business"]}]}}'以下の AUDIENCE_ID、ARTICLE_ID、ログインURLを置き換えます。最初のリクエストでヘルプセンターを公開中にし、Proプランの顧客に限定します。2つ目のリクエストで記事を公開します。アプリのログイン処理では、署名付きの顧客JWTをヘルプセンターと交換する必要があります。signInUrl を設定するだけでは、誰もログインできません。
curl --fail-with-body -X PATCH "$BASE/help-center" \
-H "Authorization: Bearer $SONNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true,"title":"Acme Help Center","audience":"verified","audienceId":"AUDIENCE_ID","signInUrl":"https://app.example.com/login"}'
# Replace ARTICLE_ID and AUDIENCE_ID with the returned data.id values.
curl --fail-with-body -X PATCH "$BASE/articles/ARTICLE_ID" \
-H "Authorization: Bearer $SONNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"audienceId":"AUDIENCE_ID","status":"published"}'GET /help-center が返すURLを使って、ログイン中の読者の表示と、ログアウトした状態のブラウザをテストします。APIキーとMCPはスタッフとして動作し、スコープ内のコンテンツを読めるため、成功のレスポンスが返っても顧客が記事を見られる証明にはなりません。
公開のヘルプセンターでは、ヘルプセンターと記事に audience: "everyone" と audienceId: null を使い、カテゴリーのアクセスも確認してください。名前付きのグループだけを外すには audienceId: null を送ります。audience を明示的に変更しない限り、認証済み限定のアクセスは残ります。audienceId を省略すると、以前の割り当てが維持されます。ヘルプセンター、カテゴリー、記事のすべての制限を満たす必要があります。
名前付きのルールでは、match: "all" または "any"、最大10個の条件、厳密に型付けされた値を使います。演算子:eq、neq、in、not_in、exists、not_exists、gt、gte、lt、lte。数値の比較には数値を、in/not_in には1〜100個のスカラー値の配列を指定し、exists/not_exists では value を省略します。存在しない属性は比較に失敗します。ルールの動作、継承、アクセスのトラブルシューティングを見る。
5. 同期を最新に保つ
コンテンツを重複させずに書き込みを繰り返す
元のデータが変わるたびに、同じ外部IDでPUTを繰り返します。外部IDはヘルプセンター内で一意です。更新時に省略したフィールドは、現在の値のままです。公開状態を維持するには、以降のアップサートで status を省略します。draft を明示的に送ると、公開中の記事は取り下げられます。publishedAt は最初の公開時に設定され、その後は変わりません。読み取り専用のため、過去の公開日をインポートすることはできません。内容が変わらない場合やメタデータだけの編集では、不要な再埋め込みは行われません。
変更と分析を読み取る
curl --fail-with-body --get "$BASE/articles" \
-H "Authorization: Bearer $SONNY_API_KEY" \
--data-urlencode "page=1" --data-urlencode "limit=100" \
--data-urlencode "status=published" \
--data-urlencode "updatedSince=2026-09-01T00:00:00Z"
# Get the full sanitized HTML and read-only analytics for one article.
curl --fail-with-body "$BASE/articles/ARTICLE_ID" \
-H "Authorization: Bearer $SONNY_API_KEY"記事とカテゴリーの一覧は、data と、pagination(page、limit、total)を返します。ページは1から始まり、limit のデフォルトは50で、最大100まで指定できます。同じ絞り込みを保ったまま、page × limit が total に達するまでページを取得してください。どちらの一覧も externalId と updatedSince を受け付け、記事は categoryId と status(draft または published)も受け付けます。updatedSince にはタイムゾーン付きのISOタイムスタンプを使ってください。その時刻に更新された記録も含まれます。
記事の詳細を個別に読み取ると、本文全体、viewCount、helpfulYes、helpfulNo を取得できます。これらの分析値は読み取り専用です。updatedSince は現在の記録を一覧表示するもので、削除は報告しません。削除は元のシステムで追跡し、必要に応じて対応するSonnyの記録を明示的に削除してください。
画像をアップロードする
curl --fail-with-body -X POST "$BASE/media" \
-H "Authorization: Bearer $SONNY_API_KEY" \
-F "file=@billing.png;type=image/png"返された data.url をMarkdownの画像構文またはHTMLのimg要素で使い、記事の本文を同期します。アップロードできるのは25MBまでのJPEG、PNG、GIF、WebPで、初期化済みのヘルプセンターが必要です。アップロードしたURLは公開されます。記事のオーディエンス制限で画像のURLは保護されません。MCPでは、multipartのフォームデータの代わりに、sourceId、fileName、contentType、ファイルの dataBase64 を指定して upload_kb_media を使います。
記事とカテゴリーを並べ替える
curl --fail-with-body -X PUT "$BASE/articles/reorder" \
-H "Authorization: Bearer $SONNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids":["ARTICLE_ID_1","ARTICLE_ID_2"]}'まず、ステータス、カテゴリー、日付の絞り込みなしで、下書きを含むすべてのページを取得します。サンプルのIDを、現在のすべての記事IDに、希望する順番でそれぞれ1回ずつ置き換えます。カテゴリー全体の並べ替えには /categories/reorder を使います。並べ替えに成功すると、本文なしで204が返されます。一覧が変わっている場合や、別のID、重複したID、欠けたIDが含まれる場合は、一覧を更新してから再試行してください。個々の書き込みで、0以上の position を設定することもできます。
MCPで同じワークフローを使う
- MCPクライアントを接続し、正しいワークスペースを選びます。kb:read と kb:write があり、チャネルにアクセスできることを確認してください。
- ダッシュボードのチャネルIDを使うか、認証情報に conversations:read もある場合は list_sources を呼び出します。ウィジェットの siteId で代用しないでください。
- 以下の最初の5つの例を順番に実行します。SOURCE_ID をチャネルIDに、ARTICLE_ID/AUDIENCE_ID をそれぞれ前の結果の data.id に置き換えます。公開する前に、ログインURLを置き換えてログインを接続してください。
- 変更の確認には list_kb_articles を使います。並べ替えは、下書きを含む絞り込みなしの完全な一覧を集めてから実行してください。書き込みに成功したら、一覧を更新してください。
各例にはツール名とその引数が示されています。アップサートのフィールドは arguments に直接、更新ツールでは変更するフィールドを updates の中に入れます。これらはツール呼び出しの入力で、MCPエンドポイントへのHTTPリクエストではありません。
1. upsert_kb_category
{
"name": "upsert_kb_category",
"arguments": {
"sourceId": "SOURCE_ID",
"externalId": "billing",
"name": "Billing",
"position": 0
}
}2. upsert_kb_article
{
"name": "upsert_kb_article",
"arguments": {
"sourceId": "SOURCE_ID",
"externalId": "billing-guide",
"title": "Manage billing",
"body": "## Find your invoices\n\nOpen **Settings → Billing** in your account.",
"bodyFormat": "markdown",
"categoryExternalId": "billing",
"status": "draft"
}
}3. create_kb_audience
{
"name": "create_kb_audience",
"arguments": {
"sourceId": "SOURCE_ID",
"name": "Pro customers",
"rules": {
"match": "all",
"conditions": [
{
"trait": "plan",
"op": "in",
"value": [
"pro",
"business"
]
}
]
}
}
}4. update_help_center
{
"name": "update_help_center",
"arguments": {
"sourceId": "SOURCE_ID",
"updates": {
"enabled": true,
"title": "Acme Help Center",
"audience": "verified",
"audienceId": "AUDIENCE_ID",
"signInUrl": "https://app.example.com/login"
}
}
}5. update_kb_article
{
"name": "update_kb_article",
"arguments": {
"sourceId": "SOURCE_ID",
"articleId": "ARTICLE_ID",
"updates": {
"audience": "verified",
"audienceId": "AUDIENCE_ID",
"status": "published"
}
}
}6. list_kb_articles
{
"name": "list_kb_articles",
"arguments": {
"sourceId": "SOURCE_ID",
"page": 1,
"limit": 100,
"updatedSince": "2026-09-01T00:00:00Z"
}
}7. reorder_kb_articles
{
"name": "reorder_kb_articles",
"arguments": {
"sourceId": "SOURCE_ID",
"ids": [
"ARTICLE_ID_1",
"ARTICLE_ID_2"
]
}
}8. update_kb_audience
{
"name": "update_kb_audience",
"arguments": {
"sourceId": "SOURCE_ID",
"audienceId": "AUDIENCE_ID",
"updates": {
"name": "Paid customers"
}
}
}関連する操作
以下のRESTのパスは BASE からの相対パスです。すべてのフィールドとレスポンスのスキーマはAPIリファレンスをご覧ください。
| 作業 | REST | MCP |
|---|---|---|
| 記事の一覧表示/取得 | GET /articles · GET /articles/{articleId} | list_kb_articles · get_kb_article |
| カテゴリーの一覧表示/取得 | GET /categories · GET /categories/{categoryId} | list_kb_categories · get_kb_category |
| 外部IDなしで作成 | POST /articles · POST /categories | create_kb_article · create_kb_category |
| 既存のコンテンツを編集 | PATCH /articles/{articleId} · PATCH /categories/{categoryId} | update_kb_article · update_kb_category |
| カテゴリーの並べ替え | PUT /categories/reorder | reorder_kb_categories |
| ヘルプセンター設定の読み取り/編集 | GET /help-center · PATCH /help-center | get_help_center · update_help_center |
| オーディエンスの一覧表示/編集 | GET /audiences · PATCH /audiences/{audienceId} | list_kb_audiences · update_kb_audience |
| 削除 | DELETE /articles/{articleId} · /categories/{categoryId} · /audiences/{audienceId} | delete_kb_article · delete_kb_category · delete_kb_audience |
記事の削除は元に戻せません。カテゴリーを削除すると、その記事は未分類になり、引き継いでいたカテゴリーの制限も外れます。オーディエンスを削除すると、その属性ルールは取り除かれますが、紐づいたコンテンツは認証済み限定のままです。削除する前に影響を受けるアクセスを確認し、記事を残したまま取り下げたい場合は下書きを使ってください。
同期のトラブルシューティング
- 400 · 不正なリクエスト
- 必須の title/body または name、正確なフィールド名、有効なルールの型、並べ替えのIDがすべてそろっているかを確認してください。categoryId と categoryExternalId は、両方ではなくどちらか一方を送ってください。
- 401 · 未認証
- 有効なBearerキーを指定してください。期限切れになっていないか、取り消されていないかを確認してください。
- 403 · アクセス禁止
- キーのスコープ、キー所有者の権限、ワークスペースの請求状況を確認してください。MCPクライアントのツールが読み取り専用になっている場合は、必要なアクセス権で再接続してください。
- 404 · 見つかりません
- ソースが選択したワークスペースに属し、認証情報で許可されていることを確認してください。新しいヘルプセンターは、まずコンテンツを作成して初期化してください。読み取りでは作成されません。
- 409 · 競合
- 現在のコンテンツを再取得し、競合するIDや古い並び順を解消してから再試行してください。
- 413 · サイズ超過
- 画像を上限の25MB未満にしてください。
- 429 · レート制限
- Retry-After の時間が経過するまで待ってから再試行してください。RateLimit-Limit、RateLimit-Remaining、RateLimit-Reset を使ってリクエストのペースを調整してください。