SACO Admin API

Documentação dos endpoints. Base: http://localhost:3001

Health

GET /api/health

Response 200

{
  "ok": true,
  "ts": "2025-02-18T12:00:00.000Z"
}

Company documents

GET /api/company/documents

Response 200 — array of company document types

[
  {
    "id": "string",
    "name": "string",
    "description": "string",
    "required": true,
    "maxUploads": 1,
    "evaluationCriteria": "string",
    "legalObligation": "string"
  }
]

Worker base documents (list all or filter)

GET /api/workers/documents ?scope=national|international|temporary&search=...

Response 200 — array of worker base document types

[
  {
    "id": "string",
    "name": "string",
    "description": "string",
    "scope": "national",
    "required": true,
    "maxUploads": 1,
    "evaluationCriteria": "string",
    "legalObligation": "string"
  }
]

Worker base documents by type (select one type)

GET /api/workers/documents/base/:type

Response 200 — array of base documents for that type only

[
  {
    "id": "string",
    "name": "string",
    "description": "string",
    "scope": "national",
    "required": true,
    "maxUploads": 1,
    "evaluationCriteria": "string",
    "legalObligation": "string"
  }
]

Response 400 — invalid type

{
  "error": "Invalid type. Use national, international, or temporary.",
  "type": null
}

Specializations (list)

GET /api/workers/specializations ?search=...&page=1&limit=20

Response 200 — paginated list

{
  "items": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "defaultMaxUploads": 1,
      "documentCount": 5,
      "createdAt": "2025-02-18T12:00:00.000Z"
    }
  ],
  "total": 10,
  "page": 1,
  "limit": 20,
  "totalPages": 1
}

Specialization by ID (with documents)

GET /api/workers/specializations/:id

Response 200

{
  "id": "string",
  "name": "string",
  "description": "string",
  "defaultMaxUploads": 1,
  "createdAt": "2025-02-18T12:00:00.000Z",
  "documents": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "required": true,
      "maxUploads": 1,
      "evaluationCriteria": "string",
      "legalObligation": "string"
    }
  ]
}

Specialization documents (by specialization)

GET /api/workers/specializations/:id/documents

Response 200 — array of document types for that specialization

[
  {
    "id": "string",
    "name": "string",
    "description": "string",
    "required": true,
    "maxUploads": 1,
    "evaluationCriteria": "string",
    "legalObligation": "string"
  }
]

Equipamentos (API pública)

Lista de tipos de equipamento e respetivos documentos e habilitações associadas. Leitura apenas.

GET /api/public/equipment/types ?search=...&page=1&limit=20

Response 200 — lista paginada de tipos de equipamento

{
  "items": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "defaultMaxUploads": 1,
      "isAutomobile": false,
      "documentCount": 2,
      "specializationDocumentCount": 1,
      "totalDocumentCount": 3,
      "specializationIds": ["sseed-11"],
      "specializationNames": ["Montagem / Desmontagem de Andaimes"],
      "createdAt": "2026-03-19T12:00:00.000Z"
    }
  ],
  "total": 37,
  "page": 1,
  "limit": 20,
  "totalPages": 2
}

Cada item: isAutomobile indica se o tipo é automóvel; documentCount = documentos próprios do tipo; specializationDocumentCount = documentos das habilitações associadas; totalDocumentCount = soma; specializationIds / specializationNames = habilitações ligadas a este tipo.

GET /api/public/equipment/types/:id

Response 200 — tipo de equipamento com documentos próprios e habilitações (cada uma com os seus documentos)

{
  "id": "string",
  "name": "string",
  "description": "string",
  "defaultMaxUploads": 1,
  "isAutomobile": false,
  "createdAt": "string",
  "documents": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "required": true,
      "maxUploads": 1,
      "evaluationCriteria": "string",
      "legalObligation": "string"
    }
  ],
  "specializations": [
    {
      "id": "string",
      "name": "string",
      "documents": [
        {
          "id": "string",
          "name": "string",
          "description": "string",
          "required": true,
          "maxUploads": 1,
          "evaluationCriteria": "string",
          "legalObligation": "string"
        }
      ]
    }
  ]
}

Response 404 — tipo não encontrado

Solicitações de tipos de equipamento (API pública)

Pedir um novo tipo de equipamento e listar os pedidos da companhia (estado e mensagem de rejeição quando aplicável). O admin aceita ou rejeita no backoffice (separador Solicitações em Equipamentos).

POST /api/public/equipment/type-requests

Criar pedido. Body: company_slug?, company_email?, name (obrigatório), title?, description?, allowed_documents_description?. Response 201.

GET /api/public/equipment/type-requests ?company_slug=... | ?company_email=... & page=1&limit=20

