Taifa MailTaifa Mail Docs
SDKs

Go SDK

Official Go SDK for the Taifa Mail API. Send email, manage domains, contacts, suppressions, templates, and webhooks with a typed, idiomatic client.

The Taifa Mail Go SDK is a typed, idiomatic client for the Taifa Mail API. It wraps every Core resource (emails, domains, contacts, suppressions, templates, and webhooks) behind a single Client, handles bearer authentication, JSON encoding, and retries, and maps every API error to a single *Error type.

The module is github.com/GovConnectKenya/taifa-mail-go and lives in its own repository: github.com/GovConnectKenya/taifa-mail-go.

Installation

go get github.com/GovConnectKenya/taifa-mail-go

The SDK requires Go 1.21 or later and depends only on the standard library.

The package name is taifamail, so you import it directly:

import taifamail "github.com/GovConnectKenya/taifa-mail-go"

Client setup

Construct a client with taifamail.New, passing your API key. API keys start with tfm_k_ and are passed as a bearer token on every request.

client := taifamail.New("tfm_k_your_api_key")

New accepts variadic taifamail.Option values to override the defaults:

OptionDefaultDescription
taifamail.WithBaseURL(url string)https://govconnect.keOverride the API base URL.
taifamail.WithMaxRetries(n int)3Total attempts on 429 and 5xx, including the first.
taifamail.WithTimeout(d time.Duration)30sPer-request timeout.
taifamail.WithHTTPClient(hc *http.Client)stdlib clientInject a custom *http.Client for proxies or testing.
client := taifamail.New(
    "tfm_k_your_api_key",
    taifamail.WithBaseURL("https://govconnect.ke"),
    taifamail.WithMaxRetries(5),
    taifamail.WithTimeout(60*time.Second),
)

The resources are reached as fields on the client: client.Emails, client.Domains, client.Contacts, client.Suppressions, client.Templates, and client.Webhooks. The client is safe for concurrent use.

Every method takes a context.Context as its first argument, so you can carry deadlines and cancellation through to the underlying HTTP request.

Send an email

The example below is a complete, runnable program. It builds a SendEmail, sends it, and prints the queued message id.

package main
 
import (
	"context"
	"fmt"
	"log"
 
	taifamail "github.com/GovConnectKenya/taifa-mail-go"
)
 
func main() {
	client := taifamail.New("tfm_k_your_api_key")
 
	result, err := client.Emails.Send(context.Background(), taifamail.SendEmail{
		From:    taifamail.Address{Email: "hello@yourdomain.com", Name: "Your Company"},
		To:      []taifamail.Address{taifamail.Addr("recipient@example.com")},
		Subject: "Welcome aboard",
		HTML:    "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
		Text:    "Welcome! Thanks for signing up.",
	})
	if err != nil {
		log.Fatalf("send failed: %v", err)
	}
 
	fmt.Printf("queued %s with status %s\n", result.ID, result.Status)
}

The From field is required and must be a registered sender on a verified domain. Provide HTML, Text, or both. taifamail.Addr("email@example.com") is a helper that builds an Address from a bare email string when you do not need a display name.

Send returns a *SendResult:

FieldTypeDescription
IDstringThe queued email id.
StatusstringQueue status, for example queued.
MessageIDstringThe RFC message id, when available.
RejectionReasonstringSet when the message was rejected.

Emails

ctx := context.Background()
 
// Send a batch (Starter plan and up).
batch, err := client.Emails.SendBatch(ctx, []taifamail.SendEmail{
	{From: taifamail.Addr("hello@yourdomain.com"), To: []taifamail.Address{taifamail.Addr("a@example.com")}, Subject: "Hi", Text: "Hello"},
	{From: taifamail.Addr("hello@yourdomain.com"), To: []taifamail.Address{taifamail.Addr("b@example.com")}, Subject: "Hi", Text: "Hello"},
})
 
// Dry-run a send without delivering it.
validation, err := client.Emails.Validate(ctx, taifamail.SendEmail{
	From:    taifamail.Addr("hello@yourdomain.com"),
	To:      []taifamail.Address{taifamail.Addr("recipient@example.com")},
	Subject: "Test",
	HTML:    "<p>Test</p>",
})
 
// List recent emails, newest first.
emails, err := client.Emails.List(ctx, taifamail.ListEmailsParams{Status: "delivered", Page: 0, Limit: 20})
 
