外部API リファレンス
SonaeTocのREST APIを使って、プログラム・CI/CDパイプライン・AIエージェントから監視を操作できます。 ベースURL: https://sonaetoc.com/v1
クイックスタート
APIキーを発行
最初のリクエストを送信
curl https://sonaetoc.com/v1/tenant \ -H "Authorization: Bearer sonaetoc_live_YOUR_API_KEY"
モニターを作成
curl -X POST https://sonaetoc.com/v1/monitors \
-H "Authorization: Bearer sonaetoc_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "本番トップページ",
"type": "visual",
"url": "https://example.com",
"interval_minutes": 60,
"visual_config": {
"viewport": { "width": 1920, "height": 1080 },
"threshold": 0.1,
"wait_for": "networkidle",
"full_page": true,
"delay_ms": 1000
}
}'認証
すべてのリクエストに Authorization: Bearer ヘッダーが必要です。
Authorization: Bearer sonaetoc_live_xxxxxxxxxx
スコープ
キー作成時にスコープを制限できます。デフォルトは全権限です。
| スコープ | 権限 |
|---|---|
| monitors:read | モニター設定の閲覧 |
| monitors:write | モニターの作成・更新・削除 |
| monitors:execute | 手動実行のトリガー |
| results:read | 監視結果の閲覧 |
| crawls:read | クロール結果の閲覧 |
| crawls:write | クロールジョブの作成・削除 |
| notifications:read | 通知設定の閲覧 |
| notifications:write | 通知設定の作成・更新・削除 |
レスポンス形式
日時はすべて ISO 8601(UTC)です。
{
"data": { "id": "mon_xxx", "name": "..." },
"meta": { "request_id": "req_abc123" }
}{
"data": [ ... ],
"meta": {
"request_id": "req_abc123",
"has_more": true,
"next_cursor": "eyJ..."
}
}{
"error": {
"code": "validation_error",
"message": "name is required (1-100 chars)."
},
"meta": { "request_id": "req_abc123" }
}モニター
/v1/monitorsmonitors:readモニター一覧を取得
| パラメータ | 型 | 説明 |
|---|---|---|
| type | string | visual / healthcheck / motion |
| status | string | ok / changed / error |
| is_active | boolean | 有効/無効でフィルタ |
| limit | number | 取得件数(デフォルト20、最大100) |
| cursor | string | ページネーションカーソル |
| include_count | boolean | trueで total_count を返却 |
/v1/monitorsmonitors:writeモニターを作成
{
"name": "API /health",
"type": "healthcheck",
"url": "https://api.example.com/health",
"interval_minutes": 15,
"healthcheck_config": {
"method": "GET",
"expected_status": 200,
"timeout_ms": 10000
}
}{
"name": "ログインフロー",
"type": "motion",
"url": "https://example.com/login",
"interval_minutes": 1440,
"motion_config": {
"viewport": { "width": 1920, "height": 1080 },
"threshold": 0.5,
"steps": [
{ "action": "fill", "selector": "#email", "value": "test@example.com" },
{ "action": "fill", "selector": "#password", "value": "secret", "is_secret": true },
{ "action": "click", "selector": "#submit", "wait_for_navigation": true }
]
}
}/v1/monitors/{id}monitors:readモニター詳細を取得
/v1/monitors/{id}monitors:writeモニターを更新(シャローマージ、配列は全置換)
/v1/monitors/{id}monitors:writeモニターを削除(結果も自動削除)
/v1/monitors/{id}/executemonitors:execute手動実行をトリガー(202 Accepted)
/v1/monitors/{id}/activatemonitors:writeモニターを有効化
/v1/monitors/{id}/deactivatemonitors:writeモニターを無効化
監視結果
/v1/monitors/{id}/resultsresults:read結果一覧を取得
| パラメータ | 型 | 説明 |
|---|---|---|
| status | string | ok / changed / error |
| from | string | 開始日時(ISO 8601) |
| to | string | 終了日時(ISO 8601) |
| limit | number | 取得件数(デフォルト20、最大100) |
| cursor | string | ページネーションカーソル |
レスポンスはモニタータイプに応じて異なります:
{
"id": "result_xyz",
"monitor_id": "mon_abc",
"status": "changed",
"is_manual": false,
"visual": {
"screenshot_url": "https://storage...(署名付き)",
"previous_screenshot_url": "https://storage...",
"diff_percentage": 3.5,
"pixel_count": 12400
},
"executed_at": "2026-04-05T10:00:00Z"
}{
"id": "result_hc1",
"monitor_id": "mon_hc",
"status": "ok",
"healthcheck": {
"status_code": 200,
"response_time_ms": 245,
"error_message": null
},
"executed_at": "2026-04-05T10:00:00Z"
}{
"id": "result_mt1",
"monitor_id": "mon_mt",
"status": "changed",
"motion": {
"video_url": "https://storage...(署名付き)",
"total_diff_percentage": 3.5,
"steps_executed": 4,
"steps_failed": 0,
"frames": [
{ "step_index": 0, "label": "メール入力", "diff_percentage": 0, "status": "ok" },
{ "step_index": 2, "label": "ログイン", "diff_percentage": 3.5, "status": "changed" }
]
},
"executed_at": "2026-04-05T10:00:00Z"
}/v1/monitors/{id}/results/latestresults:read最新結果を1件取得(レスポンスは上記と同形式)
/v1/monitors/{id}/results/{resultId}results:read結果詳細を取得(レスポンスは上記と同形式)
/v1/monitors/{id}/statsresults:read統計情報を取得
| パラメータ | 型 | 説明 |
|---|---|---|
| period | string | 24h / 7d / 30d(デフォルト 7d) |
{
"monitor_id": "mon_abc",
"period": "7d",
"total_checks": 168,
"status_counts": { "ok": 165, "changed": 2, "error": 1 },
"uptime_percentage": 99.4,
"avg_response_time_ms": 1250,
"avg_diff_percentage": 0.03,
"max_diff_percentage": 5.2
}クロール
/v1/crawlscrawls:readクロールジョブ一覧
/v1/crawlscrawls:writeクロールを開始(202 Accepted)
{
"name": "example.com 構造チェック",
"url": "https://example.com",
"config": {
"max_depth": 3,
"max_pages": 100,
"rendering": "javascript"
}
}/v1/crawls/{id}crawls:readジョブ詳細・進捗
/v1/crawls/{id}/pagescrawls:readクロール済みページ一覧
/v1/crawls/{id}crawls:writeジョブを削除
通知設定
/v1/notificationsnotifications:read通知設定一覧
/v1/notificationsnotifications:write通知設定を作成
{
"name": "Slack通知",
"type": "slack",
"events": ["visual_changed", "healthcheck_down"],
"slack_config": {
"webhook_url": "https://hooks.slack.com/services/xxx/yyy/zzz"
}
}/v1/notifications/{id}notifications:write通知設定を更新
/v1/notifications/{id}notifications:write通知設定を削除
/v1/notifications/{id}/testnotifications:writeテスト通知を送信
テナント情報
/v1/tenantプラン・使用量・制限値を取得
{
"id": "tenant_abc",
"name": "My Company",
"plan": "pro",
"limits": {
"max_visual": 5, "max_healthcheck": 10,
"max_motion": 1, "max_crawls": 20,
"min_interval_minutes": 10,
"api_keys": 3,
"api_requests_per_hour": 60,
"api_requests_per_day": 500
},
"usage": {
"visual_count": 3, "healthcheck_count": 5,
"motion_count": 0, "crawls_this_month": 2,
"api_keys_count": 1, "api_requests_today": 42
}
}レート制限
| 制限 | Free | Pro | Enterprise |
|---|---|---|---|
| APIキー数 | 1 | 3 | 10 |
| リクエスト/時 | 30 | 60 | 300 |
| リクエスト/日 | 200 | 500 | 3,000 |
レスポンスヘッダー X-RateLimit-Remaining で残りリクエスト数を確認できます。 超過時は 429 が返り、X-RateLimit-Reset に再試行可能なUnixタイムスタンプが入ります。
エラーコード
| HTTP | code | 原因と対処 |
|---|---|---|
| 400 | validation_error | リクエストボディが不正。message を確認してフィールドを修正 |
| 400 | invalid_request | URLがプライベートIPに解決される等。SSRF対策 |
| 401 | unauthorized | APIキーが無効・期限切れ・未指定。Authorizationヘッダーを確認 |
| 403 | forbidden | スコープ不足。必要なスコープ付きのキーを使用 |
| 403 | plan_limit_exceeded | プラン上限に到達。不要なリソースを削除するかアップグレード |
| 404 | not_found | リソースが存在しない。IDを確認 |
| 429 | rate_limit_exceeded | レート制限超過。X-RateLimit-Reset まで待機 |
| 500 | internal_error | サーバーエラー。しばらく待って再試行 |
CI/CD連携パターン
デプロイ後にビジュアルチェックを自動実行し、差分が閾値を超えたらパイプラインを失敗させる例です。
- name: Visual regression check
env:
API_KEY: ${{ secrets.SONAETOC_API_KEY }}
MONITOR_ID: mon_xxxxxxxxxxxx
run: |
# 1. 手動実行をトリガー
curl -sf -X POST \
"https://sonaetoc.com/v1/monitors/$MONITOR_ID/execute" \
-H "Authorization: Bearer $API_KEY"
# 2. 結果が出るまで待機
sleep 30
# 3. 最新結果を取得して差分を確認
RESULT=$(curl -sf \
"https://sonaetoc.com/v1/monitors/$MONITOR_ID/results/latest" \
-H "Authorization: Bearer $API_KEY")
DIFF=$(echo "$RESULT" | jq -r '.data.visual.diff_percentage // 0')
echo "Visual diff: $DIFF%"
if (( $(echo "$DIFF > 5.0" | bc -l) )); then
echo "::error::Visual regression detected ($DIFF%)"
exit 1
fi冪等性(Idempotency-Key)
POSTリクエストに Idempotency-Key ヘッダーを付与すると、同じキーでの再リクエスト時にキャッシュされたレスポンスが返されます。ネットワークエラー時のリトライに安全です。
curl -X POST https://sonaetoc.com/v1/monitors \
-H "Authorization: Bearer sonaetoc_live_xxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{ ... }'ページネーション
一覧エンドポイントはカーソルベースのページネーションを使用します。meta.next_cursor を次のリクエストの cursor パラメータに渡します。
# 1ページ目 curl "https://sonaetoc.com/v1/monitors?limit=10" \ -H "Authorization: Bearer $API_KEY" # → meta.next_cursor: "eyJ..." # 2ページ目 curl "https://sonaetoc.com/v1/monitors?limit=10&cursor=eyJ..." \ -H "Authorization: Bearer $API_KEY"