Go SDK
github.com/messagebird/bird-sdk-go(包 bird)是 Bird API 的官方 Go SDK。本页涵盖安装、配置、错误、重试、分页和 webhooks。要使用 SDK 发送邮件,请从 Go 邮件快速入门开始。
安装
代码示例
go get github.com/messagebird/bird-sdk-go需要 Go 1.24+。可运行的逐方法示例在 pkg.go.dev 的每个符号下渲染。
创建客户端
bird.NewClient 接受来自 option 包的函数式选项。只有 API 密钥是必需的。密钥的 bk_{region}_… 前缀决定基础 URL:
代码示例
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)
}每个 API 方法都以 context 为首参数并返回 (T, error)。传入请求的 context 以将取消传播为 context.Canceled 或 context.DeadlineExceeded,无需将其包装在 SDK 错误中。
选项
选项按顺序生效(后设置的优先)。四个选项仅限构造时使用,传入单次调用会返回错误:WithAPIKey、WithBaseURL、WithRegion 和 WithHTTPClient。其余选项既可在构造时使用(客户端级默认值),也可在单次调用时使用(该请求的覆盖值):
代码示例
client, err := bird.NewClient(
option.WithAPIKey(os.Getenv("BIRD_API_KEY")),
option.WithTimeout(10*time.Second), // per-attempt; each retry gets a fresh budget
)
// This send does not retry transient failures.
msg, err := client.Email.Send(ctx, params, option.WithMaxRetries(0))| 选项 | 作用域 | 功能说明 |
|---|---|---|
| WithAPIKey、WithBaseURL、WithRegion、WithHTTPClient | 仅限构造时 | 凭证、端点解析以及底层 *http.Client。 |
| WithTimeout、WithMaxRetries | 构造时或单次调用 | 每次尝试的超时时间和瞬时故障的重试预算。 |
| WithIdempotencyKey | 单次调用 | 为一次变更调用固定幂等键(否则会自动生成一个)。 |
| WithHeader | 构造时或单次调用 | 额外的请求头。SDK 拥有的请求头(Authorization、Idempotency-Key,…)优先。 |
| WithEmailDefaults、WithWebhookSecret、WithResponseInto | 构造时或单次调用 | 渠道级发送默认值、webhook 签名密钥和原始传输元数据捕获。 |
构建方式
线格类型和底层客户端根据 Bird 的 OpenAPI 规范生成。手写的 bird 包提供精选的资源接口(client.Email、client.Webhooks)以及带有 Go 类型(如 []string 和 time.Time)的参数结构体。其响应类型以别名方式引用生成的模型,保持与线格格式一致。核心层为每个资源管理重试、超时和幂等性。完整模型参见 SDK 概念。
错误
每个服务器故障都是一个 *bird.APIError,携带 StatusCode、Type(粗粒度的错误分类)、Code(稳定的 E##### 代码)、Message 以及用于支持关联的 RequestID。两个变体携带额外数据:*bird.RateLimitError(429,附带 RetryAfter)和 *bird.ValidationError(422,附带逐字段的 Details)。两者都可解包为 *APIError,因此单个 errors.As(err, &apiErr) 即可捕获所有服务器响应。使用 errors.As 分支处理:
代码示例
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
}
}
}没有 HTTP 响应的故障是独立类型:*bird.ConnectionError(DNS、连接被拒绝)和 *bird.TimeoutError(单次尝试超过其超时时间)。无效的 webhook 签名为 *bird.WebhookVerificationError。
安全重试
瞬时故障(包括超时、429 响应和 5xx 响应)会自动重试。默认预算为两次重试;使用 WithMaxRetries 调整,或设为零以禁用重试。对于变更调用,SDK 为每个逻辑调用生成一个幂等键,并在每次尝试中复用。传入 option.WithIdempotencyKey 可设置自定义键,使应用层重试安全。
分页
列表方法返回一个 iter.Seq2[*T, error],这是一个惰性 range-over-func 迭代器,在消费时按需获取页面:
代码示例
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
}跳出循环即停止获取;获取错误只产出一次并结束序列。若需手动控制游标,ListPage 返回一页数据加下一个游标。
Webhooks
client.Webhooks.Unwrap 对原始请求体验证 Standard Webhooks 签名,并返回类型化的事件。使用 option.WithWebhookSecret 在客户端或单次调用中配置签名密钥。将收到的原始字节传给 Unwrap,因为解析后再序列化会破坏签名:
代码示例
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)
}
})
}对 event.Type() 进行 switch 以获取判别值,或对 AsAny() 获取具体载荷。未知的未来事件类型会返回错误而非 panic,因此较旧的 SDK 仍可与较新的服务器兼容。
逃生通道
尚未纳入类型化接口的端点可通过 client.Get / Post / Put / Patch / Delete 访问,具有相同的认证、重试和幂等处理:
代码示例
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))
}在 API 参考文档中查找路径。
后续步骤
- Go 邮件快速入门:发送第一条消息并使用 Send、Get 和 List。
- SDK 概念:了解跨 SDK 的错误、幂等性、分页和 webhooks 模型。
- API 参考文档:查看底层 HTTP 契约。