Sign inGet Started

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 参考文档中查找路径。

后续步骤