Bem-vindo à Lugotech Sync API
A Lugotech Sync API permite acesso e gestão completa de empresas (Accounts), contactos (Contacts), oportunidades e eventos de timeline sincronizados com o Salesflare.
Dados Disponíveis:
•
549.998 Accounts (empresas/clientes)
•
15.714 Contacts (pessoas/contactos)
Atualizado 01-02-2026
Novidades v3.0 (Junho 2026)
- Comissões — módulo completo:
/api/commissions/opportunity (receber, resultado, consulta, CPEs, reconciliação manual)
- Oportunidades por comercial — novo filtro
ownerEmail (owner, assignee, coldcaller, discovery, closer)
- Ordenação dinâmica em oportunidades — parâmetros
orderBy e orderDir
- Limit em oportunidades — parâmetro
limit (atalho para pageSize=N&page=1)
- Auditoria — logs completos por entidade, utilizador e ação (
/api/audit)
- Campo
salesforceProposalId nas Oportunidades (referência da proposta externa)
- Índices de performance adicionados (trigram search, FK indexes)
Novidades v2.0 (Março 2026)
- Novos filtros em Accounts:
segment, tag, country, minElectricityKwh, minGasKwh
- Novos filtros em Contacts:
position, noAccount
- CRUD completo para Accounts e Contacts (PUT/DELETE)
- Importação CSV para Accounts e Contacts
- Endpoint de Oportunidades (
/api/opportunities)
- Endpoint de Timeline (
/api/timeline)
- Gestão de Duplicados (
/api/duplicates)
- Estado da Sincronização (
/api/sync/status)
- Campo
accountName devolvido nos Contacts
Casos de Uso
Exportação de Dados
Exporte todos os accounts e contacts para análise externa, relatórios ou backup.
Integração com CRM
Sincronize dados entre o Salesflare e outros sistemas internos da empresa.
Importação CSV
Importe ficheiros Excel/CSV de empresas e contactos diretamente para o sistema.
Gestão de Qualidade
Detete e resolva duplicados, gerencie contactos sem empresa associada.
Visão Geral dos Endpoints
GET/api/accounts
Listar e filtrar empresas
PUT/api/accounts/{id}
Atualizar empresa
DELETE/api/accounts/{id}
Eliminar empresa
POST/api/accounts/import-csv
Importar empresas via CSV/Excel
GET/api/contacts
Listar e filtrar contactos
PUT/api/contacts/{id}
Atualizar contacto
DELETE/api/contacts/{id}
Eliminar contacto
POST/api/contacts/import-csv
Importar contactos via CSV/Excel
GET/api/opportunities
Listar e filtrar oportunidades
GET/api/timeline
Eventos de timeline por empresa
GET/api/sync/status
Estado da sincronização com Salesflare
GET/api/duplicates
Listar registos duplicados
POST/api/commissions/opportunity
Receber proposta de comissão (integração motor de comissões externo)
PUT/api/commissions/opportunity/{id}/result
Registar resultado de comissão calculada
GET/api/commissions/opportunities
Listar comissões com filtros
GET/api/audit
Logs de auditoria por entidade/utilizador/ação
Autenticação
A API suporta dois métodos de autenticação consoante o tipo de integração.
1. JWT Bearer Token (Aplicação Web)
Utilizado pela aplicação frontend. Obtenha um token através do endpoint de login e inclua-o no header Authorization.
## 1. Login para obter token
curl -X POST https://api.lugo.tech/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"utilizador@empresa.pt","password":"password123"}'
## Resposta: { "token": "eyJ...", "user": { "id": "...", ... } }
## 2. Usar o token nas chamadas seguintes
curl -H 'Authorization: Bearer eyJ...' \
https://api.lugo.tech/api/accounts?page=1&pageSize=50
2. API Key (Integrações Externas — apenas leitura)
Para integrações externas (Python, C#, scripts, etc.), envie a API Key no header X-API-Key.
API Keys são read-only: apenas métodos GET são permitidos.
Para criar, atualizar ou eliminar registos é necessário autenticação JWT via login.
curl -H 'X-API-Key: sua_api_key_aqui' \
https://api.lugo.tech/api/accounts?page=1&pageSize=50
Segurança Importante:
• Nunca compartilhe ou exponha a sua API Key publicamente
• Não faça commit da chave em repositórios de código
• Use variáveis de ambiente para armazenar a chave
• Tokens JWT expiram — implemente renovação automática
Comparação de Métodos de Autenticação
| Operação | API Key | JWT (Login) |
| GET (leitura) | Permitido | Permitido |
| PUT (atualizar) | Bloqueado | Permitido |
| DELETE (eliminar) | Bloqueado | Permitido |
| POST (criar/importar) | Bloqueado | Permitido |
Gestão de Utilizadores
Endpoints de gestão de utilizadores — requerem JWT com role admin, exceto /me e /change-password.
GET/api/auth/me
Utilizador autenticado atual
POST/api/auth/logout
Limpa o cookie JWT
POST/api/auth/register
Criar novo utilizador (admin)
GET/api/auth/users
Listar utilizadores (admin)
PUT/api/auth/users/{id}
Atualizar utilizador
PUT/api/auth/users/{id}/toggle-active
Ativar/desativar utilizador
PUT/api/auth/users/{id}/role
Alterar role (admin/user)
PUT/api/auth/users/{id}/reset-password
Reset password (admin)
DELETE/api/auth/users/{id}
Eliminar utilizador
PUT/api/auth/change-password
Alterar password própria
Health Check
GET/health
Estado da API, ambiente (Production/Staging) e versão
{
"status": "ok",
"environment": "Production",
"stagingMode": false,
"timestamp": "2026-06-02T10:00:00Z"
}
Accounts - Empresas
O endpoint /api/accounts suporta listagem, filtragem avançada, atualização e eliminação de empresas sincronizadas com o Salesflare.
Listar / Filtrar Empresas
GET
/api/accounts
Query Parameters
| Parâmetro |
Tipo |
Obrigatório |
Descrição |
page |
integer |
Não |
Número da página (padrão: 1) |
pageSize |
integer |
Não |
Registos por página (padrão: 50, máx: 1000) |
search |
string |
Não |
Pesquisa por nome, NIF, domínio, cidade, segmento ou tag |
city |
string |
Não |
Filtrar por cidade |
country novo |
string |
Não |
Filtrar por país (ex: PT, Portugal) |
segment novo |
string |
Não |
Filtrar por segmento (ex: SILVER, GOLD) |
tag novo |
string |
Não |
Filtrar por tag (pesquisa parcial no tagsJson) |
minElectricityKwh novo |
integer |
Não |
Consumo mínimo de eletricidade em kWh/ano |
minGasKwh novo |
integer |
Não |
Consumo mínimo de gás em kWh/ano |
Exemplo de resposta
{
"total": 549998,
"page": 1,
"pageSize": 50,
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"salesflareId": 12345,
"name": "501262270 - Empresa Exemplo Lda",
"nif": "501262270",
"email": "geral@empresa.pt",
"city": "Lisboa",
"country": "PT",
"electricityConsumptionYearKwh": 85000,
"gasConsumptionYearKwh": 12000,
"tnw": 45000.00,
"segmentName": "GOLD",
"tagsJson": "[\"Leads_Solar_18032026\",\"Cliente_Ativo\"]",
"syncedAt": "2026-02-01T10:00:00Z",
"createdAt": "2025-10-15T08:30:00Z"
}
]
}
Atualizar Empresa novo
PUT
/api/accounts/{id}
Atualiza os dados de uma empresa. Apenas os campos enviados são atualizados. A alteração é sincronizada com o Salesflare.
curl -X PUT https://api.lugo.tech/api/accounts/3fa85f64-... \
-H 'Authorization: Bearer eyJ...' \
-H 'Content-Type: application/json' \
-d '{"name":"Nova Designação Lda","city":"Porto","electricityConsumptionYearKwh":95000}'
Verificar antes de Eliminar novo
GET
/api/accounts/{id}/delete-check
Verifica quantos contactos/oportunidades/eventos serão afetados
{
"canDelete": true,
"contacts": 3,
"opportunities": 1,
"timelineEvents": 12
}
Eliminar Empresa novo
DELETE
/api/accounts/{id}?force=false
Se tiver registos associados, use force=true para forçar a eliminação em cascata.
Atenção: A eliminação com force=true remove também os contactos, oportunidades e eventos de timeline associados. Esta ação não é reversível.
Empresas Sem Oportunidades novo
GET
/api/accounts/cleanup/empty
Lista empresas sem oportunidades nem contactos associados
Fundir Empresas Duplicadas novo
POST
/api/accounts/merge
Funde uma empresa duplicada na empresa principal
curl -X POST https://api.lugo.tech/api/accounts/merge \
-H 'Authorization: Bearer eyJ...' \
-H 'Content-Type: application/json' \
-d '{"masterId":"id-principal","duplicateId":"id-duplicado","deleteFromSalesflare":false}'
Campos da Empresa
| Campo | Tipo | Descrição |
id | GUID | Identificador único interno |
salesflareId | integer | ID no Salesflare |
name | string | Nome/razão social |
nif | string | NIF/NIPC da empresa (chave única global) |
email | string | Email principal |
phone | string | Telefone |
website | string | Website |
description | string | Descrição |
city | string | Cidade |
country | string | País |
tnw | decimal | Total Network Worth — valor do cliente (€) |
electricityConsumptionYearKwh | integer | Consumo anual de eletricidade (kWh) |
gasConsumptionYearKwh | integer | Consumo anual de gás (kWh) |
segmentName | string | Segmento (ex: SILVER, GOLD) |
tagsJson | JSON string | Array JSON com tags — usar JSON.parse() no cliente |
syncedAt | datetime | Data da última sincronização com Salesflare |
createdAt | datetime | Data de criação do registo |
Sobre o campo tagsJson: vem como string JSON. Exemplos:
• "[]" — sem tags
• "[\"Leads_Solar\",\"Cliente_Ativo\"]" — array de strings
Use sempre JSON.parse() para processar.
Oportunidades
O endpoint /api/opportunities devolve negócios/propostas sincronizados do Salesflare com suporte a filtragem por comercial, ordenação dinâmica e limit.
GET
/api/opportunities
GET
/api/opportunities/{id}
Detalhe completo por GUID interno (inclui StageUpdatesJson)
GET
/api/opportunities/sf/{salesflareId}
Detalhe por Salesflare ID
GET
/api/opportunities/account/{accountSalesflareId}
Todas as oportunidades de uma empresa
GET
/api/opportunities/stages
Valores distintos de stage existentes na BD
GET
/api/opportunities/pipelines
Valores distintos de pipeline existentes na BD
Query Parameters — GET /api/opportunities
| Parâmetro | Tipo | Descrição |
page | integer | Número da página (padrão: 1) |
pageSize | integer | Registos por página (padrão: 50, máx: 500) |
limit novo | integer | Atalho para pageSize=N&page=1. Ex: limit=1 para top 1. Ignora page/pageSize. |
search | string | Pesquisa por nome, empresa, assignee ou owner |
ownerEmail novo | string | Filtra oportunidades onde o email aparece como owner, assignee, coldcaller, discovery caller ou closer |
tipoProduto | string | Produto: eletricidade ou gas (pesquisa parcial) |
naturezaVenda | string | Natureza da venda (pesquisa parcial) |
done | boolean | true = fechadas; false = abertas |
pipelineId | integer | Filtrar por pipeline |
stageId | integer | Filtrar por etapa |
assigneeId | integer | Filtrar por assignee (Salesflare ID) |
ownerId | integer | Filtrar por owner (Salesflare ID) |
accountSalesflareId | integer | Filtrar por empresa (Salesflare ID) |
orderBy novo | string | Campo para ordenar (ver tabela abaixo). Padrão: salesflareModificationDate |
orderDir novo | string | asc ou desc (padrão: desc) |
Valores aceites em orderBy
| orderBy | Campo ordenado |
value | Valor da oportunidade (€) |
calculatedValue | Valor calculado (€) |
probabilityProbabilidade de fecho (%) |
volumeConsumoAnualKwh | Consumo anual (kWh) |
consumoContratadoKwh | Consumo contratado (kWh) |
margemUnitaria | Margem unitária |
duracaoContratoMeses | Duração do contrato (meses) |
inicioFornecimento | Data início de fornecimento |
fimFornecimento | Data fim de fornecimento |
closeDate / startDate | Data de fecho / início previsto |
contractStartDate / contractEndDate | Datas do contrato |
lastInteractionDate | Data da última interação |
lastStageChangeDate | Data da última mudança de etapa |
salesflareCreationDate | Data de criação no Salesflare |
name / accountName | Ordenação alfabética |
| (outro ou omitido) | salesflareModificationDate desc (comportamento padrão) |
Exemplos de uso
## Todas as oportunidades de um comercial (em qualquer papel)
GET /api/opportunities?ownerEmail=joao@lugotech.pt
## Top 1 com maior consumo (objetivo de vendas)
GET /api/opportunities?ownerEmail=joao@lugotech.pt&orderBy=volumeConsumoAnualKwh&orderDir=desc&limit=1
## Contratos ordenados por duração crescente (gestão de prazos)
GET /api/opportunities?ownerEmail=joao@lugotech.pt&orderBy=duracaoContratoMeses&orderDir=asc
## Contratos a terminar primeiro (renovações)
GET /api/opportunities?ownerEmail=joao@lugotech.pt&orderBy=fimFornecimento&orderDir=asc&done=false
Principais Campos da Oportunidade
| Campo | Tipo | Descrição |
id | GUID | Identificador único interno |
salesflareId | integer | ID no Salesflare |
name | string | Nome do negócio |
value | decimal | Valor estimado (€) |
probability | integer | Probabilidade de fecho (%) |
done | boolean | Se está fechado (ganho ou perdido) |
tipoProduto | string | Produto: eletricidade ou gas |
naturezaVenda | string | Natureza da venda |
volumeConsumoAnualKwh | number | Volume de consumo anual (kWh) |
margemUnitaria | decimal | Margem unitária (€/MWh) |
duracaoContratoMeses | integer | Duração do contrato (meses) |
inicioFornecimento | datetime | Início do fornecimento |
fimFornecimento | datetime | Fim do fornecimento |
salesforceProposalId novo | string | Referência da proposta externa |
closeDate | datetime | Data de fecho prevista |
stage | object | { stageId, stageName, stageOrder, stageProbability } |
account | object | { accountId, accountName, accountSalesflareId } |
pipeline | object | { pipelineId, pipelineName } |
owner | object | { ownerId, ownerName, ownerEmail, ownerPicture } |
assignee | object | { assigneeId, assigneeName, assigneeEmail } |
lastInteraction | object | { lastInteractionDate, lastInteractionType, lastInteractionPersonName } |
Pipeline de Vendas: Use GET /api/opportunities/stages e GET /api/opportunities/pipelines para obter os IDs e nomes de etapas e pipelines disponíveis.
Comissões novo
Módulo de receção, cálculo e consulta de comissões. Recebe propostas da empresa parceira (motor de comissões externo), faz match com oportunidades do Salesflare e mantém o histórico completo.
Receber Proposta de Comissão
POST
/api/commissions/opportunity
Recebe os dados de uma proposta adjudicada. Faz match automático com oportunidade local via SalesforceProposalId (fallback: AccountName + data). Persiste em CommissionOpportunities + CommissionProducts + CommissionCpes.
curl -X POST https://api.lugo.tech/api/commissions/opportunity \
-H 'X-API-Key: sua_api_key_aqui' \
-H 'Content-Type: application/json' \
-d '{
"id": "REF-2026-001",
"account_name": "Empresa Exemplo Lda",
"distribution_mode": "SPLIT",
"coldcaller": "12345",
"closer": "67890",
"energy": {
"sale_type": "NEW",
"tariff_type": "BTN",
"unit_margin": 2.5,
"annual_volume": 85000,
"start_date": "2026-07-01",
"end_date": "2028-06-30"
},
"cpes": ["PT0001234567890123AB", "PT0009876543210987CD"]
}'
{
"commission_opportunity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "PENDING"
}
Registar Resultado de Comissão
PUT
/api/commissions/opportunity/{salesforceProposalId}/result
Recebe o valor calculado e o breakdown. Atualiza status para CALCULATED e depois faz push ao Salesflare (SYNCED).
curl -X PUT https://api.lugo.tech/api/commissions/opportunity/REF-2026-001/result \
-H 'X-API-Key: sua_api_key_aqui' \
-H 'Content-Type: application/json' \
-d '{
"commission_value": 1250.00,
"breakdown": { "coldcaller": 375.00, "closer": 875.00 }
}'
Consultar Comissão
GET
/api/commissions/opportunity/{salesforceProposalId}
GET
/api/commissions/opportunity/{salesforceProposalId}/cpes
Lista de CPEs/CUIs da proposta
Listar Comissões
GET
/api/commissions/opportunities?status=&page=&pageSize=
Filtros: status (PENDING, CALCULATED, SYNCED, ERROR), page, pageSize.
Resumo por Comercial
GET
/api/commissions/users/{salesflareUserId}/summary
Reconciliação Manual
POST
/api/commissions/opportunity/{id}/link
Liga manualmente uma proposta a uma oportunidade (quando match automático falhou)
curl -X POST https://api.lugo.tech/api/commissions/opportunity/REF-2026-001/link \
-H 'Authorization: Bearer eyJ...' \
-H 'Content-Type: application/json' \
-d '{"opportunity_id": "uuid-da-oportunidade-local"}'
Estados da Comissão (CommissionStatus)
| Status | Significado |
PENDING | Proposta recebida, aguarda cálculo ou match manual |
CALCULATED | Valor calculado recebido da motor de comissões externo |
SYNCED | Valor sincronizado para o Salesflare |
ERROR | Erro no processamento |
Modos de Distribuição (DistributionMode)
| Valor | Descrição |
INDIVIDUAL | Comissão para um único comercial |
SPLIT | Comissão dividida entre coldcaller e closer |
EXTERNAL_PARTNER | Parceiro externo |
Match automático proposta ↔ oportunidade:
1. Por SalesforceProposalId (campo da oportunidade)
2. Fallback automático por AccountName + data aproximada
3. Se não encontrar: status fica PENDING sem OpportunityId — aparece no frontend para resolução manual via /link
Timeline novo
Eventos de timeline (chamadas, emails, reuniões, tarefas) associados a uma empresa.
GET
/api/timeline?accountId={guid}&page=1&pageSize=30
Resumo da Timeline
GET
/api/timeline/summary?accountSalesflareId={id}
Totais por tipo e tarefas pendentes
{
"total": 47,
"byType": [
{ "type": "email", "count": 23 },
{ "type": "call", "count": 12 },
{ "type": "meeting", "count": 8 },
{ "type": "task", "count": 4 }
],
"pendingTasks": 2
}
Campos do Evento de Timeline
| Campo | Tipo | Descrição |
id | GUID | Identificador único |
type | string | Tipo: email, call, meeting, task, etc. |
subject | string | Assunto |
description | string | Descrição/corpo |
eventDate | datetime | Data do evento |
createdBy | object | Criado por: { createdByUserId, createdByName } |
task | object | Dados de tarefa: { taskDone, taskDueDate, taskAssigneeName } |
Sincronização novo
Endpoints para monitorizar e gerir a sincronização com o Salesflare.
Estado da Sincronização
GET
/api/sync/status
{
"timestamp": "2026-03-22T10:30:00Z",
"accounts": { "total": 549998, "synced": 549998, "syncPercentage": 100 },
"contacts": { "total": 15714, "synced": 15714, "syncPercentage": 100 },
"queue": { "pendingChanges": 0, "conflicts": 0, "duplicates": 3 },
"estimatedTimeRemaining": "0 minutos"
}
Disparar Sincronização de Oportunidades
POST
/api/sync/trigger-opportunities
Conflitos de Sincronização
GET
/api/sync/conflicts?page=1&pageSize=50
POST
/api/sync/resolve-conflict/{id}
Duplicados novo
Detete e resolva registos duplicados de empresas e contactos.
Listar Duplicados
GET
/api/duplicates?entityType=Account
entityType: Account ou Contact
[
{
"entityType": "Account",
"uniqueValue": "501262270",
"masterId": "id-da-empresa-principal",
"duplicates": ["id-duplicado-1", "id-duplicado-2"]
}
]
Resolver Duplicado
POST
/api/duplicates/resolve
curl -X POST https://api.lugo.tech/api/duplicates/resolve \
-H 'Authorization: Bearer eyJ...' \
-H 'Content-Type: application/json' \
-d '{"entityType":"Account","keepId":"id-a-manter","removeIds":["id-a-remover"]}'
Duplicados por NIF (Empresas)
GET
/api/accounts/cleanup/duplicates
Grupos de empresas com mesmo NIF
Auditoria novo
Registo automático de todas as criações, atualizações e eliminações de Accounts, Contacts e Oportunidades. Cada entrada inclui o diff campo a campo.
Listar Logs
GET
/api/audit
Query Parameters
| Parâmetro | Tipo | Descrição |
entityType | string | Account, Contact ou Opportunity |
entityId | GUID | ID da entidade específica |
userId | GUID | Filtrar por utilizador |
source | string | API, Sync ou Bulk |
action | string | Create, Update ou Delete |
from / to | datetime | Intervalo de datas |
page / pageSize | integer | Paginação |
GET
/api/audit/account/{id}
Todos os logs de uma empresa específica
GET
/api/audit/contact/{id}
Todos os logs de um contacto específico
Exemplo de resposta
{
"total": 3,
"items": [
{
"id": "...",
"entityType": "Account",
"entityId": "...",
"entityName": "Empresa Exemplo Lda",
"action": "Update",
"source": "API",
"userId": "...",
"userName": "João Silva",
"fieldsChangedJson": "{\"city\":{\"from\":\"Lisboa\",\"to\":\"Porto\"}}",
"createdAt": "2026-06-01T14:30:00Z"
}
]
}
Campos ignorados no diff: NeedsSyncToSalesflare, SyncedAt, UpdatedAt, JsonRaw — campos internos de controlo não são auditados.
Ambientes
A API está disponível em dois ambientes distintos. Cada um tem a sua base de dados separada e comportamentos diferentes nos workers de sincronização.
Produção
https://api.lugo.tech
• Base de dados real — dados de clientes reais
• Sincronização com Salesflare ativa (a cada 24h)
• Push de alterações ao Salesflare ativo (a cada 30s)
• Qualquer escrita (PUT/POST/DELETE) afeta dados reais
• Usar apenas em produção
Staging
https://staging.lugo.tech
• Base de dados de teste — separada da produção
• Sincronização com Salesflare desligada (StagingMode: true)
• Push ao Salesflare desligado — alterações não propagam
• Seguro para testar imports, endpoints e integrações
• Usar para desenvolvimento e testes
Como confirmar em que ambiente estás
O endpoint /health devolve o ambiente atual e o estado do modo staging:
curl https://api.lugo.tech/health
curl https://staging.lugo.tech/health
Resposta — Produção
{
"status": "ok",
"environment": "Production",
"stagingMode": false
}
Resposta — Staging
{
"status": "ok",
"environment": "Staging",
"stagingMode": true
}
Diferenças de comportamento
| Funcionalidade | Produção | Staging |
| Base de dados | BD de produção (real) | BD de staging (teste) |
| Sync Salesflare → BD (24h) | Ativo | Desligado |
| Push BD → Salesflare (30s) | Ativo | Desligado |
| Leitura de dados (GET) | Dados reais | Dados de teste |
| Escrita (PUT/POST/DELETE) | Afeta dados reais | Seguro para testar |
| Import CSV | Importa para produção | ✅ Seguro para validar mapeamentos |
| Comissões (POST /commissions) | Regista em produção | Seguro para testar integração |
Autenticação por ambiente
As API Keys e credenciais JWT são independentes por ambiente — uma key de staging não funciona em produção e vice-versa. Gere chaves separadas para cada ambiente.
## Staging — obter token
curl -X POST https://staging.lugo.tech/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"utilizador@empresa.pt","password":"password"}'
## Staging — testar import CSV sem afetar produção
curl -X POST https://staging.lugo.tech/api/accounts/import-csv \
-H 'Authorization: Bearer eyJ...' \
-F 'file=@teste.xlsx'
## Staging — testar endpoint de comissões
curl -X POST https://staging.lugo.tech/api/commissions/opportunity \
-H 'X-API-Key: key_de_staging' \
-H 'Content-Type: application/json' \
-d '{"id":"TEST-001", ...}'
Migrações de base de dados: Ambos os ambientes correm migrações EF Core automaticamente no arranque. Nunca é necessário correr migrações manualmente — e nunca apagam dados existentes (apenas adicionam).
Importação CSV novo
Importe empresas e contactos em massa a partir de ficheiros CSV ou Excel (.xlsx).
Importar Empresas
POST
/api/accounts/import-csv
curl -X POST https://api.lugo.tech/api/accounts/import-csv \
-H 'Authorization: Bearer eyJ...' \
-F 'file=@empresas.xlsx' \
-F 'userId=00000000-0000-0000-0000-000000000000' \
-F 'syncToSalesflare=true'
Importar Contactos
POST
/api/contacts/import-csv
Resposta da Importação
{
"success": true,
"stats": {
"totalProcessed": 1250,
"created": 980,
"updated": 265,
"errors": 5,
"syncQueued": true
},
"errors": [
"Linha 45: NIF inválido '12345'",
"Linha 102: Email duplicado"
]
}
Mapeamento de Colunas — Empresas
| Campo interno | Colunas aceites no CSV/Excel |
Nif | nif, nipc, nif_nipc, numero_fiscal, tax_number |
Name | name, nome, company, empresa, account, nome cliente |
City | city, cidade, localidade, municipio, distrito, distrito_da_instalacao, concelho_da_instalacao |
ElectricityConsumptionYearKwh | electricity_kwh, consumo_ee, consumo_eletricidade, consumo anual, consumo_anual_kwh |
TagsJson | tags, tag, etiquetas, etiqueta, label, labels |
SegmentName | segment, segmento, segment_name, tier |
Email | email, e-mail, email_address |
Phone | phone, telefone, telephone, tel |
Upsert por NIF: Se já existir uma empresa com o mesmo NIF, os dados são atualizados (não criado duplicado). O NIF é a chave única global de empresas.
Mapeamento de Colunas — Contactos
| Campo interno | Colunas aceites no CSV/Excel |
Firstname | firstname, primeiro_nome, first_name, nome_proprio, given_name |
Lastname | lastname, apelido, last_name, surname, sobrenome |
Email | email, e-mail, email_address, correio |
Phone | phone, telefone, tel, telephone, mobile, telemovel, celular |
Position | position, cargo, funcao, role, titulo, job_title |
Exemplos de Código
cURL
Obter accounts com filtros
curl -X GET 'https://api.lugo.tech/api/accounts?page=1&pageSize=100&segment=GOLD&minElectricityKwh=50000' \
-H 'X-API-Key: sua_api_key_aqui'
Obter contactos sem empresa
curl -X GET 'https://api.lugo.tech/api/contacts?noAccount=true&pageSize=100' \
-H 'X-API-Key: sua_api_key_aqui'
Obter contacts de uma empresa
curl -X GET 'https://api.lugo.tech/api/contacts?accountId=empresa_id' \
-H 'X-API-Key: sua_api_key_aqui'
Oportunidades abertas de eletricidade
curl -X GET 'https://api.lugo.tech/api/opportunities?tipoProduto=eletricidade&done=false' \
-H 'X-API-Key: sua_api_key_aqui'
Oportunidades de um comercial (todas as roles)
curl -X GET 'https://api.lugo.tech/api/opportunities?ownerEmail=joao@lugotech.pt&done=false' \
-H 'X-API-Key: sua_api_key_aqui'
Top 1 maior consumo de um comercial (objetivo de vendas)
curl -X GET 'https://api.lugo.tech/api/opportunities?ownerEmail=joao@lugotech.pt&orderBy=volumeConsumoAnualKwh&orderDir=desc&limit=1' \
-H 'X-API-Key: sua_api_key_aqui'
Comissões pendentes de match manual
curl -X GET 'https://api.lugo.tech/api/commissions/opportunities?status=PENDING' \
-H 'X-API-Key: sua_api_key_aqui'
Logs de auditoria de hoje
curl -X GET 'https://api.lugo.tech/api/audit?from=2026-06-02T00:00:00Z&source=API' \
-H 'X-API-Key: sua_api_key_aqui'
Python
import requests
API_KEY = 'sua_api_key_aqui'
USER_ID = '00000000-0000-0000-0000-000000000000'
BASE_URL = 'https://api.lugo.tech'
headers = {'X-API-Key': API_KEY}
# Obter accounts com filtros
response = requests.get(
f'{BASE_URL}/api/accounts',
headers=headers,
params={'page': 1, 'pageSize': 100, 'segment': 'GOLD', 'minElectricityKwh': 50000}
)
data = response.json()
print(f'Total: {data["total"]}')
# Exportar TODOS os accounts
all_accounts = []
page = 1
while True:
resp = requests.get(
f'{BASE_URL}/api/accounts',
headers=headers,
params={'page': page, 'pageSize': 1000}
)
data = resp.json()
all_accounts.extend(data['items'])
if len(all_accounts) >= data['total']:
break
page += 1
print(f'Total exportado: {len(all_accounts)}')
# Contactos sem empresa
orphans = requests.get(
f'{BASE_URL}/api/contacts',
headers=headers,
params={'noAccount': 'true', 'pageSize': 1000}
).json()
print(f'Contactos sem empresa: {orphans["total"]}')
JavaScript
const API_KEY = 'sua_api_key_aqui';
const BASE = 'https://api.lugo.tech';
async function apiGet(path, params = {}) {
const q = new URLSearchParams(params);
const res = await fetch(`${BASE}/api/${path}?${q}`, {
headers: { 'X-API-Key': API_KEY }
});
return res.json();
}
// Empresas GOLD com consumo > 50 000 kWh
const result = await apiGet('accounts', { segment: 'GOLD', minElectricityKwh: 50000, pageSize: 100 });
console.log(`Empresas GOLD: ${result.total}`);
// Tags (parsear tagsJson)
result.items.forEach(a => {
const tags = JSON.parse(a.tagsJson || '[]');
console.log(`${a.name}: ${tags.join(', ')}`);
});
// Estado da sincronização
const status = await apiGet('sync/status');
console.log(`Sync accounts: ${status.accounts.syncPercentage}%`);
C#
using System.Net.Http;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "sua_api_key_aqui");
// Obter accounts
var response = await client.GetAsync(
"https://api.lugo.tech/api/accounts?page=1&pageSize=100&segment=GOLD"
);
var json = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<AccountsResponse>(json);
Console.WriteLine($"Total: {data.Total}");
// Atualizar um account
var update = JsonSerializer.Serialize(new { city = "Porto", electricityConsumptionYearKwh = 95000 });
var content = new StringContent(update, System.Text.Encoding.UTF8, "application/json");
await client.PutAsync($"https://api.lugo.tech/api/accounts/{accountId}", content);
Suporte Técnico
Melhores Práticas
• Use pageSize=1000 para exports massivos
• Implemente cache local e atualize periodicamente
• Use retry logic para erros 429 e 5xx
• Campos JSON (tagsJson, etc) vêm como string — use JSON.parse()
• Use os filtros segment, tag, city para reduzir volume de dados
• Verifique /accounts/{id}/delete-check antes de eliminar empresas
Performance
Tempos médios de resposta:
• pageSize=50 → ~200-400ms
• pageSize=1000 → ~800-1500ms
Exportação massiva:
• 549.998 accounts → ~15-25 minutos
• 15.714 contacts → ~1-2 minutos
Importação CSV:
• 1.000 linhas → ~5-15 segundos
• 10.000 linhas → ~1-2 minutos
FAQ
Os dados são atualizados em tempo real?
A sincronização com o Salesflare ocorre automaticamente a cada 24 horas. Alterações feitas via PUT são sincronizadas imediatamente para o Salesflare.
Posso modificar dados através da API?
Sim. A API suporta operações completas: GET, PUT e DELETE para Accounts e Contacts. As alterações são sincronizadas automaticamente com o Salesflare.
Como funciona o upsert no import CSV?
O sistema usa o NIF como chave única para empresas. Se já existir uma empresa com o mesmo NIF, os dados são atualizados sem criar duplicados.
Qual o limite de requisições?
Não há limite fixo, mas recomendamos não exceder 60 requisições por minuto.
Como filtrar empresas com uma tag específica?
Use o parâmetro tag: /api/accounts?tag=Leads_Solar_18032026. A pesquisa é parcial — tag=Solar encontra todas as empresas com tags que contêm Solar.
Como obter contactos sem empresa?
Use noAccount=true: /api/contacts?noAccount=true.