Webhooks & Callbacks
Comment CodeCourier gère les webhooks entrants de Clerk (cycle de vie utilisateur), la vérification de signature Svix, et le point de terminaison de callback interne Trigger.dev avec une référence complète des opérations organisée par domaine.
Les webhooks permettent à des services externes de notifier CodeCourier d’événements en temps réel. CodeCourier traite les webhooks de Clerk (pour les événements de cycle de vie utilisateur) et expose un point de terminaison de callback interne pour Trigger.dev (pour le reporting de progression de jobs en arrière-plan et les opérations de données). Cette page documente les deux surfaces de webhook, leurs modèles d’authentification, les formats de payload, la vérification de signature, et la référence complète des opérations de callback Trigger.dev organisée par domaine.
Webhooks Clerk
CodeCourier enregistre un point de terminaison de webhook à /clerk/webhook pour recevoir les événements de cycle de vie utilisateur de Clerk. Le cas d’usage principal est la gestion de la suppression d’utilisateur - quand un utilisateur supprime son compte Clerk, CodeCourier reçoit un webhook et peut nettoyer les données associées.
Événements pris en charge
- user.deleted - Déclenché quand un compte utilisateur est supprimé dans Clerk. CodeCourier utilise ceci pour déclencher des workflows de nettoyage de données, incluant la suppression ou l’anonymisation des ressources appartenant à l’utilisateur.
Format du payload
Les payloads de webhook Clerk suivent le format standard Svix. Chaque payload contient :
type- La chaîne de type d’événement (p. ex.,"user.deleted").data- Le payload d’événement contenant la ressource affectée. Pour les événements utilisateur, cela inclutid(l’ID utilisateur Clerk),email_addresses, et d’autres champs utilisateur.object- Toujours"event".
Vérification de signature
Tous les webhooks Clerk entrants sont vérifiés en utilisant le protocole de signature Svix. Ceci garantit que les payloads de webhook proviennent réellement de Clerk et n’ont pas été altérés en transit.
Processus de vérification
- Extraction des headers. Le serveur lit trois headers requis de la requête entrante :
svix-id(identifiant unique de message),svix-timestamp(timestamp Unix en secondes), etsvix-signature(une ou plusieurs signatures versionnées). - Validation du timestamp. Le serveur vérifie que le timestamp est dans une fenêtre de cinq minutes par rapport à l’heure actuelle. Les requêtes hors de cette fenêtre de tolérance sont rejetées pour prévenir les attaques par rejeu.
- Calcul de la signature attendue. Le contenu signé est construit comme
{svix-id}.{svix-timestamp}.{raw-body}. Le serveur calcule un HMAC-SHA256 de ce contenu en utilisant le secret du webhook (la variable d’environnementCLERK_WEBHOOK_SECRET, avec le préfixewhsec_retiré et le reste décodé en base64). - Comparaison des signatures. Le header
svix-signaturepeut contenir plusieurs signatures séparées par des espaces, chacune préfixée par une version (p. ex.,v1,<base64>). Le serveur vérifie chaque signaturev1par rapport à la valeur calculée en utilisant une comparaison caractère par caractère à temps constant pour prévenir les attaques par canal auxiliaire temporel. - Acceptation ou rejet. Si une signature correspond, le webhook est accepté et traité. Sinon, il est rejeté avec un statut 401.
Variables d’environnement
CLERK_WEBHOOK_SECRET- Le secret de signature Svix de votre dashboard Clerk. C’est une clé HMAC encodée en base64 préfixée parwhsec_. Le serveur refusera de traiter tout webhook si cette variable n’est pas définie.
Point de terminaison de callback Trigger.dev
Le point de terminaison /trigger/callback est une API interne utilisée exclusivement par les jobs en arrière-plan Trigger.dev de CodeCourier. Elle n’est pas destinée à un usage externe direct - elle est documentée ici afin que les développeurs puissent comprendre le flux de données entre la couche d’orchestration et la base de données Convex.
Les tâches Trigger.dev appellent ce point de terminaison pour écrire des données vers Convex (mises à jour de statut de run, messages de sandbox, extraction de learnings, enregistrement d’usage, etc.) sans nécessiter d’accès direct au client Convex depuis le runtime Trigger.dev.
Authentification
Le point de terminaison de callback utilise l’authentification par bearer token. Le token est défini dans la variable d’environnement TRIGGER_CALLBACK_SECRET et doit être inclus en tant que Authorization: Bearer <token> dans chaque requête. Le serveur effectue une comparaison à temps constant par correspondance XOR d’octets.
POST /trigger/callback
Content-Type: application/json
Authorization: Bearer {callback_secret}
{
"operation": "domain.action",
"args": { ... }
}Avertissement
/trigger/callback utilise un secret distinct de la clé API du projet (clés cc_live_*). Il est protégé par la variable d’environnement TRIGGER_CALLBACK_SECRET et n’est pas accessible avec les clés API utilisateur. Tenter de l’appeler avec une clé cc_live_* entraînera une erreur 401 Unauthorized.Format de requête
Chaque requête de callback a la même structure d’enveloppe :
{
"operation": "<domain>.<action>",
"args": {
// operation-specific arguments
}
}Format de réponse
Les opérations réussies renvoient un HTTP 200 avec { "result": <value> }. Les opérations échouées renvoient le code de statut HTTP approprié (400, 401, ou 500) avec { "error": "message" }.
Référence des opérations de callback
Les opérations sont organisées par domaine. Chaque entrée montre le nom de l’opération et une description de ce qu’elle fait et des args qu’elle attend.
run.* - Opérations Workflow Run
run.get- Récupère un enregistrement de run par ID. Args :{ runId }run.create- Crée un nouvel enregistrement de run dans la base de données (appelé au démarrage de l’orchestration). Args : payload complet de création de run incluantworkflowId,prompt,status, et champs optionnels.run.updateStatus- Met à jour le statut d’un run existant (p. ex.,running→completed). Args :{ runId, status, completedAt? }run.updatePr- Enregistre l’URL et le statut de PR d’un run. Args :{ runId, prUrl, prStatus, prNumber? }run.createChainRun- Crée un run faisant partie d’une sprint chain, en le liant à l’enregistrement de chain. Args :{ chainId, sprintIndex, ... }run.updateProgress- Écrit des informations de progression incrémentale sur un run (p. ex., description de l’étape actuelle, compteur d’itérations). Args :{ runId, progress }run.setStopFlag- Définit le flagstopAfterCurrentTurnsur un run pour l’arrêter proprement une fois le tour d’agent actuel terminé. Args :{ runId, stop: true }
runStep.* - Opérations Run Step
runStep.create- Crée un nouvel enregistrement d’étape sous un run. Args :{ runId, role, name, stepIndex }. Rôles valides :designer,checker,researcher,evaluator,judge,answerer.runStep.updateStatus- Met à jour le statut d’une étape et écrit optionnellement des scores de qualité ou des résultats de test. Args :{ stepId, status, qualityScores?, testResults? }
sandbox.* - Opérations Sandbox
sandbox.get- Récupère un enregistrement de sandbox par ID. Args :{ sandboxId }sandbox.create- Enregistre une sandbox E2B nouvellement provisionnée dans la base de données. Args : payload complet de création de sandbox incluantrunId,e2bSandboxId.sandbox.updateStatus- Met à jour le statut de cycle de vie de la sandbox (p. ex.,running→killed). Args :{ sandboxId, status }sandbox.setTriggerRunId- Associe un ID de run de tâche Trigger.dev à une sandbox pour le traçage. Args :{ sandboxId, triggerRunId }sandbox.updateLearningStatus- Met à jour le statut de l’extraction de learning d’une sandbox. Args :{ sandboxId, learningStatus }sandbox.updatePr- Écrit les métadonnées de PR (URL, numéro, statut) sur l’enregistrement de sandbox. Args :{ sandboxId, prUrl, prNumber, prStatus }sandbox.hasAssistantMessages- Vérifie si une sandbox a des messages assistant stockés (utilisé pour déterminer s’il faut émettre une notification). Args :{ sandboxId }. Renvoie{ result: boolean }.
message.* - Opérations Sandbox Message
message.store- Persiste un seul message terminé dans le journal de messages de la sandbox. Args :{ sandboxId, role, content, timestamp }message.streamCreate- Initialise un enregistrement de message en streaming pour une nouvelle réponse assistant. Args :{ sandboxId, messageId }message.streamAppend- Ajoute un fragment de texte à un message en streaming en cours. Args :{ messageId, chunk }message.streamFinalize- Marque un message en streaming comme terminé et écrit le contenu accumulé final. Args :{ messageId, finalContent }message.listBySandbox- Récupère tous les messages d’une sandbox, ordonnés par timestamp. Args :{ sandboxId }
issue.* - Opérations Issue
issue.getByRun- Récupère l’issue liée à un run (le cas échéant). Utilisé par l’orchestrateur pour injecter le contexte de l’issue dans le prompt de l’agent. Args :{ runId }
issueSession.* - Opérations Issue Session
issueSession.get- Récupère un enregistrement de session d’issue. Args :{ sessionId }issueSession.createSandbox- Crée et lie une sandbox à une session d’issue. Args :{ sessionId, sandboxPayload }issueSession.updateStatus- Met à jour le statut de cycle de vie d’une session d’issue. Args :{ sessionId, status }issueSession.createIssuesFromJson- Crée en masse des issues à partir d’un tableau JSON découvert par l’agent de scan. Args :{ sessionId, issues: Issue[] }issueSession.createSessionQuestionsFromJson- Crée en masse des questions de session pour une answering session à partir d’un tableau JSON produit par l’agent de génération de questions. Args :{ answeringSessionId, questions: Question[] }issueSession.updateIteration- Incrémente le compteur d’itérations sur une session d’issue (utilisé pour le scan multi-tour). Args :{ sessionId }issueSession.updateProgress- Écrit du texte de progression incrémentale sur une session d’issue. Args :{ sessionId, progress }
issueSessionStep.* - Opérations Issue Session Step
issueSessionStep.create- Crée un enregistrement d’étape sous une session d’issue. Args :{ sessionId, role, name, stepIndex }issueSessionStep.updateStatus- Met à jour le statut d’une étape de session. Args :{ stepId, status }
answeringSession.* - Opérations Answering Session
answeringSession.get- Récupère une answering session et ses questions. Args :{ answeringSessionId }answeringSession.createSandbox- Crée et lie une sandbox à une answering session pour l’agent answerer. Args :{ answeringSessionId, sandboxPayload }answeringSession.updateStatus- Met à jour le statut d’une answering session. Args :{ answeringSessionId, status }
sessionQuestions.* - Opérations Session Question
sessionQuestions.updateAssumptions- Met à jour en masse les hypothèses générées par l’IA pour un ensemble de questions de session (appelé après que l’agent answerer a produit ses réponses initiales). Args :{ updates: Array<{ questionId, assumption }> }sessionQuestions.updateAssumptionsByIssueSession- Met à jour les hypothèses pour toutes les questions liées à une session d’issue spécifique (utilisé quand les hypothèses sont dérivées du contexte au niveau de la session plutôt que de questions individuelles). Args :{ issueSessionId, assumptions: Record<string, string> }
learning.* - Opérations Learning
learning.dispatchExtraction- Planifie un job d’extraction de learning pour une sandbox terminée. Le job d’extraction analyse la conversation de la sandbox et distille des learnings. Args :{ sandboxId, runId }learning.store- Persiste un seul enregistrement de learning extrait d’une sandbox. Args : payload de learning avecsandboxId,content,category, et métadonnées optionnelles.learning.bulkStore- Persiste plusieurs enregistrements de learning en une seule opération. Args :{ learnings: Learning[] }learning.getCompiled- Récupère le contenu de learning compilé (fusionné et dédupliqué) pour un projet et un rôle. Utilisé par l’orchestrateur pour injecter les learnings accumulés dans les prompts système des agents. Args :{ projectId, role }
usage.* - Opérations d’enregistrement d’usage
usage.computeCostAndRecord- Calcule le coût d’un run de sandbox terminé (basé sur le nombre de tokens, le temps de compute, et les tarifs de service) et écrit un enregistrement d’usage. Args :{ sandboxId, tokenUsage, durationMs, service }
keys.* - Opérations de clé API
keys.get- Récupère une clé API de provider spécifique pour un projet (p. ex., clé API Anthropic). Utilisé par les orchestrateurs pour obtenir les clés configurées d’un projet. Args :{ projectId, keyType }keys.getWithFallback- Récupère une clé API de provider, en repli sur la valeur par défaut de la plateforme si le projet n’a pas configuré la sienne. Args :{ projectId, keyType }
settings.* - Opérations Project Settings
settings.get- Récupère les paramètres du projet (surcharges de prompt système, variables d’environnement, configuration git, feature flags). Args :{ projectId }
sprintChain.* - Opérations Sprint Chain
sprintChain.get- Récupère un enregistrement de sprint chain. Args :{ chainId }sprintChain.updateStatus- Met à jour le statut d’une sprint chain (p. ex.,running→completed). Args :{ chainId, status }sprintChain.updatePr- Ajoute une nouvelle URL de PR de sprint au tableausprintPrUrlsde la chain et met à jour l’index de sprint actuel. Args :{ chainId, prUrl, sprintIndex }
workflow.* - Opérations Workflow
workflow.get- Récupère un enregistrement de blueprint de workflow incluant la configuration des étapes et les assignations de personas. Args :{ workflowId }
persona.* - Opérations Persona
persona.get- Récupère un enregistrement de persona (sélection de modèle, prompt système, température, et autre configuration d’agent). Args :{ personaId }
contexts.* - Opérations Context
contexts.getByIdInternal- Récupère un enregistrement de context par ID pour usage au sein du runtime Trigger.dev. Renvoie les métadonnées de context complètes sans authentification par clé API publique. Args :{ contextId }
contextVersions.* - Opérations Context Version
contextVersions.getActiveInternal- Récupère le contenu de la version active d’un context. C’est l’opération principale utilisée par l’orchestrateur pour injecter le contenu du context dans les prompts d’agent au moment du run. Args :{ contextId }. Renvoie{ version, content, publishedAt }.contextVersions.ensureActiveInternal- Récupère la version active, en créant une version 1 vierge si aucune n’existe. Utilisé pour le bootstrap de nouveaux contexts. Args :{ contextId }
learningVersions.* - Opérations Learning Version
learningVersions.getActiveForRole- Récupère la version de learning compilée active pour un rôle d’agent spécifique au sein d’un projet. Les learnings sont compilés par rôle afin que les agents designer reçoivent des learnings spécifiques au design et les agents checker reçoivent des learnings spécifiques à la vérification. Args :{ projectId, role }. Renvoie le contenu de learning compilé ounullsi aucune version n’a été compilée.
Politiques de nouvelle tentative
Nouvelles tentatives des webhooks Clerk
Clerk (via Svix) retente automatiquement les livraisons de webhook échouées en utilisant un calendrier de backoff exponentiel. Si CodeCourier renvoie une réponse non-2xx, Svix retentera la livraison au cours des heures et jours suivants. La fenêtre de tolérance de cinq minutes sur le timestamp s’applique au timestamp d’origine, pas à l’heure de la nouvelle tentative, donc les webhooks retentés peuvent être rejetés s’ils arrivent trop tard.
Pour garantir un traitement fiable :
- Renvoyez une réponse 200 aussi rapidement que possible, même si le traitement réel doit se produire de façon asynchrone.
- Si le traitement échoue après acceptation du webhook, utilisez
ctx.scheduler.runAfterde Convex pour réessayer en interne plutôt que de compter sur les nouvelles tentatives de Svix.
Nouvelles tentatives des callbacks Trigger.dev
Les tâches Trigger.dev implémentent leur propre logique de nouvelle tentative. Si un callback vers CodeCourier échoue (erreur réseau ou réponse 5xx), la tâche Trigger.dev capturera l’erreur et pourra retenter l’opération de callback individuelle. L’architecture à double try-catch dans le gestionnaire de callback garantit qu’une réponse JSON est toujours renvoyée, empêchant les réinitialisations TCP qui pourraient causer des nouvelles tentatives infinies.
Événements de notification
CodeCourier génère aussi des événements de notification internes stockés dans la table notifications. Ce ne sont pas des webhooks externes mais ils servent un objectif similaire pour l’UI du dashboard. Les types de notification suivants sont générés :
run_completed- Un run de workflow s’est terminé avec succès.run_failed- Un run de workflow a rencontré une erreur.pr_created- Une pull request a été créée depuis une sandbox ou un run.pr_merged- Une pull request a été fusionnée.pr_failed- La création de pull request a échoué.member_joined- Un nouveau membre a accepté une invitation de projet.workflow_completed- Une exécution de workflow s’est terminée.sprint_completed/sprint_failed- Une sprint chain s’est terminée ou a échoué.
Les notifications sont scopées à un projet et un utilisateur, et incluent un flag read et un timestamp dismissedAt optionnel pour suivre quelles notifications l’utilisateur a vues. Voir l’ Operations API pour les points de terminaison permettant de lister, marquer comme lu, et rejeter les notifications.
Configuration des webhooks
Configuration du webhook Clerk
- Naviguez vers le dashboard Clerk de votre application.
- Allez dans Webhooks dans la barre latérale gauche.
- Cliquez sur Add Endpoint.
- Entrez l’URL de votre déploiement Convex CodeCourier suivie de
/clerk/webhook(p. ex.,https://your-deployment.convex.site/clerk/webhook). - Sélectionnez les événements auxquels vous souhaitez vous abonner (au minimum,
user.deleted). - Copiez le secret de signature et définissez-le comme variable d’environnement
CLERK_WEBHOOK_SECRETdans votre déploiement Convex.
Tester les webhooks
Utilisez la fonctionnalité «Send test event» du dashboard Clerk pour vérifier que votre point de terminaison fonctionne correctement. Vérifiez les logs de fonction Convex pour toute erreur de vérification de signature ou de traitement d’événement.