Pular para o conteúdo principal

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(), &notifica.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")
dica

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çãoPadrãoDescrição
WithBaseURLhttps://api.notifica.com.brURL base da API
WithHTTPClient30s timeoutHTTP 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, &notifica.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, &notifica.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, &notifica.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, &notifica.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, &notifica.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, &notifica.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, &notifica.ListNotificationsParams{
Cursor: *page.Meta.Cursor,
})
}

Subscribers (Assinantes)

Criar ou atualizar (upsert)

sub, err := client.Subscribers.Create(ctx, &notifica.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, &notifica.ListSubscribersParams{
Search: "maria",
Limit: 20,
})
fmt.Printf("Encontrados: %d subscribers\n", page.Total)

Atualizar

sub, err := client.Subscribers.Update(ctx, "subscriber-id", &notifica.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", &notifica.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, &notifica.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, &notifica.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, &notifica.ListTemplatesParams{
Channel: notifica.ChannelEmail,
Status: notifica.TemplateStatusPublished,
})

Preview com dados de exemplo

result, err := client.Templates.Preview(ctx, "template-id", &notifica.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, &notifica.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", &notifica.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, &notifica.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", &notifica.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, &notifica.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, &notifica.TimeseriesParams{
Period: "7d",
Granularity: "day",
})

// Top templates
top, err := client.Analytics.TopTemplates(ctx, &notifica.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", &notifica.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)

Autenticação Admin Necessária

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, &notifica.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, &notifica.ListAuditLogsParams{
Action: notifica.String("api_key.created"),
Limit: 20,
})

// Filtrar por tipo de recurso
webhookLogs, err := client.Audit.List(ctx, &notifica.ListAuditLogsParams{
ResourceType: notifica.String("webhook"),
})

// Filtrar por período
logs, err := client.Audit.List(ctx, &notifica.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âmetroTipoDescrição
Action*stringFiltrar por ação (ex: api_key.created)
ResourceType*stringFiltrar por tipo de recurso (api_key, webhook, etc.)
ResourceID*stringFiltrar por ID do recurso
ActorType*stringFiltrar por tipo de ator (user, api_key, system)
ActorID*stringFiltrar por ID do ator
StartDate*stringData inicial (ISO 8601)
EndDate*stringData 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", &notifica.UpdateSubscriberParams{
Name: notifica.String("Novo Nome"),
})

// Ativar canal
ch, err := client.Channels.Update(ctx, notifica.ChannelEmail, &notifica.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(), &notifica.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.