Boas Práticas
Padrões recomendados para paginação, tratamento de erros, retry e mais
Visão geral
Este guia reúne as boas práticas para construir uma integração robusta com a API Conta Simples. Aqui você encontra padrões para paginação, tratamento de erros, retry e mais — sem duplicar detalhes técnicos de endpoints, que estão na API Reference.
Paginação
A API usa paginação baseada em cursor para retornar grandes volumes de dados. O campo nextPageStartKey funciona como cursor de navegação.
Como funciona
Primeira requisição
Envie os parâmetros de filtro e limit. Não inclua nextPageStartKey.
Verifique a resposta
Se nextPageStartKey estiver presente e não for null, há mais páginas.
Próxima página
Envie a mesma requisição incluindo o nextPageStartKey da resposta anterior.
Última página
Quando nextPageStartKey for null ou ausente, você chegou ao fim.
Exemplo completo
async function getAllTransactions(
baseUrl: string,
token: string,
startDate: string,
endDate: string
) {
const allTransactions: any[] = [];
let nextPageKey: string | undefined;
while (true) {
const payload: Record<string, any> = {
startDate,
endDate,
limit: 100,
};
if (nextPageKey) {
payload.nextPageStartKey = nextPageKey;
}
const response = await fetch(`${baseUrl}/statements/v1/credit-card`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
allTransactions.push(...data.transactions);
nextPageKey = data.nextPageStartKey;
if (!nextPageKey) break;
}
return allTransactions;
}
O nextPageStartKey é um token opaco. Não tente decodificar ou modificar — use exatamente como recebido.
Regras
| Regra | Valor |
|---|---|
| Limite mínimo por página | 5 itens |
| Limite máximo por página | 100 itens |
| Período máximo por consulta | 62 dias |
Para períodos maiores, divida em múltiplas consultas sequenciais.
Tratamento de erros
Categorias de erros
| Código | Significado | Ação recomendada |
|---|---|---|
400 | Parâmetros inválidos | Corrija os parâmetros e tente novamente |
401 | Token inválido ou expirado | Renove o token e repita a requisição |
403 | Sem permissão | Verifique escopos e ambiente |
404 | Recurso não encontrado | Verifique o ID no path |
5xx | Erro do servidor | Retry com backoff exponencial |
Diagnóstico detalhado
Veja causas, exemplos e soluções para cada código de erro.
Retry com backoff exponencial
Para erros transitórios (5xx, timeout), implemente retry automático:
const RETRYABLE_STATUS = new Set([500, 502, 503, 504]);
async function requestWithRetry(
method: string,
url: string,
maxRetries = 3,
options?: RequestInit
): Promise<Response> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30_000);
const response = await fetch(url, {
method,
...options,
signal: controller.signal,
});
clearTimeout(timeout);
if (!RETRYABLE_STATUS.has(response.status)) {
return response;
}
} catch (err) {
if (!(err instanceof DOMException && err.name === "AbortError")) throw err;
// Trata timeout como retryable
}
if (attempt < maxRetries - 1) {
const waitTime = 2 ** attempt + Math.random();
await new Promise((resolve) => setTimeout(resolve, waitTime * 1000));
}
}
throw new Error("Max retries exceeded");
}
Renovação automática de token
Para erros 401, renove o token automaticamente:
async function requestWithAuthRetry(
method: string,
url: string,
options: RequestInit & { headers: Record<string, string> }
): Promise<Response> {
let response = await fetch(url, { method, ...options });
if (response.status === 401) {
invalidateTokenCache();
const newToken = await getFreshToken();
options.headers["Authorization"] = `Bearer ${newToken}`;
response = await fetch(url, { method, ...options });
}
return response;
}
Tratamento de valores e datas
Valores monetários
Valores vêm como number (decimais) em Reais (BRL):
{
"amountBrl": 150.75
}
Use tipos decimais no seu sistema (não float) para evitar erros de arredondamento em cálculos financeiros.
Datas
| Contexto | Formato | Exemplo |
|---|---|---|
| Parâmetros de entrada | YYYY-MM-DD | 2025-01-15 |
| Campos de resposta | ISO 8601 (UTC) | 2025-01-15T14:30:00.000Z |
A timezone de resposta é UTC. Converta para o fuso local da sua aplicação se necessário.
Transações internacionais
Para transações internacionais, o campo exchangeRateUsd indica a taxa de câmbio. O valor em amountBrl já vem convertido para Reais — use exchangeRateUsd apenas como referência para auditoria.
Idempotência
Use o id de cada recurso como chave de idempotência para evitar duplicações:
function processTransaction(transaction: { id: string; [key: string]: any }) {
const txnId = transaction.id;
// Verifica se já processou esta transação
if (alreadyProcessed(txnId)) {
return; // Ignora duplicata
}
// Processa a transação
handleTransaction(transaction);
// Marca como processada
markAsProcessed(txnId);
}
Download de anexos
Transações podem incluir comprovantes no array attachments. Para baixá-los:
- Extraia o
iddo anexo na resposta de transações - Faça
GET /attachments/v1/content/{attachmentId}— veja API Reference - Use o header
Content-Typeda resposta para determinar o formato
Tipos suportados
| Content-Type | Extensão | Uso típico |
|---|---|---|
image/png | .png | Screenshots, fotos |
image/jpeg | .jpg | Fotos, recibos escaneados |
application/pdf | .pdf | Notas fiscais, documentos |
Exemplo
import { writeFile, mkdir } from "fs/promises";
import path from "path";
async function downloadAttachment(
baseUrl: string,
token: string,
attachmentId: string,
outputDir = "./downloads"
): Promise<string> {
const response = await fetch(`${baseUrl}/attachments/v1/content/${attachmentId}`, {
headers: { Authorization: `Bearer ${token}` },
});
const contentType = response.headers.get("Content-Type") ?? "";
const extMap: Record<string, string> = {
"image/png": ".png",
"image/jpeg": ".jpg",
"application/pdf": ".pdf",
};
const ext = extMap[contentType] ?? ".bin";
await mkdir(outputDir, { recursive: true });
const filepath = path.join(outputDir, `${attachmentId}${ext}`);
const buffer = Buffer.from(await response.arrayBuffer());
await writeFile(filepath, buffer);
return filepath;
}
Segurança
Armazenamento de credenciais
- AWS Secrets Manager
- HashiCorp Vault
- Azure Key Vault
- GCP Secret Manager
- Variáveis de ambiente em runtime (não em código)
- Hardcoded em código fonte
- Arquivos
.envcommitados - Logs de aplicação
- Repositórios Git (mesmo privados)
Segregação de ambientes
Use variáveis de ambiente distintas para Sandbox e Produção:
# Sandbox
export OPENAPI_BASE_URL=https://api-sandbox.contasimples.com
export OPENAPI_API_KEY={API_KEY_SANDBOX}
# Produção
export OPENAPI_BASE_URL=https://api.contasimples.com
export OPENAPI_API_KEY={API_KEY_PRODUCAO}
Nunca use credenciais de Produção em ambientes de desenvolvimento ou testes.
Checklist de produção
- Autenticação com renovação automática de token
- Paginação implementada corretamente
- Tratamento de erros por código HTTP
- Retry com backoff exponencial para 5xx
- Idempotência por ID de recurso
- Credenciais em cofre de segredos
- Ambientes segregados (Sandbox vs Produção)
- Monitoramento e alertas configurados
- Logging estruturado com request_id
Próximos passos
Last updated today
Built with Documentation.AI