Simは、ワークフローの実行ログを照会したり、ワークフローが完了したときにリアルタイム通知を設定するためのWebhookを設定したりするための包括的な外部APIを提供しています。
認証
すべてのAPIリクエストには、x-api-keyヘッダーで渡されるAPIキーが必要です:
curl -H "x-api-key: YOUR_API_KEY" \
https://sim.ai/api/v1/logs?workspaceId=YOUR_WORKSPACE_IDSimダッシュボードのユーザーセッティングからAPIキーを生成できます。
ログAPI
すべてのAPIレスポンスには、ワークフロー実行の制限と使用状況に関する情報が含まれています:
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"requestsPerMinute": 60, // Sustained rate limit per minute
"maxBurst": 120, // Maximum burst capacity
"remaining": 118, // Current tokens available (up to maxBurst)
"resetAt": "..." // When tokens next refill
},
"async": {
"requestsPerMinute": 200, // Sustained rate limit per minute
"maxBurst": 400, // Maximum burst capacity
"remaining": 398, // Current tokens available
"resetAt": "..." // When tokens next refill
}
},
"usage": {
"currentPeriodCost": 1.234, // Current billing period usage in USD
"limit": 10, // Usage limit in USD
"plan": "pro", // Current subscription plan
"isExceeded": false // Whether limit is exceeded
}
}注意: レート制限はトークンバケットアルゴリズムを使用しています。最近の割り当てを完全に使用していない場合、remainingはrequestsPerMinuteを超えてmaxBurstまで達することができ、バーストトラフィックを許可します。レスポンスボディのレート制限はワークフロー実行のためのものです。このAPIエンドポイントを呼び出すためのレート制限はレスポンスヘッダー(X-RateLimit-*)にあります。
ログの照会
広範なフィルタリングオプションでワークフロー実行ログを照会します。
GET /api/v1/logs必須パラメータ:
workspaceId- ワークスペースID
オプションフィルター:
workflowIds- カンマ区切りのワークフローIDfolderIds- カンマ区切りのフォルダIDtriggers- カンマ区切りのトリガータイプ:api,webhook,schedule,manual,chatlevel- レベルでフィルタリング:info,errorstartDate- 日付範囲開始のISOタイムスタンプendDate- 日付範囲終了のISOタイムスタンプexecutionId- 正確な実行ID一致minDurationMs- 最小実行時間(ミリ秒)maxDurationMs- 最大実行時間(ミリ秒)minCost- 最小実行コストmaxCost- 最大実行コストmodel- 使用されたAIモデルでフィルタリング
ページネーション:
limit- ページあたりの結果数(デフォルト:100)cursor- 次ページのカーソルorder- ソート順:desc,asc(デフォルト:desc)
詳細レベル:
details- レスポンス詳細レベル:basic,full(デフォルト: basic)includeTraceSpans- トレーススパンを含める (デフォルト: false)includeFinalOutput- 最終出力を含める (デフォルト: false)
{
"data": [
{
"id": "log_abc123",
"workflowId": "wf_xyz789",
"executionId": "exec_def456",
"level": "info",
"trigger": "api",
"startedAt": "2025-01-01T12:34:56.789Z",
"endedAt": "2025-01-01T12:34:57.123Z",
"totalDurationMs": 334,
"cost": {
"total": 0.00234
},
"files": null
}
],
"nextCursor": "eyJzIjoiMjAyNS0wMS0wMVQxMjozNDo1Ni43ODlaIiwiaWQiOiJsb2dfYWJjMTIzIn0",
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"requestsPerMinute": 60,
"maxBurst": 120,
"remaining": 118,
"resetAt": "2025-01-01T12:35:56.789Z"
},
"async": {
"requestsPerMinute": 200,
"maxBurst": 400,
"remaining": 398,
"resetAt": "2025-01-01T12:35:56.789Z"
}
},
"usage": {
"currentPeriodCost": 1.234,
"limit": 10,
"plan": "pro",
"isExceeded": false
}
}
}ログ詳細の取得
特定のログエントリに関する詳細情報を取得します。
GET /api/v1/logs/{id}{
"data": {
"id": "log_abc123",
"workflowId": "wf_xyz789",
"executionId": "exec_def456",
"level": "info",
"trigger": "api",
"startedAt": "2025-01-01T12:34:56.789Z",
"endedAt": "2025-01-01T12:34:57.123Z",
"totalDurationMs": 334,
"workflow": {
"id": "wf_xyz789",
"name": "My Workflow",
"description": "Process customer data"
},
"executionData": {
"traceSpans": [...],
"finalOutput": {...}
},
"cost": {
"total": 0.00234,
"tokens": {
"prompt": 123,
"completion": 456,
"total": 579
},
"models": {
"gpt-4o": {
"input": 0.001,
"output": 0.00134,
"total": 0.00234,
"tokens": {
"prompt": 123,
"completion": 456,
"total": 579
}
}
}
},
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"requestsPerMinute": 60,
"maxBurst": 120,
"remaining": 118,
"resetAt": "2025-01-01T12:35:56.789Z"
},
"async": {
"requestsPerMinute": 200,
"maxBurst": 400,
"remaining": 398,
"resetAt": "2025-01-01T12:35:56.789Z"
}
},
"usage": {
"currentPeriodCost": 1.234,
"limit": 10,
"plan": "pro",
"isExceeded": false
}
}
}
}実行詳細の取得
ワークフロー状態のスナップショットを含む実行詳細を取得します。
GET /api/v1/logs/executions/{executionId}{
"executionId": "exec_def456",
"workflowId": "wf_xyz789",
"workflowState": {
"blocks": {...},
"edges": [...],
"loops": {...},
"parallels": {...}
},
"executionMetadata": {
"trigger": "api",
"startedAt": "2025-01-01T12:34:56.789Z",
"endedAt": "2025-01-01T12:34:57.123Z",
"totalDurationMs": 334,
"cost": {...}
}
}通知
ワークフローの実行が完了したときに、Webhook、メール、またはSlackを通じてリアルタイム通知を受け取ることができます。通知はログページからワークスペースレベルで設定されます。
設定
ログページからメニューボタンをクリックし、「通知を設定する」を選択して通知を設定します。
通知チャネル:
- Webhook: エンドポイントにHTTP POSTリクエストを送信
- メール: 実行詳細を含むメール通知を受信
- Slack: Slackチャンネルにメッセージを投稿
ワークフロー選択:
- 監視する特定のワークフローを選択
- または「すべてのワークフロー」を選択して現在および将来のワークフローを含める
フィルタリングオプション:
levelFilter: 受信するログレベル (info,error)triggerFilter: 受信するトリガータイプ (api,webhook,schedule,manual,chat)
オプションデータ:
includeFinalOutput: ワークフローの最終出力を含めるincludeTraceSpans: 詳細な実行トレーススパンを含めるincludeRateLimits: レート制限情報(同期/非同期の制限と残り)を含めるincludeUsageData: 請求期間の使用状況と制限を含める
アラートルール
すべての実行について通知を受け取る代わりに、問題が検出された場合にのみ通知されるようにアラートルールを設定できます:
連続失敗
- X回連続して実行が失敗した後にアラート(例:3回連続の失敗)
- 実行が成功すると、リセットされます
失敗率
- 過去Y時間の失敗率がX%を超えた場合にアラート
- ウィンドウ内で最低5回の実行が必要
- 完全な時間ウィンドウが経過した後にのみトリガーされます
レイテンシーしきい値
- 実行がX秒以上かかった場合にアラート
- 遅いまたは停止しているワークフローを検出するのに役立ちます
レイテンシースパイク
- 実行が平均よりX%遅い場合にアラート
- 設定された時間ウィンドウでの平均所要時間と比較
- ベースラインを確立するために最低5回の実行が必要
コスト閾値
- 単一の実行コストが$Xを超えた場合にアラート
- 高価なLLM呼び出しを検出するのに役立つ
アクティビティなし
- X時間以内に実行がない場合にアラート
- 定期的に実行されるべきスケジュールされたワークフローの監視に役立つ
エラー数
- 時間枠内でエラー数がXを超えた場合にアラート
- 連続ではなく、総エラー数を追跡
すべてのアラートタイプには、通知スパムを防ぐための1時間のクールダウンが含まれています。
Webhook設定
Webhookの場合、追加オプションが利用可能です:
url:WebhookエンドポイントURLsecret:HMAC署名検証用のオプションシークレット
ペイロード構造
ワークフロー実行が完了すると、Simは以下のペイロードを送信します(webhook POST、メール、またはSlackを介して):
{
"id": "evt_123",
"type": "workflow.execution.completed",
"timestamp": 1735925767890,
"data": {
"workflowId": "wf_xyz789",
"executionId": "exec_def456",
"status": "success",
"level": "info",
"trigger": "api",
"startedAt": "2025-01-01T12:34:56.789Z",
"endedAt": "2025-01-01T12:34:57.123Z",
"totalDurationMs": 334,
"cost": {
"total": 0.00234,
"tokens": {
"prompt": 123,
"completion": 456,
"total": 579
},
"models": {
"gpt-4o": {
"input": 0.001,
"output": 0.00134,
"total": 0.00234,
"tokens": {
"prompt": 123,
"completion": 456,
"total": 579
}
}
}
},
"files": null,
"finalOutput": {...}, // Only if includeFinalOutput=true
"traceSpans": [...], // Only if includeTraceSpans=true
"rateLimits": {...}, // Only if includeRateLimits=true
"usage": {...} // Only if includeUsageData=true
},
"links": {
"log": "/v1/logs/log_abc123",
"execution": "/v1/logs/executions/exec_def456"
}
}Webhookヘッダー
各Webhookリクエストには以下のヘッダーが含まれます(Webhookチャンネルのみ):
sim-event:イベントタイプ(常にworkflow.execution.completed)sim-timestamp:ミリ秒単位のUnixタイムスタンプsim-delivery-id:べき等性のための一意の配信IDsim-signature:検証用のHMAC-SHA256署名(シークレットが設定されている場合)Idempotency-Key:重複検出のための配信IDと同じ
署名検証
Webhookシークレットを設定した場合、署名を検証してWebhookがSimからのものであることを確認します:
import crypto from 'crypto';
function verifyWebhookSignature(body, signature, secret) {
const [timestampPart, signaturePart] = signature.split(',');
const timestamp = timestampPart.replace('t=', '');
const expectedSignature = signaturePart.replace('v1=', '');
const signatureBase = `${timestamp}.${body}`;
const hmac = crypto.createHmac('sha256', secret);
hmac.update(signatureBase);
const computedSignature = hmac.digest('hex');
return computedSignature === expectedSignature;
}
// In your webhook handler
app.post('/webhook', (req, res) => {
const signature = req.headers['sim-signature'];
const body = JSON.stringify(req.body);
if (!verifyWebhookSignature(body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook...
});import hmac
import hashlib
import json
def verify_webhook_signature(body: str, signature: str, secret: str) -> bool:
timestamp_part, signature_part = signature.split(',')
timestamp = timestamp_part.replace('t=', '')
expected_signature = signature_part.replace('v1=', '')
signature_base = f"{timestamp}.{body}"
computed_signature = hmac.new(
secret.encode(),
signature_base.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(computed_signature, expected_signature)
# In your webhook handler
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('sim-signature')
body = json.dumps(request.json)
if not verify_webhook_signature(body, signature, os.environ['WEBHOOK_SECRET']):
return 'Invalid signature', 401
# Process the webhook...リトライポリシー
失敗したWebhook配信は指数バックオフとジッターを使用して再試行されます:
- 最大試行回数:5回
- リトライ遅延:5秒、15秒、1分、3分、10分
- ジッター:サンダリングハード問題を防ぐために最大10%の追加遅延
- HTTP 5xxと429レスポンスのみがリトライをトリガー
- 配信は30秒後にタイムアウト
Webhook配信は非同期で処理され、ワークフロー実行のパフォーマンスに影響しません。
ベストプラクティス
-
ポーリング戦略: ログをポーリングする場合、
order=ascとstartDateを使用したカーソルベースのページネーションを利用して、新しいログを効率的に取得してください。 -
Webhookセキュリティ: 常にWebhookシークレットを設定し、署名を検証して、リクエストがSimからのものであることを確認してください。
-
べき等性:
Idempotency-Keyヘッダーを使用して、重複するWebhook配信を検出し処理してください。 -
プライバシー: デフォルトでは、
finalOutputとtraceSpansはレスポンスから除外されます。データが必要で、プライバシーへの影響を理解している場合にのみ有効にしてください。 -
レート制限: 429レスポンスを受け取った場合は指数バックオフを実装してください。推奨待機時間については
Retry-Afterヘッダーを確認してください。
レート制限
APIはトークンバケットアルゴリズムをレート制限に使用し、バーストトラフィックを許可しながら公平な使用を提供します:
| プラン | リクエスト/分 | バースト容量 |
|---|---|---|
| 無料 | 10 | 20 |
| プロ | 30 | 60 |
| チーム | 60 | 120 |
| エンタープライズ | 120 | 240 |
仕組み:
- トークンは
requestsPerMinuteのレートで補充されます - アイドル状態のとき、最大
maxBurstトークンまで蓄積できます - 各リクエストは1トークンを消費します
- バースト容量によりトラフィックスパイクの処理が可能になります
レート制限情報はレスポンスヘッダーに含まれています:
X-RateLimit-Limit:1分あたりのリクエスト数(補充レート)X-RateLimit-Remaining:現在利用可能なトークンX-RateLimit-Reset:トークンが次に補充されるISOタイムスタンプ
例:新しいログのポーリング
let cursor = null;
const workspaceId = 'YOUR_WORKSPACE_ID';
const startDate = new Date().toISOString();
async function pollLogs() {
const params = new URLSearchParams({
workspaceId,
startDate,
order: 'asc',
limit: '100'
});
if (cursor) {
params.append('cursor', cursor);
}
const response = await fetch(
`https://sim.ai/api/v1/logs?${params}`,
{
headers: {
'x-api-key': 'YOUR_API_KEY'
}
}
);
if (response.ok) {
const data = await response.json();
// Process new logs
for (const log of data.data) {
console.log(`New execution: ${log.executionId}`);
}
// Update cursor for next poll
if (data.nextCursor) {
cursor = data.nextCursor;
}
}
}
// Poll every 30 seconds
setInterval(pollLogs, 30000);例:ウェブフックの処理
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
app.post('/sim-webhook', (req, res) => {
// Verify signature
const signature = req.headers['sim-signature'];
const body = JSON.stringify(req.body);
if (!verifyWebhookSignature(body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Check timestamp to prevent replay attacks
const timestamp = parseInt(req.headers['sim-timestamp']);
const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
if (timestamp < fiveMinutesAgo) {
return res.status(401).send('Timestamp too old');
}
// Process the webhook
const event = req.body;
switch (event.type) {
case 'workflow.execution.completed':
const { workflowId, executionId, status, cost } = event.data;
if (status === 'error') {
console.error(`Workflow ${workflowId} failed: ${executionId}`);
// Handle error...
} else {
console.log(`Workflow ${workflowId} completed: ${executionId}`);
console.log(`Cost: $${cost.total}`);
// Process successful execution...
}
break;
}
// Return 200 to acknowledge receipt
res.status(200).send('OK');
});
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});