Hakela Investments — API REST

Sistema de Gestão de Água · Moçambique · v1.0

Base URL: https://apihakela.brilliantmind.co.mz/api Auth: Sanctum Bearer Token Tenant: X-Operator-Id header Formato: JSON

Autenticação & 2FA

POST /api/auth/login Login

Request Body / Params

{"email":"admin@aguas.mz","password":"changeme123!"}

Response (200/201)

{"token":"1|abc...","user":{...}}
POST /api/auth/logout Logout (requer token)

Request Body / Params

{}

Response (200/201)

{"message":"Sessão encerrada com sucesso."}
GET /api/auth/me Utilizador actual (requer token)

Response (200/201)

{"id":"...","name":"...","roles":[...]}
POST /api/auth/forgot-password Iniciar recuperação de senha

Request Body / Params

{"email":"user@aguas.mz"}

Response (200/201)

{"message":"Código enviado.","expires_in":600}
POST /api/auth/reset-password Redefinir senha com OTP

Request Body / Params

{"email":"user@aguas.mz","otp":"123456","password":"newpass!","password_confirmation":"newpass!"}

Response (200/201)

{"token":"1|abc...","user":{...}}
POST /api/auth/otp/resend Reenviar OTP

Request Body / Params

{"email":"user@aguas.mz","purpose":"password_reset"}

Response (200/201)

{"message":"OTP reenviado.","wait_seconds":0,"resends_left":4}
POST /api/auth/2fa/verify-login Verificar 2FA no login

Request Body / Params

{"challenge_token":"tok...","code":"123456"}

Response (200/201)

{"token":"1|abc...","user":{...}}
GET /api/auth/2fa Estado 2FA (requer token)

Response (200/201)

{"enabled":false,"channel":"email"}
POST /api/auth/2fa/enable Iniciar activação 2FA (requer token)

Request Body / Params

{}

Response (200/201)

{"message":"OTP enviado."}
POST /api/auth/2fa/confirm Confirmar activação 2FA (requer token)

Request Body / Params

{"code":"123456"}

Response (200/201)

{"message":"2FA activado."}
POST /api/auth/2fa/disable Desactivar 2FA (requer token)

Request Body / Params

{"password":"mypassword"}

Response (200/201)

{"message":"2FA desactivado."}
PUT /api/auth/2fa/channel Alterar canal 2FA (requer token)

Request Body / Params

{"channel":"sms"}

Response (200/201)

{"message":"Canal actualizado."}

Utilizadores

GET /api/users Listar utilizadores

Request Body / Params

?search=joão&per_page=20

Response (200/201)

{"data":[...],"meta":{...}}
POST /api/users Criar utilizador

Request Body / Params

{"name":"João","email":"joao@aguas.mz","password":"pass!","password_confirmation":"pass!"}

Response (200/201)

{"id":"...","name":"João",...}
GET /api/users/{id} Ver utilizador

Response (200/201)

{"id":"...","name":"João","roles":[...]}
PUT /api/users/{id} Actualizar utilizador

Request Body / Params

{"name":"João Silva"}

Response (200/201)

{"id":"...","name":"João Silva"}
PUT /api/users/{id}/password Alterar senha

Request Body / Params

{"password":"newpass!","password_confirmation":"newpass!"}

Response (200/201)

{"message":"Palavra-passe actualizada."}
DELETE /api/users/{id} Remover utilizador

Response (200/201)

HTTP 204

Roles & Permissões

Hierarquia de roles: super_admin (100) › admin_national (80) › admin_provincial (60) › admin_operator (40) › operator_staff (20) › technician (10) › consumer (5). Nenhum utilizador pode atribuir ou revogar roles de nível igual ou superior ao seu.
GET /api/roles Listar todos os roles

Response (200/201)

[{"slug":"super_admin","label":"...","level":100},...]
GET /api/users/{id}/roles Listar roles de utilizador

Response (200/201)

[{"slug":"technician","operator_id":"..."}]
POST /api/users/{id}/roles Atribuir role

Request Body / Params

{"role_slug":"technician","operator_id":"op_id","expires_at":"2026-12-31"}

Response (200/201)

{"message":"Role atribuído."}
DELETE /api/users/{id}/roles Revogar role

Request Body / Params

{"role_slug":"technician","operator_id":"op_id"}

Response (200/201)

{"message":"Role revogado."}

Consumidores

GET /api/consumers Listar consumidores

Request Body / Params

?search=João&type=individual&per_page=20

Response (200/201)

{"data":[...],"meta":{...}}
POST /api/consumers/individual Registar consumidor individual

Request Body / Params

{"name":"João","surname":"Silva","email":"joao@gmail.com","phone_number":"+258841234567","nuit":"123456789","address":{...}}

