Integrações SaaS com Claude Code: API keys, OAuth, webhooks e auditoria
Guia prático para integrar SaaS com Claude Code: API keys, OAuth, webhooks, retries, secrets e logs de auditoria.
Ao criar integrações SaaS com Claude Code, a primeira pergunta não é “qual endpoint devo chamar?”. A primeira pergunta é como autorizar com segurança, como repetir uma tentativa sem criar efeitos duplicados e como provar depois quem fez o quê. Sem essa camada, até uma notificação simples no Slack pode virar um problema de segurança, cobrança ou operação.
Este guia transforma API keys, OAuth, webhooks, limites de requisição, retries, idempotência, logs de auditoria, secrets e ambientes de teste em um workflow prático para Claude Code. Um webhook é um evento que o SaaS envia para o seu servidor. Idempotência significa que repetir uma operação não cria resultado duplicado. Um log de auditoria é o registro que permite explicar a automação mais tarde.
Os exemplos usam Node.js 20 ou superior e podem ser copiados para uma pasta scripts/.
Arquitetura de Integração
Não deixe o estado da integração viver apenas dentro do Claude Code. Claude Code é bom para planejar, gerar código e executar comandos; tokens, estado de retry e histórico de auditoria devem ficar no código do conector.
flowchart LR
A[Claude Code] --> B[CLI or MCP connector]
B --> C[Auth and secret store]
B --> D[Retry and rate-limit wrapper]
D --> E[SaaS API]
E --> F[Webhook receiver]
F --> G[Queue]
G --> H[Worker]
H --> I[Audit log]
A menor versão útil é um wrapper CLI, por exemplo node scripts/slack-notify.mjs chamado pelo Claude Code. Se o workflow se tornar recorrente, promova para um servidor MCP para reutilizar schema de entrada, permissões e tratamento de erros. Receivers de webhook dão mais trabalho, mas são necessários quando Stripe, GitHub, Slack ou outro SaaS inicia o fluxo.
Quatro Casos de Uso Concretos
O primeiro caso é operação de releases. Claude Code lê commits ainda não publicados no GitHub, escreve release notes e publica um resumo curto no Slack. Slack Incoming Webhooks são rápidos de configurar, mas não são ideais para apagar mensagens ou criar fluxos de chat complexos; nesses casos use a Slack Web API.
O segundo caso é automação de billing. Um webhook da Stripe recebe checkout.session.completed, registra o cliente em uma ferramenta interna e envia falhas para uma fila de retry. Verificação de assinatura, idempotency keys e separação rígida entre test mode e live mode são obrigatórias aqui.
O terceiro caso é triagem de suporte. Google Workspace OAuth permite que um backend leia um CSV de suporte; Claude Code classifica as linhas; e issues do GitHub são criadas para acompanhamento de engenharia. Como o fluxo toca dados de usuários, OAuth com scopes estreitos e read-only é mais seguro que uma API key compartilhada.
O quarto caso é um painel de auditoria. Toda ação SaaS disparada pelo Claude Code é gravada em NDJSON para que o time veja actor, provider, action, target e idempotency key. Isso já ajuda muito antes de existir uma plataforma formal de auditoria.
Escolher API Key, OAuth, Webhook ou Conector
| Método | Explicação simples | Melhor para | Atenção |
|---|---|---|---|
| API key | Credencial compartilhada do servidor | Chamadas de servidor Stripe, alertas internos no Slack | Guardar em environment variables ou secret manager, nunca no source |
| OAuth | Usuário ou workspace concede acesso | Google Drive, GitHub Apps, ações por usuário | Refresh tokens e scopes exigem desenho cuidadoso |
| Webhook | SaaS envia um evento ao seu endpoint | Pagamentos Stripe, issues GitHub, eventos Slack | Verificar assinaturas, aceitar duplicatas, não assumir ordem |
| Conector CLI/MCP | Ferramenta estável chamada pelo Claude Code | Runbooks, ops internas, fluxos multi-SaaS | Colocar validação e logging no conector |
A documentação de Google OAuth 2.0 explica o fluxo de access token e refresh token. Em GitHub e Slack, logs com actor também importam, porque “foi o bot” não basta ao depurar permissões.
Checklist de Variáveis de Ambiente
Comece por .env.example e compartilhe apenas nomes. Valores reais não entram no Git.
# .env.example
INTEGRATION_ENV=sandbox
SAAS_API_TOKEN=
SLACK_WEBHOOK_URL=
GITHUB_WEBHOOK_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
AUDIT_LOG_PATH=logs/saas-audit.ndjson
# .gitignore
.env
.env.*
!.env.example
logs/
Antes de deixar Claude Code executar a integração:
- Mantenha live keys e test keys em ambientes separados.
- Nunca cole secrets em um prompt do Claude Code.
- Nunca imprima headers
Authorizationou webhook secrets em erros. - Comece OAuth com scopes read-only e amplie só quando necessário.
- Registre valores de CI como secrets, não como variáveis comuns do repositório.
- Registre quem rotacionou uma chave e quando.
Wrapper de Retry e Rate Limit
Rate limit é o ritmo máximo de requisições permitido por um provider. As APIs REST do GitHub têm limites primários e secundários; ao bater no limite, respeite os headers. Slack retorna 429 Too Many Requests com Retry-After. Esse comportamento deve ficar no código, não em instruções improvisadas.
// scripts/saas-request.mjs
import crypto from "node:crypto";
import { pathToFileURL } from "node:url";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function saasRequest(url, options = {}) {
const {
method = "GET",
token = process.env.SAAS_API_TOKEN,
body,
idempotencyKey = method === "POST" ? crypto.randomUUID() : undefined,
maxRetries = 4,
headers = {},
} = options;
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
const res = await fetch(url, {
method,
headers: {
Accept: "application/json",
...(body ? { "Content-Type": "application/json" } : {}),
...(token ? { Authorization: `Bearer ${token}` } : {}),
...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
...headers,
},
body: body ? JSON.stringify(body) : undefined,
});
if (res.ok) return res;
const retryAfter = Number(res.headers.get("retry-after"));
const shouldRetry = [408, 409, 425, 429, 500, 502, 503, 504].includes(res.status);
if (!shouldRetry || attempt === maxRetries) {
const text = await res.text();
throw new Error(`SaaS request failed: ${res.status} ${text.slice(0, 200)}`);
}
const backoffMs = Number.isFinite(retryAfter)
? retryAfter * 1000
: Math.min(30000, 500 * 2 ** attempt);
await sleep(backoffMs);
}
throw new Error("unreachable");
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const url = process.argv[2];
if (!url) throw new Error("Usage: node scripts/saas-request.mjs <url>");
const res = await saasRequest(url);
console.log(await res.text());
}
Em APIs como Stripe, que aceitam idempotency keys, o mesmo POST pode ser repetido com segurança. Nos seus workers, use uma chave como provider + event_id + action e ignore o processamento se ela já existir.
Fluxo de Verificação de Webhook
Webhooks são entradas HTTP públicas. Sem verificação de assinatura, qualquer pessoa pode enviar eventos falsos. GitHub usa X-Hub-Signature-256; Stripe usa Stripe-Signature. A regra principal é verificar o raw body antes de fazer parse do JSON.
1. Ler o raw body.
2. Ler o header de assinatura.
3. Calcular HMAC com o secret compartilhado.
4. Comparar com método timing-safe.
5. Salvar o delivery id como idempotency key.
6. Retornar 202 rapidamente e processar trabalho pesado em uma fila.
// scripts/verify-github-webhook.mjs
import crypto from "node:crypto";
import http from "node:http";
const secret = process.env.GITHUB_WEBHOOK_SECRET;
if (!secret) throw new Error("Set GITHUB_WEBHOOK_SECRET");
function verifyGitHubSignature(rawBody, signatureHeader) {
const received = Array.isArray(signatureHeader)
? signatureHeader[0]
: signatureHeader ?? "";
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const receivedBytes = Buffer.from(received);
const expectedBytes = Buffer.from(expected);
return (
receivedBytes.length === expectedBytes.length &&
crypto.timingSafeEqual(receivedBytes, expectedBytes)
);
}
http
.createServer(async (req, res) => {
const chunks = [];
for await (const chunk of req) chunks.push(chunk);
const rawBody = Buffer.concat(chunks);
if (!verifyGitHubSignature(rawBody, req.headers["x-hub-signature-256"])) {
res.writeHead(401);
res.end("invalid signature");
return;
}
const event = req.headers["x-github-event"];
const delivery = req.headers["x-github-delivery"];
console.log(JSON.stringify({ event, delivery, receivedAt: new Date().toISOString() }));
res.writeHead(202, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ok: true }));
})
.listen(3000, () => console.log("Listening on http://localhost:3000"));
Para Stripe, em produção use constructEvent() do SDK oficial. Os webhook secrets de test e live são diferentes, então STRIPE_WEBHOOK_SECRET deve ser específico por ambiente.
Audit Logs
Audit log é o registro que explica uma ação automatizada depois. O histórico do chat Claude Code não basta; grave timestamp, actor, provider, action, target, idempotency key e status.
{
"ts": "2026-06-03T09:15:00.000Z",
"actor": "claude-code",
"provider": "github",
"action": "create_issue",
"target": "owner/repo#123",
"idempotencyKey": "github:issue:customer-42:2026-06-03",
"status": "succeeded"
}
// scripts/audit-log.mjs
import { appendFile, mkdir } from "node:fs/promises";
import { dirname } from "node:path";
export async function writeAudit(event) {
const record = {
ts: new Date().toISOString(),
actor: event.actor ?? "claude-code",
provider: event.provider,
action: event.action,
target: event.target,
idempotencyKey: event.idempotencyKey,
status: event.status ?? "started",
};
const file = process.env.AUDIT_LOG_PATH ?? "logs/saas-audit.ndjson";
await mkdir(dirname(file), { recursive: true });
await appendFile(file, `${JSON.stringify(record)}\n`, "utf8");
}
if (process.argv[1]?.endsWith("audit-log.mjs")) {
await writeAudit({
provider: "slack",
action: "post_message",
target: "#release",
idempotencyKey: "demo-2026-06-03",
status: "succeeded",
});
}
NDJSON é suficiente para a primeira versão. Depois você pode importar para BigQuery, DuckDB ou uma planilha.
Armadilhas Comuns
Uma falha comum é tentar entregar webhooks diretamente para localhost. A documentação de troubleshooting do GitHub destaca que URLs de webhook precisam ser acessíveis publicamente. Use um serviço de forwarding em testes locais e HTTPS em produção.
Outro erro é confiar na ordem dos webhooks. Providers podem entregar eventos atrasados ou fora de ordem. Use timestamps do evento, delivery ids e o estado atual do recurso.
O terceiro problema é duplicar trabalho em retry. Pagamentos, issues, posts no Slack e e-mails são visíveis para usuários. Adicione idempotency keys em POST e armazene delivery ids já processados nos workers.
A quarta armadilha é pedir scope OAuth amplo demais. Comece com read-only, uma pasta ou workspace, e tokens de vida curta. Amplie apenas quando o risco estiver claro.
A quinta armadilha é colar secrets no Claude Code. Peça para Claude Code usar nomes de variáveis de ambiente; os valores reais ficam no secret manager, CI secret ou .env local.
Quando Criar uma Abstração de Conector
O primeiro script pode ser direto e simples. Quando autorização, paginação, rate limiting, audit logging e formatação de erros se repetirem em três workflows, crie um conector.
Nomeie a abstração com linguagem de negócio: sendMessage(), createTicket(), recordPaymentEvent(), writeAudit(). Um wrapper fino como callSlackApi() ajuda menos Claude Code do que uma função que expressa o resultado esperado.
Para detalhes próximos, combine com Claude Code API Design Assistant e Claude Code API Testing. Para revisão de risco, leia Claude Code Security Best Practices antes do acesso de produção.
Referências Oficiais
- Stripe: webhook signature verification
- Stripe: idempotent requests
- Slack: Incoming Webhooks
- Slack: Web API rate limits
- GitHub: validating webhook deliveries
- GitHub: REST API rate limits
- Google: OAuth 2.0 for Google APIs
Se integrações Claude Code forem entrar em workflows de equipe, prepare templates reutilizáveis antes do primeiro incidente. As checklists práticas estão em /products/ e o treinamento para equipes está em /training/.
Ao testar na prática, as integrações que já nasceram com verificação de assinatura, idempotência, audit logs e ambientes de teste separados foram muito mais fáceis de expandir. Até uma pequena notificação no Slack fica mais confiável quando falhas podem ser rastreadas e repetidas de forma deliberada.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.