NFS-e/Repasse: Médico

Endpoints da WebApiAlcance para gerenciar médicos vinculados a prestadores no módulo NFS-e/Repasse. Cada médico pertence a um prestador (id_prestador) e pode ter vínculos com usuários internos do sistema. Cobrem criação, listagem geral com filtros, listagem restrita a um prestador, detalhamento por ID, atualização, exclusão e o gerenciamento completo dos vínculos usuário↔médico.

Todos os endpoints exigem autenticação. Veja Autenticação para o fluxo de API Key. O Swagger oficial está em api.contabilidadealcance.com.br/docs.

Escopo compartilhado do módulo NFS-e/Repasse

O primeiro segmento da rota é nfse-repasse (compartilhado por todo o módulo). O escopo é derivado dele: nfse_repasse:read para leitura e nfse_repasse:write para escrita. Ou seja, os recursos internos do módulo (médicos, prestadores etc.) compartilham o mesmo par de escopos nfse_repasse:read / nfse_repasse:write, e não escopos por sub-recurso.

Base path

/api/v1/nfse-repasse/medicos

Escopos necessários

  • nfse_repasse:read - listagens, detalhamento e leitura de vínculos (GET)
  • nfse_repasse:write - criação, atualização e exclusão de médicos e vínculos (POST/PUT/DELETE)

O escopo é derivado do primeiro segmento da rota: /nfse-repasse vira o recurso nfse_repasse (hífen → underscore), compartilhado por todo o módulo. O método HTTP define a ação (read para GET, write para POST/PUT/DELETE).

Perfil administrador nas escritas

Além do escopo nfse_repasse:write, as operações de escrita (criar/atualizar/excluir médico e vínculos) exigem perfil administrador no backend. Leituras (GET) são liberadas a qualquer usuário autenticado.

Endpoints

POST /nfse-repasse/medicos/

Cria um novo médico vinculado a um prestador. O id_usuario_created não entra no body - é forçado pelo backend a partir do usuário autenticado.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (MedicoCreate). Campos obrigatórios: id_prestador e nome.

Request

{
  "id_prestador": 1,
  "nome": "Dr. João da Silva",
  "cpf": "12345678901",
  "crm": "CRM/BA 12345",
  "email": "joao@exemplo.com.br",
  "telefone": "(71) 99999-9999",
  "status": "ativo",
  "data_inicio": "2026-01-01",
  "medico_administrador": false,
  "participa_rateio_repasse": true
}

Response 201 Created

{
  "success": true,
  "message": "Médico criado com sucesso!",
  "data": {
    "id": 1,
    "id_prestador": 1,
    "nome": "Dr. João da Silva",
    "cpf": "12345678901",
    "crm": "CRM/BA 12345",
    "email": "joao@exemplo.com.br",
    "telefone": "(71) 99999-9999",
    "status": "ativo",
    "data_inicio": "2026-01-01",
    "data_fim": null,
    "medico_administrador": false,
    "participa_rateio_repasse": true,
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-01-01T12:00:00Z",
    "updated_at": "2026-01-01T12:00:00Z"
  }
}

Escopo: nfse_repasse:write

POST /nfse-repasse/medicos/vinculos

Cria um vínculo usuário↔médico. Rota estática declarada antes de /{medico_id} (ver Notas). O id_usuario_created é forçado pelo backend.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (MedicoUsuarioVinculoCreate). Campo obrigatório: id_medico.

Request

{
  "id_medico": 1,
  "id_usuario": 42,
  "uuid_usuario_legado": "7a06b064-0dc9-414e-bb68-05a3cf7be813"
}

Response 201 Created

{
  "success": true,
  "message": "Vínculo criado com sucesso!",
  "data": {
    "id": 1,
    "id_medico": 1,
    "id_usuario": 42,
    "uuid_usuario_legado": "7a06b064-0dc9-414e-bb68-05a3cf7be813",
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-01-01T12:00:00Z",
    "updated_at": "2026-01-01T12:00:00Z"
  }
}

Escopo: nfse_repasse:write

GET /nfse-repasse/medicos/vinculos/by-medico/{id_medico}

Lista os vínculos usuário↔médico de um médico específico. Lista vazia é um estado válido - retorna data: [].

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
id_medicointpathsimID do médico (medico.id)

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Vínculos recuperados com sucesso!",
  "data": [
    {
      "id": 1,
      "id_medico": 1,
      "id_usuario": 42,
      "uuid_usuario_legado": null,
      "uuid_legado": null,
      "id_usuario_created": 42,
      "created_at": "2026-01-01T12:00:00Z",
      "updated_at": "2026-01-01T12:00:00Z"
    }
  ]
}

Escopo: nfse_repasse:read

PUT /nfse-repasse/medicos/vinculos/{vinculo_id}

Atualiza um vínculo usuário↔médico. O body é um MedicoUsuarioVinculoUpdate com campos parciais - somente o que vier preenchido é alterado.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
vinculo_idintpathsimID do vínculo

Request (MedicoUsuarioVinculoUpdate - todos os campos opcionais)

