sass-imobiliaria/specs/028-trabalhe-conosco/contracts/jobs-api.md
MatheusAlves96 cf5603243c
Some checks failed
CI/CD → Deploy via SSH / Build & Push Docker Images (push) Successful in 1m0s
CI/CD → Deploy via SSH / Deploy via SSH (push) Successful in 4m35s
CI/CD → Deploy via SSH / Validate HTTPS & Endpoints (push) Failing after 46s
feat: features 025-032 - favoritos, contatos, trabalhe-conosco, area-cliente, navbar, hero-light-dark, performance-homepage
- feat(025): favoritos locais com FavoritesContext, HeartButton, PublicFavoritesPage
- feat(026): central de contatos admin (leads/contatos unificados)
- feat(027): configuração da página de contato via admin
- feat(028): trabalhe conosco - candidaturas com upload e admin
- feat(029): UX área do cliente - visitas, comparação, perfil
- feat(030): navbar UX - menu mobile, ThemeToggle, useFavorites
- feat(031): hero light/dark - imagens separadas por tema, upload, preview, seed
- feat(032): performance homepage - Promise.all parallel fetches, sessionStorage cache,
  preload hero image, loading=lazy nos cards, useInView hook, will-change carrossel,
  keyframes em index.css, AgentsCarousel e HomeScrollScene via props
- fix: light mode HomeScrollScene - gradiente, cores de texto, scroll hint

migrations: g1h2i3j4k5l6 (source em leads), h1i2j3k4l5m6 (contact_config),
            i1j2k3l4m5n6 (job_applications), j2k3l4m5n6o7 (hero theme images)
2026-04-22 22:35:17 -03:00

6.5 KiB
Raw Blame History

API Contracts: Trabalhe Conosco

Feature: 028-trabalhe-conosco
Phase: 1 — Design & Contracts
Base URL: /api/v1


Endpoints

Método Path Auth Descrição
POST /jobs/apply Nenhuma Submeter candidatura (público)
GET /admin/jobs @require_admin (JWT) Listar candidaturas paginadas (admin)

POST /api/v1/jobs/apply

Endpoint público. Recebe os dados textuais da candidatura e persiste na tabela job_applications.

Request

Headers

Content-Type: application/json

Body (JSON)

Campo Tipo Obrigatório Validações
name string Sim Não pode ser vazio ou apenas espaços; strip aplicado
email string Sim Formato de e-mail válido (RFC-5321 via pydantic.EmailStr)
phone string Não Qualquer string; sem validação de formato nesta versão
role_interest string Sim Deve ser exatamente um de: "Corretor(a)", "Assistente Administrativo", "Estagiário(a)", "Outro"
message string Sim Não pode ser vazio; máximo 5000 caracteres
file_name string Não Nome do arquivo de currículo; sem conteúdo binário

Exemplo de request

{
  "name": "Ana Lima",
  "email": "ana.lima@email.com",
  "phone": "(11) 98765-4321",
  "role_interest": "Corretor(a)",
  "message": "Tenho 5 anos de experiência no mercado imobiliário e gostaria de integrar a equipe.",
  "file_name": "curriculo-ana-lima.pdf"
}

Responses

201 Created — Candidatura registrada com sucesso

{
  "message": "Candidatura recebida com sucesso"
}

422 Unprocessable Entity — Dados inválidos

{
  "error": "Dados inválidos",
  "details": [
    {
      "type": "value_error",
      "loc": ["role_interest"],
      "msg": "Value error, role_interest deve ser um de: Assistente Administrativo, Corretor(a), Estagiário(a), Outro",
      "input": "Diretor",
      "url": "https://errors.pydantic.dev/..."
    }
  ]
}

400 Bad Request — Body ausente ou não é JSON válido

{
  "error": "Dados inválidos",
  "details": [...]
}

GET /api/v1/admin/jobs

Endpoint protegido. Retorna listagem paginada de todas as candidaturas em ordem decrescente de created_at.

Request

Headers

Authorization: Bearer <jwt_token>
Content-Type: application/json

Query Parameters

Parâmetro Tipo Default Restrições Descrição
page integer 1 ≥ 1 Número da página
per_page integer 20 1 100 Registros por página

Exemplo de request

GET /api/v1/admin/jobs?page=1&per_page=20

Responses

200 OK — Lista retornada com sucesso

{
  "items": [
    {
      "id": 7,
      "name": "Ana Lima",
      "email": "ana.lima@email.com",
      "phone": "(11) 98765-4321",
      "role_interest": "Corretor(a)",
      "message": "Tenho 5 anos de experiência no mercado imobiliário...",
      "file_name": "curriculo-ana-lima.pdf",
      "status": "pending",
      "created_at": "2026-04-21T14:35:00"
    },
    {
      "id": 6,
      "name": "Carlos Souza",
      "email": "carlos@email.com",
      "phone": null,
      "role_interest": "Estagiário(a)",
      "message": "Estudante de Administração em busca do primeiro emprego.",
      "file_name": null,
      "status": "pending",
      "created_at": "2026-04-20T09:12:00"
    }
  ],
  "total": 42,
  "page": 1,
  "per_page": 20,
  "pages": 3
}

Schema do item (JobApplicationOut)

Campo Tipo Nullable Descrição
id integer Não Identificador único
name string Não Nome completo do candidato
email string Não E-mail do candidato
phone string | null Sim Telefone (opcional)
role_interest string Não Cargo de interesse selecionado
message string Não Mensagem/apresentação
file_name string | null Sim Nome do arquivo de currículo
status string Não Estado: "pending" (padrão)
created_at string (ISO 8601) Não Data/hora do envio

Schema de paginação

Campo Tipo Descrição
total integer Total de candidaturas no sistema
page integer Página atual
per_page integer Registros retornados nesta página
pages integer Total de páginas

200 OK — Nenhuma candidatura registrada

{
  "items": [],
  "total": 0,
  "page": 1,
  "per_page": 20,
  "pages": 0
}

401 Unauthorized — Token ausente ou inválido

{
  "error": "Token inválido ou ausente"
}

403 Forbidden — Usuário autenticado sem permissão de admin

{
  "error": "Acesso negado"
}

Notas de Implementação

  • O endpoint POST /api/v1/jobs/apply não possui autenticação — qualquer cliente pode submeter.
  • O endpoint GET /api/v1/admin/jobs usa o decorator @require_admin já existente no projeto, que valida o JWT e verifica a flag de administrador.
  • O campo created_at é serializado pelo Pydantic como ISO 8601 sem timezone (TIMESTAMP WITHOUT TIME ZONE no PostgreSQL).
  • per_page deve ser limitado a 100 no backend para evitar queries excessivamente grandes.
  • Campos ausentes no body do POST são tratados pelo Pydantic: obrigatórios geram erro 422, opcionais recebem None.