Documentation
¶
Overview ¶
Package bird is the Go SDK for the Bird email platform (ADR-0044).
The wire types and a low-level client are generated from the OpenAPI spec into internal/oapi and never hand-edited. This package is the hand-written, idiomatic layer on top: a curated resource surface, a typed error hierarchy, context cancellation, functional options, safe retries with reused idempotency keys, range-over-func pagination, and webhook verification. The request lifecycle lives in internal/requestconfig and the error model in internal/apierror; both are re-exported here.
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil { ... }
msg, err := client.Email.Send(ctx, bird.EmailSendParams{
From: "hello@acme.com", To: []string{"customer@example.com"},
Subject: "Welcome", HTML: "<h1>Hi</h1>",
})
Example ¶
Example constructs a client and sends an email. The region is taken from the API key's prefix; pass option.WithBaseURL or option.WithRegion to override.
package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev",
To: []string{"delivered@messagebird.dev"},
Subject: "Hello from Bird",
HTML: "<p>My first Bird email.</p>",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id)
}
Output:
Index ¶
- Constants
- func Bool(v bool) *bool
- func Int(v int) *int
- func Ptr[T any](v T) *T
- func String(v string) *string
- type APIError
- type Category
- type Client
- func (c *Client) Delete(ctx context.Context, path string, out any, opts ...option.RequestOption) error
- func (c *Client) Do(ctx context.Context, method, path string, body, out any, ...) error
- func (c *Client) Get(ctx context.Context, path string, out any, opts ...option.RequestOption) error
- func (c *Client) Patch(ctx context.Context, path string, body, out any, opts ...option.RequestOption) error
- func (c *Client) Post(ctx context.Context, path string, body, out any, opts ...option.RequestOption) error
- func (c *Client) Put(ctx context.Context, path string, body, out any, opts ...option.RequestOption) error
- type ConnectionError
- type DomainFailedEvent
- type DomainVerifiedEvent
- type EmailAcceptedEvent
- type EmailAttachment
- type EmailBouncedEvent
- type EmailClickedEvent
- type EmailComplainedEvent
- type EmailDefaults
- type EmailDeferredEvent
- type EmailDeliveredEvent
- type EmailListParams
- type EmailListUnsubscribedEvent
- type EmailMessage
- type EmailMessageList
- type EmailOpenedEvent
- type EmailOutOfBandBounceEvent
- type EmailProcessedEvent
- type EmailReceivedEvent
- type EmailRejectedEvent
- type EmailSendParams
- type EmailService
- func (s *EmailService) Get(ctx context.Context, id string, opts ...option.RequestOption) (*EmailMessage, error)
- func (s *EmailService) List(ctx context.Context, params EmailListParams, opts ...option.RequestOption) iter.Seq2[*EmailMessage, error]
- func (s *EmailService) ListPage(ctx context.Context, params EmailListParams, startingAfter string, ...) (*EmailMessageList, error)
- func (s *EmailService) Send(ctx context.Context, params EmailSendParams, opts ...option.RequestOption) (*EmailMessage, error)
- type EmailStatus
- type EmailSuppressionCreatedEvent
- type EmailTag
- type EmailUnsubscribedEvent
- type ErrorDetail
- type ErrorType
- type Event
- type RateLimitError
- type Response
- type TimeoutError
- type ValidationError
- type WebhookEventType
- type WebhookService
- type WebhookVerificationError
Examples ¶
Constants ¶
const ( ErrorTypeBadRequest = apierror.ErrorTypeBadRequest ErrorTypeAuth = apierror.ErrorTypeAuth ErrorTypeBilling = apierror.ErrorTypeBilling ErrorTypePermission = apierror.ErrorTypePermission ErrorTypeNotFound = apierror.ErrorTypeNotFound ErrorTypeConflict = apierror.ErrorTypeConflict ErrorTypePrecondition = apierror.ErrorTypePrecondition ErrorTypePayloadTooLarge = apierror.ErrorTypePayloadTooLarge ErrorTypeMisdirected = apierror.ErrorTypeMisdirected ErrorTypeValidation = apierror.ErrorTypeValidation ErrorTypeRateLimit = apierror.ErrorTypeRateLimit ErrorTypeInternal = apierror.ErrorTypeInternal ErrorTypeNotImplemented = apierror.ErrorTypeNotImplemented )
ErrorType values — the coarse categories clients branch on (ADR-0016).
Variables ¶
This section is empty.
Functions ¶
Types ¶
type APIError ¶
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
type Category ¶
type Category = oapi.EmailMessageCategory
Category classifies a send's suppression policy.
type Client ¶
type Client struct {
Email *EmailService
Webhooks *WebhookService
// contains filtered or unexported fields
}
Client is the entry point to the SDK. Construct it with NewClient and reach the API through its resource fields.
func NewClient ¶
func NewClient(opts ...option.RequestOption) (*Client, error)
NewClient builds a Client. An API key is required (option.WithAPIKey); the base URL is derived from the key's region prefix unless option.WithBaseURL or option.WithRegion is given.
func (*Client) Do ¶
func (c *Client) Do(ctx context.Context, method, path string, body, out any, opts ...option.RequestOption) error
Do is the low-level call the verb methods build on: it marshals body as JSON (when non-nil), runs the request lifecycle, and decodes a 2xx body into out (when non-nil).
func (*Client) Get ¶
Get, Post, Put, Patch, and Delete are the escape hatch for endpoints outside the curated surface. They run through the same auth, retry, idempotency, and base-URL handling as the typed methods. body (if non-nil) is sent as JSON; a 2xx response is decoded into out (if non-nil).
var out SuppressionList err := client.Get(ctx, "/v1/email/suppressions", &out)
Example ¶
The verb methods reach endpoints outside the curated surface, decoding the response into a value you provide.
package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
var out struct {
Data []struct {
Recipient string `json:"recipient"`
} `json:"data"`
}
if err := client.Get(context.Background(), "/v1/email/suppressions", &out); err != nil {
log.Fatal(err)
}
fmt.Println(len(out.Data))
}
Output:
type ConnectionError ¶
type ConnectionError = apierror.ConnectionError
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
type DomainFailedEvent ¶
type DomainFailedEvent = oapi.EventDomainFailed
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type DomainVerifiedEvent ¶
type DomainVerifiedEvent = oapi.EventDomainVerified
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailAcceptedEvent ¶
type EmailAcceptedEvent = oapi.EventEmailAccepted
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailAttachment ¶
type EmailAttachment = oapi.EmailAttachment
EmailAttachment is a file attachment on a send.
type EmailBouncedEvent ¶
type EmailBouncedEvent = oapi.EventEmailBounced
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailClickedEvent ¶
type EmailClickedEvent = oapi.EventEmailClicked
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailComplainedEvent ¶
type EmailComplainedEvent = oapi.EventEmailComplained
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailDefaults ¶
type EmailDefaults = requestconfig.EmailDefaults
EmailDefaults are values applied to an email send when the per-send params leave the field unset. Configure with option.WithEmailDefaults.
Example ¶
EmailDefaults set common send fields once; a per-send value always wins.
package main
import (
"context"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(
option.WithAPIKey(os.Getenv("BIRD_API_KEY")),
option.WithEmailDefaults(bird.EmailDefaults{
From: "hello@acme.com",
Category: bird.CategoryTransactional,
}),
)
if err != nil {
log.Fatal(err)
}
// From is filled from the default.
if _, err := client.Email.Send(context.Background(), bird.EmailSendParams{
To: []string{"customer@example.com"}, Subject: "Hi", HTML: "<p>hi</p>",
}); err != nil {
log.Fatal(err)
}
}
Output:
type EmailDeferredEvent ¶
type EmailDeferredEvent = oapi.EventEmailDeferred
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailDeliveredEvent ¶
type EmailDeliveredEvent = oapi.EventEmailDelivered
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailListParams ¶
type EmailListParams struct {
Limit int
Status EmailStatus
Category Category
Tag string
To string
From string
CreatedAfter time.Time
CreatedBefore time.Time
}
EmailListParams filters the message list. Zero-value fields are omitted.
type EmailListUnsubscribedEvent ¶
type EmailListUnsubscribedEvent = oapi.EventEmailListUnsubscribed
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailMessage ¶
type EmailMessage = oapi.EmailMessage
EmailMessage is a sent message with aggregate delivery status.
type EmailMessageList ¶
type EmailMessageList = oapi.EmailMessageList
EmailMessageList is one page of messages plus its pagination cursors.
type EmailOpenedEvent ¶
type EmailOpenedEvent = oapi.EventEmailOpened
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailOutOfBandBounceEvent ¶
type EmailOutOfBandBounceEvent = oapi.EventEmailOutOfBandBounce
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailProcessedEvent ¶
type EmailProcessedEvent = oapi.EventEmailProcessed
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailReceivedEvent ¶
type EmailReceivedEvent = oapi.EventEmailReceived
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailRejectedEvent ¶
type EmailRejectedEvent = oapi.EventEmailRejected
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailSendParams ¶
type EmailSendParams struct {
From string // sender; bare address or "Name <addr>" form; must be on a verified domain
To []string // primary recipients; each may be bare or "Name <addr>" form
Cc []string // optional; same syntax as To
Bcc []string // optional; same syntax as To
ReplyTo []string // optional Reply-To; same syntax as To
Subject string // subject line
HTML string // HTML body; at least one of HTML or Text is required
Text string // plain-text body
Tags []EmailTag // structured {name,value} labels for filtering and analytics
Metadata map[string]any // arbitrary JSON, echoed on reads and in webhook payloads
Headers map[string]string // custom email headers
Attachments []EmailAttachment // file attachments
Category Category // transactional (default) or marketing
IpPool string // IP pool ID (ipp_…); workspace default when empty
// TrackOpens and TrackClicks are pointers because the server default is
// true — a nil leaves the default, false explicitly disables tracking.
TrackOpens *bool
TrackClicks *bool
}
EmailSendParams is an email send. Optional fields are omitted from the request when left at their zero value.
Address fields (From, To, Cc, Bcc, ReplyTo) accept either a bare email address or RFC 5322 mailbox syntax with a display name: "Support Team <support@example.com>".
type EmailService ¶
type EmailService struct {
// contains filtered or unexported fields
}
EmailService sends and reads email messages. Reach it via Client.Email.
func (*EmailService) Get ¶
func (s *EmailService) Get(ctx context.Context, id string, opts ...option.RequestOption) (*EmailMessage, error)
Get returns a single message by ID, with aggregate delivery status rolled up across recipients.
Example ¶
package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Email.Get(context.Background(), "em_abc123")
if err != nil {
log.Fatal(err)
}
fmt.Println(*msg.Status, *msg.DeliveredCount)
}
Output:
func (*EmailService) List ¶
func (s *EmailService) List(ctx context.Context, params EmailListParams, opts ...option.RequestOption) iter.Seq2[*EmailMessage, error]
List walks every message matching params, fetching pages lazily. Range over it; the second value is non-nil only on the iteration where a fetch failed, after which the sequence ends.
for msg, err := range client.Email.List(ctx, bird.EmailListParams{Status: bird.EmailStatusBounced}) {
if err != nil { return err }
log.Println(msg.Id)
}
Example ¶
List auto-paginates: it lazily fetches each page and yields every matching message across all of them.
package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
for msg, err := range client.Email.List(context.Background(), bird.EmailListParams{Status: bird.EmailStatusBounced}) {
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id)
}
page, err := client.Email.ListPage(context.Background(), bird.EmailListParams{}, "")
if err != nil {
log.Fatal(err)
}
fmt.Println(len(page.Data)) // page.NextCursor carries the next starting_after
}
Output:
func (*EmailService) ListPage ¶
func (s *EmailService) ListPage(ctx context.Context, params EmailListParams, startingAfter string, opts ...option.RequestOption) (*EmailMessageList, error)
ListPage fetches one page of messages. Pass the previous page's NextCursor as startingAfter to advance; "" starts from the most recent.
func (*EmailService) Send ¶
func (s *EmailService) Send(ctx context.Context, params EmailSendParams, opts ...option.RequestOption) (*EmailMessage, error)
Send delivers an email and returns the created message. Sends are retried safely: a single idempotency key is reused across attempts, so a retry never double-delivers. Provide your own key with option.WithIdempotencyKey.
Example ¶
package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev",
To: []string{"delivered@messagebird.dev"},
Subject: "Hello from Bird",
HTML: "<p>My first Bird email.</p>",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}
Output:
Example (DisplayNames) ¶
Send with display names: "Name <addr>" syntax in From and To.
package main
import (
"context"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
_, err = client.Email.Send(context.Background(), bird.EmailSendParams{
From: "Bird Support <support@acme.com>",
To: []string{"Jane Doe <jane@example.com>", "bob@example.com"},
Subject: "Your order is confirmed",
HTML: "<p>Thanks for your order!</p>",
})
if err != nil {
log.Fatal(err)
}
}
Output:
Example (Errors) ¶
Branch on the typed error hierarchy. The SDK already retries transient failures (timeouts, 429, 5xx), so a returned error is terminal — most callers just propagate it; branch only to act on a category.
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
_, err = client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev", To: []string{"delivered@messagebird.dev"}, Subject: "Hello from Bird", HTML: "<p>My first Bird email.</p>",
})
if err != nil {
var rle *bird.RateLimitError
var ve *bird.ValidationError
var ae *bird.APIError
switch {
case errors.As(err, &rle):
fmt.Println("rate limited; retry after", rle.RetryAfter)
case errors.As(err, &ve):
for _, d := range ve.Details {
fmt.Printf("%s: %s\n", d.Param, d.Message)
}
case errors.As(err, &ae):
fmt.Printf("API error %s (status %d, request %s)\n", ae.Code, ae.StatusCode, ae.RequestID)
default:
log.Print(err) // transport: *bird.ConnectionError or *bird.TimeoutError
}
}
}
Output:
Example (Rich) ¶
A richer send: cc/bcc, reply-to, tags, metadata, opt-out of click tracking, and an idempotency key (safe to retry — the server dedupes).
package main
import (
"context"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
_, err = client.Email.Send(context.Background(), bird.EmailSendParams{
From: "hello@acme.com",
To: []string{"a@example.com", "b@example.com"},
Cc: []string{"manager@example.com"},
ReplyTo: []string{"support@acme.com"},
Subject: "Your March invoice",
HTML: "<p>Attached.</p>",
Tags: []bird.EmailTag{{Name: "category", Value: "billing"}},
Metadata: map[string]any{"invoice_id": "inv_123"},
TrackClicks: bird.Bool(false),
}, option.WithIdempotencyKey("invoice-march/cust_1"))
if err != nil {
log.Fatal(err)
}
}
Output:
type EmailStatus ¶
type EmailStatus = oapi.EmailMessageStatus
EmailStatus is a message's aggregate delivery status.
const ( EmailStatusAccepted EmailStatus = "accepted" EmailStatusProcessed EmailStatus = "processed" EmailStatusDelivered EmailStatus = "delivered" EmailStatusDeferred EmailStatus = "deferred" EmailStatusBounced EmailStatus = "bounced" EmailStatusComplained EmailStatus = "complained" EmailStatusRejected EmailStatus = "rejected" EmailStatusPartialFailure EmailStatus = "partial_failure" )
type EmailSuppressionCreatedEvent ¶
type EmailSuppressionCreatedEvent = oapi.EventEmailSuppressionCreated
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type EmailUnsubscribedEvent ¶
type EmailUnsubscribedEvent = oapi.EventEmailUnsubscribed
Webhook event payloads, returned by Event.AsAny. Type-switch on these.
type ErrorDetail ¶
type ErrorDetail = apierror.ErrorDetail
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
type ErrorType ¶
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
type Event ¶
type Event struct {
// contains filtered or unexported fields
}
Event is a verified webhook event. Switch on Type, or call AsAny and type-switch on the concrete payload (e.g. EmailDeliveredEvent).
func (Event) AsAny ¶
AsAny decodes the event into its concrete payload type. An unknown future event type returns an error rather than a panic, so an older SDK keeps working against a newer server.
func (Event) Type ¶
func (e Event) Type() WebhookEventType
Type returns the event's discriminant, e.g. EventTypeEmailDelivered.
type RateLimitError ¶
type RateLimitError = apierror.RateLimitError
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
type Response ¶
type Response = requestconfig.Response
Response is the transport metadata for one call, captured via option.WithResponseInto.
type TimeoutError ¶
type TimeoutError = apierror.TimeoutError
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
type ValidationError ¶
type ValidationError = apierror.ValidationError
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
type WebhookEventType ¶
type WebhookEventType = oapi.WebhookEventType
WebhookEventType is a webhook event's discriminant.
const ( EventTypeDomainFailed WebhookEventType = "domain.failed" EventTypeDomainVerified WebhookEventType = "domain.verified" EventTypeEmailAccepted WebhookEventType = "email.accepted" EventTypeEmailBounced WebhookEventType = "email.bounced" EventTypeEmailClicked WebhookEventType = "email.clicked" EventTypeEmailComplained WebhookEventType = "email.complained" EventTypeEmailDeferred WebhookEventType = "email.deferred" EventTypeEmailDelivered WebhookEventType = "email.delivered" EventTypeEmailListUnsubscribed WebhookEventType = "email.list_unsubscribed" EventTypeEmailOpened WebhookEventType = "email.opened" EventTypeEmailOutOfBandBounce WebhookEventType = "email.out_of_band_bounce" EventTypeEmailProcessed WebhookEventType = "email.processed" EventTypeEmailReceived WebhookEventType = "email.received" EventTypeEmailRejected WebhookEventType = "email.rejected" EventTypeEmailSuppressionCreated WebhookEventType = "email_suppression.created" EventTypeEmailUnsubscribed WebhookEventType = "email.unsubscribed" )
type WebhookService ¶
type WebhookService struct {
// contains filtered or unexported fields
}
WebhookService verifies inbound webhook deliveries. Reach it via Client.Webhooks. Configure the signing secret with option.WithWebhookSecret on the client (or per call on Unwrap). It is pure crypto — no transport.
func (*WebhookService) Unwrap ¶
func (s *WebhookService) Unwrap(payload []byte, headers http.Header, opts ...option.RequestOption) (Event, error)
Unwrap verifies the Standard Webhooks signature over the raw request body and returns the decoded event. Hand it the exact bytes received — parsing and re-serializing before verifying breaks the signature.
Example ¶
Unwrap verifies the Standard Webhooks signature over the raw request body and returns a typed event to dispatch on.
package main
import (
"fmt"
"io"
"log"
"net/http"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(
option.WithAPIKey(os.Getenv("BIRD_API_KEY")),
option.WithWebhookSecret(os.Getenv("BIRD_WEBHOOK_SECRET")),
)
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/webhooks/bird", func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
event, err := client.Webhooks.Unwrap(body, r.Header)
if err != nil {
http.Error(w, "invalid signature", http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusNoContent) // ack fast, then process
payload, _ := event.AsAny()
switch p := payload.(type) {
case bird.EmailDeliveredEvent:
fmt.Println("delivered:", p.Data.EmailId, p.Data.Recipient)
case bird.EmailBouncedEvent:
fmt.Println("bounced:", p.Type)
}
})
}
Output:
type WebhookVerificationError ¶
type WebhookVerificationError = apierror.WebhookVerificationError
The SDK error model, re-exported from internal/apierror so these names are the semver-locked public surface. Catch *APIError (via errors.As) to handle any server error; the variants carry extra data. Transport failures with no HTTP response are *ConnectionError / *TimeoutError; a bad webhook signature is *WebhookVerificationError.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
quickstart-email
command
|
|
|
internal
|
|
|
apierror
Package apierror holds the SDK's error model: the wire-error mapping and the typed error hierarchy returned to callers.
|
Package apierror holds the SDK's error model: the wire-error mapping and the typed error hierarchy returned to callers. |
|
oapi
Package oapi provides primitives to interact with the openapi HTTP API.
|
Package oapi provides primitives to interact with the openapi HTTP API. |
|
requestconfig
Package requestconfig holds the resolved per-request configuration.
|
Package requestconfig holds the resolved per-request configuration. |
|
Package option carries the functional options that configure a bird.Client at construction and override settings for a single call.
|
Package option carries the functional options that configure a bird.Client at construction and override settings for a single call. |