{"openapi":"3.1.0","info":{"title":"AXIClinic API","version":"1.0.0","summary":"Pacientes, agenda, unidades e profissionais da clínica, com webhooks.","description":"API REST do AXIClinic para integrar os sistemas da clínica: site e agendamento online, CRM, BI, totens, chatbots.\n\n## Começo rápido\n\n1. Na clínica, o dono ou um administrador abre **Configurações › API e webhooks** e cria uma chave com as permissões necessárias. A chave aparece uma única vez.\n2. Envie a chave em toda requisição: `Authorization: Bearer axi_live_...`\n3. Teste com `GET /v1/organization`.\n\n```bash\ncurl https://app.axiclinic.com/api/v1/organization \\\n  -H \"Authorization: Bearer $AXICLINIC_API_KEY\"\n```\n\n## Ambiente e testes\n\nO endereço é um só: `https://app.axiclinic.com/api/v1`, com chaves `axi_live_...`. Não há ambiente de testes público. Para desenvolver, peça à clínica uma chave só para isso, com as permissões mínimas e validade curta, e use dados de teste: um paciente identificado como teste e consultas canceladas depois. Chaves `axi_test_...` são de uso interno da Comm7 e não funcionam neste endereço.\n\n## Convenções\n\n- JSON em UTF-8; campos em `snake_case`; todo objeto tem `object` e `id` (UUID).\n- Datas e horas em ISO 8601. As respostas vêm em UTC (`2026-11-03T17:30:00Z`); na entrada, informe sempre o fuso (`-03:00` ou `Z`). Datas puras: `AAAA-MM-DD`.\n- Campo sem valor vem como `null`. Em PATCH, só os campos enviados mudam; `null` apaga o valor.\n- Campos desconhecidos no corpo são recusados (422), para um erro de digitação não passar em silêncio.\n- Listas são paginadas por cursor: `{ \"object\": \"list\", \"data\": [...], \"has_more\": true, \"next_cursor\": \"...\" }`.\n- Toda resposta tem `X-Request-Id`. Você pode mandar o seu (8 a 64 caracteres) para rastrear de ponta a ponta.\n\n## Limites\n\nOs limites dependem do plano da clínica e valem para **todas as chaves da clínica somadas**:\n\n- **Ritmo:** requisições por minuto (janela fixa de 1 minuto).\n- **Cota diária:** requisições por dia, renovada à meia-noite (horário de Brasília).\n\nToda resposta informa `RateLimit-Limit`, `RateLimit-Remaining` e `RateLimit-Reset` (segundos até a janela renovar) e, quando há cota diária, `X-Quota-Limit`, `X-Quota-Remaining` e `X-Quota-Reset`. Ao estourar, a resposta é `429` com `Retry-After`.\nBoas práticas: espere o `Retry-After`, use espera exponencial com variação aleatória (jitter) nas novas tentativas, prefira `updated_since` a varrer tudo e use webhooks no lugar de consultas repetidas.\n\n## Erros\n\nFormato *Problem Details* (RFC 9457), `Content-Type: application/problem+json`:\n\n```json\n{\n  \"type\": \"https://axiclinic.com/desenvolvedores/erros#validation_failed\",\n  \"title\": \"Dados inválidos\",\n  \"status\": 422,\n  \"code\": \"validation_failed\",\n  \"detail\": \"Um ou mais campos estão inválidos.\",\n  \"request_id\": \"req_8f2c...\",\n  \"errors\": [{ \"field\": \"cpf\", \"message\": \"CPF inválido.\" }]\n}\n```\n\nUse `code` (estável) no seu código; `title` e `detail` são para pessoas e podem mudar.\n\n## Idempotência\n\nEnvie `Idempotency-Key` nos `POST`. Se a conexão cair, repita com a mesma chave: em até 24 h a API devolve a resposta original (com `Idempotent-Replayed: true`) em vez de agendar duas vezes. Mesma chave com corpo diferente = `409 idempotency_conflict`.\n\n## Webhooks\n\nCadastrados pela clínica em **Configurações › API e webhooks** ou pelo seu sistema, com `POST /webhooks` (escopo `webhooks:write`; o segredo de assinatura vem uma única vez na resposta). Cada evento chega por `POST` JSON assinado no padrão [Standard Webhooks](https://www.standardwebhooks.com) (cabeçalhos `webhook-id`, `webhook-timestamp`, `webhook-signature`). Os eventos são \"magros\": trazem ids e, na agenda, data, hora e status; dados pessoais do paciente você busca pela API. Responda `2xx` em até 10 segundos; sem isso, há 7 novas tentativas ao longo de ~2 dias. Eventos disponíveis: `patient.created`, `patient.updated`, `patient.deleted`, `appointment.created`, `appointment.rescheduled`, `appointment.status_changed`, `appointment.canceled`, `appointment.deleted`.\n\n## Privacidade (LGPD)\n\nOs dados de pacientes pertencem à clínica, que é a controladora. Quem integra atua como operador: guarde só o necessário, proteja a chave (variável de ambiente ou cofre, nunca no navegador ou no aplicativo) e não registre CPF em logs. A API nunca devolve prontuário, anamnese, imagens ou dados financeiros.","contact":{"name":"Suporte AXIClinic","url":"https://axiclinic.com/desenvolvedores","email":"suporte@axiclinic.com"},"termsOfService":"https://axiclinic.com/termos-de-uso","license":{"name":"Proprietária (uso conforme os Termos de uso)","url":"https://axiclinic.com/termos-de-uso"}},"servers":[{"url":"https://app.axiclinic.com/api/v1","description":"Produção"}],"security":[{"chave":[]}],"tags":[{"name":"Clínica","description":"A clínica dona da chave, unidades e profissionais."},{"name":"Pacientes","description":"Cadastro de pacientes (dados civis e de contato). O prontuário nunca sai pela API."},{"name":"Agenda","description":"Consultas e horários livres, com as mesmas regras da agenda do AXIClinic."},{"name":"Webhooks","description":"Endereços que recebem os eventos da clínica. O segredo de assinatura só aparece na criação e na troca."}],"paths":{"/organization":{"get":{"tags":["Clínica"],"operationId":"getOrganization","summary":"Clínica da chave","description":"Dados da clínica dona da chave e os limites da API no plano. Bom para testar a chave. Escopo: `organization:read`.","x-scope":"organization:read","responses":{"200":{"description":"Clínica","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/units":{"get":{"tags":["Clínica"],"operationId":"listUnits","summary":"Listar unidades","description":"Todas as unidades (endereços) da clínica. Escopo: `organization:read`.","x-scope":"organization:read","responses":{"200":{"description":"Unidades","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more","next_cursor"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Unit"}},"has_more":{"type":"boolean","description":"Há mais páginas: repita a chamada com cursor=next_cursor."},"next_cursor":{"type":["string","null"],"description":"Cursor opaco da próxima página."}}}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/units/{id}":{"get":{"tags":["Clínica"],"operationId":"getUnit","summary":"Buscar unidade","x-scope":"organization:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Unidade","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unit"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrada (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/professionals":{"get":{"tags":["Clínica"],"operationId":"listProfessionals","summary":"Listar profissionais","description":"Escopo: `organization:read`.","x-scope":"organization:read","parameters":[{"name":"unit_id","in":"query","description":"Só quem atende nesta unidade.","schema":{"type":"string","format":"uuid"}},{"name":"active","in":"query","description":"Filtra ativos (true) ou inativos (false).","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Profissionais","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more","next_cursor"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Professional"}},"has_more":{"type":"boolean","description":"Há mais páginas: repita a chamada com cursor=next_cursor."},"next_cursor":{"type":["string","null"],"description":"Cursor opaco da próxima página."}}}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/professionals/{id}":{"get":{"tags":["Clínica"],"operationId":"getProfessional","summary":"Buscar profissional","x-scope":"organization:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Profissional","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Professional"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/patients":{"get":{"tags":["Pacientes"],"operationId":"listPatients","summary":"Listar e buscar pacientes","description":"Lista paginada, da mais antiga para a mais nova. Filtros combináveis. Escopo: `patients:read`.","x-scope":"patients:read","parameters":[{"name":"search","in":"query","description":"Parte do nome ou do nome social (2+ caracteres).","schema":{"type":"string","minLength":2}},{"name":"cpf","in":"query","description":"CPF (com ou sem pontuação).","schema":{"type":"string"}},{"name":"email","in":"query","schema":{"type":"string","format":"email"}},{"name":"phone","in":"query","description":"Telefone ou WhatsApp, com DDD (aceita +55).","schema":{"type":"string"}},{"name":"external_id","in":"query","description":"Código do paciente no seu sistema.","schema":{"type":"string"}},{"name":"updated_since","in":"query","description":"Só o que foi criado ou alterado a partir deste instante (ISO 8601 com fuso). A lista passa a vir ordenada pela alteração: ideal para sincronização incremental.","schema":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}},{"name":"limit","in":"query","description":"Itens por página (1-100).","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"next_cursor da página anterior.","schema":{"type":"string"}}],"responses":{"200":{"description":"Pacientes","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more","next_cursor"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Patient"}},"has_more":{"type":"boolean","description":"Há mais páginas: repita a chamada com cursor=next_cursor."},"next_cursor":{"type":["string","null"],"description":"Cursor opaco da próxima página."}}}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Filtro inválido (code: validation_failed, invalid_cursor)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Pacientes"],"operationId":"createPatient","summary":"Cadastrar paciente","description":"Cadastra o paciente. Se o CPF já pertence a um paciente da clínica, a resposta é `409 duplicate_patient` com `existing_patient_id`. Se o CPF é de alguém já cadastrado como responsável ou contato, a mesma pessoa vira paciente. Escopo: `patients:write`.","x-scope":"patients:write","parameters":[{"name":"Idempotency-Key","in":"header","description":"Chave única por operação (recomendado: UUID v4). Repetir a requisição com a mesma chave em até 24 h devolve a mesma resposta, sem duplicar.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientCreate"}}}},"responses":{"201":{"description":"Paciente criado","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Location":{"description":"Endereço do paciente criado.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Patient"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"CPF já cadastrado ou Idempotency-Key em conflito (code: duplicate_patient, idempotency_conflict)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Dados inválidos (code: validation_failed)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/patients/{id}":{"get":{"tags":["Pacientes"],"operationId":"getPatient","summary":"Buscar paciente","x-scope":"patients:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Paciente","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Patient"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Pacientes"],"operationId":"updatePatient","summary":"Alterar paciente","description":"Só os campos enviados mudam; `null` apaga (menos `name`). Escopo: `patients:write`.","x-scope":"patients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientUpdate"}}}},"responses":{"200":{"description":"Paciente alterado","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Patient"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"CPF de outro paciente (code: duplicate_patient)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Dados inválidos (code: validation_failed)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/appointments":{"get":{"tags":["Agenda"],"operationId":"listAppointments","summary":"Listar consultas","description":"Lista paginada por data de início (ou de alteração, com `updated_since`). Consultas excluídas não aparecem; use o webhook `appointment.deleted`. Escopo: `appointments:read`.","x-scope":"appointments:read","parameters":[{"name":"unit_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"professional_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"patient_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","description":"Um ou mais, separados por vírgula.","schema":{"type":"string","examples":["agendado,confirmado"]}},{"name":"starts_after","in":"query","description":"Início a partir deste instante (inclusive).","schema":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}},{"name":"starts_before","in":"query","description":"Início antes deste instante.","schema":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}},{"name":"updated_since","in":"query","description":"Só o que foi criado ou alterado a partir deste instante (ISO 8601 com fuso). A lista passa a vir ordenada pela alteração: ideal para sincronização incremental.","schema":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}},{"name":"limit","in":"query","description":"Itens por página (1-100).","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"next_cursor da página anterior.","schema":{"type":"string"}}],"responses":{"200":{"description":"Consultas","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more","next_cursor"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Appointment"}},"has_more":{"type":"boolean","description":"Há mais páginas: repita a chamada com cursor=next_cursor."},"next_cursor":{"type":["string","null"],"description":"Cursor opaco da próxima página."}}}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Filtro inválido (code: validation_failed, invalid_cursor)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Agenda"],"operationId":"createAppointment","summary":"Agendar consulta","description":"Agenda no horário pedido, se estiver livre no expediente do profissional (mesmas regras de `GET /availability`: consultas, bloqueios, Google Agenda, cadeiras e outras consultas do paciente). Sem `room_id`, o sistema escolhe uma cadeira livre. Regras da clínica (paciente menor sem responsável, responsável com parcelas em atraso) podem recusar com `422 business_rule`. Use `Idempotency-Key`. Escopo: `appointments:write`.","x-scope":"appointments:write","parameters":[{"name":"Idempotency-Key","in":"header","description":"Chave única por operação (recomendado: UUID v4). Repetir a requisição com a mesma chave em até 24 h devolve a mesma resposta, sem duplicar.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentCreate"}}}},"responses":{"201":{"description":"Consulta agendada (status agendado)","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Location":{"description":"Endereço da consulta.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Unidade, profissional ou paciente não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Horário indisponível (code: schedule_conflict, idempotency_conflict)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Dados inválidos ou regra da clínica (code: validation_failed, business_rule)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/appointments/{id}":{"get":{"tags":["Agenda"],"operationId":"getAppointment","summary":"Buscar consulta","x-scope":"appointments:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Consulta","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrada (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Agenda"],"operationId":"updateAppointment","summary":"Remarcar ou alterar consulta","description":"Muda horário, duração, profissional ou cadeira (só consultas `agendado` ou `confirmado`), tipo ou motivo. Mudou o início: volta para `agendado`, porque precisa de nova confirmação. Escopo: `appointments:write`.","x-scope":"appointments:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentUpdate"}}}},"responses":{"200":{"description":"Consulta alterada","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrada (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Horário indisponível ou status não permite (code: schedule_conflict, conflict)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Dados inválidos (code: validation_failed, business_rule)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/appointments/{id}/confirm":{"post":{"tags":["Agenda"],"operationId":"confirmAppointment","summary":"Confirmar presença","description":"`agendado` → `confirmado`. Confirmar de novo não muda nada. Escopo: `appointments:write`.","x-scope":"appointments:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Consulta confirmada","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrada (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Status não permite (code: conflict)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/appointments/{id}/cancel":{"post":{"tags":["Agenda"],"operationId":"cancelAppointment","summary":"Cancelar consulta","description":"Cancela uma consulta `agendado` ou `confirmado`. Ela continua no histórico. Cancelar de novo não muda nada. Escopo: `appointments:write`.","x-scope":"appointments:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":3,"maxLength":300}}}}}},"responses":{"200":{"description":"Consulta cancelada","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrada (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Status não permite (code: conflict)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Motivo ausente (code: validation_failed)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/availability":{"get":{"tags":["Agenda"],"operationId":"getAvailability","summary":"Horários livres","description":"Horários livres de uma unidade, já descontados expediente, consultas, bloqueios, Google Agenda dos profissionais e cadeiras ocupadas. Use antes de `POST /appointments`. Horários a menos de 5 minutos de agora não aparecem. Escopo: `appointments:read`.","x-scope":"appointments:read","parameters":[{"name":"unit_id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"date_from","in":"query","description":"Primeiro dia (AAAA-MM-DD no fuso da unidade). Padrão: hoje.","schema":{"type":"string","format":"date"}},{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":14,"default":7}},{"name":"duration_minutes","in":"query","schema":{"type":"integer","minimum":5,"maximum":480,"default":30}},{"name":"professional_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"period","in":"query","schema":{"type":"string","enum":["any","morning","afternoon","evening"],"default":"any"},"description":"morning: até 12h; afternoon: 12h-18h; evening: depois das 18h."},{"name":"step_minutes","in":"query","description":"Intervalo entre os horários oferecidos.","schema":{"type":"integer","minimum":5,"maximum":120,"default":30}},{"name":"max_per_day","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Horários livres","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Availability"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Unidade ou profissional (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Parâmetro inválido (code: validation_failed)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks":{"get":{"tags":["Webhooks"],"operationId":"listWebhooks","summary":"Listar webhooks","description":"Todos os endereços de webhook da clínica (os cadastrados pela tela também), sem o segredo. Não há paginação: o plano limita a quantidade. Escopo: `webhooks:read`.","x-scope":"webhooks:read","responses":{"200":{"description":"Webhooks","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","required":["object","data","has_more","next_cursor"],"properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}},"has_more":{"type":"boolean","description":"Há mais páginas: repita a chamada com cursor=next_cursor."},"next_cursor":{"type":["string","null"],"description":"Cursor opaco da próxima página."}}}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Webhooks"],"operationId":"createWebhook","summary":"Cadastrar webhook","description":"Cadastra um endereço https público e os eventos que ele recebe. A resposta traz o **segredo de assinatura** (`secret`, `whsec_…`) **uma única vez**: guarde-o num cofre ou variável de ambiente. Por isso esta rota ignora `Idempotency-Key`. Passou do limite de endereços do plano: `422 business_rule`. Endereço na rede interna, http ou com porta fora de 443/8443: `422 validation_failed`. Escopo: `webhooks:write`.","x-scope":"webhooks:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreate"}}}},"responses":{"201":{"description":"Webhook criado, com o segredo","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Location":{"description":"Endereço do webhook criado.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Dados inválidos ou limite do plano (code: validation_failed, business_rule)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}":{"get":{"tags":["Webhooks"],"operationId":"getWebhook","summary":"Buscar webhook","x-scope":"webhooks:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Webhook","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Webhooks"],"operationId":"updateWebhook","summary":"Alterar webhook","description":"Só os campos enviados mudam. `active: false` desliga o endereço e cancela os envios pendentes; `active: true` religa e zera a sequência de falhas (um endereço desligado por falhas também volta assim). Escopo: `webhooks:write`.","x-scope":"webhooks:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdate"}}}},"responses":{"200":{"description":"Webhook alterado","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Dados inválidos (code: validation_failed)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Webhooks"],"operationId":"deleteWebhook","summary":"Remover webhook","description":"Remove o endereço e o histórico de envios dele. Escopo: `webhooks:write`.","x-scope":"webhooks:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removido.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}/rotate-secret":{"post":{"tags":["Webhooks"],"operationId":"rotateWebhookSecret","summary":"Trocar o segredo","description":"Gera um segredo novo e o devolve **uma única vez**. O anterior deixa de valer na hora: atualize o seu sistema antes de processar os próximos eventos. Ignora `Idempotency-Key`. Escopo: `webhooks:write`.","x-scope":"webhooks:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Segredo novo","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}/test":{"post":{"tags":["Webhooks"],"operationId":"testWebhook","summary":"Enviar evento de teste","description":"Põe um evento `webhook.test` na fila do endereço, que sai em até 1 minuto, assinado como os demais. O endereço precisa estar ativo. O resultado (status HTTP ou erro) aparece em Configurações › API e webhooks › Últimos envios. Escopo: `webhooks:write`.","x-scope":"webhooks:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Evento de teste na fila","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTest"}}}},"401":{"description":"Chave ausente, inválida, revogada ou vencida (code: missing_api_key, invalid_api_key, api_key_expired)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Sem permissão (code: insufficient_scope, plan_without_api, ip_not_allowed, organization_inactive)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Não encontrado (code: not_found)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Webhook desligado (code: conflict)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Ritmo por minuto ou cota diária esgotados (code: rate_limited, daily_quota_exceeded). Respeite Retry-After.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Erro interno (informe o request_id ao suporte) (code: internal_error)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Instabilidade temporária: tente de novo com espera crescente (code: service_unavailable)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{"patient.created":{"post":{"operationId":"webhook_patient_created","summary":"Paciente cadastrado","description":"Enviado para os endereços que assinam `patient.created`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}},"patient.updated":{"post":{"operationId":"webhook_patient_updated","summary":"Cadastro do paciente alterado","description":"Enviado para os endereços que assinam `patient.updated`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}},"patient.deleted":{"post":{"operationId":"webhook_patient_deleted","summary":"Paciente excluído","description":"Enviado para os endereços que assinam `patient.deleted`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}},"appointment.created":{"post":{"operationId":"webhook_appointment_created","summary":"Consulta agendada","description":"Enviado para os endereços que assinam `appointment.created`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}},"appointment.rescheduled":{"post":{"operationId":"webhook_appointment_rescheduled","summary":"Consulta remarcada","description":"Enviado para os endereços que assinam `appointment.rescheduled`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}},"appointment.status_changed":{"post":{"operationId":"webhook_appointment_status_changed","summary":"Status da consulta mudou (confirmou, chegou, faltou...)","description":"Enviado para os endereços que assinam `appointment.status_changed`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}},"appointment.canceled":{"post":{"operationId":"webhook_appointment_canceled","summary":"Consulta cancelada","description":"Enviado para os endereços que assinam `appointment.canceled`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}},"appointment.deleted":{"post":{"operationId":"webhook_appointment_deleted","summary":"Consulta excluída","description":"Enviado para os endereços que assinam `appointment.deleted`. Valide `webhook-signature` antes de processar e use `webhook-id` para não processar duas vezes.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id do evento (igual nas novas tentativas).","schema":{"type":"string","format":"uuid"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Segundos desde 1970 (recuse se tiver mais de 5 minutos).","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"v1,<base64 do HMAC-SHA256 de `id.timestamp.corpo` com o segredo>","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentEvent"}}}},"responses":{"2XX":{"description":"Recebido. Qualquer outra resposta (ou nenhuma em 10 s) gera nova tentativa."}}}}},"components":{"securitySchemes":{"chave":{"type":"http","scheme":"bearer","bearerFormat":"axi_live_<40 caracteres>","description":"Chave criada pela clínica em Configurações › API e webhooks. Escopos: `organization:read` (Clínica, unidades e profissionais (leitura)); `patients:read` (Pacientes (leitura)); `patients:write` (Pacientes (cadastro)); `appointments:read` (Agenda (leitura)); `appointments:write` (Agenda (agendar)); `webhooks:read` (Webhooks (leitura)); `webhooks:write` (Webhooks (gerenciar))."}},"headers":{"X-Request-Id":{"description":"Id da requisição (informe ao suporte).","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Requisições por minuto do plano.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Restantes na janela atual.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos até a janela renovar.","schema":{"type":"integer"}},"Retry-After":{"description":"Segundos para tentar de novo.","schema":{"type":"integer"}}},"schemas":{"Problem":{"type":"object","required":["type","title","status","code","request_id"],"properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string","enum":["invalid_request","validation_failed","invalid_json","payload_too_large","unsupported_media_type","invalid_cursor","missing_api_key","invalid_api_key","api_key_expired","ip_not_allowed","insufficient_scope","plan_without_api","organization_inactive","organization_read_only","not_found","method_not_allowed","conflict","duplicate_patient","schedule_conflict","idempotency_conflict","idempotency_in_progress","business_rule","rate_limited","daily_quota_exceeded","internal_error","service_unavailable"]},"detail":{"type":"string"},"request_id":{"type":"string"},"errors":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}},"required_scope":{"type":"string","description":"Em insufficient_scope."},"existing_patient_id":{"type":"string","format":"uuid","description":"Em duplicate_patient."}}},"Address":{"type":["object","null"],"properties":{"zip_code":{"type":["string","null"],"description":"CEP, 8 dígitos.","examples":["01310100"]},"street":{"type":["string","null"]},"number":{"type":["string","null"]},"complement":{"type":["string","null"]},"neighborhood":{"type":["string","null"]},"city":{"type":["string","null"]},"state":{"type":["string","null"],"enum":["AC","AL","AP","AM","BA","CE","DF","ES","GO","MA","MT","MS","MG","PA","PB","PR","PE","PI","RJ","RN","RS","RO","RR","SC","SP","SE","TO",null]}}},"Organization":{"type":"object","properties":{"object":{"const":"organization"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"legal_name":{"type":["string","null"]},"cnpj":{"type":["string","null"]},"read_only":{"type":"boolean","description":"Assinatura suspensa: só leituras."},"api_limits":{"type":"object","properties":{"requests_per_minute":{"type":"integer"},"requests_per_day":{"type":["integer","null"],"description":"null = sem cota diária."},"max_api_keys":{"type":"integer"},"max_webhook_endpoints":{"type":"integer"}}},"created_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}}},"Unit":{"type":"object","properties":{"object":{"const":"unit"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"phone":{"type":["string","null"]},"address":{"$ref":"#/components/schemas/Address"},"timezone":{"type":"string","examples":["America/Sao_Paulo"],"description":"Fuso IANA da unidade (date_from e local_time de /availability usam este fuso)."},"is_main":{"type":"boolean"},"created_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}}},"Professional":{"type":"object","properties":{"object":{"const":"professional"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"council":{"type":"string","examples":["CRO"]},"council_number":{"type":["string","null"]},"council_state":{"type":["string","null"],"examples":["SP"]},"specialty":{"type":["string","null"]},"active":{"type":"boolean"},"unit_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"created_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}}},"Patient":{"type":"object","properties":{"object":{"const":"patient"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"social_name":{"type":["string","null"]},"cpf":{"type":["string","null"],"description":"11 dígitos, sem pontuação.","examples":["12345678909"]},"birth_date":{"type":["string","null"],"format":"date","examples":["1990-05-20"]},"sex":{"type":["string","null"],"enum":["feminino","masculino","outro","nao_informado",null]},"phone":{"type":["string","null"],"description":"DDD + número, só dígitos.","examples":["1133334444"]},"whatsapp":{"type":["string","null"],"examples":["11988887777"]},"email":{"type":["string","null"],"format":"email"},"address":{"$ref":"#/components/schemas/Address"},"origin":{"type":["string","null"],"description":"Como conheceu a clínica."},"external_id":{"type":["string","null"],"description":"Código do paciente no seu sistema (ou no sistema anterior da clínica)."},"responsible_professional_id":{"type":["string","null"],"format":"uuid"},"created_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}}},"PatientCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":3,"maxLength":150},"social_name":{"type":["string","null"],"maxLength":150},"cpf":{"type":["string","null"],"description":"Com ou sem pontuação; validado."},"birth_date":{"type":["string","null"],"format":"date","examples":["1990-05-20"]},"sex":{"type":["string","null"],"enum":["feminino","masculino","outro","nao_informado",null]},"phone":{"type":["string","null"],"description":"Aceita +55 e pontuação; guarda DDD + número."},"whatsapp":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"},"address":{"$ref":"#/components/schemas/Address"},"origin":{"type":["string","null"],"maxLength":60},"external_id":{"type":["string","null"],"maxLength":60},"responsible_professional_id":{"type":["string","null"],"format":"uuid"}},"examples":[{"name":"Maria Fictícia da Silva","cpf":"123.456.789-09","whatsapp":"+55 11 98888-7777","birth_date":"1990-05-20","origin":"Site","external_id":"CRM-1042"}]},"PatientUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Mesmos campos do cadastro, todos opcionais.","properties":{"name":{"type":"string","minLength":3,"maxLength":150},"social_name":{"type":["string","null"],"maxLength":150},"cpf":{"type":["string","null"],"description":"Com ou sem pontuação; validado."},"birth_date":{"type":["string","null"],"format":"date","examples":["1990-05-20"]},"sex":{"type":["string","null"],"enum":["feminino","masculino","outro","nao_informado",null]},"phone":{"type":["string","null"],"description":"Aceita +55 e pontuação; guarda DDD + número."},"whatsapp":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"},"address":{"$ref":"#/components/schemas/Address"},"origin":{"type":["string","null"],"maxLength":60},"external_id":{"type":["string","null"],"maxLength":60},"responsible_professional_id":{"type":["string","null"],"format":"uuid"}}},"Appointment":{"type":"object","properties":{"object":{"const":"appointment"},"id":{"type":"string","format":"uuid"},"unit_id":{"type":"string","format":"uuid"},"professional_id":{"type":"string","format":"uuid"},"room_id":{"type":["string","null"],"format":"uuid","description":"Cadeira/sala."},"patient_id":{"type":"string","format":"uuid"},"starts_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"ends_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"duration_minutes":{"type":"integer"},"status":{"type":"string","enum":["agendado","confirmado","chegou","em_atendimento","concluido","faltou","cancelado"]},"kind":{"type":"string","enum":["avaliacao","tratamento","retorno","urgencia","encaixe"]},"reason":{"type":["string","null"],"description":"Motivo da consulta."},"cancel_reason":{"type":["string","null"]},"source":{"type":["string","null"],"enum":["ai_agent",null],"description":"ai_agent = marcada pelo agente de IA do AXIClinic."},"created_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}}},"AppointmentCreate":{"type":"object","additionalProperties":false,"required":["unit_id","professional_id","patient_id","starts_at","duration_minutes"],"properties":{"unit_id":{"type":"string","format":"uuid"},"professional_id":{"type":"string","format":"uuid"},"patient_id":{"type":"string","format":"uuid"},"room_id":{"type":["string","null"],"format":"uuid","description":"Opcional: sem ela, o sistema escolhe uma cadeira livre."},"starts_at":{"type":"string","format":"date-time","description":"Com fuso.","examples":["2026-11-03T14:30:00-03:00"]},"duration_minutes":{"type":"integer","minimum":5,"maximum":480},"kind":{"type":"string","enum":["avaliacao","tratamento","retorno","urgencia","encaixe"],"default":"avaliacao"},"reason":{"type":["string","null"],"maxLength":500}}},"AppointmentUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"professional_id":{"type":"string","format":"uuid"},"room_id":{"type":["string","null"],"format":"uuid"},"starts_at":{"type":"string","format":"date-time"},"duration_minutes":{"type":"integer","minimum":5,"maximum":480},"kind":{"type":"string","enum":["avaliacao","tratamento","retorno","urgencia","encaixe"]},"reason":{"type":["string","null"],"maxLength":500}}},"Availability":{"type":"object","properties":{"object":{"const":"availability"},"unit_id":{"type":"string","format":"uuid"},"timezone":{"type":"string"},"date_from":{"type":"string","format":"date"},"date_to":{"type":"string","format":"date"},"duration_minutes":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"starts_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"ends_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"local_date":{"type":"string","format":"date","description":"Data no fuso da unidade."},"local_time":{"type":"string","examples":["14:30"],"description":"Hora no fuso da unidade."},"professional_id":{"type":"string","format":"uuid"},"room_id":{"type":["string","null"],"format":"uuid"}}}}}},"WebhookEndpoint":{"type":"object","properties":{"object":{"const":"webhook_endpoint"},"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri","examples":["https://crm.exemplo.com/webhooks/axiclinic"]},"description":{"type":["string","null"],"maxLength":200},"events":{"type":"array","items":{"type":"string","enum":["patient.created","patient.updated","patient.deleted","appointment.created","appointment.rescheduled","appointment.status_changed","appointment.canceled","appointment.deleted"]},"description":"Eventos que este endereço recebe, em ordem alfabética."},"active":{"type":"boolean"},"disabled_reason":{"type":["string","null"],"description":"Por que está desligado (pela clínica, pela API ou sozinho, após 3 dias de falhas). null quando ativo."},"failing_since":{"type":["string","null"],"format":"date-time","examples":["2026-11-03T17:30:00Z"],"description":"Primeira falha da sequência atual; null quando as entregas estão chegando."},"created_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"updated_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}}},"WebhookEndpointWithSecret":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"Segredo de assinatura (Standard Webhooks). Aparece só nesta resposta: guarde num cofre ou variável de ambiente.","examples":["whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"]}}}]},"WebhookCreate":{"type":"object","additionalProperties":false,"required":["url","events"],"properties":{"url":{"type":"string","format":"uri","maxLength":500,"description":"https público, porta 443 ou 8443, sem usuário e senha. Redirecionamentos não são seguidos."},"events":{"type":"array","minItems":1,"items":{"type":"string","enum":["patient.created","patient.updated","patient.deleted","appointment.created","appointment.rescheduled","appointment.status_changed","appointment.canceled","appointment.deleted"]}},"description":{"type":["string","null"],"maxLength":200,"description":"Para a clínica reconhecer o endereço na tela (ex.: CRM)."}},"examples":[{"url":"https://crm.exemplo.com/webhooks/axiclinic","events":["appointment.created","appointment.canceled","patient.created"],"description":"CRM"}]},"WebhookUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"url":{"type":"string","format":"uri","maxLength":500},"events":{"type":"array","minItems":1,"items":{"type":"string","enum":["patient.created","patient.updated","patient.deleted","appointment.created","appointment.rescheduled","appointment.status_changed","appointment.canceled","appointment.deleted"]}},"description":{"type":["string","null"],"maxLength":200},"active":{"type":"boolean","description":"false desliga (cancela os envios pendentes); true religa."}}},"WebhookTest":{"type":"object","properties":{"object":{"const":"webhook_test"},"webhook_id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid","description":"Virá no cabeçalho webhook-id da entrega."},"type":{"const":"webhook.test"},"message":{"type":"string"}}},"EventBase":{"type":"object","required":["id","type","api_version","created_at","data"],"properties":{"id":{"type":"string","format":"uuid","description":"Igual ao cabeçalho webhook-id."},"type":{"type":"string","enum":["patient.created","patient.updated","patient.deleted","appointment.created","appointment.rescheduled","appointment.status_changed","appointment.canceled","appointment.deleted","webhook.test"]},"api_version":{"const":"v1"},"created_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]}}},"PatientEvent":{"allOf":[{"$ref":"#/components/schemas/EventBase"},{"type":"object","properties":{"data":{"type":"object","properties":{"object":{"const":"patient"},"id":{"type":"string","format":"uuid"}},"description":"Só o id: busque o paciente em GET /patients/{id}."}}}]},"AppointmentEvent":{"allOf":[{"$ref":"#/components/schemas/EventBase"},{"type":"object","properties":{"data":{"type":"object","properties":{"object":{"const":"appointment"},"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["agendado","confirmado","chegou","em_atendimento","concluido","faltou","cancelado"]},"kind":{"type":"string","enum":["avaliacao","tratamento","retorno","urgencia","encaixe"]},"starts_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"ends_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"]},"unit_id":{"type":"string","format":"uuid"},"professional_id":{"type":"string","format":"uuid"},"room_id":{"type":["string","null"],"format":"uuid"},"patient_id":{"type":"string","format":"uuid"},"previous_status":{"type":"string","description":"Em status_changed e canceled."},"previous_starts_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"],"description":"Em rescheduled."},"previous_ends_at":{"type":"string","format":"date-time","examples":["2026-11-03T17:30:00Z"],"description":"Em rescheduled."},"previous_professional_id":{"type":"string","format":"uuid","description":"Em rescheduled."}}}}}]}}}}