Conta azul
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).From its SKILL.md
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.
2 things to look at
- 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.
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.
What ships with it: 1 file
69.5 KB alongside SKILL.md
references/
- api-completa.md69.5 KB