Growth OS API

Webhooks

Assine eventos, valide a assinatura HMAC e gerencie tudo por chave — sem precisar de dono logado.

Assine eventos e receba um POST a cada ocorrência. CRUD completo por chave (GET, POST, PATCH, DELETE /webhooks), escopo webhooks.manage — nenhum dono logado precisa intervir.

bash
curl -X POST https://api.growthaisystem.com/api/v1/webhooks \
  -H "Authorization: Bearer gos_SEU_TOKEN_AQUI" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://seu-sistema.com/hooks","events":["contact.created"]}'

Eventos disponíveis

  • contact.created / contact.updated — precisa de contacts.read.
  • opportunity.created / opportunity.stage_changed / opportunity.won / opportunity.lost — precisa de opportunities.read.
  • meeting.booked / meeting.rescheduled / meeting.canceled / meeting.outcome — precisa de calendars.read.

Assinar um evento exige o escopo de leitura correspondente listado acima, além de webhooks.manage — se esse escopo for revogado depois, a assinatura desativa sozinha na próxima entrega (disabledReason: "scope_revoked").

Validando a assinatura

Cada entrega chega com o header X-Gos-Signature, no formato t=<timestamp>,v1=<assinatura>. A assinatura é um HMAC-SHA256 do texto {timestamp}.{corpo} (timestamp e corpo colados com um ponto), calculado com o secret devolvido (uma única vez) na criação da assinatura — mesmo modelo do Stripe, se você já validou webhook deles antes.

pseudocódigo
t, assinaturaRecebida = extrair de "t=...,v1=..."
esperada = hmac_sha256(secret, t + "." + corpoCru)
se não bater (comparação em tempo constante) → rejeitar
se abs(agora - t) > alguns minutos → rejeitar (proteção contra replay)

Use o corpo cru da requisição no cálculo (antes de qualquer parse de JSON) — reformatar o JSON muda os bytes e quebra a conta.

Reentrega e desativação automática

Falhou a entrega (timeout, seu sistema fora do ar, resposta não-2xx)? Novas tentativas em 1min, 5min, 30min, 2h e 12h — 6 tentativas no total, depois a entrega vira dead. 3 dias seguidos falhando desativa a assinatura inteira sozinha (disabledReason: "circuit_breaker") — reative manualmente depois de corrigir o seu lado. Redirecionamento (3xx) conta como falha: a entrega não segue redirect.