SDK Go
🐹 Para backends Go: SDK oficial com zero dependências, context.Context nativo e tipos completos
O SDK Go da Notifica permite integrar notificações multi-canal em qualquer aplicação Go. Este guia cobre:
- Instalação e setup
- Envio de notificações (email, SMS, WhatsApp, in-app, push)
- Gerenciamento de subscribers, templates e workflows
- Webhooks e analytics
- Tratamento de erros
Instalação
go get github.com/notifica-tech/notifica-go
Requisitos: Go 1.21+ (usa generics).
Zero dependências externas — apenas a biblioteca padrão do Go.
Setup básico
package main
import (
"context"
"fmt"
"log"
notifica "github.com/notifica-tech/notifica-go"
)
func main() {
client := notifica.NewClient("nk_live_sua_chave")
n, err := client.Notifications.Send(context.Background(), ¬ifica.SendNotificationParams{
Channel: notifica.ChannelEmail,
Recipient: "[email protected]",
TemplateID: "welcome",
Payload: map[string]any{"name": "Fernanda"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Notificação enviada:", n.ID, "status:", n.Status)
}
Autenticação
O SDK usa Bearer Token no header Authorization automaticamente:
// Chave de produção
client := notifica.NewClient("nk_live_sua_chave")
// Chave de sandbox (desenvolvimento)
client := notifica.NewClient("nk_test_sua_chave")
Use variáveis de ambiente para não commitar chaves no código:
client := notifica.NewClient(os.Getenv("NOTIFICA_API_KEY"))
Configuração avançada
client := notifica.NewClient("nk_live_...",
// URL customizada (self-hosted ou staging)
notifica.WithBaseURL("https://api.staging.notifica.com.br"),
// HTTP client customizado (timeout, proxy, TLS)
notifica.WithHTTPClient(&http.Client{
Timeout: 60 * time.Second,
}),
)
| Opção | Padrão | Descrição |
|---|---|---|
WithBaseURL | https://api.notifica.com.br | URL base da API |
WithHTTPClient | 30s timeout | HTTP client customizado |
Enviando notificações
Todas as notificações são assíncronas e retornam status accepted (202).
Email
n, err := client.Notifications.Send(ctx, ¬ifica.SendNotificationParams{
Channel: notifica.ChannelEmail,
Recipient: "[email protected]",
TemplateID: "order-confirmation",
Payload: map[string]any{
"name": "Roberto",
"order_id": "ORD-12345",
},
})
WhatsApp
n, err := client.Notifications.Send(ctx, ¬ifica.SendNotificationParams{
Channel: notifica.ChannelWhatsApp,
Recipient: "+5511998765432",
TemplateID: "welcome",
Payload: map[string]any{
"customer_name": "João",
"company": "Acme",
},
})
SMS
n, err := client.Notifications.Send(ctx, ¬ifica.SendNotificationParams{
Channel: notifica.ChannelSMS,
Recipient: "+5511998765432",
Payload: map[string]any{
"content": "Seu código de verificação é: 583921",
},
})
In-App
n, err := client.Notifications.Send(ctx, ¬ifica.SendNotificationParams{
Channel: notifica.ChannelInApp,
Recipient: "subscriber-id-aqui",
Payload: map[string]any{
"title": "Novo comentário",
"body": "Maria comentou na sua tarefa #123",
},
})
Com chave de idempotência
Para evitar duplicatas em retries:
n, err := client.Notifications.Send(ctx, ¬ifica.SendNotificationParams{
Channel: notifica.ChannelEmail,
Recipient: "[email protected]",
TemplateID: "payment-receipt",
Payload: map[string]any{"amount": "R$ 99,90"},
IdempotencyKey: "payment-12345", // Use ID do seu sistema
})
Tracking de entrega
Ver status de uma notificação
n, err := client.Notifications.Get(ctx, "notification-id")
fmt.Println(n.Status) // "delivered"
fmt.Println(n.DeliveredAt) // 2024-01-15T14:30:05Z
Listar tentativas de entrega
attempts, err := client.Notifications.ListAttempts(ctx, "notification-id")
for _, a := range attempts {
fmt.Printf("Tentativa %d: %s (provider: %s)\n", a.AttemptNumber, a.Status, a.Provider)
}
Listar notificações com filtros
page, err := client.Notifications.List(ctx, ¬ifica.ListNotificationsParams{
Status: notifica.StatusFailed,
Channel: notifica.ChannelEmail,
Limit: 50,
})
for _, n := range page.Data {
fmt.Println(n.ID, n.Status)
}
// Paginação via cursor
if page.Meta.HasMore {
nextPage, err := client.Notifications.List(ctx, ¬ifica.ListNotificationsParams{
Cursor: *page.Meta.Cursor,
})
}
Subscribers (Assinantes)
Criar ou atualizar (upsert)
sub, err := client.Subscribers.Create(ctx, ¬ifica.CreateSubscriberParams{
ExternalID: "user-123", // ID do seu sistema
Email: "[email protected]",
Phone: "+5511999999999",
Name: "Maria Silva",
CustomProperties: map[string]any{
"plan": "pro",
"company": "Acme",
},
})
Se o ExternalID já existir, o subscriber é atualizado automaticamente.
Listar com busca
page, err := client.Subscribers.List(ctx, ¬ifica.ListSubscribersParams{
Search: "maria",
Limit: 20,
})
fmt.Printf("Encontrados: %d subscribers\n", page.Total)
Atualizar
sub, err := client.Subscribers.Update(ctx, "subscriber-id", ¬ifica.UpdateSubscriberParams{
Name: notifica.String("Maria Santos"),
})
Deletar (LGPD)
Soft delete com nullificação de PII:
err := client.Subscribers.Delete(ctx, "subscriber-id")
Preferências de notificação
// Consultar
prefs, err := client.Subscribers.GetPreferences(ctx, "subscriber-id")
// Atualizar
prefs, err = client.Subscribers.SetPreferences(ctx, "subscriber-id", ¬ifica.SetPreferencesParams{
Preferences: []notifica.PreferenceInput{
{Category: "marketing", Channel: "email", Enabled: false},
{Category: "transacional", Channel: "email", Enabled: true},
},
})
Importação em massa
result, err := client.Subscribers.BulkImport(ctx, ¬ifica.BulkImportParams{
Subscribers: []notifica.CreateSubscriberParams{
{ExternalID: "user-1", Email: "[email protected]", Name: "Ana"},
{ExternalID: "user-2", Email: "[email protected]", Name: "Bruno"},
},
})
fmt.Printf("Importados: %d subscribers\n", result.Imported)
Templates
Criar template
tmpl, err := client.Templates.Create(ctx, ¬ifica.CreateTemplateParams{
Channel: notifica.ChannelEmail,
Slug: "welcome",
Name: "Email de Boas-vindas",
Content: "Olá {{name}}, bem-vindo ao {{company}}!",
Status: notifica.TemplateStatusPublished,
})
Listar por canal
templates, err := client.Templates.List(ctx, ¬ifica.ListTemplatesParams{
Channel: notifica.ChannelEmail,
Status: notifica.TemplateStatusPublished,
})
Preview com dados de exemplo
result, err := client.Templates.Preview(ctx, "template-id", ¬ifica.PreviewParams{
Variables: map[string]any{
"name": "Maria",
"company": "Acme",
},
})
fmt.Println(result.Output) // "Olá Maria, bem-vindo ao Acme!"
fmt.Println(result.Valid) // true
Validar template
result, err := client.Templates.Validate(ctx, "template-id")
if !result.Valid {
fmt.Println("Erros:", result.Errors)
}
Workflows
Criar workflow
wf, err := client.Workflows.Create(ctx, ¬ifica.CreateWorkflowParams{
Slug: "onboarding",
Name: "Onboarding do Usuário",
Steps: []map[string]any{
{"type": "send", "channel": "email", "template": "welcome"},
{"type": "delay", "duration": "24h"},
{"type": "send", "channel": "email", "template": "getting-started"},
{"type": "delay", "duration": "72h"},
{"type": "send", "channel": "whatsapp", "template": "check-in"},
},
})
Disparar workflow
run, err := client.Workflows.Trigger(ctx, "onboarding", ¬ifica.TriggerWorkflowParams{
Recipient: "user-123",
Data: map[string]any{"name": "João", "plan": "pro"},
})
fmt.Println("Workflow run:", run.ID, "status:", run.Status)
Acompanhar execução
run, err := client.Workflows.GetRun(ctx, "run-id")
fmt.Println("Status:", run.Status)
Cancelar workflow em execução
run, err := client.Workflows.CancelRun(ctx, "run-id")
Webhooks
Criar webhook
O signing secret só é retornado na criação — guarde-o!
wh, err := client.Webhooks.Create(ctx, ¬ifica.CreateWebhookParams{
URL: "https://api.empresa.com/webhooks/notifica",
Events: []string{
"notification.sent",
"notification.delivered",
"notification.failed",
},
})
fmt.Println("Secret (guarde!):", wh.Secret)
Listar deliveries
page, err := client.Webhooks.ListDeliveries(ctx, "webhook-id", ¬ifica.ListDeliveriesParams{
Limit: 50,
})
for _, d := range page.Data {
fmt.Printf("Evento: %s, Status HTTP: %d\n", d.EventType, *d.ResponseStatus)
}
Testar webhook
err := client.Webhooks.Test(ctx, "webhook-id")
Domínios de envio
Registrar domínio
domain, err := client.Domains.Create(ctx, ¬ifica.CreateDomainParams{
Domain: "mail.empresa.com.br",
})
fmt.Println("Configure estes registros DNS e depois verifique")
Verificar DNS
domain, err := client.Domains.Verify(ctx, domain.ID)
if domain.Status == notifica.DomainStatusVerified {
fmt.Println("Domínio verificado!")
}
Health check
health, err := client.Domains.CheckHealth(ctx, domain.ID)
fmt.Println("SPF:", health.SPF.Status)
fmt.Println("DKIM:", health.DKIM.Status)
fmt.Println("DMARC:", health.DMARC.Status)
Analytics
// Resumo dos últimos 7 dias
overview, err := client.Analytics.Overview(ctx, "7d")
fmt.Printf("Enviados: %d, Entregues: %d, Falhas: %d\n",
overview.Sent, overview.Delivered, overview.Failed)
// Estatísticas por canal
stats, err := client.Analytics.ByChannel(ctx, "30d")
for _, s := range stats {
fmt.Printf("%s: %d enviados\n", s.Channel, s.Sent)
}
// Timeseries para gráficos
points, err := client.Analytics.Timeseries(ctx, ¬ifica.TimeseriesParams{
Period: "7d",
Granularity: "day",
})
// Top templates
top, err := client.Analytics.TopTemplates(ctx, ¬ifica.TopTemplatesParams{
Period: "30d",
Limit: 5,
})
Notificações In-App
Para listar e gerenciar notificações do inbox de um subscriber:
// Listar não-lidas
notifications, err := client.InApp.List(ctx, "subscriber-id", ¬ifica.ListInAppParams{
UnreadOnly: true,
Limit: 20,
})
// Contagem de não-lidas
count, err := client.InApp.GetUnreadCount(ctx, "subscriber-id")
fmt.Printf("%d notificações não lidas\n", count)
// Marcar como lida
err = client.InApp.MarkRead(ctx, "subscriber-id", "notification-id")
// Marcar todas como lidas
err = client.InApp.MarkAllRead(ctx, "subscriber-id")
Audit Logs (Admin Only)
Audit logs requerem autenticação admin (Bearer token do backoffice). Não estão disponíveis com API keys regulares (nk_live_... ou nk_test_...).
Rastreie ações sensíveis na sua organização:
- API Keys: created, rotated, revoked
- Team Members: invited, removed, role_changed
- Subscriptions: created, plan_changed, cancelled, payment_failed
- Domains: added, verified, removed
- Webhooks: created, updated, deleted
// Listar logs recentes
result, err := client.Audit.List(ctx, ¬ifica.ListAuditLogsParams{
Limit: 50,
})
for _, log := range result.Data {
fmt.Printf("%s by %s at %s\n", log.Action, log.Actor.Name, log.CreatedAt)
}
// Filtrar por tipo de ação
apiKeyLogs, err := client.Audit.List(ctx, ¬ifica.ListAuditLogsParams{
Action: notifica.String("api_key.created"),
Limit: 20,
})
// Filtrar por tipo de recurso
webhookLogs, err := client.Audit.List(ctx, ¬ifica.ListAuditLogsParams{
ResourceType: notifica.String("webhook"),
})
// Filtrar por período
logs, err := client.Audit.List(ctx, ¬ifica.ListAuditLogsParams{
StartDate: notifica.String("2024-01-01T00:00:00Z"),
EndDate: notifica.String("2024-01-31T23:59:59Z"),
})
// Obter um audit log específico
log, err := client.Audit.Get(ctx, "audit_abc123")
Filtros disponíveis
| Parâmetro | Tipo | Descrição |
|---|---|---|
Action | *string | Filtrar por ação (ex: api_key.created) |
ResourceType | *string | Filtrar por tipo de recurso (api_key, webhook, etc.) |
ResourceID | *string | Filtrar por ID do recurso |
ActorType | *string | Filtrar por tipo de ator (user, api_key, system) |
ActorID | *string | Filtrar por ID do ator |
StartDate | *string | Data inicial (ISO 8601) |
EndDate | *string | Data final (ISO 8601) |
Tratamento de erros
O SDK retorna *notifica.Error com código, mensagem e detalhes:
n, err := client.Notifications.Send(ctx, params)
if err != nil {
// Helpers para verificar tipo de erro
if notifica.IsValidationError(err) {
// 422: Dados inválidos
apiErr := err.(*notifica.Error)
fmt.Println("Campos inválidos:", apiErr.Details)
}
if notifica.IsRateLimited(err) {
// 429: Limite excedido — faça backoff
fmt.Println("Rate limited, tente novamente em alguns segundos")
}
if notifica.IsNotFound(err) {
// 404: Recurso não encontrado
}
if notifica.IsConflict(err) {
// 409: Conflito de idempotência
}
// Acesso completo ao erro
var apiErr *notifica.Error
if errors.As(err, &apiErr) {
fmt.Printf("HTTP %d | Code: %s | Message: %s\n",
apiErr.StatusCode, apiErr.Code, apiErr.Message)
}
}
Implementando retry com backoff
func sendWithRetry(ctx context.Context, client *notifica.Client, params *notifica.SendNotificationParams, maxRetries int) (*notifica.Notification, error) {
for i := 0; i <= maxRetries; i++ {
n, err := client.Notifications.Send(ctx, params)
if err == nil {
return n, nil
}
// Não retry em erros de validação (4xx)
if notifica.IsValidationError(err) || notifica.IsNotFound(err) {
return nil, err
}
// Retry em rate limit e erros de servidor
if i < maxRetries {
delay := time.Duration(1<<uint(i)) * time.Second // 1s, 2s, 4s...
time.Sleep(delay)
}
}
return nil, fmt.Errorf("max retries exceeded")
}
Helpers
O SDK inclui funções para criar ponteiros (útil para campos opcionais):
// Atualizar apenas o nome (sem alterar outros campos)
sub, err := client.Subscribers.Update(ctx, "id", ¬ifica.UpdateSubscriberParams{
Name: notifica.String("Novo Nome"),
})
// Ativar canal
ch, err := client.Channels.Update(ctx, notifica.ChannelEmail, ¬ifica.UpdateChannelParams{
Active: notifica.Bool(true),
})
Exemplo completo: Servidor HTTP
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
notifica "github.com/notifica-tech/notifica-go"
)
var client *notifica.Client
func init() {
apiKey := os.Getenv("NOTIFICA_API_KEY")
if apiKey == "" {
log.Fatal("NOTIFICA_API_KEY não configurada")
}
client = notifica.NewClient(apiKey)
}
func handleSendWelcome(w http.ResponseWriter, r *http.Request) {
var req struct {
Email string `json:"email"`
Name string `json:"name"`
}
json.NewDecoder(r.Body).Decode(&req)
n, err := client.Notifications.Send(r.Context(), ¬ifica.SendNotificationParams{
Channel: notifica.ChannelEmail,
Recipient: req.Email,
TemplateID: "welcome",
Payload: map[string]any{"name": req.Name},
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"notification_id": n.ID,
"status": string(n.Status),
})
}
func main() {
http.HandleFunc("POST /send-welcome", handleSendWelcome)
log.Println("Servidor rodando em :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
FAQ
Q: Preciso de alguma dependência externa?
A: Não. O SDK usa apenas a biblioteca padrão do Go (net/http, encoding/json, context).
Q: O SDK é thread-safe?
A: Sim. O Client pode ser compartilhado entre goroutines.
Q: Como passo timeout por requisição?
A: Use context.WithTimeout:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
n, err := client.Notifications.Send(ctx, params)
Q: O SDK faz retry automático? A: Não. Implementamos a menor superfície possível. Veja o exemplo de retry com backoff acima.
Q: Funciona com Go modules?
A: Sim. go get github.com/notifica-tech/notifica-go adiciona ao seu go.mod.
Links
- GitHub: github.com/notifica-tech/notifica-go
- pkg.go.dev: pkg.go.dev/github.com/notifica-tech/notifica-go
- API Reference: API Reference
- Templates: Email Templates
- Webhooks: Webhooks