// Fetch one email with its bodies and event timeline.
detail, err := client.Emails.Get(ctx, "a1b2c3d4-...")
 
// List delivery, open, click, and bounce events.
events, err := client.Emails.Events(ctx, "a1b2c3d4-...")
 
// Re-send a bounced, rejected, or failed email.
retry, err := client.Emails.Retry(ctx, "a1b2c3d4-...")
 
// Search with inline tokens (to:, from:, status:, domain:, tag:).
hits, err := client.Emails.Search(ctx, taifamail.SearchEmailsParams{Q: "welcome status:delivered"})
 
// Scheduled sends.
scheduled, err := client.Emails.ListScheduled(ctx)
cancelled, err := client.Emails.CancelScheduled(ctx, "b2c3d4e5-...")
sentNow, err := client.Emails.SendScheduledNow(ctx, "b2c3d4e5-...")
 
// Poll for status changes since a timestamp (capped at 50 rows).
updated, err := client.Emails.Updates(ctx, "2026-06-13T10:00:00Z")

To schedule a send for later, set SendAt on the SendEmail to an ISO 8601 timestamp (Starter plan and up).

Pagination is zero-based everywhere: Page: 0 is the first page.

Domains

ctx := context.Background()
 
// List your sending domains.
domains, err := client.Domains.List(ctx)
 
// Register a domain and get the DNS records to publish.
domain, err := client.Domains.Create(ctx, "yourdomain.com")
 
// Fetch, verify, and inspect a domain.
got, err := client.Domains.Get(ctx, "domain-id")
verified, err := client.Domains.Verify(ctx, "domain-id")
health, err := client.Domains.Health(ctx, "domain-id")
diagnosis, err := client.Domains.Diagnose(ctx, "domain-id")
 
// Rotate the DKIM key, returning the new record to publish.
rotation, err := client.Domains.RotateDkim(ctx, "domain-id")
 
// Transfer a domain to another Taifa Mail account.
transfer, err := client.Domains.Transfer(ctx, "domain-id", taifamail.TransferParams{
	TargetEmail: "owner@otheraccount.com",
	Note:        "Handing over the marketing domain.",
})
 
// Availability and existence checks.
availability, err := client.Domains.CheckAvailability(ctx, "newdomain.com")
check, err := client.Domains.Check(ctx, "yourdomain.com")
 
// Delete a domain.
err = client.Domains.Delete(ctx, "domain-id")

The newly created Domain carries a DnsRecords slice. Publish each record's Host and Value, then call Verify to complete setup.

Contacts

ctx := context.Background()
 
// Manage subscriber lists.
lists, err := client.Contacts.ListLists(ctx)
name := "Newsletter"
list, err := client.Contacts.CreateList(ctx, taifamail.CreateListParams{Name: name})
detail, err := client.Contacts.GetList(ctx, "list-id", taifamail.ListContactsParams{Page: 0, Limit: 50})
newName := "Weekly Newsletter"
updated, err := client.Contacts.UpdateList(ctx, "list-id", taifamail.UpdateListParams{Name: &newName})
err = client.Contacts.DeleteList(ctx, "list-id")
 
// Add and remove contacts.
contact, err := client.Contacts.AddContact(ctx, "list-id", taifamail.AddContactParams{Email: "jane@example.com"})
err = client.Contacts.RemoveContact(ctx, "list-id", "contact-id")
 
// Import contacts from a CSV file (header row required).
csv := []byte("email,name\njane@example.com,Jane Doe\n")
imported, err := client.Contacts.UploadCSV(ctx, "list-id", csv, "contacts.csv")
 
// Send a templated email to every contact in a list.
result, err := client.Contacts.BulkSend(ctx, "list-id", taifamail.BulkSendParams{
	SenderAddressID: "sender-address-id",
	Subject:         "Hello {{name}}",
	HTML:            "<p>Hi {{name}}, thanks for subscribing.</p>",
})

BulkSend injects the list id as contact_list_id automatically, so you leave that field unset. Subject and body may use {{email}}, {{name}}, and {{metadata_key}} placeholders. Optional string fields on the params structs are pointers, so unset fields are omitted from the request.

Suppressions

ctx := context.Background()
 
// List suppressed addresses (paginated envelope).
page, err := client.Suppressions.List(ctx, taifamail.ListSuppressionsParams{Page: 0, Limit: 50})
for _, s := range page.Items {
	fmt.Println(s.EmailAddress, s.Reason)
}
fmt.Println("total:", page.Total)
 
