API externe
Sim fournit une API externe complÚte pour interroger les journaux d'exécution des workflows et configurer des webhooks pour des notifications en temps réel lorsque les workflows sont terminés.
Authentification
Toutes les requĂȘtes API nĂ©cessitent une clĂ© API transmise dans l'en-tĂȘte x-api-key
:
curl -H "x-api-key: YOUR_API_KEY" \
https://sim.ai/api/v1/logs?workspaceId=YOUR_WORKSPACE_ID
Vous pouvez générer des clés API depuis vos paramÚtres utilisateur dans le tableau de bord Sim.
API des journaux
Toutes les réponses API incluent des informations sur vos limites d'exécution de workflow et votre utilisation :
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"limit": 60, // Max sync workflow executions per minute
"remaining": 58, // Remaining sync workflow executions
"resetAt": "..." // When the window resets
},
"async": {
"limit": 60, // Max async workflow executions per minute
"remaining": 59, // Remaining async workflow executions
"resetAt": "..." // When the window resets
}
},
"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
}
}
Remarque : Les limites de dĂ©bit dans le corps de la rĂ©ponse concernent les exĂ©cutions de workflow. Les limites de dĂ©bit pour l'appel de ce point de terminaison API se trouvent dans les en-tĂȘtes de rĂ©ponse (X-RateLimit-*
).
Interrogation des journaux
Interrogez les journaux d'exécution des workflows avec de nombreuses options de filtrage.
GET /api/v1/logs
ParamĂštres requis :
workspaceId
- Votre ID d'espace de travail
Filtres optionnels :
workflowIds
- IDs de workflow séparés par des virgulesfolderIds
- IDs de dossier séparés par des virgulestriggers
- Types de déclencheurs séparés par des virgules :api
,webhook
,schedule
,manual
,chat
level
- Filtrer par niveau :info
,error
startDate
- Horodatage ISO pour le début de la plage de datesendDate
- Horodatage ISO pour la fin de la plage de datesexecutionId
- Correspondance exacte de l'ID d'exécutionminDurationMs
- Durée minimale d'exécution en millisecondesmaxDurationMs
- Durée maximale d'exécution en millisecondesminCost
- Coût minimal d'exécutionmaxCost
- Coût maximal d'exécutionmodel
- Filtrer par modÚle d'IA utilisé
Pagination :
limit
- Résultats par page (par défaut : 100)cursor
- Curseur pour la page suivanteorder
- Ordre de tri :desc
,asc
(par défaut : desc)
Niveau de détail :
details
- Niveau de détail de la réponse :basic
,full
(par défaut : basic)includeTraceSpans
- Inclure les intervalles de trace (par défaut : false)includeFinalOutput
- Inclure la sortie finale (par défaut : 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": {
"limit": 60,
"remaining": 58,
"resetAt": "2025-01-01T12:35:56.789Z"
},
"async": {
"limit": 60,
"remaining": 59,
"resetAt": "2025-01-01T12:35:56.789Z"
}
},
"usage": {
"currentPeriodCost": 1.234,
"limit": 10,
"plan": "pro",
"isExceeded": false
}
}
}
Obtenir les détails du journal
Récupérer des informations détaillées sur une entrée de journal spécifique.
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": {
"limit": 60,
"remaining": 58,
"resetAt": "2025-01-01T12:35:56.789Z"
},
"async": {
"limit": 60,
"remaining": 59,
"resetAt": "2025-01-01T12:35:56.789Z"
}
},
"usage": {
"currentPeriodCost": 1.234,
"limit": 10,
"plan": "pro",
"isExceeded": false
}
}
}
}
Obtenir les détails d'exécution
Récupérer les détails d'exécution, y compris l'instantané de l'état du workflow.
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": {...}
}
}
Abonnements aux webhooks
Recevez des notifications en temps réel lorsque les exécutions de workflow sont terminées. Les webhooks sont configurés via l'interface utilisateur Sim dans l'éditeur de workflow.
Configuration
Les webhooks peuvent ĂȘtre configurĂ©s pour chaque workflow via l'interface utilisateur de l'Ă©diteur de workflow. Cliquez sur l'icĂŽne webhook dans la barre de contrĂŽle pour configurer vos abonnements aux webhooks.
Options de configuration disponibles :
url
: URL de votre endpoint webhooksecret
: Secret optionnel pour la vérification de signature HMACincludeFinalOutput
: Inclure la sortie finale du workflow dans la charge utileincludeTraceSpans
: Inclure les intervalles de trace d'exécution détaillésincludeRateLimits
: Inclure les informations de limite de débit du propriétaire du workflowincludeUsageData
: Inclure les données d'utilisation et de facturation du propriétaire du workflowlevelFilter
: Tableau des niveaux de journal Ă recevoir (info
,error
)triggerFilter
: Tableau des types de déclencheurs à recevoir (api
,webhook
,schedule
,manual
,chat
)active
: Activer/désactiver l'abonnement webhook
Charge utile du webhook
Lorsqu'une exĂ©cution de workflow est terminĂ©e, Sim envoie une requĂȘte POST Ă votre URL webhook :
{
"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"
}
}
En-tĂȘtes de webhook
Chaque requĂȘte webhook inclut ces en-tĂȘtes :
sim-event
: Type d'événement (toujoursworkflow.execution.completed
)sim-timestamp
: Horodatage Unix en millisecondessim-delivery-id
: ID de livraison unique pour l'idempotencesim-signature
: Signature HMAC-SHA256 pour vérification (si un secret est configuré)Idempotency-Key
: Identique à l'ID de livraison pour la détection des doublons
Vérification de signature
Si vous configurez un secret webhook, vérifiez la signature pour vous assurer que le webhook provient de 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...
Politique de réessai
Les livraisons de webhook échouées sont réessayées avec un backoff exponentiel et du jitter :
- Nombre maximum de tentatives : 5
- Délais de réessai : 5 secondes, 15 secondes, 1 minute, 3 minutes, 10 minutes
- Jitter : jusqu'à 10 % de délai supplémentaire pour éviter l'effet de horde
- Seules les réponses HTTP 5xx et 429 déclenchent des réessais
- Les livraisons expirent aprĂšs 30 secondes
Les livraisons de webhook sont traitées de maniÚre asynchrone et n'affectent pas les performances d'exécution du workflow.
Bonnes pratiques
-
Stratégie de polling : lors de l'interrogation des logs, utilisez la pagination basée sur curseur avec
order=asc
etstartDate
pour récupérer efficacement les nouveaux logs. -
SĂ©curitĂ© des webhooks : configurez toujours un secret webhook et vĂ©rifiez les signatures pour vous assurer que les requĂȘtes proviennent de Sim.
-
Idempotence : utilisez l'en-tĂȘte
Idempotency-Key
pour détecter et gérer les livraisons de webhook en double. -
Confidentialité : par défaut,
finalOutput
ettraceSpans
sont exclus des réponses. Activez-les uniquement si vous avez besoin des données et comprenez les implications en matiÚre de confidentialité. -
Limitation de dĂ©bit : implĂ©mentez un backoff exponentiel lorsque vous recevez des rĂ©ponses 429. VĂ©rifiez l'en-tĂȘte
Retry-After
pour connaßtre le temps d'attente recommandé.
Limitation de débit
L'API implémente une limitation de débit pour garantir une utilisation équitable :
- Plan gratuit : 10 requĂȘtes par minute
- Plan Pro : 30 requĂȘtes par minute
- Plan Ăquipe : 60 requĂȘtes par minute
- Plan Entreprise : Limites personnalisées
Les informations de limite de dĂ©bit sont incluses dans les en-tĂȘtes de rĂ©ponse :
X-RateLimit-Limit
: Nombre maximum de requĂȘtes par fenĂȘtreX-RateLimit-Remaining
: RequĂȘtes restantes dans la fenĂȘtre actuelleX-RateLimit-Reset
: Horodatage ISO indiquant quand la fenĂȘtre se rĂ©initialise
Exemple : Interrogation pour nouveaux journaux
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);
Exemple : Traitement des webhooks
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');
});