Pular para conteúdo

Endpoints OTIMUS

A API OTIMUS usada pela AgentProIA expõe 4 endpoints essenciais. Todos são POST (mesmo os "de leitura"), retornam JSON com envelope {"data": ...} e aceitam os headers definidos em Autenticação.

# Endpoint Método Uso
1 /api/v1/agendamento/horarios POST Consultar / verificar horários disponíveis
2 /api/v1/cadastros/pacientes/cpf/{cpf} POST Buscar paciente por CPF
3 /api/v1/paciente/multiplos POST Cadastrar novo(s) paciente(s) em lote
4 /api/v1/agendamento/agendamento POST Confirmar / criar agendamento

Regra do envelope

Toda resposta OTIMUS segue o shape {"data": X}. X pode ser:

  • lista de dias com agendas/horários (endpoint 1)
  • dict com campos do paciente (endpoint 2 sucesso)
  • string de erro amigável (endpoint 2 falha: "O CPF informado não foi encontrado.")
  • dict com description / preparo (endpoint 4 sucesso)

1. POST /api/v1/agendamento/horarios

Retorna, para um filtro (agenda + convênio + procedimento + intervalo de datas), as datas e horários disponíveis na agenda do médico.

Request

POST /api/v1/agendamento/horarios HTTP/1.1
{
  "filter": {
    "agendas": [{"id": 58, "tipo": "cs"}],
    "convenio_id": 212,
    "data": "2026-04-14",
    "endDate": "2026-07-13",
    "suceder": 0,
    "especialidade_id": 129,
    "limit": 100,
    "ordenacao": "asc",
    "procedimentos": [170]
  }
}

Campos:

Campo Tipo Notas
filter.agendas[].id int ID da agenda do médico na clínica
filter.agendas[].tipo string Literal "cs" (consulta). Não descobrimos outros valores.
filter.convenio_id int ID do convênio
filter.data string Data inicial em ISO YYYY-MM-DD
filter.endDate string Data final YYYY-MM-DD. Recomenda-se data + 90 dias
filter.suceder int Sempre 0 nos dois projetos
filter.especialidade_id int ID da especialidade
filter.limit int 100 tem sido suficiente
filter.ordenacao string "asc" (Clinfeto) ou "desc" (Medcenter)
filter.procedimentos list[int] Lista de IDs de procedimento

Response (sucesso)

{
  "data": [
    {
      "data": "15-04-2026",
      "agendas": [
        {
          "agendaNome": "JOANA SILVA",
          "horarios": [
            {"hora": "14:00", "dataconsulta": "..."},
            {"hora": "14:30", "dataconsulta": "..."}
          ]
        }
      ]
    }
  ]
}

Formato de data no response

A request vai em YYYY-MM-DD, mas o response volta em DD-MM-YYYY. Trate isso no parser. O campo data do item é dia (não datetime).

Response (falha / vazio)

  • {"data": []} → agenda sem disponibilidade (ou filtro errado).
  • 4xx/5xx → erro de autenticação/validação/infra.

Validações obrigatórias no cliente

  1. agendaNome bate com o médico pedido. O OTIMUS às vezes retorna outras agendas; valide pelo primeiro nome do médico (ver Resolver médico).
  2. Descarte dias no passado e horários de hoje já passados (timezone America/Sao_Paulo).
  3. Selecione N primeiros dias com disponibilidade — típico: 5 dias, 2 manhã
  4. 2 tarde. Ver Filtragem de slots.

2. POST /api/v1/cadastros/pacientes/cpf/{cpf}

Busca paciente por CPF. Note que o CPF vai na URL, não no body.

Request

POST /api/v1/cadastros/pacientes/cpf/12345678900 HTTP/1.1

Sem body (alguns clientes mandam {}, funciona também).

Response — paciente encontrado

{
  "data": {
    "id": 42,
    "paciente_id": 42,
    "nome": "MARIA DA SILVA",
    "cpf": "12345678900",
    "dataNascimento": "10-05-1985",
    "celular": "5599999999999"
  }
}

