GitHub Marketplace integration
Marketplace webhook
A signed, idempotent HTTPS POST endpoint that accepts the GitHub Marketplace marketplace_purchase topic. Post the URL below into the publisher UI; set the secret once; duplicate deliveries are accepted idempotently.
Webhook URL — paste into the publisher
https://getbivouac.com/api/marketplace-webhook
Signature scheme
Every delivery must arrive with an HMAC over the raw body bytes using GITHUB_MARKETPLACE_WEBHOOK_SECRET as the key.
- Header
- X-Hub-Signature-256: sha256=<lowercase hex>
- Algorithm
- HMAC-SHA256
- Input
- Raw request body (literal bytes)
- Content-Type
- application/json
The endpoint also accepts the legacy X-Hub-Signature: sha1=<hex> fallback until the publisher UI migrates. Both are recomputed locally over the same raw bytes; sha512 (or any non-sha256 / non-sha1 prefix) is rejected with a 401.
Implemented events
Bivouac processes exactly five actions on the marketplace_purchase topic — the documented plan-lifecycle signals for the dependency-remediation listing. Any other topic or any other action is rejected with 400so a delivery is never silently 200'd on something Bivouac doesn't implement.
- purchased
- cancelled
- changed
- pending_change
- pending_change_cancelled
Idempotency
Every delivery carries an X-GitHub-Delivery UUID. We key the persistence layer on this UUID with a unique index, so a replayed delivery matches a row that already exists on the second arrival and acknowledges with 200 { accepted: true, duplicate: true } — no side effects rerun.
Publisher configuration
- In the GitHub Marketplace publisher UI, open Listings and paste the webhook URL above into Webhook URL.
- Set Webhook secretto a fresh random string; paste the same value into the operator's
GITHUB_MARKETPLACE_WEBHOOK_SECRETdeploy env. The endpoint never echoes the secret. - Select only the
marketplace_purchasetopic — other Marketplace topics and GitHub App webhook events are not part of this endpoint. - Submit the listing. The first delivery will arrive with
X-GitHub-Deliverypopulated; verify the deferred JSON page in the operator console to confirm the row landed.