{
  "id_usuario": 43,
  "uuid_usuario_legado": "7a06b064-0dc9-414e-bb68-05a3cf7be813"
}

Response 200 OK

{
  "success": true,
  "message": "Vínculo atualizado!",
  "data": {
    "id": 1,
    "id_medico": 1,
    "id_usuario": 43,
    "uuid_usuario_legado": "7a06b064-0dc9-414e-bb68-05a3cf7be813",
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-01-01T12:00:00Z",
    "updated_at": "2026-01-01T13:30:00Z"
  }
}

Escopo: nfse_repasse:write

DELETE /nfse-repasse/medicos/vinculos/{vinculo_id}

Remove um vínculo usuário↔médico pelo ID.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
vinculo_idintpathsimID do vínculo

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Vínculo removido!",
  "data": null
}

Escopo: nfse_repasse:write

GET /nfse-repasse/medicos/by-prestador/{id_prestador}

Lista os médicos vinculados a um prestador, com filtro de status e paginação. Rota estática declarada antes de /{medico_id}.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
id_prestadorintpathsimID do prestador (prestador_medico.id)
statusstrquerynãoFiltro por status: ativo ou inativo
pageint >= 1querynãoPágina (1-based)
per_pageint 1..500querynãoItens por página

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Médicos recuperados com sucesso!",
  "data": [
    {
      "id": 1,
      "id_prestador": 1,
      "nome": "Dr. João da Silva",
      "cpf": "12345678901",
      "crm": "CRM/BA 12345",
      "email": "joao@exemplo.com.br",
      "telefone": "(71) 99999-9999",
      "status": "ativo",
      "data_inicio": "2026-01-01",
      "data_fim": null,
      "medico_administrador": false,
      "participa_rateio_repasse": true,
      "uuid_legado": null,
      "id_usuario_created": 42,
      "created_at": "2026-01-01T12:00:00Z",
      "updated_at": "2026-01-01T12:00:00Z"
    }
  ]
}

Escopo: nfse_repasse:read

GET /nfse-repasse/medicos/

Lista todos os médicos (visão compartilhada single-company), com filtros opcionais e paginação. Lista vazia é um estado válido - retorna data: [].

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
statusstrquerynãoFiltro por status: ativo ou inativo
id_prestadorintquerynãoFiltro por prestador vinculado (prestador_medico.id)
pageint >= 1querynãoPágina (1-based)
per_pageint 1..500querynãoItens por página

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Médicos recuperados com sucesso!",
  "data": [
    {
      "id": 1,
      "id_prestador": 1,
      "nome": "Dr. João da Silva",
      "cpf": "12345678901",
      "crm": "CRM/BA 12345",
      "email": "joao@exemplo.com.br",
      "telefone": "(71) 99999-9999",
      "status": "ativo",
      "data_inicio": "2026-01-01",
      "data_fim": null,
      "medico_administrador": false,
      "participa_rateio_repasse": true,
      "uuid_legado": null,
      "id_usuario_created": 42,
      "created_at": "2026-01-01T12:00:00Z",
      "updated_at": "2026-01-01T12:00:00Z"
    }
  ]
}

Escopo: nfse_repasse:read

GET /nfse-repasse/medicos/{medico_id}

Detalha um médico pelo ID inteiro. Quando não encontrado, retorna 404 Not Found.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
medico_idintpathsimID interno do médico

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Médico encontrado!",
  "data": {
    "id": 1,
    "id_prestador": 1,
    "nome": "Dr. João da Silva",
    "cpf": "12345678901",
    "crm": "CRM/BA 12345",
    "email": "joao@exemplo.com.br",
    "telefone": "(71) 99999-9999",
    "status": "ativo",
    "data_inicio": "2026-01-01",
    "data_fim": null,
    "medico_administrador": false,
    "participa_rateio_repasse": true,
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-01-01T12:00:00Z",
    "updated_at": "2026-01-01T12:00:00Z"
  }
}

Escopo: nfse_repasse:read

PUT /nfse-repasse/medicos/{medico_id}

Atualiza um médico existente. O body é um MedicoUpdate com campos parciais - somente o que vier preenchido é alterado. Os campos id, uuid_legado, id_usuario_created, created_at e updated_at são imutáveis e não podem ser enviados.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
medico_idintpathsimID do médico

Request (MedicoUpdate - todos os campos opcionais)

{
  "status": "inativo",
  "data_fim": "2026-12-31",
  "participa_rateio_repasse": false
}

Response 200 OK

{
  "success": true,
  "message": "Médico atualizado!",
  "data": {
    "id": 1,
    "id_prestador": 1,
    "nome": "Dr. João da Silva",
    "cpf": "12345678901",
    "crm": "CRM/BA 12345",
    "email": "joao@exemplo.com.br",
    "telefone": "(71) 99999-9999",
    "status": "inativo",
    "data_inicio": "2026-01-01",
    "data_fim": "2026-12-31",
    "medico_administrador": false,
    "participa_rateio_repasse": false,
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-01-01T12:00:00Z",
    "updated_at": "2026-07-08T09:00:00Z"
  }
}