Response — paciente NÃO encontrado

{"data": "O CPF informado não foi encontrado."}

Idiossincrasia

O OTIMUS retorna 200 OK mesmo quando não encontra. O campo data muda de dict para string. O parser precisa detectar essa variante:

result = resp.json()
if isinstance(result.get("data"), dict) and result["data"].get("cpf"):
    return result["data"]
if result.get("data") == "O CPF informado não foi encontrado.":
    return None
return None

CPF — formato

Sempre enviar apenas dígitos (11 chars). Normalizar antes:

def _only_digits(s: str) -> str:
    return "".join(c for c in (s or "") if c.isdigit())

3. POST /api/v1/paciente/multiplos

Cadastra um ou mais pacientes em lote.

Request

{
  "pacientes": [
    {
      "id": 123,
      "nome": "MARIA DA SILVA",
      "dataNascimento": "1985-05-10",
      "cpf": "12345678900",
      "celular": "5599999999999"
    }
  ]
}

Campo id: 123 nos projetos atuais

Tanto MEDCENTER quanto CLINFETO mandam "id": 123 hardcoded. Parece que o OTIMUS ignora e gera um ID real, mas isso não é contrato — pode quebrar no futuro. Idealmente enviar sem o campo ou com 0/null.

Formato dos campos

Campo Formato Exemplo
nome string livre "MARIA..."
dataNascimento YYYY-MM-DD "1985-05-10"
cpf apenas dígitos "12345678900"
celular apenas dígitos, c/ DDI "5599999999999"

Response

Tem variado entre instâncias OTIMUS. O padrão defensivo é:

  1. Enviar o POST.
  2. Não confiar no response do cadastro — imediatamente fazer _buscar_paciente_cpf(cpf) e usar o paciente_id retornado.

Ver Cadastro de paciente.

4. POST /api/v1/agendamento/agendamento

Cria o agendamento em si.

Request

{
  "paciente_id": 42,
  "cpf": "12345678900",
  "convenio_id": 212,
  "data": "15-04-2026",
  "hora": "14:00",
  "agenda_id": 58,
  "agenda_tipo": "cs",
  "status": "ativo",
  "servicos": [{"id": 170}]
}

Formato de data aqui é DD-MM-YYYY

Diferente do endpoint 1 (que usa YYYY-MM-DD no request!). Mantenha um helper _yyyymmdd_to_ddmmyyyy().

Campos:

Campo Tipo Notas
paciente_id int Obtido do endpoint 2
cpf string Apenas dígitos
convenio_id int ID do convênio
data string DD-MM-YYYY
hora string HH:MM 24h
agenda_id int ID da agenda do médico
agenda_tipo string "cs"
status string "ativo"
servicos list [{"id": <procedimento_id>}]

Response (sucesso)

{
  "description": "Consulta agendada com sucesso",
  "preparo": "Comparecer 15 minutos antes, trazer documento..."
}

O campo preparo varia por procedimento. Inclua ambos na mensagem que o agente devolve ao paciente.

Resumo em uma página

flowchart TD
    A[Paciente diz:<br/>Quero agendar USG com Joana, particular] --> B{Resolver IDs}
    B --> C[agenda_id=58<br/>especialidade_id=129<br/>procedimento_id=170<br/>convenio_id=212]
    C --> D[POST /agendamento/horarios]
    D --> E{Slots?}
    E -->|não| X[FALHA_CONSULTA]
    E -->|sim| F[Agente oferece até 5 dias x 4 slots]
    F --> G[Paciente escolhe + fornece nome/CPF/nasc]
    G --> H[POST /cadastros/pacientes/cpf/CPF]
    H --> I{Existe?}
    I -->|não| J[POST /paciente/multiplos]
    J --> H
    I -->|sim| K[POST /agendamento/agendamento]
    K --> L[Retorna description + preparo]
    L --> M[Agente confirma ao paciente]