Webhooks & Callbacks
Wie CodeCourier eingehende Webhooks von Clerk (User-Lifecycle) verarbeitet, Svix-Signaturverifizierung und der interne Trigger.dev-Callback-Endpunkt mit vollständiger, nach Domäne organisierter Operationsreferenz.
Webhooks ermöglichen es externen Diensten, CodeCourier in Echtzeit über Ereignisse zu benachrichtigen. CodeCourier verarbeitet Webhooks von Clerk (für User-Lifecycle-Events) und stellt einen internen Callback-Endpunkt für Trigger.dev bereit (zur Fortschritts-Berichterstattung von Hintergrundjobs und für Datenoperationen). Diese Seite dokumentiert beide Webhook-Flächen, ihre Authentifizierungsmodelle, Payload-Formate, Signaturverifizierung und die vollständige, nach Domäne organisierte Trigger.dev-Callback-Operationsreferenz.
Clerk-Webhooks
CodeCourier registriert einen Webhook-Endpunkt unter /clerk/webhook, um User-Lifecycle-Events von Clerk zu empfangen. Der primäre Anwendungsfall ist der Umgang mit User-Löschungen - wenn ein Benutzer sein Clerk-Konto löscht, erhält CodeCourier einen Webhook und kann zugehörige Daten bereinigen.
Unterstützte Events
- user.deleted -- Wird ausgelöst, wenn ein Benutzerkonto in Clerk gelöscht wird. CodeCourier nutzt dies zum Auslösen von Datenbereinigungs-Workflows, einschließlich Entfernen oder Anonymisieren benutzereigener Ressourcen.
Payload-Format
Clerk-Webhook-Payloads folgen dem Svix-Standardformat. Jede Payload enthält:
type-- Der Event-Typ-String (z. B."user.deleted").data-- Die Event-Payload mit der betroffenen Ressource. Bei User-Events enthält diesid(die Clerk-User-ID),email_addressesund andere User-Felder.object-- Immer"event".
Signaturverifizierung
Alle eingehenden Clerk-Webhooks werden mit dem Svix-Signatur-Protokoll verifiziert. Damit wird sichergestellt, dass Webhook-Payloads tatsächlich von Clerk stammen und auf dem Transportweg nicht manipuliert wurden.
Verifizierungsprozess
- Header extrahieren. Der Server liest drei erforderliche Header aus dem eingehenden Request:
svix-id(eindeutiger Nachrichten-Bezeichner),svix-timestamp(Unix-Zeitstempel in Sekunden) undsvix-signature(eine oder mehrere versionierte Signaturen). - Zeitstempel validieren. Der Server prüft, dass der Zeitstempel innerhalb von fünf Minuten der aktuellen Zeit liegt. Anfragen außerhalb dieses Toleranzfensters werden abgelehnt, um Replay-Attacken vorzubeugen.
- Erwartete Signatur berechnen. Der zu signierende Inhalt wird konstruiert als
{svix-id}.{svix-timestamp}.{raw-body}. Der Server berechnet einen HMAC-SHA256 dieses Inhalts mit dem Webhook-Secret (der UmgebungsvariableCLERK_WEBHOOK_SECRET, wobei das Präfixwhsec_entfernt und der Rest base64-dekodiert wird). - Signaturen vergleichen. Der Header
svix-signaturekann mehrere durch Leerzeichen getrennte Signaturen enthalten, jeweils mit einer Version präfigiert (z. B.v1,<base64>). Der Server prüft jedev1-Signatur gegen den berechneten Wert mit einem zeitkonstanten Zeichen-für-Zeichen-Vergleich, um Timing-Seitenkanal-Angriffe zu vermeiden. - Akzeptieren oder ablehnen. Wenn irgendeine Signatur übereinstimmt, wird der Webhook akzeptiert und verarbeitet. Andernfalls wird er mit Status 401 abgelehnt.
Umgebungsvariablen
CLERK_WEBHOOK_SECRET-- Das Svix-Signing-Secret aus Ihrem Clerk-Dashboard. Dies ist ein base64-kodierter HMAC-Key mit dem Präfixwhsec_. Der Server verarbeitet keine Webhooks, wenn diese Variable nicht gesetzt ist.
Trigger.dev-Callback-Endpunkt
Der Endpunkt /trigger/callback ist eine interne API, die ausschließlich von den Trigger.dev-Hintergrundjobs von CodeCourier verwendet wird. Er ist nicht für die direkte externe Nutzung gedacht - er ist hier dokumentiert, damit Entwickler den Datenfluss zwischen der Orchestrierungs-Schicht und der Convex-Datenbank verstehen können.
Trigger.dev-Tasks rufen diesen Endpunkt auf, um Daten zurück nach Convex zu schreiben (Run-Statusaktualisierungen, Sandbox-Nachrichten, Learning-Extraktion, Usage-Erfassung usw.), ohne direkten Convex-Client-Zugriff aus dem Trigger.dev-Runtime heraus zu benötigen.
Authentifizierung
Der Callback-Endpunkt verwendet Bearer-Token-Authentifizierung. Der Token wird in der Umgebungsvariable TRIGGER_CALLBACK_SECRET gesetzt und muss in jeder Anfrage als Authorization: Bearer <token> enthalten sein. Der Server führt einen zeitkonstanten Vergleich per XOR-Byte-Matching durch.
POST /trigger/callback
Content-Type: application/json
Authorization: Bearer {callback_secret}
{
"operation": "domain.action",
"args": { ... }
}Warnung
/trigger/callback verwendet ein separates Secret vom Projekt-API-Schlüssel (cc_live_*-Keys). Er ist durch die Umgebungsvariable TRIGGER_CALLBACK_SECRET geschützt und nicht mit User-API-Schlüsseln erreichbar. Versuche, ihn mit einem cc_live_*-Key aufzurufen, führen zu einem 401-Unauthorized-Fehler.Request-Format
Jeder Callback-Request hat dieselbe Envelope-Struktur:
{
"operation": "<domain>.<action>",
"args": {
// operation-specific arguments
}
}Antwortformat
Erfolgreiche Operationen liefern HTTP 200 mit { "result": <value> }. Fehlgeschlagene Operationen liefern den passenden HTTP-Statuscode (400, 401 oder 500) mit { "error": "message" }.
Callback-Operationen-Referenz
Operationen sind nach Domäne organisiert. Jeder Eintrag zeigt den Operationsnamen und eine Beschreibung, was er tut und welche args er erwartet.
run.* - Workflow-Run-Operationen
run.get-- Einen Run-Datensatz per ID abrufen. Args:{ runId }run.create-- Einen neuen Run-Datensatz in der Datenbank anlegen (zum Orchestrierungs-Start aufgerufen). Args: vollständige Run-Erstellungs-Payload inklusiveworkflowId,prompt,statusund optionalen Feldern.run.updateStatus-- Den Status eines bestehenden Runs aktualisieren (z. B.running→completed). Args:{ runId, status, completedAt? }run.updatePr-- PR-URL und Status für einen Run erfassen. Args:{ runId, prUrl, prStatus, prNumber? }run.createChainRun-- Einen Run als Teil einer Sprint-Chain anlegen und ihn mit dem Chain-Datensatz verknüpfen. Args:{ chainId, sprintIndex, ... }run.updateProgress-- Inkrementelle Fortschrittsinformationen in einen Run schreiben (z. B. Beschreibung des aktuellen Schritts, Iterations-Anzahl). Args:{ runId, progress }run.setStopFlag-- Das FlagstopAfterCurrentTurnauf einem Run setzen, um nach Abschluss des aktuellen Agent-Turns sauber zu stoppen. Args:{ runId, stop: true }
runStep.* - Run-Schritt-Operationen
runStep.create-- Einen neuen Schritt-Datensatz unter einem Run anlegen. Args:{ runId, role, name, stepIndex }. Gültige Rollen:designer,checker,researcher,evaluator,judge,answerer.runStep.updateStatus-- Den Status eines Schritts aktualisieren und optional Quality Scores oder Testergebnisse schreiben. Args:{ stepId, status, qualityScores?, testResults? }
sandbox.* - Sandbox-Operationen
sandbox.get-- Einen Sandbox-Datensatz per ID abrufen. Args:{ sandboxId }sandbox.create-- Eine neu provisionierte E2B-Sandbox in der Datenbank registrieren. Args: vollständige Sandbox-Erstellungs-Payload inklusiverunId,e2bSandboxId.sandbox.updateStatus-- Sandbox-Lifecycle-Status aktualisieren (z. B.running→killed). Args:{ sandboxId, status }sandbox.setTriggerRunId-- Eine Trigger.dev-Task-Run-ID zwecks Tracing mit einer Sandbox verknüpfen. Args:{ sandboxId, triggerRunId }sandbox.updateLearningStatus-- Den Status der Learning-Extraktion für eine Sandbox aktualisieren. Args:{ sandboxId, learningStatus }sandbox.updatePr-- PR-Metadaten (URL, Nummer, Status) in den Sandbox-Datensatz schreiben. Args:{ sandboxId, prUrl, prNumber, prStatus }sandbox.hasAssistantMessages-- Prüfen, ob eine Sandbox gespeicherte Assistant-Nachrichten hat (verwendet, um zu entscheiden, ob eine Benachrichtigung ausgelöst wird). Args:{ sandboxId }. Liefert{ result: boolean }.
message.* - Sandbox-Nachrichten-Operationen
message.store-- Eine einzelne abgeschlossene Nachricht in das Sandbox-Nachrichten-Log persistieren. Args:{ sandboxId, role, content, timestamp }message.streamCreate-- Einen Streaming-Nachrichten-Datensatz für eine neue Assistant-Antwort initialisieren. Args:{ sandboxId, messageId }message.streamAppend-- Einen Text-Chunk an eine laufende Streaming-Nachricht anfügen. Args:{ messageId, chunk }message.streamFinalize-- Eine Streaming-Nachricht als abgeschlossen markieren und den final akkumulierten Inhalt schreiben. Args:{ messageId, finalContent }message.listBySandbox-- Alle Nachrichten einer Sandbox abrufen, geordnet nach Zeitstempel. Args:{ sandboxId }
issue.* - Issue-Operationen
issue.getByRun-- Das mit einem Run verknüpfte Issue abrufen (falls vorhanden). Wird vom Orchestrator verwendet, um Issue-Kontext in den Agent-Prompt einzufügen. Args:{ runId }
issueSession.* - Issue-Sitzungs-Operationen
issueSession.get-- Einen Issue-Sitzungs-Datensatz abrufen. Args:{ sessionId }issueSession.createSandbox-- Eine Sandbox erstellen und mit einer Issue-Sitzung verknüpfen. Args:{ sessionId, sandboxPayload }issueSession.updateStatus-- Den Lifecycle-Status einer Issue-Sitzung aktualisieren. Args:{ sessionId, status }issueSession.createIssuesFromJson-- Issues massenweise aus einem vom Scan-Agenten entdeckten JSON-Array anlegen. Args:{ sessionId, issues: Issue[] }issueSession.createSessionQuestionsFromJson-- Sitzungsfragen für eine Antwortsitzung massenweise aus einem JSON-Array anlegen, das vom Fragenerstellungs-Agenten erzeugt wurde. Args:{ answeringSessionId, questions: Question[] }issueSession.updateIteration-- Den Iterations-Zähler einer Issue-Sitzung erhöhen (verwendet für mehrturniges Scanning). Args:{ sessionId }issueSession.updateProgress-- Inkrementellen Fortschrittstext in eine Issue-Sitzung schreiben. Args:{ sessionId, progress }
issueSessionStep.* - Issue-Sitzungs-Schritt-Operationen
issueSessionStep.create-- Einen Schritt-Datensatz unter einer Issue-Sitzung anlegen. Args:{ sessionId, role, name, stepIndex }issueSessionStep.updateStatus-- Den Status eines Sitzungs-Schritts aktualisieren. Args:{ stepId, status }
answeringSession.* - Antwortsitzungs-Operationen
answeringSession.get-- Eine Antwortsitzung und ihre Fragen abrufen. Args:{ answeringSessionId }answeringSession.createSandbox-- Eine Sandbox erstellen und mit einer Antwortsitzung für den Answerer-Agenten verknüpfen. Args:{ answeringSessionId, sandboxPayload }answeringSession.updateStatus-- Den Status einer Antwortsitzung aktualisieren. Args:{ answeringSessionId, status }
sessionQuestions.* - Sitzungsfragen-Operationen
sessionQuestions.updateAssumptions-- Die KI-generierten Annahmen für eine Menge von Sitzungsfragen massenweise aktualisieren (aufgerufen, nachdem der Answerer-Agent seine initialen Antworten erzeugt hat). Args:{ updates: Array<{ questionId, assumption }> }sessionQuestions.updateAssumptionsByIssueSession-- Annahmen für alle Fragen aktualisieren, die mit einer bestimmten Issue-Sitzung verknüpft sind (verwendet, wenn Annahmen aus Kontext auf Sitzungsebene statt aus einzelnen Fragen abgeleitet werden). Args:{ issueSessionId, assumptions: Record<string, string> }
learning.* - Learning-Operationen
learning.dispatchExtraction-- Einen Learning-Extraktions-Job für eine abgeschlossene Sandbox planen. Der Extraktions-Job analysiert die Sandbox-Konversation und destilliert Learnings. Args:{ sandboxId, runId }learning.store-- Einen einzelnen Learning-Datensatz, extrahiert aus einer Sandbox, persistieren. Args: Learning-Payload mitsandboxId,content,categoryund optionalen Metadaten.learning.bulkStore-- Mehrere Learning-Datensätze in einer einzigen Operation persistieren. Args:{ learnings: Learning[] }learning.getCompiled-- Den kompilierten (zusammengeführten und deduplizierten) Learning-Inhalt für ein Projekt und eine Rolle abrufen. Wird vom Orchestrator verwendet, um akkumulierte Learnings in Agent-System-Prompts einzufügen. Args:{ projectId, role }
usage.* - Usage-Erfassungs-Operationen
usage.computeCostAndRecord-- Die Kosten für einen abgeschlossenen Sandbox-Run berechnen (basierend auf Token-Anzahlen, Compute-Zeit und Service-Tarifen) und einen Usage-Datensatz schreiben. Args:{ sandboxId, tokenUsage, durationMs, service }
keys.* - API-Schlüssel-Operationen
keys.get-- Einen bestimmten Provider-API-Schlüssel eines Projekts abrufen (z. B. Anthropic-API-Schlüssel). Wird von Orchestratoren verwendet, um die konfigurierten Schlüssel eines Projekts zu erhalten. Args:{ projectId, keyType }keys.getWithFallback-- Einen Provider-API-Schlüssel abrufen und auf den Plattform-Default zurückfallen, falls das Projekt keinen eigenen konfiguriert hat. Args:{ projectId, keyType }
settings.* - Projekteinstellungs-Operationen
settings.get-- Projekteinstellungen abrufen (System-Prompt-Overrides, Umgebungsvariablen, Git-Konfiguration, Feature-Flags). Args:{ projectId }
sprintChain.* - Sprint-Chain-Operationen
sprintChain.get-- Einen Sprint-Chain-Datensatz abrufen. Args:{ chainId }sprintChain.updateStatus-- Den Status einer Sprint-Chain aktualisieren (z. B.running→completed). Args:{ chainId, status }sprintChain.updatePr-- Eine neue Sprint-PR-URL an dassprintPrUrls-Array der Chain anhängen und den aktuellen Sprint-Index aktualisieren. Args:{ chainId, prUrl, sprintIndex }
workflow.* - Workflow-Operationen
workflow.get-- Einen Workflow-Blueprint-Datensatz inklusive Schrittkonfiguration und Persona-Zuweisungen abrufen. Args:{ workflowId }
persona.* - Persona-Operationen
persona.get-- Einen Persona-Datensatz abrufen (Modellauswahl, System-Prompt, Temperatur und andere Agent-Konfiguration). Args:{ personaId }
contexts.* - Kontext-Operationen
contexts.getByIdInternal-- Einen Kontext-Datensatz per ID zur Verwendung innerhalb des Trigger.dev-Runtime abrufen. Liefert vollständige Kontext-Metadaten ohne öffentliche API-Schlüssel-Authentifizierung. Args:{ contextId }
contextVersions.* - Kontext-Versions-Operationen
contextVersions.getActiveInternal-- Den Inhalt der aktiven Version eines Kontextes abrufen. Dies ist die primäre Operation, die der Orchestrator nutzt, um Kontextinhalt zur Laufzeit in Agent-Prompts einzufügen. Args:{ contextId }. Liefert{ version, content, publishedAt }.contextVersions.ensureActiveInternal-- Die aktive Version abrufen und eine leere Version 1 erzeugen, falls keine existiert. Wird für das Bootstrapping neuer Kontexte verwendet. Args:{ contextId }
learningVersions.* - Learning-Versions-Operationen
learningVersions.getActiveForRole-- Die aktive kompilierte Learning-Version für eine bestimmte Agent-Rolle innerhalb eines Projekts abrufen. Learnings werden pro Rolle kompiliert, sodass Designer-Agenten designer-spezifische Learnings und Checker-Agenten checker-spezifische Learnings erhalten. Args:{ projectId, role }. Liefert den kompilierten Learning-Inhalt odernull, wenn keine Version kompiliert wurde.
Retry-Richtlinien
Clerk-Webhook-Retries
Clerk (über Svix) wiederholt fehlgeschlagene Webhook-Lieferungen automatisch mit einem Exponential-Backoff-Zeitplan. Wenn CodeCourier eine Nicht-2xx-Antwort liefert, wiederholt Svix die Lieferung über die folgenden Stunden und Tage. Das Fünf-Minuten-Toleranzfenster bezieht sich auf den ursprünglichen Zeitstempel, nicht auf die Retry-Zeit, sodass erneut versuchte Webhooks abgelehnt werden können, wenn sie zu spät eintreffen.
Für zuverlässige Verarbeitung:
- Liefern Sie so schnell wie möglich eine 200-Antwort, selbst wenn die eigentliche Verarbeitung asynchron erfolgen muss.
- Wenn die Verarbeitung nach Annahme des Webhooks fehlschlägt, verwenden Sie Convex'
ctx.scheduler.runAfter, um intern zu wiederholen, statt sich auf Svix-Retries zu verlassen.
Trigger.dev-Callback-Retries
Trigger.dev-Tasks implementieren ihre eigene Retry-Logik. Wenn ein Callback an CodeCourier fehlschlägt (Netzwerkfehler oder 5xx-Antwort), fängt der Trigger.dev-Task den Fehler ab und kann die einzelne Callback-Operation erneut versuchen. Die Doppelt-try-catch-Architektur im Callback-Handler stellt sicher, dass immer eine JSON-Antwort zurückgegeben wird, was TCP-Resets verhindert, die endlose Retries verursachen könnten.
Benachrichtigungs-Events
CodeCourier erzeugt außerdem interne Benachrichtigungs-Events, die in der Tabelle notifications gespeichert werden. Dies sind keine externen Webhooks, dienen aber einem ähnlichen Zweck für die Dashboard-UI. Die folgenden Benachrichtigungstypen werden erzeugt:
run_completed-- Ein Workflow-Run wurde erfolgreich abgeschlossen.run_failed-- Ein Workflow-Run hat einen Fehler gemeldet.pr_created-- Ein Pull Request wurde aus einer Sandbox oder einem Run erstellt.pr_merged-- Ein Pull Request wurde gemerged.pr_failed-- Die Erstellung eines Pull Requests ist fehlgeschlagen.member_joined-- Ein neues Mitglied hat eine Projekt-Einladung angenommen.workflow_completed-- Eine Workflow-Ausführung wurde abgeschlossen.sprint_completed/sprint_failed-- Eine Sprint-Chain wurde abgeschlossen oder ist fehlgeschlagen.
Benachrichtigungen sind auf ein Projekt und einen Benutzer beschränkt und enthalten ein read-Flag und einen optionalen dismissedAt-Zeitstempel zur Verfolgung, welche Benachrichtigungen der Benutzer gesehen hat. Siehe die Operations-API für Endpunkte zum Auflisten, Als-gelesen-Markieren und Verwerfen von Benachrichtigungen.
Webhooks einrichten
Clerk-Webhook-Konfiguration
- Navigieren Sie zum Clerk-Dashboard Ihrer Anwendung.
- Gehen Sie in der linken Seitenleiste zu Webhooks.
- Klicken Sie auf Add Endpoint.
- Geben Sie die URL Ihres CodeCourier-Convex-Deployments gefolgt von
/clerk/webhookein (z. B.https://your-deployment.convex.site/clerk/webhook). - Wählen Sie die Events aus, die Sie abonnieren möchten (mindestens
user.deleted). - Kopieren Sie das Signing-Secret und setzen Sie es als Umgebungsvariable
CLERK_WEBHOOK_SECRETin Ihrem Convex-Deployment.
Webhooks testen
Verwenden Sie die Funktion „Send test event“ im Clerk-Dashboard, um zu überprüfen, ob Ihr Endpunkt korrekt funktioniert. Prüfen Sie die Convex-Funktions-Logs auf Fehler bei der Signaturverifizierung oder Event-Verarbeitung.