Guia completo de integração com o sistema federal Contratos Gov.br para sincronização bidirecional de contratos.
A integração com Contratos Gov.br permite:
- Push Sync: Enviar contratos locais para o portal Gov.br
- Pull Sync: Importar contratos do Gov.br para o sistema local
- Conflict Resolution: Resolução automática de conflitos usando estratégia Last-Write-Wins (LWW)
- Sync Logs: Auditoria completa de todas as operações de sincronização
Issues Relacionadas:
- Issue #1289 (Parent) - Integração com Contratos Gov.br
- Issue #1674 - Autenticação Gov.br OAuth
- Issue #1675 - Sincronização Push
- Issue #1676 - Sincronização Pull
- Issue #1677 - Tratamento de conflitos
- Issue #1678 - Testes de integração e documentação
┌─────────────────────┐ ┌──────────────────────┐
│ Sistema ETP │ │ Contratos Gov.br │
│ Express │ │ (API Federal) │
├─────────────────────┤ ├──────────────────────┤
│ │ │ │
│ ContratosGovBr │ ◄────── │ OAuth 2.0 Auth │
│ SyncService │ Push │ │
│ │ ──────► │ REST API v1 │
│ │ Pull │ │
└─────────────────────┘ └──────────────────────┘
│
│ Persiste
▼
┌─────────────────────┐
│ PostgreSQL │
│ - Contratos │
│ - ContratoSyncLog │
└─────────────────────┘
Componentes:
- ContratosGovBrSyncService: Serviço principal de sincronização
- ContratosGovBrAuthService: Autenticação OAuth 2.0 com Gov.br
- ContratoSyncLog Entity: Log de sincronização e resolução de conflitos
- Contrato Entity: Entidade de contrato com campos de sincronização
- Acesse o portal Contratos Gov.br
- Navegue para Configurações → Integrações → API
- Crie uma nova aplicação OAuth 2.0
- Anote as credenciais:
- Client ID
- Client Secret
- Redirect URI
Adicione as credenciais no arquivo .env:
# API Contratos Gov.br
CONTRATOS_GOVBR_API_URL=https://contratos.comprasnet.gov.br/api/v1
CONTRATOS_GOVBR_CLIENT_ID=seu-client-id-aqui
CONTRATOS_GOVBR_CLIENT_SECRET=seu-client-secret-aqui
CONTRATOS_GOVBR_REDIRECT_URI=https://seu-dominio.com/auth/govbr/callback
# OAuth 2.0 Endpoints
GOVBR_OAUTH_URL=https://sso.acesso.gov.br/oauth2
GOVBR_TOKEN_URL=https://sso.acesso.gov.br/oauth2/tokenExecute o comando de verificação:
npm run check:govbr-configSe tudo estiver correto, você verá:
✅ Contratos Gov.br API URL configurada
✅ Credenciais OAuth encontradas
✅ Conexão com API Gov.br: OK
Quando usar:
- Após criar ou editar um contrato no sistema local
- Para sincronizar contratos existentes pela primeira vez
Endpoint REST:
POST /api/contratos/:id/sync/push
Authorization: Bearer <token>Exemplo com curl:
curl -X POST https://api.etp-express.com/api/contratos/uuid-do-contrato/sync/push \
-H "Authorization: Bearer $TOKEN"Exemplo com interface:
- Acesse o contrato em Contratos → Detalhes
- Clique em Sincronizar com Gov.br
- Aguarde confirmação de sucesso
Validações Obrigatórias:
O contrato deve conter os seguintes campos obrigatórios:
- ✅
numero- Número do contrato - ✅
objeto- Objeto do contrato - ✅
contratadoCnpj- CNPJ do contratado - ✅
contratadoRazaoSocial- Razão social - ✅
valorGlobal- Valor global do contrato - ✅
vigenciaInicio- Data de início da vigência - ✅
vigenciaFim- Data de fim da vigência - ✅
gestorResponsavelcom CPF no campocargo(formato:XXX.XXX.XXX-XX) - ✅
fiscalResponsavelcom CPF no campocargo - ✅
dataAssinatura- Para contratos não-minuta
Fluxo de Push:
1. Validar campos obrigatórios
2. Mapear entity local → formato API Gov.br
3. Enviar POST para /contratos
4. Receber govBrId da API
5. Atualizar contrato local:
- govBrId = ID retornado
- govBrSyncStatus = 'synced'
- govBrSyncedAt = timestamp atual
Tratamento de Erros:
| Erro | Status | Solução |
|---|---|---|
| Campos obrigatórios faltando | 400 | Completar campos antes de sincronizar |
| CPF de gestor/fiscal não encontrado | 400 | Adicionar CPF no campo cargo do usuário |
| API Gov.br indisponível | 500 | Aguardar e tentar novamente |
| Credenciais inválidas | 401 | Verificar Client ID/Secret no .env |
Quando usar:
- Para importar contratos criados diretamente no portal Gov.br
- Para sincronizar atualizações feitas no Gov.br
- Execução periódica via cron job (recomendado: diário)
Endpoint REST:
POST /api/contratos/sync/pull
Authorization: Bearer <token>
Content-Type: application/json
{
"organizationId": "uuid-da-organizacao"
}Exemplo com curl:
curl -X POST https://api.etp-express.com/api/contratos/sync/pull \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"organizationId": "uuid-da-org"}'Fluxo de Pull:
1. Buscar contratos da organização na API Gov.br
2. Para cada contrato remoto:
a. Buscar contrato local por número
b. Se NÃO existe: criar novo contrato
c. Se existe: atualizar campos (upsert)
3. Retornar estatísticas:
- created: contratos novos criados
- updated: contratos atualizados
- errors: erros durante sincronização
Resposta de Exemplo:
{
"success": true,
"stats": {
"created": 5,
"updated": 12,
"errors": 0
},
"message": "Pull sync completed successfully"
}Configure um job periódico para sincronização automática:
Usando NestJS Scheduler:
import { Cron, CronExpression } from '@nestjs/schedule';
@Injectable()
export class ContratosGovBrCronService {
constructor(
private readonly syncService: ContratosGovBrSyncService,
private readonly orgRepository: Repository<Organization>,
) {}
@Cron(CronExpression.EVERY_DAY_AT_2AM)
async handleDailyPullSync() {
const organizations = await this.orgRepository.find({ isActive: true });
for (const org of organizations) {
try {
const stats = await this.syncService.pullContratos(org.id);
this.logger.log(
`Pull sync for org ${org.name}: ${stats.created} created, ${stats.updated} updated`,
);
} catch (error) {
this.logger.error(
`Failed to pull contracts for org ${org.name}`,
error.stack,
);
}
}
}
}A integração detecta conflitos quando:
- Contrato foi editado localmente E remotamente desde a última sincronização
- Campos críticos divergem:
valorGlobal,vigenciaFim,status,objeto,contratadoCnpj
Lógica de Resolução:
Se (govBrSyncedAt > updatedAt):
→ Remote Wins (Gov.br mais recente)
→ Aplicar valores do Gov.br
Senão:
→ Local Wins (dados locais editados após último sync)
→ Preservar valores locais
→ Agendar Push automático para sincronizar Gov.br
| Campo | Descrição | Crítico? |
|---|---|---|
valorGlobal |
Valor total do contrato | ✅ Sim |
vigenciaFim |
Data fim vigência | ✅ Sim |
status |
Status do contrato | ✅ Sim |
objeto |
Objeto contratual | ✅ Sim |
contratadoCnpj |
CNPJ do contratado | ✅ Sim |
descricaoObjeto |
Descrição detalhada | ❌ Não |
observacoes |
Observações gerais | ❌ Não |
Todos os conflitos são registrados em ContratoSyncLog:
SELECT
csl.id,
csl.action,
csl.conflicts,
csl.resolution,
csl."createdAt",
c.numero AS contrato_numero
FROM contrato_sync_logs csl
JOIN contratos c ON csl."contratoId" = c.id
WHERE csl.action = 'conflict_resolved'
ORDER BY csl."createdAt" DESC;Exemplo de Log de Conflito:
{
"id": "uuid-do-log",
"contratoId": "uuid-do-contrato",
"action": "conflict_resolved",
"conflicts": [
{
"field": "valorGlobal",
"localValue": "100000.00",
"remoteValue": "150000.00"
},
{
"field": "vigenciaFim",
"localValue": "2024-12-31T00:00:00.000Z",
"remoteValue": "2025-06-30T00:00:00.000Z"
}
],
"resolution": {
"valorGlobal": "150000.00",
"vigenciaFim": "2025-06-30T00:00:00.000Z"
},
"createdAt": "2024-01-25T10:30:00.000Z"
}Cada contrato possui campos de sincronização:
| Campo | Tipo | Descrição |
|---|---|---|
govBrId |
string | ID do contrato no Gov.br |
govBrSyncStatus |
enum | 'synced' | 'error' | 'pending' | null |
govBrSyncedAt |
timestamp | Data/hora da última sincronização |
govBrSyncErrorMessage |
text | Mensagem de erro (se houver) |
SELECT
id,
numero,
"govBrSyncStatus",
"govBrSyncErrorMessage",
"updatedAt"
FROM contratos
WHERE "govBrSyncStatus" = 'error'
OR ("govBrSyncStatus" IS NULL AND status != 'minuta')
ORDER BY "updatedAt" DESC;Ver últimas sincronizações:
SELECT
csl.action,
c.numero AS contrato,
csl."createdAt",
CASE
WHEN csl.conflicts IS NOT NULL THEN jsonb_array_length(csl.conflicts)
ELSE 0
END AS num_conflicts
FROM contrato_sync_logs csl
JOIN contratos c ON csl."contratoId" = c.id
ORDER BY csl."createdAt" DESC
LIMIT 50;O serviço emite logs estruturados:
[ContratosGovBrSyncService] Starting push sync for contrato <uuid>
[ContratosGovBrSyncService] Contrato <uuid> successfully synced to Gov.br with ID <govbr-id>
[ContratosGovBrSyncService] Failed to push contrato <uuid> to Gov.br
[ContratosGovBrSyncService] Starting pull sync for organization <org-id>
[ContratosGovBrSyncService] Found 25 contracts in Gov.br for organization <org-id>
[ContratosGovBrSyncService] Pull sync completed: 5 created, 12 updated, 0 errors
[ContratosGovBrSyncService] Contract <numero> updated from Gov.br with conflict resolution: 2 conflicts resolvedFiltrar logs no terminal:
# Apenas erros de sincronização
npm run logs | grep "Failed to.*Gov.br"
# Sucessos de push
npm run logs | grep "successfully synced to Gov.br"
# Conflitos resolvidos
npm run logs | grep "conflict resolution"Causa: Campos obrigatórios estão faltando no contrato.
Solução:
- Verificar mensagem de erro completa:
{
"message": "Contract validation failed for Gov.br sync",
"errors": [
"gestorResponsavel CPF could not be determined (add CPF to cargo field)"
]
}- Adicionar CPF no campo
cargodo gestor/fiscal:
UPDATE users
SET cargo = 'Gestor de Contratos - CPF: 123.456.789-01'
WHERE id = '<uuid-do-gestor>';- Tentar sincronizar novamente.
Causa: API Gov.br está indisponível ou com timeout.
Solução:
- Verificar status da API Gov.br:
curl -I https://contratos.comprasnet.gov.br/api/v1/health- Verificar conectividade de rede:
ping contratos.comprasnet.gov.br-
Aguardar alguns minutos e tentar novamente.
-
Se persistir, verificar se houve mudanças na API Gov.br (consultar changelog oficial).
Causa: Client ID ou Client Secret incorretos, ou token expirado.
Solução:
- Verificar credenciais no
.env:
echo $CONTRATOS_GOVBR_CLIENT_ID
echo $CONTRATOS_GOVBR_CLIENT_SECRET-
Regenerar credenciais no portal Gov.br se necessário.
-
Atualizar
.enve reiniciar aplicação:
npm run restartCausa: Timestamps de sincronização desatualizados.
Solução:
- Verificar timestamps do contrato:
SELECT
numero,
"updatedAt",
"govBrSyncedAt",
"govBrSyncStatus"
FROM contratos
WHERE numero = '<numero-do-contrato>';- Se
govBrSyncedAtestiver desatualizado, forçar novo pull:
curl -X POST https://api.etp-express.com/api/contratos/sync/pull \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"organizationId": "<org-id>"}'Causa: Falha sistemática na sincronização (pode ser configuração ou API).
Solução:
- Identificar padrão de erros:
SELECT
"govBrSyncErrorMessage",
COUNT(*) AS total
FROM contratos
WHERE "govBrSyncStatus" = 'error'
GROUP BY "govBrSyncErrorMessage"
ORDER BY total DESC;-
Se erros forem os mesmos, corrigir causa raiz (credenciais, validação, etc.).
-
Após correção, re-sincronizar em lote:
const contratosComErro = await contratoRepository.find({
where: { govBrSyncStatus: 'error' },
});
for (const contrato of contratosComErro) {
try {
await syncService.pushContrato(contrato.id);
} catch (error) {
console.error(`Retry failed for ${contrato.numero}`, error.message);
}
}- Manual Contratos Gov.br: https://www.gov.br/compras/pt-br/acesso-a-informacao/manuais/manual-contratos-gov-br
- API Reference: https://contratos.comprasnet.gov.br/api/docs
- Portal Acesso Gov.br (OAuth): https://sso.acesso.gov.br/docs
- ContratosGovBrSyncService:
backend/src/modules/contratos/services/contratos-govbr-sync.service.ts - ContratosGovBrAuthService:
backend/src/modules/gov-api/services/contratos-govbr-auth.service.ts - Entidades:
backend/src/entities/contrato.entity.tsbackend/src/entities/contrato-sync-log.entity.ts
- Testes de Integração:
backend/test/integration/contratos-govbr-sync.spec.ts
- Issue #1289 - Integração com Contratos Gov.br (Parent)
- Issue #1673 - Pesquisa e documentação API
- Issue #1674 - Autenticação OAuth Gov.br
- Issue #1675 - Sincronização Push
- Issue #1676 - Sincronização Pull
- Issue #1677 - Tratamento de conflitos
- Issue #1678 - Testes e documentação
O sistema detectará o conflito e aplicará a estratégia Last-Write-Wins (LWW). Se o contrato foi editado localmente após a última sincronização, os dados locais prevalecerão e será agendado um push automático para atualizar o Gov.br. Caso contrário, os dados do Gov.br serão aplicados.
Sim. Remova ou comente o cron job de Pull Sync no arquivo contratos-govbr-cron.service.ts. A sincronização manual via API continuará funcionando.
Use o endpoint de Pull Sync, que importa todos os contratos da organização de uma vez. Para Push em lote, crie um script que itera sobre os contratos e chama pushContrato() para cada um.
Sim. A integração suporta todos os tipos de contratação previstos na Lei 14.133/2021, incluindo licitação, dispensa e inexigibilidade.
Sim. Por padrão, todos os logs são mantidos para auditoria. Você pode configurar uma rotina de limpeza periódica se necessário (ex: excluir logs > 2 anos).
Última atualização: 2026-01-25 Versão do documento: 1.0 Responsável: Time de Desenvolvimento ETP Express