Listar pedidos da companhia (obrigatório company_slug ou company_email). Response 200: { items, total, page, limit, totalPages }. Cada item: mesmo formato que solicitações de habilitação (id, companySlug, companyEmail, name, title, description, allowedDocumentsDescription, status, rejectionMessage?, createdAt, updatedAt).

Specialization requests (solicitações de habilitações)

Solicitar nova habilitação e listar as solicitações da companhia (com estado e mensagem de rejeição quando aplicável).

POST /api/public/specialization-requests

Solicitar nova habilitação (criar pedido). Body: company_slug?, company_email?, name (obrigatório), title?, description?, allowed_documents_description?. Response 201.

GET /api/public/specialization-requests ?company_slug=... | ?company_email=... & page=1&limit=20

Listar solicitações da companhia (obrigatório company_slug ou company_email). Response 200: { items, total, page, limit, totalPages }. Cada item: id, companySlug, companyEmail, name, title, description, allowedDocumentsDescription, status (pending|accepted|rejected), rejectionMessage? (se rejeitada), createdAt, updatedAt.

Empresas

Há um único conceito de empresa. As capacidades são definidas pelo subscription_plan: basic, intermediate ou advanced. Ao criar ou convidar uma empresa, o plano é sempre basic por defeito.

Planos de subscrição

GET /api/companies ?page=1&limit=20&search=...

Response 200 — lista paginada. Cada item inclui subscription_plan, slug e receiving_contract_invites (boolean).

{
  "items": [
    {
      "id": "string",
      "name": "string",
      "logo": "string",
      "nif": "string",
      "email": "string",
      "subscription_plan": "basic | intermediate | advanced",
      "slug": "string",
      "receiving_contract_invites": true,
      "createdAt": "2025-02-18T12:00:00.000Z"
    }
  ],
  "total": 25,
  "page": 1,
  "limit": 20,
  "totalPages": 2
}
GET /api/public/companies ?page=1&limit=20&search=...

API pública — mesma forma que GET /api/companies (lista paginada; subscription_plan, slug, receiving_contract_invites).

GET /api/public/companies/by-slug/:slug

Obter uma empresa pelo slug. Response 200 com o objeto empresa (inclui receiving_contract_invites). 404 se não existir.

GET /api/public/companies/:id

Obter uma empresa pelo id. Response 200 com o objeto empresa (inclui receiving_contract_invites). 404 se não existir. Usado pelo Prototic para /me, companyPlan, etc.

POST /api/public/companies

Criar empresa (registo público). Body: { "name": "obrigatório", "nif?", "email?" }. Plano é sempre basic. slug gerado a partir do nome (único). receiving_contract_invites fica true por defeito. Response 201.

{
  "id": "co-...",
  "name": "string",
  "logo": "",
  "nif": "string",
  "email": "string",
  "subscription_plan": "basic",
  "slug": "string",
  "receiving_contract_invites": true,
  "createdAt": "2025-02-18T12:00:00.000Z"
}
POST /api/companies

Criar empresa (admin). Body: { "name": "obrigatório", "logo?", "nif?", "email?", "subscription_plan?" }. Plano default basic. slug gerado a partir do nome.

PUT /api/companies/update

Atualizar empresa. Identificação por um de: lookupId, lookupEmail ou lookupSlug. Campos opcionais a atualizar: name, nif, email, subscription_plan, receiving_contract_invites (boolean, sim/não para receber convites de contrato). O logo não é atualizável por este endpoint. Se name for alterado, o slug é recalculado (único).

Response 200 — objeto empresa atualizado (inclui receiving_contract_invites). 404 se não encontrada. 400 se nenhum identificador for enviado.

{
  "id": "string",
  "name": "string",
  "logo": "string",
  "nif": "string",
  "email": "string",
  "subscription_plan": "basic | intermediate | advanced",
  "slug": "string",
  "receiving_contract_invites": true,
  "createdAt": "2025-02-18T12:00:00.000Z"
}
POST /api/companies/invite-company

Convidar empresa — enviar convite por email. Body: { "responsableEmail": "email@empresa.pt" }. No registo, a empresa fica com plano basic.

{
  "id": "inv-...",
  "message": "An email will be sent to the responsable with a link and instructions to complete registration.",
  "responsableEmail": "email@empresa.pt"
}
POST /api/companies/seed

Inserir dados exemplares (empresas com emails @example.com; planos distribuídos). Response 201: { "count": N, "message": "N empresas inseridas." }

DELETE /api/companies/all

Apagar todas as empresas. Response 204.

DELETE /api/companies/:id

Apagar uma empresa. Response 204 ou 404.

SACO Admin API · Documentação