> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ararahq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Go

> SDK Go oficial da Arara (github.com/ararahq/arara-go-sdk): só biblioteca padrão, context.Context em toda chamada, retry automático e erro tipado.

## Instalar

```bash theme={null}
go get github.com/ararahq/arara-go-sdk@v1.0.0
```

Requer Go 1.22+. Sem dependências externas (`net/http`, `encoding/json`, `context`).

## Configurar

```go theme={null}
import (
    "log"
    "os"
    "time"

    arara "github.com/ararahq/arara-go-sdk"
)

client, err := arara.NewClient(
    os.Getenv("ARARA_API_KEY"),
    arara.WithBaseURL("https://api.ararahq.com"), // opcional, este é o default
    arara.WithTimeout(15*time.Second),            // opcional, default 10s
    arara.WithMaxRetries(3),                      // opcional, default 3
)
if err != nil {
    log.Fatal(err) // apiKey vazia (*arara.APIError, code INVALID_CONFIG)
}
```

`arara.WithHTTPClient(hc)` substitui o `*http.Client` inteiro (transporte e timeout).

## Enviar a primeira mensagem

Você precisa de plano assinado, número conectado, um template aprovado e saldo. Veja [Antes de começar](/quickstart#antes-de-começar). Troque `pedido_confirmado` pelo nome do seu template.

Salve como `main.go`:

```go theme={null}
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    arara "github.com/ararahq/arara-go-sdk"
)

func main() {
    client, err := arara.NewClient(os.Getenv("ARARA_API_KEY"))
    if err != nil {
        log.Fatal(err)
    }

    orderID := "12345" // id do evento no seu sistema

    resp, err := client.Messages.Send(context.Background(), &arara.SendMessageRequest{
        Receiver:          "whatsapp:+5511999999999",
        TemplateName:      "pedido_confirmado",
        TemplateVariables: []string{"João", orderID},
    }, arara.SendOptions{IdempotencyKey: "hello-" + orderID})
    if err != nil {
        log.Fatal(err)
    }

    if resp.ID != nil { // o id vem como *string e pode ser nulo
        fmt.Println(*resp.ID, resp.Status) // ara_msg_... e status inicial
    }
}
```

Rode:

```bash theme={null}
go mod init exemplo
go get github.com/ararahq/arara-go-sdk@v1.0.0
ARARA_API_KEY=ara_live_... go run .
```

Com variáveis no corpo do template e mídia no header:

```go theme={null}
_, err = client.Messages.Send(ctx, &arara.SendMessageRequest{
    Receiver:     "whatsapp:+5511999999999",
    TemplateName: "confirmacao_pedido",
    TemplateVariables: []string{
        "https://meusite.com/nota/123.pdf", // header (mídia opcional)
        "João",                              // {{1}}
        "ARA-12345",                         // {{2}}
    },
}, arara.SendOptions{IdempotencyKey: "pedido-12345"})
```

Para responder dentro da janela de 24h, mande `Body` em vez de `TemplateName`:

```go theme={null}
_, err = client.Messages.Send(ctx, &arara.SendMessageRequest{
    Receiver: "whatsapp:+5511999999999",
    Body:     "Oi! Vi sua dúvida e respondo já já.",
})
```

Para consultar a mensagem depois, use `client.Messages.Get(ctx, id)` ou o webhook `message.status_updated`. `Get` aceita chave de API com permissão `READ`.

## Templates

`Get`, `GetStatus` e `Delete` recebem o **id** do template (UUID), não o nome. Para achar o id pelo nome, use `client.Templates.FindByName(ctx, "promo_natal")`.

```go theme={null}
tpl, err := client.Templates.Create(ctx, &arara.CreateTemplateRequest{
    Name:     "promo_natal",
    Category: "MARKETING",
    Language: "pt_BR",
    Body:     "Oi {{1}}, oferta de Natal: {{2}}% off até {{3}}.",
    Samples:  map[string]any{"1": "João", "2": "30", "3": "sexta"},
})
if err != nil {
    log.Fatal(err)
}

status, err := client.Templates.GetStatus(ctx, tpl.ID)
if err == nil {
    fmt.Println(status.Status) // PENDING, APPROVED, REJECTED...
}
```

## Tratar erros

Toda falha devolve `*arara.APIError`:

```go theme={null}
import "errors"

_, err := client.Messages.Send(ctx, req)
var apiErr *arara.APIError
if errors.As(err, &apiErr) {
    fmt.Println(apiErr.StatusCode, apiErr.Code, apiErr.Message, apiErr.Details)
    if apiErr.StatusCode == 429 && apiErr.RetryAfter != nil {
        fmt.Println("tente de novo em", *apiErr.RetryAfter, "segundos")
    }
}
```

| Campo | Conteúdo |
| - | - |
| `StatusCode` | Status HTTP. Zero em falha de rede. |
| `Code` | `error.code` da API. Quando a resposta não traz o envelope: `AUTHENTICATION_ERROR` (401), `FORBIDDEN` (403), `NOT_FOUND` (404) ou `UNKNOWN_ERROR`. Erros locais: `INVALID_CONFIG`, `NETWORK_ERROR`, `DECODE_ERROR`, `CONTEXT_CANCELED`. |
| `Message` | `error.message` da API. |
| `Details` | `error.details` da API. |
| `RetryAfter` | Segundos do header `Retry-After`, quando houver (`*int`). |

Helpers: `arara.IsAuthError(err)` (401), `arara.IsForbidden(err)` (403 sem código), `arara.IsNotFound(err)` (404) e `arara.IsPlanFeatureLocked(err)`. `errors.Is(err, context.Canceled)` funciona.

A API responde 401 para chave inválida, revogada ou expirada, e 403 quando o header `Authorization` está ausente ou a chave não tem permissão para a rota. Lista de códigos em [Erros](/erros).

## Retentativas e idempotência

O SDK retenta até `WithMaxRetries` vezes em falha de rede, 429 e qualquer 5xx, e só em requisições `GET` ou com `Idempotency-Key`. Escritas sem a chave nunca são repetidas. Respeita `Retry-After` (até 30s); sem ele, espera 500ms e dobra a cada tentativa, até 30s. Cancelar o `context` interrompe a espera. Para desligar: `arara.WithMaxRetries(0)`.

<Warning>
  `Messages.Send`, `Messages.SendBatch` e `Campaigns.Create` sempre mandam `Idempotency-Key`: se você não passar uma, o SDK gera um UUID v4, que só vale dentro daquela chamada (e é a mesma em todas as retentativas dela). Para que um retry do seu lado não duplique o envio, passe `SendOptions{IdempotencyKey: ...}`.
</Warning>

`SendOptions` é opcional. Derive a chave de um id do seu sistema (id do pedido, id da tentativa), não de um valor novo a cada chamada. Repetir a mesma `IdempotencyKey` devolve a mensagem original. A API não compara o corpo nem expira a chave: com destinatário ou variáveis diferentes, a resposta continua sendo a mensagem original.

`Campaigns.Create` segue a mesma regra, com `CampaignCreateOptions`:

```go theme={null}
campaign, err := client.Campaigns.Create(ctx, &arara.CampaignRequest{
    Name:         "blackfriday-2026",
    TemplateName: "bf_oferta_geral",
    Contacts: []arara.CampaignContactRequest{
        {To: "whatsapp:+5511999999999", Variables: []string{"João", "30"}},
        {To: "whatsapp:+5511888888888", Variables: []string{"Maria", "25"}},
    },
}, arara.CampaignCreateOptions{IdempotencyKey: "bf-2026-lote-1"})
if err != nil {
    log.Fatal(err)
}

detail, err := client.Campaigns.Get(ctx, campaign.ID)
```

## Webhooks

O SDK não tem helper de webhook. Valide `X-Arara-Signature` com `crypto/hmac` sobre `"{X-Arara-Timestamp}.{corpo cru}"` e compare com `hmac.Equal`. Detalhes em [Webhooks](/webhooks).

```go theme={null}
const toleranceSeconds = 300

func validSignature(secret, timestamp, signature string, body []byte) bool {
    sent, err := strconv.ParseInt(timestamp, 10, 64)
    if err != nil || math.Abs(float64(time.Now().Unix()-sent)) > toleranceSeconds {
        return false
    }
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(timestamp + "."))
    mac.Write(body)
    expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}
```

Chame com `r.Header.Get("X-Arara-Timestamp")`, `r.Header.Get("X-Arara-Signature")` e o corpo lido com `io.ReadAll(r.Body)`, antes de decodificar o JSON.

## Recursos disponíveis

Todo método recebe `context.Context` como primeiro argumento.

| Recurso | Acesso | Métodos |
| - | - | - |
| Messages | `client.Messages` | `Send`, `SendBatch`, `Get`, `ListByBatch` |
| Templates | `client.Templates` | `List`, `FindByName`, `Create`, `Get`, `GetStatus`, `Delete`, `Analytics`, `TemplateAnalytics` |
| Campaigns | `client.Campaigns` | `Create`, `List`, `Estimate`, `Get`, `Cancel` |
| Contacts | `client.Contacts` | `List`, `ImportBatch`, `Stats`, `ReactivationCandidates`, `ListTags`, `Get`, `Update`, `Messages` |
| Conversations | `client.Conversations` | `List`, `LeadStats`, `Messages`, `Reply`, `UpdateStatus`, `WindowStatus` |
| Wallet | `client.Wallet` | `Transactions`, `GetAutoRecharge`, `UpdateAutoRecharge` |
| Numbers | `client.Numbers` | `List`, `Update`, `Delete`, `Request`, `ListRequests`, `Sync`, `Warming` |
| Smart links | `client.SmartLinks` | `Create`, `Update`, `List`, `Stats` |
| Auth | `client.Auth` | `Me` (exige chave de API ADMIN) |
| Opt-outs | `client.OptOuts` | `List`, `Get`, `Create`, `Delete` (exige chave de API ADMIN) |

Exemplo de contatos e conversas:

```go theme={null}
contacts, err := client.Contacts.List(ctx, arara.ContactListParams{Page: 0, Size: 20, Lifecycle: "ENGAGED"})

_, err = client.Conversations.Reply(ctx, &arara.ConversationReplyRequest{
    ConversationID: "3f1e...",
    Body:           "Seu pedido saiu para entrega.",
})
```

## Limitações conhecidas

| Item | Comportamento |
| - | - |
| `Templates.Get/GetStatus/Delete` | Precisam do id (UUID). Com o nome a API responde 400. |
| Webhook, chaves de API | O SDK não gerencia. Configure webhooks em `https://app.ararahq.com/dashboard/webhooks` e crie e revogue chaves em `https://app.ararahq.com/dashboard/apikeys`. |
| `MessageResponse.ID` | É `*string`: confira `nil` antes de ler. |

## Próximos passos

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Endpoints REST por baixo do SDK.
  </Card>

  <Card title="CLI" icon="terminal" href="/cli">
    Envio e escuta de webhooks pelo terminal.
  </Card>
</CardGroup>

Código-fonte: [github.com/ararahq/arara-go-sdk](https://github.com/ararahq/arara-go-sdk).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.