Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mivicall.com/llms.txt

Use this file to discover all available pages before exploring further.

Multi-tenancy

Cada clínica = um tenant. Toda a API é tenant-scoped: a API key identifica o tenant, todas as listagens/escritas vivem dentro dele. Nunca vês dados de outra clínica.
tenant (Clínica X)
├── professionals (Dr. A, Dra. B, Dr. C)
├── service_types (Limpeza, Restauração, Urgência)
├── availability_rules (horários semanais por médico)
├── appointments (marcações)
├── calls (chamadas Telnyx + transcripts + recordings)
├── patients (PII encriptado AES-256-GCM)
├── clinic_knowledge (FAQs com embeddings OpenAI)
└── audit_log (RGPD, retenção 6 anos)

Professionals (médicos)

Cada profissional pertence a 1 tenant. Tem name, title, specialty, bio, opcionalmente email. Não tem login — é uma entidade de agendamento, não um utilizador.

Service types (tipos de consulta)

name, durationMinutes, bufferMinutes, priceEur, lista de eligibleProfessionalIds. A AI ao marcar verifica se o profissional escolhido pode executar o serviço.

Availability rules

Horários recorrentes por dia da semana e por profissional:
{
  "professionalId": "uuid-...",
  "dayOfWeek": 1,            // 0=Dom, 1=Seg, ..., 6=Sáb
  "startTime": "09:00:00",
  "endTime": "13:00:00",
  "label": "Manhã"
}
Múltiplas regras por dia permitem manhã + tarde com almoço (09:00-13:00 e 14:00-19:00). Excepções (folgas, feriados, dias com horário diferente) vivem em availability_exceptions — sobrepõem-se às regras recorrentes.

Appointments (marcações)

type Appointment = {
  id: string
  professionalId: string
  serviceTypeId: string
  patientName: string
  patientPhoneHash: string  // HMAC-SHA256 — para matching sem expor PII
  startsAt: string          // ISO 8601 com timezone
  endsAt: string
  status:
    | 'confirmed'           // marcada, esperamos o paciente
    | 'attended'            // paciente veio (marca via PMS check-in)
    | 'no_show'             // 30 min após startsAt sem check-in
    | 'cancelled'           // cancelada (paciente ou clínica)
    | 'rescheduled'         // remarcada (substituída por outra)
  source:
    | 'voice_ai'            // marcada pela recepcionista AI
    | 'manual'              // marcada pela equipa no dashboard
    | 'self_service'        // marcada pelo paciente em /c/{slug}
    | 'sync_external'       // sincronizada de Google Calendar / PMS
}

Transições de status

[ confirmed ] ──► [ attended ]      via PATCH (PMS check-in)
[ confirmed ] ──► [ no_show ]       via cron horário (auto, 30 min após)
[ confirmed ] ──► [ cancelled ]     via PATCH (cancellation)
[ confirmed ] ──► [ rescheduled ]   via POST (replacement)

Calls (chamadas)

Cada chamada Telnyx fica registada com:
  • from_number, to_number — E.164 (+351...)
  • started_at, ended_at, duration_seconds
  • statusin_progress, completed, failed
  • outcomebooking_created, info_only, handoff_to_human, voicemail, etc.
  • recording_url — path R2 (signed URL via GET /v1/calls/:id/recording)
E call_events com:
  • transcription_final — texto do que foi dito
  • tool_call — function call do LLM (check_availability, etc.)
  • tool_result — resposta da nossa API
  • speech — frase pronunciada pela AI

Patients (RGPD)

Pacientes NÃO são utilizadores. Existem como edges ligadas a marcações via:
  • patient_phone_hash (HMAC-SHA256 + salt) — usado para lookups sem expor PII
  • patient_phone_encrypted (AES-256-GCM) — desencriptado on-demand para SMS
O paciente “existe” implicitamente — agregação por hash devolve histórico de marcações + chamadas.

Tenant config (jsonb)

Personalização da AI por clínica:
{
  "voice": "AWS.Polly.Ines-Neural",
  "voiceSpeed": 1.0,
  "greeting": "Clínica X, boa tarde. Daqui Mivicall, assistente automática da clínica. Em que posso ajudar?",
  "promptTemplate": null,
  "fallbackHumanNumber": "+351912004471",
  "timezone": "Europe/Lisbon",
  "publicPhone": "+351217624471",
  "address": "R. Alexandre Herculano 22, 1250-009 Lisboa"
}

Idempotency

Endpoints de escrita (POST, PATCH) aceitam header Idempotency-Key. Garantia: a mesma key dentro da mesma janela de 24h devolve a resposta original sem re-executar a acção. Recomendado para integrações PMS — usa um identificador estável do teu lado (ex.: pms-checkin-{appointmentId}).