Escopo: nfse_repasse:write

DELETE /nfse-repasse/medicos/{medico_id}

Remove um médico pelo ID.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
medico_idintpathsimID do médico

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Médico removido!",
  "data": null
}

Escopo: nfse_repasse:write

Schemas

MedicoCreate

CampoTipoObrigatórioObservações
id_prestadorintsimPrestador (prestador_medico.id) ao qual o médico é vinculado.
nomestrsimNome do médico (máx. 200).
cpfstrnãoAceita máscara - normalizado para apenas dígitos (máx. 20). Vazio → null.
crmstrnãoNúmero do CRM (máx. 30).
emailstrnãoE-mail de contato (máx. 100).
telefonestrnãoTelefone de contato (máx. 50).
statusstrnãoativo ou inativo. Default ativo.
data_iniciodatenãoData de início do vínculo (YYYY-MM-DD).
data_fimdatenãoData de fim do vínculo (YYYY-MM-DD).
medico_administradorboolnãoSe true, vê relatórios de repasse de todos os médicos do prestador. Default false.
participa_rateio_repasseboolnãoSe false, não entra na divisão de despesas rateadas. Default true.
uuid_legadostrnãoUUID do registro original no Supabase (rastreabilidade da migração; máx. 36). Imutável depois.

MedicoUpdate

Todos os campos são opcionais; apenas os enviados são atualizados. id, uuid_legado, id_usuario_created, created_at e updated_at são imutáveis (bloqueio de mass-assignment).

CampoTipoObservações
id_prestadorintReatribui o médico a outro prestador.
nomestrNovo nome (máx. 200).
cpfstrNormalizado para dígitos (máx. 20).
crmstrNovo CRM (máx. 30).
emailstrNovo e-mail (máx. 100).
telefonestrNovo telefone (máx. 50).
statusstrativo ou inativo.
data_iniciodateData de início.
data_fimdateData de fim.
medico_administradorboolAlterna acesso administrativo do médico.
participa_rateio_repasseboolAlterna participação no rateio.

MedicoRead

Estende MedicoBase com campos gerenciados pelo servidor.

CampoTipoNotas
idintIdentificador interno do médico.
id_prestadorintPrestador vinculado.
nomestrNome do médico.
cpfstr?CPF (apenas dígitos).
crmstr?CRM.
emailstr?E-mail de contato.
telefonestr?Telefone de contato.
statusstrativo ou inativo.
data_iniciodate?Data de início do vínculo.
data_fimdate?Data de fim do vínculo.
medico_administradorboolAcesso administrativo do médico.
participa_rateio_repasseboolParticipa do rateio de despesas.
uuid_legadostr?UUID do registro original (migração).
id_usuario_createdint?Usuário que criou (auditoria).
created_atdatetimeTimestamp de criação (server-side).
updated_atdatetimeTimestamp da última atualização.

MedicoUsuarioVinculoCreate

CampoTipoObrigatórioObservações
id_medicointsimMédico (medico.id) vinculado.
id_usuariointnãoUsuário interno (user.id) vinculado - opcional na carga.
uuid_usuario_legadostrnãoUUID original do usuário no Supabase (auth.users.id; máx. 36).
uuid_legadostrnãoUUID do registro original do vínculo (migração; máx. 36).

MedicoUsuarioVinculoUpdate

Todos os campos opcionais; apenas os enviados são atualizados.

CampoTipoObservações
id_medicointReaponta o vínculo para outro médico.
id_usuariointReaponta o vínculo para outro usuário.
uuid_usuario_legadostrUUID original do usuário (máx. 36).

MedicoUsuarioVinculoRead

CampoTipoNotas
idintIdentificador interno do vínculo.
id_medicointMédico vinculado.
id_usuarioint?Usuário interno vinculado.
uuid_usuario_legadostr?UUID original do usuário (migração).
uuid_legadostr?UUID original do vínculo (migração).
id_usuario_createdint?Usuário que criou (auditoria).
created_atdatetimeTimestamp de criação.
updated_atdatetimeTimestamp da última atualização.

Notas

  • Toda resposta segue o envelope { success, message, data } (ou { success: false, message, details } em erro). Códigos HTTP refletem semântica REST padrão.
  • Ordem de rotas no FastAPI. As rotas estáticas POST /vinculos, GET /vinculos/by-medico/{id_medico}, PUT/DELETE /vinculos/{vinculo_id} e GET /by-prestador/{id_prestador} são declaradas antes da rota dinâmica GET /{medico_id}. Isso garante que vinculos e by-prestador sejam resolvidos como rotas estáticas (e não interpretados como um medico_id).
  • Mapeamento de erros do backend: mensagens com "não encontrado" → 404, "já existe" → 409, "acesso negado" → 403; caso contrário → 422.
  • O campo cpf é normalizado para apenas dígitos antes da persistência (o front pode enviar com máscara); string vazia vira null.
  • id_usuario_created nunca entra no body de criação - é preenchido pelo backend a partir do usuário autenticado.