- 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)
6.5 KiB
6.5 KiB
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/applynão possui autenticação — qualquer cliente pode submeter. - O endpoint
GET /api/v1/admin/jobsusa o decorator@require_adminjá 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 ZONEno PostgreSQL). per_pagedeve 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.