This is the Golang SDK for Convoy. It makes it easy to interact with the Convoy API. You can view the full API Reference here
$ go get github.com/frain-dev/convoy-go/v2The client subpackage is generated from Convoy's OpenAPI spec via
oapi-codegen and covers the
full /api/v1 surface with typed requests and responses:
import "github.com/frain-dev/convoy-go/v2/client"
c, err := client.NewClientWithResponses("https://us.getconvoy.cloud/api",
client.WithRequestEditorFn(func(_ context.Context, req *http.Request) error {
req.Header.Set("Authorization", "Bearer "+apiKey)
// Pin the API version this client was generated from.
req.Header.Set("X-Convoy-Version", "2025-11-24")
return nil
}))
data := map[string]interface{}{"amount": 100, "currency": "USD"}
endpointID, eventType := "endpoint-id", "invoice.paid"
resp, err := c.CreateEndpointEventWithResponse(ctx, projectID,
client.CreateEndpointEventJSONRequestBody{
EndpointId: &endpointID,
EventType: &eventType,
Data: &data,
})Do not edit client/client.gen.go by hand; regenerate with
./scripts/generate.sh (CI on frain-dev/convoy dispatches this when the
spec changes). The hand-written client below and webhook verify are never
touched by generation.
To begin you need to define a Client.
Below are the several ways you can configure a client depending on your needs.
// Convoy Cloud (US): https://us.getconvoy.cloud/api/v1
// Convoy Cloud (EU): https://eu.getconvoy.cloud/api/v1
// Self-hosted: https://your-instance/api/v1
baseURL := "https://us.getconvoy.cloud/api/v1"
// Regular Client
c := convoy.New(baseURL, apiKey, projectID)
// Add a Custom HTTP Client
client := &http.Client{}
c := convoy.New(baseURL, apiKey, projectID,
convoy.OptionHTTPClient(client))
// Add a SQS Client
so := &convoy.SQSOptions{
Client: sqs.New(),
QueueUrl: "queue-url",
}
c := convoy.New(baseURL, apiKey, projectID,
convoy.OptionSQSOptions(so))
// Add a Kafka Client
ko := &convoy.KafkaOptions{
Client: &kafka.Client{},
Topic: "kafka-topic",
}
c := convoy.New(baseURL, apiKey, projectID,
convoy.OptionKafkaOptions(ko))Please see go reference for other options available to use to configure your client.
body := &convoy.CreateEndpointRequest{
Name: "endpoint-name",
// Get a test ingest URL from https://playground.getconvoy.io
URL: "https://us.getconvoy.cloud/ingest/DQzxCcNKTB7SGqzm",
Secret: "endpoint-secret",
SupportEmail: "notifications@getconvoy.io",
}
endpoint, err := c.Endpoints.Create(ctx, body, nil)
if err != nil {
return err
}Store the Endpoint ID, so you can use it in subsequent requests for creating subscriptions or sending events.
body := &convoy.CreateSubscriptionRequest{
Name: "endpoint-subscription",
EndpointID: "endpoint-id",
FilterConfig: &convoy.FilterConfiguration{
EventTypes: []string{"*"},
},
}
subscription, err := c.Subscriptions.Create(ctx, body)
if err != nil {
return err
}You can send events to Convoy via HTTP or via any supported message broker. See here to see the list of supported brokers.
// Send an event to a single endpoint.
body := &CreateEventRequest{
EventType: "event.type",
EndpointID: "endpoint-id",
IdempotencyKey: "unique-event-id",
Data: []byte(`{"version": "Convoy v24.0.0"}`),
}
event, err := c.Events.Create(ctx, body)
if err != nil {
return err
}
// Send event to multiple endpoints.
body := &CreateFanoutEventRequest{
EventType: "event.type",
OwnerID: "unique-user-id",
IdempotencyKey: "unique-event-id",
Data: []byte(`{"version": "Convoy v24.0.0"}`),
}
event, err := c.Events.FanoutEvent(ctx, body)
if err != nil {
return err
}Note: The body struct used above is the same used for the message brokers below.
// Send event to a single endpoint.
err := c.SQS.WriteEvent(ctx, body)
if err != nil {
return err
}
// Send event to multiple endpoints.
err := c.SQS.WriteFanoutEvent(ctx, body)
if err != nil {
return err
}This library depends on kafka-go to configure Kafka Clients.
// Send event to a single endpoint.
err := c.Kafka.WriteEvent(ctx, body)
if err != nil {
return err
}
// Send event to multiple endpoints.
err := c.Kafka.WriteFanoutEvent(ctx, body)
if err != nil {
return err
}This client supports verifying simple and advanced webhook signatures. Verify with the raw request body, before parsing it.
webhook := convoy.NewWebhook(&convoy.WebhookOpts{
Secret: "endpoint-secret",
})
// Verify an incoming *http.Request. This reads the request body; read it
// yourself first and use VerifyPayload if you need the body afterwards.
func handler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "failed to read body", http.StatusBadRequest)
return
}
if err := webhook.VerifyPayload(body, r.Header.Get("X-Convoy-Signature")); err != nil {
http.Error(w, "invalid signature", http.StatusBadRequest)
return
}
// signature is valid; process the event using body
w.WriteHeader(http.StatusOK)
}The following table identifies which version of the Convoy API is supported by this (and past) versions of this repo (convoy-go)
| convoy-go Version | Convoy API Version |
|---|---|
| v2.1.5 | 0001-01-01 |
| v2.1.6 | 0001-01-01 |
| v2.1.7 | 0001-01-01 |
| v2.2.0 | 2025-11-24 |
The MIT License (MIT). Please see License File for more information.
Existing GetDeliveryAttempts(ctx, projectID, deliveryID, editors...),
GetDeliveryAttemptsWithResponse(...), and NewGetDeliveryAttemptsRequest(...)
retain their original signatures and array response types. Existing client
interfaces do not require any new methods.
Use GetDeliveryAttemptsWithParams or
GetDeliveryAttemptsWithParamsWithResponse with GetDeliveryAttemptsParams
for the optional Paginated and Before query parameters. Paginated responses
contain content and next_cursor; use the returned cursor as Before for
older history. The generated response type supports both array and page data.
Custom clients used with ClientWithResponses can opt in by implementing
DeliveryAttemptsWithParamsClient.
scripts/generate.sh runs the checked-in compatibility transform after
oapi-codegen. Do not bypass it or hand-edit the generated file. Run
./scripts/check-compatibility.sh after generation; both PR CI and the SDK
approval workflow require these tests, and fail if a required test disappears.