# 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** ```json { "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 ```json { "message": "Candidatura recebida com sucesso" } ``` #### 422 Unprocessable Entity — Dados inválidos ```json { "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 ```json { "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 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 ```json { "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 ```json { "items": [], "total": 0, "page": 1, "per_page": 20, "pages": 0 } ``` #### 401 Unauthorized — Token ausente ou inválido ```json { "error": "Token inválido ou ausente" } ``` #### 403 Forbidden — Usuário autenticado sem permissão de admin ```json { "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`.