Response (200/201)

{"id":"...","type":"individual"}
POST /api/consumers/corporate Registar empresa

Request Body / Params

{"company_name":"Empresa Lda","nuit":"987654321","phone_number":"+258841234567","address":{...}}

Response (200/201)

{"id":"...","type":"corporate"}
GET /api/consumers/{id} Ver consumidor

Response (200/201)

{"id":"...","type":"individual","consumerable":{...},"contracts":[...]}
PUT /api/consumers/{id} Actualizar consumidor

Request Body / Params

{"email":"newemail@gmail.com"}

Response (200/201)

{"id":"..."}
POST /api/consumers/{id}/suspend Suspender consumidor

Request Body / Params

{"reason":"Dívida em atraso"}

Response (200/201)

{"message":"Consumidor suspenso."}
POST /api/consumers/{id}/reactivate Reactivar consumidor

Request Body / Params

{}

Response (200/201)

{"message":"Consumidor reactivado."}

Contratos

GET /api/contracts Listar contratos

Request Body / Params

?status=active&consumer_id=...

Response (200/201)

{"data":[...],"meta":{...}}
POST /api/contracts Criar contrato

Request Body / Params

{"consumer_id":"...","operator_id":"...","operational_zone_id":"...","service_type":"domestic","connection_address":{...}}

Response (200/201)

{"id":"...","contract_number":"CTR-2026-0001"}
GET /api/contracts/{id} Ver contrato

Response (200/201)

{"id":"...","status":"active","meter":{...},"invoices":[...]}
PUT /api/contracts/{id} Actualizar contrato

Request Body / Params

{"operational_zone_id":"..."}

Response (200/201)

{"id":"..."}
POST /api/contracts/{id}/suspend Suspender contrato

Request Body / Params

{"reason":"Fatura em atraso"}

Response (200/201)

{"message":"Contrato suspenso."}
POST /api/contracts/{id}/terminate Encerrar contrato

Request Body / Params

{"reason":"Pedido do consumidor"}

Response (200/201)

{"message":"Contrato encerrado."}

Leituras de Contador

Validação de zona: O técnico só pode registar leituras nas zonas às quais está alocado (ver Técnicos › Alocações de Zona). A validação usa as alocações dinâmicas activas na data da leitura.
GET /api/readings Listar leituras

Request Body / Params

?contract_id=...&technician_id=...&requires_review=1&per_page=20

Response (200/201)

{"data":[...],"meta":{...}}
POST /api/readings Registar leitura

Request Body / Params

{"contract_id":"...","technician_id":"...","reading_value":125.5,"read_at":"2026-03-28T10:00:00Z","gps_lat":-25.96,"gps_lng":32.58,"photo_path":null,"notes":"Leitura normal"}

Response (200/201)

{"id":"...","reading_value":125.5,"consumption_m3":8.2}
GET /api/readings/{id} Ver leitura

Response (200/201)

{"id":"...","type":"real","reading_value":125.5,"consumption_m3":8.2,"technician":{...}}
PUT /api/readings/{id} Corrigir leitura (motivo obrigatório)

Request Body / Params

{"reading_value":126.0,"correction_reason":"Erro de transcrição — valor lido foi 126.0 m³ e não 125.5","notes":"Correcção após validação no campo"}

Response (200/201)

{"id":"...","reading_value":126.0,"corrected_at":"..."}
POST /api/readings/{id}/reviewed Marcar como revista

Request Body / Params

{"notes":"Verificado — consumo justificado por enchimento de piscina"}

Response (200/201)

{"message":"Leitura marcada como revista."}

Técnicos & Alocações de Zona

Alocações dinâmicas: Um técnico pode trabalhar em diferentes zonas ao longo do tempo. A alocação define valid_from e valid_until (null = sem data de fim). Se existirem alocações dinâmicas, a zona estática do registo do técnico é ignorada.
GET /api/technicians Listar técnicos

Request Body / Params

?operator_id=...&status=active

Response (200/201)

{"data":[...],"meta":{...}}
POST /api/technicians Criar técnico

Request Body / Params

{"user_id":"...","operator_id":"...","operational_zone_id":"...","employee_number":"TEC-001","joined_at":"2026-01-01"}

Response (200/201)

{"id":"...","employee_number":"TEC-001"}
GET /api/technicians/{id} Ver técnico

Response (200/201)

{"id":"...","user":{...},"operationalZone":{...}}
POST /api/technicians/{id}/suspend Suspender técnico

Request Body / Params

{}

Response (200/201)

{"message":"Técnico suspenso."}
POST /api/technicians/{id}/reactivate Reactivar técnico

Request Body / Params

{}

Response (200/201)

