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.

Este guia é para developers de PMS (Practice Management Systems) que querem integrar o software da clínica com Mivicall. O padrão é simples e segue convenções standard de mercado.

Cenário típico

A clínica usa o vosso PMS para gerir agenda + pacientes. Adiciona Mivicall para atender chamadas. Quer:
  1. Marcações criadas pela AI aparecerem automaticamente no PMS
  2. Check-in no PMS marcar a consulta como attended no Mivicall (evita no-shows falsos)
  3. Cancelamentos sincronizarem nos dois sentidos
  4. Histórico de chamadas acessível para a equipa da recepção

Arquitectura recomendada

       ┌─────────────────┐                   ┌─────────────────┐
       │      PMS        │                   │    Mivicall     │
       │ (vosso sistema) │                   │                 │
       └───┬─────────────┘                   └──────────┬──────┘
           │                                            │
           │  GET /v1/appointments    (poll inicial)    │
           │ ─────────────────────────────────────────► │
           │                                            │
           │  POST Webhook handler  (eventos push)      │
           │ ◄───────────────────────────────────────── │
           │                                            │
           │  PATCH /v1/appointments/{id}  (check-in)   │
           │ ─────────────────────────────────────────► │
3 padrões coexistem: inicial poll (sync state), webhooks (eventos contínuos), writes (acções do PMS reflectidas em Mivicall).

Passo 1 — Autenticação

A clínica gera uma API key no dashboard em Settings → Integrations → Generate API key. Cola na vossa configuração.
Authorization: Bearer miv_live_AbC123def456...
API keys são server-to-server — usar apenas em backend, nunca expor em código cliente.
Não armazenem a key plain-text. Cifrem at-rest. Mivicall mostra a key uma única vez no momento de geração — depois fica em hash irreversível na nossa DB.

Passo 2 — Sync inicial

Quando o cliente do PMS instalar o conector pela primeira vez, façam um backfill das marcações dos últimos N dias:
curl "https://api.mivicall.com/v1/appointments?from=2026-04-01T00:00:00Z&to=2026-05-13T23:59:59Z&limit=200" \
  -H "Authorization: Bearer $MIVI_KEY"
Resposta paginada (cursor):
{
  "items": [ /* até 200 appointments */ ],
  "nextCursor": "eyJzdGFydHNBdCI6IjIwMjYtMDQtMjAuLi4ifQ",
  "hasMore": true
}
Continuar com ?cursor={nextCursor} até hasMore: false. Recomendamos rate de 5 requests/segundo (limite default por API key é 300/min).

Passo 3 — Subscrever webhooks

Em Settings → Integrations → Webhooks, a clínica adiciona:
URL:    https://api.vosso-pms.pt/integrations/mivicall/webhook
Events: appointment.created, appointment.updated, appointment.cancelled,
        appointment.attended, appointment.no_show
Secret: (gerado automaticamente, mostrado 1× — cifrem at-rest)
A partir desse momento, recebem POST em tempo real. Lista completa de eventos: Webhooks → Eventos.

Verificar signature

Validem sempre a signature antes de processar o payload. Sem verificação, qualquer pessoa que descubra o vosso endpoint pode injectar eventos falsos.
import crypto from 'node:crypto'

const SECRET = process.env.MIVI_WEBHOOK_SECRET!
const MAX_AGE_SECONDS = 300 // 5 min, anti-replay

function verifyMivicallWebhook(rawBody: string, header: string): boolean {
  // Header format: "t=1778649600,v1=hexsignature..."
  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')))
  const ts = Number.parseInt(parts.t!, 10)
  const sig = parts.v1!

  // Anti-replay
  if (Math.abs(Date.now() / 1000 - ts) > MAX_AGE_SECONDS) return false

  const expected = crypto
    .createHmac('sha256', SECRET)
    .update(`${ts}.${rawBody}`)
    .digest('hex')

  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
}

Passo 4 — Escrever de volta (PMS → Mivicall)

4.1 — Marcar attended no check-in

Quando o paciente chega à clínica e o vosso PMS regista presença:
PATCH /v1/appointments/{appointmentId}
Authorization: Bearer miv_live_...
Idempotency-Key: pms-checkin-{vosso-id-interno}
Content-Type: application/json

{ "status": "attended" }
Idempotency-Key é crítico — se a vossa rede falhar e re-tentarem, o mesmo key garante que o appointment não fica marcado 2×.

4.2 — Cancelar

PATCH /v1/appointments/{appointmentId}
Authorization: Bearer miv_live_...
Idempotency-Key: pms-cancel-{vosso-id-interno}

{ "status": "cancelled" }
O paciente recebe SMS automático de confirmação do cancelamento.

4.3 — Reagendar

PATCH /v1/appointments/{appointmentId}
Authorization: Bearer miv_live_...

{ "startsAt": "2026-05-14T11:30:00+01:00", "status": "rescheduled" }

Padrão idempotente recomendado

A vossa lógica de mapping deve usar campos estáveis em ambos os lados:
PMS                         Mivicall
─────────────────────────   ─────────────────────────
ID interno PMS  ◄──────────►  Mivicall appointment ID
                              (guardado em metadata.pmsAppointmentId)
Quando criam o appointment no Mivicall via webhook receive:
  1. Procurem na vossa DB se já existe um row com mivicall_appointment_id = {id}
  2. Se existe → update; se não → insert
  3. Guardem o vosso ID na coluna external_id do vosso PMS
Inversamente, quando criam um appointment de novo no PMS e querem sincronizar para o Mivicall:
POST /v1/appointments
Authorization: Bearer miv_live_...
Idempotency-Key: pms-create-{vosso-id}
Content-Type: application/json

{
  "professionalId": "uuid-mivicall",
  "serviceTypeId": "uuid-mivicall",
  "patientName": "Maria Silva",
  "patientPhone": "+351912345678",
  "startsAt": "2026-05-14T10:00:00+01:00",
  "metadata": {
    "source": "pms-vendor-x",
    "pmsAppointmentId": "PMS-2026-05-14-001"
  }
}

Mapping de dados — atenção

Algumas convenções que ajudam:
Mivicall fieldPMS típico equivalenteNota
professionalIddoctor_id, practitioner_idUUIDs diferentes — mapping table necessária
serviceTypeIdprocedure_code, service_codeidem
patientPhoneHash(não exposto)use para matching anonymous, NUNCA inverter
startsAtappointment_datetimesempre ISO 8601 com timezone (+01:00)
status='attended'check-in / “patient arrived”usem PATCH ao receber check-in no vosso lado
source(Mivicall-only)voice_ai quando criada pela AI

Erros comuns

Status já foi alterado entretanto (ex: cron marcou no_show antes do vosso check-in). Façam GET ao appointment para ver o estado actual, e decidam se forçam (status=attended continua aceitável até 24h após startsAt).
Payload não passa validação Zod. A resposta inclui errors[] com path + message. Verifiquem que startsAt tem timezone e que o professionalId pertence ao tenant.
Atingiram o rate limit (300/min por API key). Header Retry-After indica segundos a esperar. Implementem backoff exponencial.
Key revogada ou inválida. Notifiquem a clínica para gerar nova em Settings → Integrations.

Testar em sandbox

(Em construção) — Está planeado um ambiente sandbox.mivicall.com com tenant pré-popoulado para testes E2E sem afectar produção. Contactem support@mivicall.com para acesso antecipado.

Suporte para vendors

Vendors com pelo menos 5 clínicas-clientes têm canal dedicado:
  • Slack Connect partilhado
  • SLA resposta < 4h
  • Roadmap shared (input em features novas)
Email: support@mivicall.com com assunto [Vendor].