Skip to content

Plugin webhooks

SCM + tracker plugins optionally participate in Coro’s webhook story so hybrid tenants can resume parked jobs without relying solely on polling.

Manifest descriptor

webhook?: {
pathSuffix?: string
algorithm: 'hmac-sha256' | 'hmac-sha1' | 'none'
header: string // e.g. X-Hub-Signature-256
format: 'sha256=<hex>' | 'sha1=<hex>' | '<hex>' | '<plain>'
}

The cloud control plane stores per-team rows describing the same tuple; when Bitbucket/GitHub POSTs to https://cloud/webhook/:teamId/:pluginId, the edge:

  1. Loads the secret + algorithm.
  2. Recomputes HMAC using rawBody.
  3. Compares against the header respecting format.
  4. Forwards { pluginId, headers, rawBodyBase64 } across WS /ws/runner.

normalizeInbound

Once the payload reaches the owning runner, makePluginWebhookNormalizer dispatches to plugin.normalizeInbound which must:

  • Decode provider JSON/XML.
  • Populate an ExternalRef via buildExternalRef({ kind, pluginId, externalId, repoKey?, url? }).
  • Return null if the event should be ignored.

buildExternalRef enforces invariants (e.g., pull_request must carry repoKey).

HMAC helpers

@coro-ai/plugin-sdk exposes verifyHmacSignature mirroring cloud behaviour so plugins can double-check signatures in local dev or custom HTTP routes.

Legacy routes

Some releases still accept /webhook/:provider/:teamId aliases, but operators should configure providers against the generic /webhook/:teamId/:pluginId path documented in packages/runner/src/cloud/routes/webhooks.ts.