This information only applies to organizations on the Pro and Enterprise plans.
Private BSR instances offer webhooks to notify your backend services when a specific event of interest happens. This allows you to build integrations triggering further actions—for example:
- Run a CI/CD pipeline to run appropriate downstream builds, tests, deployments
- Notify interested parties to inform them of any changes to
.proto
files or versions - Tag your git repository based on your BSR tags
Webhooks are in alpha, which includes support for a single event: a successful buf push
on a repository.
Webhooks are disabled by default. Contact Support or your Buf representative if you want webhooks enabled for your private BSR instance. If webhooks are enabled, then by default they will process batches of up to 20 events every 5 seconds.
Integrating consists of writing an event listener and then subscribing to events. The event listener must implement the EventService and subscriptions are managed through the WebhookService. These services can be easily interacted with using the Connect library, as shown below in Webhooks with Connect.
If you can't use Connect, see the Webhooks without Connect guide below that shows you how to:
- manage webhook subscriptions using a regular curl+JSON payload; and
- build an event listener that's compatible with Connect (
application/proto
) requests.
All of the code snippets shared in this guide point to the production BSR at https://api.buf.build/
. In the samples
below, please use the equivalent address for your private BSR server (for example, https://api.buf.example.com
).
Webhooks with Connect
This guide uses a Go Connect client and generated Go code for the webhooks service. This code
was generated using the connectrpc/go
plugin in the
BSR public repository.
The generated module from that plugin includes:
- packages based in the original proto packages from the BSR module
- a nested
connect
package for RPC handlers and clients that use the connect-go library.
The steps should be very similar for the JS generated code, which uses the
protocolbuffers/js
plugin.
Prepare to receive webhooks
You build the webhook listener using the Connect client code generated from the Webhook Event service. First, fetch the generated go module from the connect-go plugin:
go get buf.build/gen/go/bufbuild/buf/connectrpc/go
Here's an example Connect implementation:
package main
import (
"context"
"fmt"
"net/http"
connect "connectrpc.com/connect"
registryv1alpha1 "buf.build/gen/go/bufbuild/buf/connectrpc/go/buf/alpha/registry/v1alpha1"
webhookv1alpha1 "buf.build/gen/go/bufbuild/buf/connectrpc/go/buf/alpha/webhook/v1alpha1"
webhookconnect "buf.build/gen/go/bufbuild/buf/connectrpc/go/buf/alpha/webhook/v1alpha1/webhookv1alpha1connect"
)
type webhookEventHandler struct{}
func (h *webhookEventHandler) Event(
ctx context.Context,
req *connect.Request[webhookv1alpha1.EventRequest],
) (*connect.Response[webhookv1alpha1.EventResponse], error) {
// Handle the type-safe incoming request for the push event:
payload := req.Msg
switch payload.Event {
case registryv1alpha1.WebhookEvent_WEBHOOK_EVENT_REPOSITORY_PUSH:
pushEvent := payload.Payload.GetRepositoryPush()
fmt.Println("received repo push event:", pushEvent)
default:
fmt.Println("unknown event:", payload.Event)
}
// Webhook listener has an empty response
return connect.NewResponse(&webhookv1alpha1.EventResponse{}), nil
}
// Connect handler based on: https://connectrpc.com/docs/go/getting-started#implement-handler
func main() {
mux := http.NewServeMux()
mux.Handle(webhookconnect.NewEventServiceHandler(&webhookEventHandler{}))
http.ListenAndServe("localhost:8080", mux)
}
Manage subscriptions
Webhooks are managed through the BSR API. The easiest way to interact with it is to use the generated Connect client. Users with the Admin role in a repository can manage webhook subscriptions for that repository.
Below is an example of managing webhooks using the BSR Webhook service. First, fetch the generated go module from the connect-go template if you didn't in the previous step:
go get buf.build/gen/go/bufbuild/buf/connectrpc/go
Then in your Go code, set up the subscription:
package main
import (
"context"
"log"
"net/http"
connect "connectrpc.com/connect"
registryv1alpha1 "buf.build/gen/go/bufbuild/buf/connectrpc/go/buf/alpha/registry/v1alpha1"
registryconnect "buf.build/gen/go/bufbuild/buf/connectrpc/go/buf/alpha/registry/v1alpha1/registryv1alpha1connect"
)
func main() {
connectClient := registryconnect.NewWebhookServiceClient(
http.DefaultClient,
"https://api.buf.build", // BSR API
)
// Creating the webhook. Each repository event allows a single webhook. Since
// we support only push events at the moment, this means that a repository can
// have just one webhook subscription in total.
createReq := connect.NewRequest(®istryv1alpha1.CreateWebhookRequest{
WebhookEvent: registryv1alpha1.WebhookEvent_WEBHOOK_EVENT_REPOSITORY_PUSH,
OwnerName: "ORG_NAME_OR_USERNAME",
RepositoryName: "REPOSITORY_NAME",
CallbackUrl: "https://your.callback.url/buf.alpha.webhook.v1alpha1.EventService/Event",
})
createReq.Header().Add("Authorization", "Bearer YOUR_API_TOKEN")
createResp, err := connectClient.CreateWebhook(context.Background(), createReq)
if err != nil {
log.Fatalf("creating webhook failed: %v", err)
}
webhook := createResp.Msg.Webhook
if webhook == nil {
log.Fatal("nil webhook response")
}
log.Println("new webhook created!", webhook)
// Checking webhook exists
listReq := connect.NewRequest(®istryv1alpha1.ListWebhooksRequest{
OwnerName: "ORG_NAME_OR_USERNAME",
RepositoryName: "REPOSITORY_NAME",
})
listReq.Header().Add("Authorization", "Bearer YOUR_API_TOKEN")
listResp, err := connectClient.ListWebhooks(context.Background(), listReq)
if err != nil {
log.Fatalf("list webhooks failed: %v", err)
}
log.Println("existing webhooks:", listResp)
// Deleting the webhook. Is your callback URL going to change and you need to
// update your integration? You can remove you webhook and recreate it again.
// Note that pending notifications for events that were recently triggered may
// be delivered anyway after deleting the webhook subscription.
deleteReq := connect.NewRequest(®istryv1alpha1.DeleteWebhookRequest{
WebhookId: webhook.WebhookId,
})
deleteReq.Header().Add("Authorization", "Bearer YOUR_API_TOKEN")
deleteResp, err := connectClient.DeleteWebhook(context.Background(), deleteReq)
if err != nil {
log.Fatalf("delete webhook failed: %v", err)
}
log.Println("webhook deleted:", deleteResp)
}}
Webhooks without Connect
Connect isn't required for communicating with the webhooks service—because the server is also written with Connect handlers, you can make the same calls using curl with JSON.
Prepare to receive webhooks
The BSR supports Connect-compatible backend services for webhooks. Events will trigger an
HTTP POST to the subscribed callback URL, with a Content-Type: application/proto
header and a proto payload with the
event details.
A listener service must exist at the callback URL that is compatible with the Event
RPC signature in the public
bufbuild
proto docs. Here are the
possible payloads,
and the
expected response.
Manage subscriptions with the Buf CLI
You can manage your webhooks with the buf beta registry webhook
commands. WEBHOOK_EVENT_REPOSITORY_PUSH
is the only
event type that's currently avaialble.
Create a webhook subscription with the Buf CLI
buf beta registry webhook create \
--owner="<the organization or username that owns the repository>" \
--repository="<the repository name>" \
--callback-url="https://your.callback.url/buf.alpha.webhook.v1alpha1.EventService/Event" \
--event="WEBHOOK_EVENT_REPOSITORY_PUSH" \
--remote="buf.build"
List webhook subscriptions with the Buf CLI
buf beta registry webhook list \
--owner="<the organization or username that owns the repository>" \
--repository="<the repository name>" \
--remote="buf.build"
Delete webhook subscriptions with the Buf CLI
buf beta registry webhook delete \
--id="the-webhook-id-that-will-be-deleted" \
--remote="buf.build"
Manage subscriptions with curl
Webhooks are managed through the BSR API. Requests can be made to this API using an RPC gent like
Connect or via curl
commands with JSON payloads.
Create webhook subscription with curl
Use the [CreateWebhook
RPC]
(https://buf.build/bufbuild/buf/docs/main:buf.alpha.registry.v1alpha1#buf.alpha.registry.v1alpha1.WebhookService.CreateWebhook)
to create webhooks for a repository:
curl --location --request POST 'https://api.buf.build/buf.alpha.registry.v1alpha1.WebhookService/CreateWebhook' \
--header 'Authorization: Bearer <BSR api token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"owner_name": "<the organization or username that owns the repository>",
"repository_name": "<the repository name>",
"webhook_event": "WEBHOOK_EVENT_REPOSITORY_PUSH",
"callback_url": "https://your.callback.url/buf.alpha.webhook.v1alpha1.EventService/Event"
}'
For the callback_url
parameter, make sure you use an https scheme, and you suffix the complete RPC path to the
Event
method, with no queries, fragments, or any additional characters._
This should return a successful response in the form of:
HTTP Status: 200 OK
Content-Type: application/json
{
"webhook": {
"event": "WEBHOOK_EVENT_REPOSITORY_PUSH",
"webhookId": "the-new-webhook-id",
"createTime": "2022-07-11T21:15:27.633999Z",
"updateTime": "2022-07-11T21:15:27.633999Z",
"repository_name": "<the repository name>",
"owner_name": "<the organization or username that owns the repository>",
"callbackUrl": "https://your.callback.url/buf.alpha.webhook.v1alpha1.EventService/Event"
}
}
List webhook subscriptions with curl
After creating a webhook subscription for a repository, you can confirm its existence using the
ListWebhooks
RPC:
curl --location --request POST 'https://api.buf.build/buf.alpha.registry.v1alpha1.WebhookService/ListWebhooks' \
--header 'Authorization: Bearer <your BSR api token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"owner_name": "<the organization or username that owns the repository>",
"repository_name": "<the repository name>"
}'
This should return a successful response in the form of:
HTTP Status: 200 OK
Content-Type: application/json
{
"webhooks": [
{
"event": "WEBHOOK_EVENT_REPOSITORY_PUSH",
"webhookId": "the-webhook-id",
"createTime": "2022-07-11T21:15:27.633999Z",
"updateTime": "2022-07-11T21:15:27.633999Z",
"repository_name": "<the repository name>",
"owner_name": "<the organization or username that owns the repository>",
"callbackUrl": "https://your.callback.url/buf.alpha.webhook.v1alpha1.EventService/Event"
}
]
}
Delete webhook subscription with curl
Webhooks can be deleted using the
DeleteWebhook
RPC):
curl --location --request POST 'https://api.buf.build/buf.alpha.registry.v1alpha1.WebhookService/DeleteWebhook' \
--header 'Authorization: Bearer <your BSR api token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"webhook_id": "the-webhook-id-that-will-be-deleted"
}'
This should return a successful response in the form of:
HTTP Status: 200 OK
Content-Type: application/json
{}
Test receiving a webhook push event
You can test receiving repository push events with this snippet:
# Testing your webhook service using binary proto.
# You can use `buf beta convert` to take a JSON payload and
# convert it into binary proto format, and then use that
# as a body payload to send via curl.
echo '{
"event": "WEBHOOK_EVENT_REPOSITORY_PUSH",
"payload": {
"repositoryPush": {
"eventTime": "2022-07-11T15:07:30Z",
"repository": {
"id": "my-repo-id",
"name": "my-repo-name",
"createTime": "2022-07-10T15:07:30Z",
"updateTime": "2022-07-10T18:07:30Z",
"userId": "the-user-id",
"visibility": "VISIBILITY_PUBLIC"
},
"repositoryCommit": {
"author": "the-author-username",
"commitSequenceId": 10,
"createTime": "2022-07-11T15:07:30Z",
"id": "the-commit-id",
"name": "the-commit-name",
"digest": "the-commit-digest",
"tags": [
{
"author": "the-tag-author",
"commitName": "the-commit-hash",
"id": "the-tag-id",
"createTime": "2022-07-11T15:07:30Z",
"name": "the-tag-name"
}
]
}
}
}
}' | \
buf beta convert buf.build/bufbuild/buf \
--input=-#format=json \
--type buf.alpha.webhook.v1alpha1.EventRequest | \
curl -X POST https://your.callback.url/buf.alpha.webhook.v1alpha1.EventService/Event \
-H "Content-Type: application/proto" \
--data-binary @-
Once a webhook subscription is configured, your listener will begin receiving buf push
events with a proto payload in
the EventRequest
format. The payload includes the commit object that triggered the event (including the author), as
well as the repository object that this commit was pushed to. For more details on the payload, visit the docs
definition.