{"message":"Técnico reactivado."}
GET /api/technicians/{id}/performance Ver desempenho

Response (200/201)

{"period":"2026-03","score":87.5,"operator_rank":2}
GET /api/technicians/{id}/zones Listar alocações de zona

Request Body / Params

?active_only=1

Response (200/201)

[{"id":"...","zone":{"name":"Zona 1 - Polana"},"valid_from":"2026-01-01","valid_until":null,"is_active":true}]
POST /api/technicians/{id}/zones Criar alocação de zona

Request Body / Params

{"operational_zone_id":"...","valid_from":"2026-04-01","valid_until":"2026-06-30","notes":"Substituição temporária"}

Response (200/201)

{"message":"Alocação criada.","allocation":{...}}
PUT /api/technicians/{id}/zones/{allocation} Actualizar alocação

Request Body / Params

{"valid_until":"2026-05-31","notes":"Extensão do período"}

Response (200/201)

{"message":"Alocação actualizada."}
DELETE /api/technicians/{id}/zones/{allocation} Encerrar alocação (valid_until = hoje)

Response (200/201)

{"message":"Alocação encerrada."}

Faturas

GET /api/invoices Listar faturas

Request Body / Params

?contract_id=...&status=overdue&per_page=20

Response (200/201)

{"data":[...],"meta":{...}}
GET /api/invoices/{id} Ver fatura

Response (200/201)

{"id":"...","total_amount":450.00,"status":"pending","line_items":[...]}
POST /api/invoices/{id}/cancel Cancelar fatura

Request Body / Params

{"reason":"Leitura incorrecta — corrigida"}

Response (200/201)

{"message":"Fatura cancelada."}

Pagamentos

GET /api/payments Listar pagamentos

Request Body / Params

?status=confirmed&invoice_id=...

Response (200/201)

{"data":[...],"meta":{...}}
POST /api/payments Registar pagamento

Request Body / Params

{"invoice_id":"...","amount":450.00,"payment_method":"mpesa","reference":"M-PESA-REF-12345"}

Response (200/201)

{"id":"...","status":"pending"}
POST /api/payments/{id}/confirm Confirmar pagamento

Request Body / Params

{}

Response (200/201)

{"message":"Pagamento confirmado."}
POST /api/payments/{id}/reverse Estornar pagamento

Request Body / Params

{"reason":"Pagamento duplicado"}

Response (200/201)

{"message":"Estorno processado."}

KPIs & Relatórios

Veja exemplos completos de consumo dos KPIs em Vue.js e Flutter. Relatórios automáticos são enviados por email (diário às 22:00, semanal às 2ªf 07:00, mensal dia 2, anual 2 Jan).
GET /api/kpi/summary Resumo geral do operador

Request Body / Params

?period=2026-03

Response (200/201)

{"total_consumers":1250,"active_contracts":980,"revenue_mtd":450000,"collection_rate":87.5}
GET /api/kpi/revenue Receita por período

Request Body / Params

?from=2026-01-01&to=2026-03-31&group_by=month

Response (200/201)

{"data":[{"period":"2026-01","amount":145000},...],"total":435000}
GET /api/kpi/readings KPI de leituras

Request Body / Params

?period=2026-03

Response (200/201)

{"total":980,"real":910,"estimated":70,"coverage_rate":92.8}
GET /api/kpi/technicians Ranking de técnicos

Request Body / Params

?period=2026-03

Response (200/201)

[{"rank":1,"name":"António","score":95.2,"readings":142}]
GET /api/kpi/contracts Evolução de contratos

Request Body / Params

?from=2026-01-01&to=2026-03-31

Response (200/201)

{"new":45,"suspended":12,"terminated":3,"active_total":980}
POST /api/kpi/report/send Enviar relatório por email

Request Body / Params

{"period":"daily","email":"gestor@aguas.mz"}

Response (200/201)

{"message":"Relatório enviado."}

Auditoria

GET /api/audit Listar eventos de auditoria

Request Body / Params

?user_id=...&event=payment.confirmed&from=2026-03-01&per_page=50

Response (200/201)

{"data":[{"event":"...","user":{},"metadata":{},"created_at":"..."}],"meta":{...}}

Códigos de Resposta

CódigoSignificadoExemplo
200 OK Pedido processado com sucesso
201 Created Recurso criado (POST)
204 No Content Eliminação bem-sucedida (DELETE)
401 Unauthorized Token em falta ou inválido
403 Forbidden Sem permissão (hierarquia ou tenant)
404 Not Found Recurso não encontrado
422 Unprocessable Validação falhou (ver campo "errors")
423 Locked Conta bloqueada por tentativas falhadas
429 Too Many Requests Rate limit excedido
500 Server Error Erro interno — contacte suporte