// Suppress a single address (Reason defaults to "manual").
suppression, err := client.Suppressions.Add(ctx, taifamail.AddSuppressionParams{Email: "bounce@example.com"})
 
// Bulk import from a file (one email per line).
file := []byte("a@example.com\nb@example.com\n")
bulk, err := client.Suppressions.BulkUpload(ctx, file, "suppressions.txt")
 
// Remove an address from the suppression list.
err = client.Suppressions.Remove(ctx, "suppression-id")

Suppressions.List returns a *Page[Suppression], the generic envelope used by endpoints that paginate with a total count.

Templates

ctx := context.Background()
 
// Templates are available on the Starter plan and up.
templates, err := client.Templates.List(ctx)
 
subject := "Welcome, {{name}}"
html := "<h1>Hi {{name}}</h1>"
created, err := client.Templates.Create(ctx, taifamail.CreateTemplateParams{
	Name:    "Welcome email",
	Subject: &subject,
	HTML:    &html,
})
 
got, err := client.Templates.Get(ctx, "template-id")
 
newName := "Welcome email v2"
updated, err := client.Templates.Update(ctx, "template-id", taifamail.UpdateTemplateParams{Name: &newName})
 
copied, err := client.Templates.Duplicate(ctx, "template-id")
 
err = client.Templates.Delete(ctx, "template-id")

Variables on a Template are derived server-side from {{name}} placeholders and are read-only, so you do not pass them.

Webhooks

ctx := context.Background()
 
// List, create, update, and delete webhooks.
hooks, err := client.Webhooks.List(ctx)
webhook, err := client.Webhooks.Create(ctx, taifamail.CreateWebhookParams{
	URL:    "https://example.com/hooks/taifamail",
	Events: []string{"email.delivered", "email.bounced"},
})
active := false
updated, err := client.Webhooks.Update(ctx, "webhook-id", taifamail.UpdateWebhookParams{IsActive: &active})
err = client.Webhooks.Delete(ctx, "webhook-id")
 
// Send a test delivery to the endpoint.
test, err := client.Webhooks.Test(ctx, "webhook-id")
 
// Inspect delivery attempts (paginated envelope).
deliveries, err := client.Webhooks.ListDeliveries(ctx, "webhook-id", taifamail.ListDeliveriesParams{Page: 0, Limit: 20})
delivery, err := client.Webhooks.GetDelivery(ctx, "webhook-id", "delivery-id")

The signing Secret is generated by the API and returned in plaintext on every read of a Webhook.

Error handling

Every method returns an error that, on any non-2xx response or a transport failure that survives all retries, is an *taifamail.Error. Type-assert to inspect it:

result, err := client.Emails.Send(ctx, msg)
if err != nil {
	var apiErr *taifamail.Error
	if errors.As(err, &apiErr) {
		fmt.Printf("status=%d code=%s message=%s\n", apiErr.Status, apiErr.Code, apiErr.Message)
		switch apiErr.Status {
		case 422:
			// Validation problem, for example an unverified sender.
		case 429:
			// Rate limited.
		}
	}
	return err
}

The Error struct has three fields:

FieldTypeDescription
StatusintThe HTTP status code. 0 means a transport or network failure where no response was received.
CodestringThe machine-readable error code from the API body, when present.
MessagestringA human-readable description of the failure.

Configuration

Retries. The client retries 429 and 5xx responses, as well as transient network failures, up to WithMaxRetries total attempts (default 3). Backoff is exponential starting at 250ms and honors a Retry-After header when the API sends one. Other 4xx responses are never retried. Multipart uploads (UploadCSV, BulkUpload) are not retried because they are not idempotent.

Timeout. WithTimeout sets the per-request timeout (default 30s). For finer control, pass a context.Context with its own deadline; the retry loop respects context cancellation between attempts.

Base URL. WithBaseURL overrides the API host (default https://govconnect.ke). A trailing slash is trimmed for you.

client := taifamail.New(
	"tfm_k_your_api_key",
	taifamail.WithMaxRetries(5),
	taifamail.WithTimeout(45*time.Second),
	taifamail.WithBaseURL("https://govconnect.ke"),
)

Next steps

  • SDKs overview for the full list of official SDKs and shared conventions.
  • Emails API reference for the underlying HTTP endpoints, request fields, and response shapes.

On this page