Conta azul
Skill do Claude Code para integrar com a API do ERP Conta Azul: OAuth 2.0, 10 armadilhas mapeadas, mapa de ~45 endpoints e referencia completa navegavel.
npx -y skills add augustotecnos/claude-skill-conta-azul --skill conta-azulAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- 14 days oldThe repository was created 14 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Como integrar com a API do ERP Conta Azul (api-v2). Use ao autenticar via OAuth 2.0 (authorization code, refresh token rotativo), ou ao integrar vendas, orçamentos, pessoas (clientes/fornecedores/transportadoras), produtos, contratos recorrentes, notas fiscais e financeiro (contas a pagar/receber, parcelas, categorias, centros de custo, rateio, cobranças). Traz base URLs, o fluxo OAuth completo, validade dos tokens, onde guardar o refresh_token, rate limit, 10 armadilhas, erros comuns, o mapa de endpoints, como buscar payloads na doc-para-IA (llms.txt / .md por endpoint) e o aviso de que a API não tem webhook (exige polling).
SKILL.md
13.0 KB, ~3.9k tokens by cl100k_base, as published. Nobody here has run it
Conta Azul — API v2
ERP Conta Azul. REST + JSON, autenticação OAuth 2.0 Authorization Code — não é API key.
- Base URL:
https://api-v2.contaazul.com - Servidor de autorização:
https://auth.contaazul.com - Auth nas chamadas: header
Authorization: Bearer <access_token> - Rate limit: 600 req/min e 10 req/s por conta ERP conectada → estourou,
429. - Validades:
code3 min (uso único) ·access_token1 h ·refresh_tokenaté 5 anos ou até a próxima renovação - Portal do Desenvolvedor: https://developers-portal.contaazul.com — client_id/secret, app de dev com dados fictícios, e o único canal de suporte técnico da API
- Multi-tenant: cada empresa Conta Azul autoriza individualmente e tem seu próprio par de tokens
OAuth 2.0 — as 3 chamadas que importam
1. Cliente autoriza (abrir no navegador; usar conta do ERP, não a do Portal):
https://auth.contaazul.com/login?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&state=ALEATORIO&scope=openid+profile+aws.cognito.signin.user.admin
Volta em REDIRECT_URI?code=...&state=... — confira o state (proteção CSRF).
2. Trocar code por tokens (até 3 min depois):
curl -X POST 'https://auth.contaazul.com/oauth2/token' \
-H 'Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=CODIGO' \
--data-urlencode 'redirect_uri=REDIRECT_URI'
3. Renovar (mesma URL e mesmo header, trocando o corpo):
--data-urlencode 'grant_type=refresh_token' --data-urlencode 'refresh_token=REFRESH_TOKEN'
Resposta de 2 e 3: {"access_token":"…","expires_in":3600,"refresh_token":"…","token_type":"Bearer"}
Gerar o Base64:
# Linux / macOS
echo -n "CLIENT_ID:CLIENT_SECRET" | base64
# Windows
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("CLIENT_ID:CLIENT_SECRET"))
⚠️ Dez armadilhas
refresh_tokené rotativo. Cada renovação devolve um novo e invalida o anterior. Não gravar o novo = integração morre em 1 h cominvalid_grant. Causa nº 1 de "funcionava e parou".- Não existe webhook (confirmado no FAQ oficial). Toda sincronização é polling agendado — desenhe o fluxo assim desde o início, respeitando o rate limit.
- Plural × singular é inconsistente e o erro vira 404 silencioso:
- plural →
/v1/pessoas,/v1/produtos,/v1/contratos,/v1/orcamentos,/v1/categorias,/v1/notas-fiscais - singular →
/v1/venda,/v1/conta-financeira,/v1/centro-de-custo
- plural →
- O verbo da exclusão em lote muda por área:
POST /v1/venda/exclusao-lotemasDELETE /v1/orcamentos/exclusao-lote. Inativar/excluir pessoa é POST (/v1/pessoas/inativar,/v1/pessoas/excluir), emboraDELETE /v1/pessoas/{id}também exista. codevale 3 minutos e é de uso único — reutilizar dáinvalid_grant.scopeé fixo e literal:openid+profile+aws.cognito.signin.user.admin. Não inventar escopo.redirect_uribate byte a byte com a cadastrada no Portal, senão erro de redirecionamento. Usando a extensão Chrome, tem que ser exatamentehttps://api.contaazul.com/extension/callback.- Renovação é estado compartilhado: grave o novo
refresh_tokenantes de usar o novoaccess_token, e deixe um único processo renovar por vez. Dois renovando junto = um recebeinvalid_grante a corrente quebra — só volta reautorizando no navegador, na mão. - É
id_<coisa>, nunca<coisa>_id. A API usa prefixo:id_categoria,id_centro_custo,id_conta_financeira,id_empresa,id_evento,id_venda,id_vendedor. O único<coisa>_idem toda a documentação éclient_id— e esse é do OAuth, não da API. Wrappers de terceiros, exemplos da internet e a memória do modelo tendem à forma invertida (cliente_id,produto_id): ela não existe aqui. - Formato de data diverge entre requisição e resposta. No corpo de entrada a API usa ISO —
"data_competencia": "2024-07-15". Na resposta vemDD/MM/AAAA—"data_vencimento": "01/01/2030". Mesmo recurso, formato diferente na ida e na volta. Sempre confira o exemplo do endpoint (ver "Buscar payloads e endpoints").
Onde guardar os tokens
O refresh_token rotaciona a cada hora. Isso o torna estado mutável compartilhado, não uma
configuração: quem guarda precisa conseguir reescrever o valor, de forma atômica, com um único
dono. Variável de ambiente e arquivo .env não servem — não são reescrevíveis com segurança em
tempo de execução.
Se quem chama é o n8n → não guarde nada você mesmo. Use a credencial genérica OAuth2 API; o n8n persiste e renova sozinho:
| Campo | Valor |
|---|---|
| Grant Type | Authorization Code |
| Authorization URL | https://auth.contaazul.com/login |
| Access Token URL | https://auth.contaazul.com/oauth2/token |
| Scope | openid profile aws.cognito.signin.user.admin |
| Authentication | Header (Basic base64(client_id:client_secret)) |
Cadastre no Portal a callback do seu próprio n8n (https://SEU-N8N/rest/oauth2-credential/callback) como redirect_uri.
⚠️ Não validado com a Conta Azul — teste deixando o workflow parado >1 h e reexecutando.
Fora do n8n (Python, script, app) → tabela no seu banco:
CREATE TABLE integracao_tokens (
integracao TEXT NOT NULL, -- 'conta_azul'
id_empresa TEXT NOT NULL, -- GET /v1/pessoas/conta-conectada
access_token TEXT NOT NULL,
refresh_token TEXT NOT NULL,
expira_em TIMESTAMPTZ NOT NULL,
atualizado_em TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (integracao, id_empresa)
);
Renove dentro de transação com SELECT … FOR UPDATE na linha (ver armadilha 8). Chaveada por
id_empresa desde o início: serve para uma conta ou várias, sem migração depois.
Chamada base
Python:
import requests
BASE = "https://api-v2.contaazul.com"
def ca(method, path, token, **kw):
r = requests.request(method, f"{BASE}{path}",
headers={"Authorization": f"Bearer {token}",
"Content-Type": "application/json"},
timeout=30, **kw)
if r.status_code == 401:
raise RuntimeError("token expirado/inválido — renove antes de repetir")
r.raise_for_status()
return r.json()
# ex.: ca("GET", "/v1/pessoas", token)
n8n Code node:
const httpRequest = this.helpers.httpRequest;
async function ca(method, path, token, body) {
return await httpRequest.call(this, {
method, url: `https://api-v2.contaazul.com${path}`,
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body, json: true,
});
}
No node HTTP Request nativo: Authentication = Generic → OAuth2 API, apontando a credencial descrita acima. É o caminho preferido — evita gerenciar token na mão.
Mapa de endpoints (índice rápido)
| Área | Endpoints |
|---|---|
| Pessoas (clientes/fornecedores/transportadoras) | GET/POST /v1/pessoas · GET/PUT/DELETE /v1/pessoas/{id} · POST /v1/pessoas/ativar · POST /v1/pessoas/inativar · POST /v1/pessoas/excluir · GET /v1/pessoas/legado/{id} · GET /v1/pessoas/conta-conectada (traz id_empresa) |
| Vendas | POST /v1/venda · GET/PUT /v1/venda/{id} · GET /v1/venda/busca · GET /v1/venda/{id}/imprimir (PDF) · GET /v1/venda/{id_venda}/itens · GET /v1/venda/vendedores · POST /v1/venda/exclusao-lote |
| Orçamentos | POST /v1/orcamentos · GET /v1/orcamentos/{id} · GET /v1/orcamentos/busca · DELETE /v1/orcamentos/exclusao-lote |
| Financeiro | GET /v1/categorias · GET /v1/categorias/configuracao-padrao · GET /v1/financeiro/categorias-dre · GET/POST /v1/centro-de-custo · GET /v1/conta-financeira · GET /v1/conta-financeira/{id}/saldo-atual · GET /v1/financeiro/eventos-financeiros/{id_evento}/parcelas · GET /v1/financeiro/eventos-financeiros/parcelas/{id} (rateio, categoria, centro de custo) · POST /v1/financeiro/eventos-financeiros/contas-a-receber/gerar-cobranca |
| Produtos | GET/POST /v1/produtos · GET/PUT/DELETE /v1/produtos/{id} · GET /v1/produtos/categorias · /ncm · /cest · /unidades-medida · /ecommerce-categorias · /ecommerce-marcas |
| Notas fiscais | GET /v1/notas-fiscais · GET /v1/notas-fiscais/{chave} · GET /v1/notas-fiscais-servico · POST /v1/notas-fiscais/vinculo-mdfe |
| Contratos (recorrência) | GET/POST /v1/contratos · GET /v1/contratos/proximo-numero |
| Serviços | área open-api-service — CRUD de serviços (busque os paths, ver abaixo) |
| Protocolos | área protocol-apis-openapi — consulta/emissão de protocolos |
| Baixas · Cobranças · Análises | acquittance-apis-openapi · charge-apis-openapi · open-api-analytics — áreas extras |
Este mapa é um índice para achar o endpoint rápido — não é exaustivo. Ex.: Financeiro tem 18 endpoints, não 9 — faltam aqui POST .../contas-a-receber, POST .../contas-a-pagar, os dois /buscar, PATCH .../parcelas/{id}, GET .../alteracoes (ótimo para polling incremental — contorna a falta de webhook), /saldo-inicial e /transferencias. A lista completa e atual de qualquer área sai do llms.txt (ver abaixo).
Regras de validação de contrato (POST /v1/contratos): intervalo_dias entre 1 e 60 · dia_vencimento ≤ 31 · primeira_data_vencimento não pode ser anterior a hoje · data_inicio ≤ data_fim · valor_desconto positivo e ≤ total dos itens.
Erros
| Erro | Causa provável | Ação |
|---|---|---|
invalid_grant | code reutilizado ou expirado (3 min); refresh_token já rotacionado/revogado; redirect_uri ou client_id divergente | Refazer a autorização no navegador; conferir se gravou o último refresh_token |
401 Unauthorized | access_token ausente, expirado (1 h) ou malformado | Renovar e repetir uma vez — não entrar em loop de retry |
429 Too Many Requests | Passou de 600/min ou 10/s | Backoff exponencial; ler os headers de limite da resposta |
500 | Erro interno da API | Retry com backoff; persistindo, abrir chamado no Portal do Desenvolvedor |
Buscar payloads e endpoints (a doc tem versão para IA)
O portal é Redocly e serve tudo em .md legível por IA — inclusive os corpos de requisição que não estão em references/. Não empacote spec: busque com WebFetch nesta cadeia de 3 níveis, do geral ao específico:
- Índice de tudo —
https://developers.contaazul.com/llms.txt(~3 KB): lista todas as áreas e páginas. - Uma área — anexe
.mdà URL da área. Ex.:.../docs/financial-apis-openapi.md(~9 KB): lista os endpoints, cada um com um slug de operação. - Um endpoint —
.../docs/<area>/v1/<slug>.md(~4 KB): traz## Request fieldscom nome, tipo, obrigatoriedade, descrição e exemplo de cada campo. É o payload que faltava.
Exemplo real (POST /v1/financeiro/eventos-financeiros/contas-a-receber → slug createreceivablefinancialevent):
data_competencia (string, req, "2024-07-15") · valor (number, req) · contato (string, req, UUID) · conta_financeira (string, req) · rateio[].id_categoria · condicao_pagamento.parcelas[] …
Regra de ouro: para qualquer POST/PUT/PATCH, busque o .md do endpoint e use os campos de lá — nunca invente, e desconfie de nomes vindos de wrapper/MCP (ver armadilha 9: a API usa id_<coisa>, e criar pessoa usa cnpj/cpf/codigo, não o perfis:[{tipo_perfil}] que wrappers documentam).
Essa fonte é sempre atual — sem snapshot para envelhecer.
references/api-completa.mdcontinua útil como visão geral offline (auth, changelog até 2026-07-10, guias), mas para campo de payload a verdade é o.mddo endpoint.
Navegar a referência offline
references/api-completa.md tem ~1.430 linhas — não leia inteiro (use para auth/guias sem rede):
grep -n '^## SEÇÃO' references/api-completa.md # 8 seções principais
grep -n '^### ' references/api-completa.md # todos os subtópicos
grep -n '/v1/venda' references/api-completa.md # onde um endpoint aparece
Depois Read com offset na linha encontrada.
Gives 0 of the 12 instructions most apis services skills give in ~3.9k tokens
Counted across 424 of the 426 authors here whose files we hold, read 2026-08-06
- use plural nouns for resource namesin 41 of 424, across 32 files
- use cursor-based pagination for large datasetsin 35 of 424, across 20 files
- include rate limit headers in responsesin 25 of 424, across 13 files
- Use kebab-case for multi-word resourcesin 23 of 424, across 13 files
- version APIs in the URL pathin 19 of 424, across 9 files
- use semantic HTTP status codesin 18 of 424, across 8 files
- verify webhook signaturesin 18 of 424, across 11 files
- use query parameters for filteringin 17 of 424, across 6 files
- use async database operationsin 14 of 424, across 7 files
- wrap successful responses in a data fieldin 13 of 424, across 3 files
- prefix sorting parameters with a hyphen for descending orderin 13 of 424, across 3 files
- set appropriate HTTP status codesin 13 of 424, across 6 files
Said here and by no other author read
- authenticate using OAuth 2.0 Authorization Code
- store new refresh token before using access token
- use a single process for token renewal
- implement polling for synchronization
- respect rate limits of 600 per minute and 10 per second
- use the exact OAuth scope required
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.