Super Admin
Endpoints sob/api/admin/*. Todos protegidos por auth:api + middleware super_admin (checa user.is_super_admin = true).
Super admins ignoram os scopes BelongsToTenant/BelongsToTenantAndUnit, podendo consultar e modificar dados de qualquer clínica.
Respostas de erro padrão:
| Status | Quando |
|---|---|
| 401 | JWT inválido ou ausente |
| 403 | Usuário autenticado mas não é super admin ({"error": "Forbidden"}) |
| 404 | Recurso não encontrado (via findOrFail) |
| 422 | Validação |
Tenants (Clínicas)
GET /api/admin/tenants
Lista paginada de todas as clínicas.Query params
Busca
ILIKE em name e slug. Máximo 255 caracteres.Response 200
Paginação de 50 itens por página:POST /api/admin/tenants
Cria uma nova clínica.Request body
Nome da clínica. Máximo 255 caracteres.
Slug único (normalizado via
Str::slug() — lowercase, hífens). Máximo 255 caracteres.Se a clínica nasce ativa.
Response 201
Erros
| Status | Situação |
|---|---|
| 422 | Nome duplicado (comparação case-insensitive: "Já existe uma clínica com esse nome") ou slug não único |
PATCH /api/admin/tenants//status
Ativa ou desativa uma clínica. Efeito cascata crítico.Request body
Novo estado da clínica.
Response 200
ObjetoTenant atualizado.
Efeitos colaterais
Erros
| Status | Situação |
|---|---|
| 404 | Tenant não encontrado |
Units (Unidades)
GET /api/admin/units
Lista paginada de unidades de todas as clínicas.Query params
Filtra por clínica.
Busca
ILIKE em name e slug.Response 200
Paginação de 50 itens por página:POST /api/admin/units
Cria uma unidade vinculada a uma clínica.Request body
FK para a clínica.
Nome da unidade. Máximo 255 caracteres. Unicidade case-insensitive dentro da mesma clínica.
Slug único global. Máximo 255 caracteres.
Response 201
ObjetoUnit criado.
Erros
| Status | Situação |
|---|---|
| 404 | tenant_id não encontrado |
| 422 | Nome duplicado na mesma clínica ("Já existe uma unidade com esse nome para esta clínica") ou slug não único |
Modalities (Aparelhos DICOM)
GET /api/admin/modalities
Lista paginada de aparelhos DICOM cadastrados.Query params
Filtra por clínica.
Filtra por unidade.
Busca
ILIKE em name e aet.Response 200
Paginação de 50 itens por página:POST /api/admin/modalities
Cadastra um aparelho DICOM. Oaet (AETitle) é usado pelo webhook do Orthanc para identificar a origem das imagens e resolver o tenant/unit.
Request body
FK para a clínica.
FK para a unidade. Deve pertencer ao
tenant_id informado.Nome descritivo. Máximo 255 caracteres.
Application Entity Title. Normalizado para maiúsculas (
strtoupper(trim())). Único globalmente — o webhook usa isso pra resolver o tenant.IP fixo do aparelho no túnel WireGuard da clínica. Validado como IP (v4 ou v6).
Response 201
ObjetoModality criado.
Erros
| Status | Situação |
|---|---|
| 404 | tenant_id ou unit_id não encontrado, ou unit_id não pertence ao tenant |
| 422 | Nome duplicado na mesma unidade, ou aet já existe em outra clínica |
Users
GET /api/admin/users
Lista paginada de todos os usuários da plataforma, comtenant e unit via eager load.
Query params
Filtra por clínica.
Filtra por unidade.
Busca
ILIKE em name e email.Response 200
Paginação de 50 itens por página:signature (base64, pesado) é hidden — só é exposto o booleano has_signature. O password nunca é exposto.
POST /api/admin/users
Cria um usuário.Request body
FK para a clínica.
FK para a unidade. Deve pertencer ao
tenant_id informado.Nome. Máximo 255 caracteres.
Normalizado para lowercase. Único globalmente.
Mínimo 8 caracteres. Hasheada automaticamente pelo cast do model. Sem confirmation no body.
Formato
NNNN/SS a NNNNNN/SS (4-6 dígitos + / + 2 letras maiúsculas). Ex.: 123456/SP.Data URL base64 (
data:image/jpeg;base64,... ou data:image/png;base64,...). Mesma validação regex do logo do tenant.Se o usuário é administrador do tenant.
Se o usuário nasce ativo.
Response 201
ObjetoUser criado (com has_signature, sem signature/password).
Erros
| Status | Situação |
|---|---|
| 404 | tenant_id ou unit_id não encontrado |
| 422 | Email duplicado, unit_id não pertence ao tenant, ou validação de crm/signature/password falhou |
Notas importantes
- O campo
is_super_adminé sempre forçado parafalseno create — super admins só podem ser criados via seed ou console - Email sempre normalizado para lowercase antes de salvar
PATCH /api/admin/users/
Atualiza um usuário. Todos os campos opcionais.Request body
Mesmos campos doPOST, exceto:
password— não é aceito (não há endpoint admin para resetar senha)is_super_admin— imutável via API
email ignora o próprio registro (unique('users', 'email')->ignore($user->id)).
Response 200
Usuário atualizado.Erros
| Status | Situação |
|---|---|
| 404 | Usuário, tenant_id ou unit_id não encontrado |
| 422 | Email duplicado, unit_id não pertence ao tenant, ou validação falhou |
Cobertura
Este documento cobre todos os 10 endpoints atualmente expostos sob/api/admin/* (conferidos em routes/api.php):
- Tenants:
GET,POST,PATCH /{id}/status - Units:
GET,POST - Modalities:
GET,POST - Users:
GET,POST,PATCH /{id}
PATCH /admin/units/{id}/status, DELETE /admin/modalities/{id}, DELETE /admin/users/{id}, reset de senha via admin.