evo360-types 1.3.570 → 1.3.574

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/apps/evo-crm/lead/zod-schemas.d.ts +9 -9
  2. package/dist/apps/evo-csq/zod-schemas.d.ts +12 -12
  3. package/dist/apps/evo-finops/zod-schemas.d.ts +18 -18
  4. package/dist/apps/evo-med/dic/zod-schemas.d.ts +2 -2
  5. package/dist/apps/evo-med/people/zod-schemas.d.ts +45 -45
  6. package/dist/apps/evo-notifications/quick-reply-action/zod-schemas.d.ts +3 -0
  7. package/dist/apps/evo-notifications/quick-reply-action/zod-schemas.js +3 -0
  8. package/dist/apps/evo-notifications/quick-reply-action/zod-schemas.ts +3 -0
  9. package/dist/apps/evo-notifications/zod-schemas.d.ts +69 -39
  10. package/dist/apps/evo-task/zod-schemas.d.ts +69 -69
  11. package/dist/apps/evo-task/zod-schemas.js +8 -0
  12. package/dist/apps/evo-task/zod-schemas.ts +8 -0
  13. package/dist/apps/evo-telemedicine/zod-schemas.d.ts +1445 -0
  14. package/dist/apps/evo-telemedicine/zod-schemas.js +641 -0
  15. package/dist/apps/evo-telemedicine/zod-schemas.ts +723 -0
  16. package/dist/apps/evo-tenant/zod-schemas.d.ts +94 -0
  17. package/dist/apps/evo-tenant/zod-schemas.js +29 -1
  18. package/dist/apps/evo-tenant/zod-schemas.ts +32 -1
  19. package/dist/apps/hub-automation/zod-schemas.d.ts +172 -172
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +2 -0
  22. package/dist/index.ts +2 -0
  23. package/dist/types/evo-med/calendar/index.d.ts +6 -0
  24. package/dist/types/evo-med/calendar/index.js +6 -0
  25. package/dist/types/evo-med/calendar/index.ts +6 -0
  26. package/dist/types/evo-notifications/index.d.ts +13 -0
  27. package/dist/types/evo-notifications/index.js +6 -0
  28. package/dist/types/evo-notifications/index.ts +13 -0
  29. package/dist/types/evo-task/index.d.ts +1 -0
  30. package/dist/types/evo-task/index.js +8 -0
  31. package/dist/types/evo-task/index.ts +8 -0
  32. package/dist/types/evo-telemedicine/fb_collections.d.ts +16 -0
  33. package/dist/types/evo-telemedicine/fb_collections.js +38 -0
  34. package/dist/types/evo-telemedicine/fb_collections.ts +37 -0
  35. package/dist/types/evo-telemedicine/index.d.ts +890 -0
  36. package/dist/types/evo-telemedicine/index.js +361 -0
  37. package/dist/types/evo-telemedicine/index.ts +1046 -0
  38. package/dist/types/evo-tenant/index.d.ts +45 -0
  39. package/dist/types/evo-tenant/index.js +20 -1
  40. package/dist/types/evo-tenant/index.ts +65 -0
  41. package/dist/types/shared/external-links.d.ts +6 -0
  42. package/dist/types/shared/external-links.js +6 -0
  43. package/dist/types/shared/external-links.ts +6 -0
  44. package/package.json +1 -1
@@ -0,0 +1,1046 @@
1
+ export * from "./fb_collections";
2
+ import type { IFireDoc, IFireGlobalDoc } from "../shared";
3
+ import type { ITenantModuleEntitlement } from "../evo-tenant";
4
+
5
+ // ======================================================
6
+ // evo-telemedicine (feat-160 — Telemedicina integrada)
7
+ //
8
+ // Contratos compartilhados entre `functions-telemedicine`, `packages/model`,
9
+ // o web-app (painel) e o SPA `web-virtual-care`. Shapes seguem
10
+ // `hub-medica-docs/features/feat-160-telemedicine-platform-mvp/data-model.md`.
11
+ //
12
+ // Regras que os tipos carregam:
13
+ // - nada de PII do paciente (nome/telefone/e-mail/DOB), token do provider,
14
+ // capability `/qr`, cookie, URL assinada ou texto de transcrição nos docs;
15
+ // - segredos de grant existem só como hash/HMAC (`secret_hash`);
16
+ // - eventos são append-only e a `metadata` é allowlist sem PII/conteúdo médico;
17
+ // - o acesso exige entitlement do tenant AND rollout operacional AND permissão.
18
+ //
19
+ // Convenção de datas: docs Firestore usam `Date` (como o resto do repo — o zod
20
+ // converte Timestamp via `zFirestoreDateSchema`); DTOs HTTP usam ISO 8601 em
21
+ // `string` (JSON não transporta Date).
22
+ // ======================================================
23
+
24
+ // ----- Permissões (spec §9.3)
25
+ export const EvoTelemedicinePermissions = {
26
+ /** Ver estado e dados operacionais básicos. */
27
+ Read: "evo_telemedicine_read",
28
+ /** Preparar, admitir, entrar como médico e encerrar. */
29
+ Host: "evo_telemedicine_host",
30
+ /** Solicitar/iniciar/parar gravação. */
31
+ Record: "evo_telemedicine_record",
32
+ /** Listar e baixar gravações/transcrições. */
33
+ ArtifactsRead: "evo_telemedicine_artifacts_read",
34
+ /** Alterar defaults permitidos do tenant; reatribuir host. */
35
+ Configure: "evo_telemedicine_configure",
36
+ /** Consultar trilha detalhada (events). */
37
+ AuditRead: "evo_telemedicine_audit_read",
38
+ } as const;
39
+
40
+ export type EvoTelemedicinePermissions =
41
+ (typeof EvoTelemedicinePermissions)[keyof typeof EvoTelemedicinePermissions];
42
+
43
+ // ----- Constantes de integração
44
+ /** `action_type` do quick-reply `/qr` que redireciona para `/api/public/v1/enter/{cap}` (redirect puro, reexecutável). */
45
+ export const TELEMEDICINE_JOIN_ACTION_TYPE = "telemedicine_join";
46
+
47
+ /** Handler do lembrete/CTA de entrada — igual a `TaskAutoHandlerEnum.TelemedicineJoinReminder`. */
48
+ export const TELEMEDICINE_JOIN_REMINDER_HANDLER = "telemedicine_join_reminder";
49
+
50
+ /**
51
+ * Defaults de janela do MVP (spec §8.2/§8.3/D23) — configuráveis só em nível de
52
+ * plataforma. Em minutos, salvo indicação.
53
+ */
54
+ export const TELEMEDICINE_WINDOW_DEFAULTS = {
55
+ /** Médico prepara/entra desde N minutos antes do início previsto. */
56
+ doctor_access_before_minutes: 30,
57
+ /** Paciente abre o fluxo desde N minutos antes do início previsto. */
58
+ patient_access_before_minutes: 15,
59
+ /** Se nunca iniciou: expira N minutos após o fim previsto. */
60
+ unused_expires_after_end_minutes: 120,
61
+ /** Reconexão após última presença do médico. */
62
+ reconnect_grace_minutes: 10,
63
+ /** Code one-time de exchange (`launch_code`). */
64
+ launch_code_ttl_minutes: 5,
65
+ /** Access da sessão pública do browser (`browser_session`), renovável de forma rotativa. */
66
+ browser_session_ttl_minutes: 15,
67
+ /** Verificação de identidade: N falhas na janela bloqueiam o grant. */
68
+ identity_max_failures: 5,
69
+ identity_failure_window_minutes: 15,
70
+ identity_block_minutes: 30,
71
+ /** URL assinada de artefato. */
72
+ signed_url_max_minutes: 5,
73
+ /** Bits aleatórios mínimos de capability/code. */
74
+ secret_min_bits: 128,
75
+ } as const;
76
+
77
+ // ----- Enums (espelhados à mão em apps/evo-telemedicine/zod-schemas.ts)
78
+
79
+ /** Estado clínico da teleconsulta (spec §10). Presença é separada (`ParticipantState`). */
80
+ export const TeleconsultationStatusEnum = {
81
+ /** Vínculo criado, janela fechada. */
82
+ Scheduled: "scheduled",
83
+ /** Janela aberta, nenhum encontro ativo. */
84
+ Open: "open",
85
+ /** Ao menos uma sessão física ativa ou presença clínica em curso. */
86
+ Live: "live",
87
+ /** Consulta iniciada, médico sem presença dentro da tolerância. */
88
+ Reconnecting: "reconnecting",
89
+ /** Encerramento em andamento; novos joins proibidos. */
90
+ Ending: "ending",
91
+ Ended: "ended",
92
+ Cancelled: "cancelled",
93
+ Expired: "expired",
94
+ } as const;
95
+ export type TeleconsultationStatus =
96
+ (typeof TeleconsultationStatusEnum)[keyof typeof TeleconsultationStatusEnum];
97
+
98
+ /** Terminais: nunca ressuscitam (webhook atrasado, reschedule, join). */
99
+ export const TELECONSULTATION_TERMINAL_STATUSES: readonly TeleconsultationStatus[] = [
100
+ "ended",
101
+ "cancelled",
102
+ "expired",
103
+ ] as const;
104
+
105
+ export const WaitingPolicyEnum = {
106
+ /** Default. Médico admite manualmente. */
107
+ ManualAdmit: "manual_admit",
108
+ /** Entrada automática só com médico `connected` e `identity_level = strong`; nunca em sala vazia. */
109
+ AutoWhenDoctorPresent: "auto_when_doctor_present",
110
+ } as const;
111
+ export type WaitingPolicy = (typeof WaitingPolicyEnum)[keyof typeof WaitingPolicyEnum];
112
+
113
+ export const TeleconsultationModeEnum = {
114
+ AudioVideo: "audio_video",
115
+ AudioOnlyAllowed: "audio_only_allowed",
116
+ } as const;
117
+ export type TeleconsultationMode =
118
+ (typeof TeleconsultationModeEnum)[keyof typeof TeleconsultationModeEnum];
119
+
120
+ export const TeleconsultationProviderEnum = {
121
+ RealtimeKit: "realtimekit",
122
+ } as const;
123
+ export type TeleconsultationProvider =
124
+ (typeof TeleconsultationProviderEnum)[keyof typeof TeleconsultationProviderEnum];
125
+
126
+ export const ParticipantRoleEnum = {
127
+ Doctor: "doctor",
128
+ Patient: "patient",
129
+ } as const;
130
+ export type ParticipantRole = (typeof ParticipantRoleEnum)[keyof typeof ParticipantRoleEnum];
131
+
132
+ /** Presença — separada do estado clínico da teleconsulta. */
133
+ export const ParticipantStateEnum = {
134
+ Invited: "invited",
135
+ Verified: "verified",
136
+ Waiting: "waiting",
137
+ Connected: "connected",
138
+ Disconnected: "disconnected",
139
+ Left: "left",
140
+ } as const;
141
+ export type ParticipantState =
142
+ (typeof ParticipantStateEnum)[keyof typeof ParticipantStateEnum];
143
+
144
+ /** Subconjunto de `ParticipantState` que o cliente pode reportar em `POST /presence`. */
145
+ export const PresenceStateEnum = {
146
+ Waiting: "waiting",
147
+ Connected: "connected",
148
+ Disconnected: "disconnected",
149
+ Left: "left",
150
+ } as const;
151
+ export type PresenceState = (typeof PresenceStateEnum)[keyof typeof PresenceStateEnum];
152
+
153
+ /**
154
+ * Nível de verificação da identidade do paciente (spec §8.3): `strong` = nome +
155
+ * DOB conferidos; `weak` = só nome (paciente sem DOB) — proíbe `auto_admit`.
156
+ */
157
+ export const IdentityLevelEnum = {
158
+ None: "none",
159
+ Weak: "weak",
160
+ Strong: "strong",
161
+ } as const;
162
+ export type IdentityLevel = (typeof IdentityLevelEnum)[keyof typeof IdentityLevelEnum];
163
+
164
+ export const RecordingStatusEnum = {
165
+ Idle: "idle",
166
+ ConsentPending: "consent_pending",
167
+ Recording: "recording",
168
+ Stopping: "stopping",
169
+ Failed: "failed",
170
+ } as const;
171
+ export type RecordingStatus = (typeof RecordingStatusEnum)[keyof typeof RecordingStatusEnum];
172
+
173
+ export const TranscriptionStatusEnum = {
174
+ NotRequested: "not_requested",
175
+ Queued: "queued",
176
+ Processing: "processing",
177
+ Ready: "ready",
178
+ Failed: "failed",
179
+ Deleted: "deleted",
180
+ } as const;
181
+ export type TranscriptionStatus =
182
+ (typeof TranscriptionStatusEnum)[keyof typeof TranscriptionStatusEnum];
183
+
184
+ export const ConsentSubjectEnum = {
185
+ Terms: "terms",
186
+ Privacy: "privacy",
187
+ Recording: "recording",
188
+ Transcription: "transcription",
189
+ } as const;
190
+ export type ConsentSubject = (typeof ConsentSubjectEnum)[keyof typeof ConsentSubjectEnum];
191
+
192
+ export const ConsentDecisionEnum = {
193
+ Accepted: "accepted",
194
+ Declined: "declined",
195
+ Withdrawn: "withdrawn",
196
+ } as const;
197
+ export type ConsentDecision = (typeof ConsentDecisionEnum)[keyof typeof ConsentDecisionEnum];
198
+
199
+ export const ConsentActorTypeEnum = {
200
+ Patient: "patient",
201
+ Doctor: "doctor",
202
+ } as const;
203
+ export type ConsentActorType =
204
+ (typeof ConsentActorTypeEnum)[keyof typeof ConsentActorTypeEnum];
205
+
206
+ export const ArtifactTypeEnum = {
207
+ RecordingAudioVideo: "recording_audio_video",
208
+ RecordingAudio: "recording_audio",
209
+ Transcript: "transcript",
210
+ } as const;
211
+ export type ArtifactType = (typeof ArtifactTypeEnum)[keyof typeof ArtifactTypeEnum];
212
+
213
+ export const ArtifactStatusEnum = {
214
+ Pending: "pending",
215
+ Processing: "processing",
216
+ Ready: "ready",
217
+ Failed: "failed",
218
+ Deleting: "deleting",
219
+ Deleted: "deleted",
220
+ } as const;
221
+ export type ArtifactStatus = (typeof ArtifactStatusEnum)[keyof typeof ArtifactStatusEnum];
222
+
223
+ export const AccessGrantKindEnum = {
224
+ /** Capability de 128 bits no link `/qr`; reexecutável na janela da teleconsulta. */
225
+ QrCapability: "qr_capability",
226
+ /** Code one-time de exchange (5 min) — médico (`/launch`) ou filho do `/enter`. */
227
+ LaunchCode: "launch_code",
228
+ /** Sessão do browser (cookie `__session`), 15 min, rotativa. */
229
+ BrowserSession: "browser_session",
230
+ } as const;
231
+ export type AccessGrantKind = (typeof AccessGrantKindEnum)[keyof typeof AccessGrantKindEnum];
232
+
233
+ export const AccessGrantStateEnum = {
234
+ Active: "active",
235
+ Consumed: "consumed",
236
+ Blocked: "blocked",
237
+ Revoked: "revoked",
238
+ Expired: "expired",
239
+ } as const;
240
+ export type AccessGrantState =
241
+ (typeof AccessGrantStateEnum)[keyof typeof AccessGrantStateEnum];
242
+
243
+ /** Canal pelo qual o CTA de acesso do paciente é (re)enviado. `manual` só devolve a URL mascarada (smoke). */
244
+ export const AccessSendChannelKindEnum = {
245
+ WhatsApp: "whatsapp",
246
+ Email: "email",
247
+ Manual: "manual",
248
+ } as const;
249
+ export type AccessSendChannelKind =
250
+ (typeof AccessSendChannelKindEnum)[keyof typeof AccessSendChannelKindEnum];
251
+
252
+ export const EndedByTypeEnum = {
253
+ User: "user",
254
+ System: "system",
255
+ } as const;
256
+ export type EndedByType = (typeof EndedByTypeEnum)[keyof typeof EndedByTypeEnum];
257
+
258
+ export const TeleconsultationEventActorTypeEnum = {
259
+ User: "user",
260
+ Patient: "patient",
261
+ System: "system",
262
+ Provider: "provider",
263
+ } as const;
264
+ export type TeleconsultationEventActorType =
265
+ (typeof TeleconsultationEventActorTypeEnum)[keyof typeof TeleconsultationEventActorTypeEnum];
266
+
267
+ export const TeleconsultationEventSourceEnum = {
268
+ Api: "api",
269
+ Webhook: "webhook",
270
+ AppointmentReconciler: "appointment_reconciler",
271
+ RetentionJob: "retention_job",
272
+ /** `telemed_sweep`: expiração de não usadas, timeout de reconexão, outbox órfã. */
273
+ Sweep: "sweep",
274
+ } as const;
275
+ export type TeleconsultationEventSource =
276
+ (typeof TeleconsultationEventSourceEnum)[keyof typeof TeleconsultationEventSourceEnum];
277
+
278
+ /** Tipos de evento append-only (data-model §5 + acréscimos do plan). */
279
+ export const TeleconsultationEventTypeEnum = {
280
+ TeleconsultationCreated: "teleconsultation.created",
281
+ TeleconsultationRescheduled: "teleconsultation.rescheduled",
282
+ TeleconsultationCancelled: "teleconsultation.cancelled",
283
+ TeleconsultationExpired: "teleconsultation.expired",
284
+ /** Alteração tardia na agenda com consulta já iniciada — tratamento operacional, não apaga o encontro. */
285
+ TeleconsultationDiverged: "teleconsultation.diverged",
286
+ AccessCreated: "access.created",
287
+ AccessRevoked: "access.revoked",
288
+ AccessExchanged: "access.exchanged",
289
+ AccessVerificationFailed: "access.verification_failed",
290
+ AccessBlocked: "access.blocked",
291
+ HostClaimed: "host.claimed",
292
+ HostReassigned: "host.reassigned",
293
+ WaitingPolicyChanged: "waiting_policy.changed",
294
+ DoctorPrepared: "doctor.prepared",
295
+ DoctorJoined: "doctor.joined",
296
+ DoctorDisconnected: "doctor.disconnected",
297
+ DoctorRejoined: "doctor.rejoined",
298
+ DoctorLeft: "doctor.left",
299
+ PatientVerified: "patient.verified",
300
+ PatientWaiting: "patient.waiting",
301
+ PatientAdmitted: "patient.admitted",
302
+ PatientRejected: "patient.rejected",
303
+ PatientJoined: "patient.joined",
304
+ PatientDisconnected: "patient.disconnected",
305
+ PatientRejoined: "patient.rejoined",
306
+ PatientLeft: "patient.left",
307
+ RecordingConsentRequested: "recording.consent_requested",
308
+ RecordingConsentAccepted: "recording.consent_accepted",
309
+ RecordingConsentDeclined: "recording.consent_declined",
310
+ RecordingConsentWithdrawn: "recording.consent_withdrawn",
311
+ RecordingStarted: "recording.started",
312
+ RecordingStopped: "recording.stopped",
313
+ RecordingFailed: "recording.failed",
314
+ TranscriptionQueued: "transcription.queued",
315
+ TranscriptionReady: "transcription.ready",
316
+ TranscriptionFailed: "transcription.failed",
317
+ TranscriptionDeleted: "transcription.deleted",
318
+ ConsultationStarted: "consultation.started",
319
+ ConsultationReconnecting: "consultation.reconnecting",
320
+ ConsultationEnded: "consultation.ended",
321
+ ArtifactAccessed: "artifact.accessed",
322
+ ArtifactDeleted: "artifact.deleted",
323
+ EntitlementDenied: "entitlement.denied",
324
+ RolloutDenied: "rollout.denied",
325
+ PermissionDenied: "permission.denied",
326
+ /** Evento do provider desconhecido/ignorado — registrado, não processado. */
327
+ WebhookObserved: "webhook.observed",
328
+ } as const;
329
+ export type TeleconsultationEventType =
330
+ (typeof TeleconsultationEventTypeEnum)[keyof typeof TeleconsultationEventTypeEnum];
331
+
332
+ /** Outbox durável do webhook (data-model §6): claim/lease como o task-runner. */
333
+ export const WebhookEventStatusEnum = {
334
+ Pending: "pending",
335
+ Claimed: "claimed",
336
+ Completed: "completed",
337
+ Failed: "failed",
338
+ } as const;
339
+ export type WebhookEventStatus =
340
+ (typeof WebhookEventStatusEnum)[keyof typeof WebhookEventStatusEnum];
341
+
342
+ export const LegalTextStatusEnum = {
343
+ Draft: "draft",
344
+ Published: "published",
345
+ Retired: "retired",
346
+ } as const;
347
+ export type LegalTextStatus = (typeof LegalTextStatusEnum)[keyof typeof LegalTextStatusEnum];
348
+
349
+ export const TranscriptionProviderEnum = {
350
+ None: "none",
351
+ RealtimeKit: "realtimekit",
352
+ } as const;
353
+ export type TranscriptionProvider =
354
+ (typeof TranscriptionProviderEnum)[keyof typeof TranscriptionProviderEnum];
355
+
356
+ // ----- Value objects
357
+
358
+ export interface ITeleconsultationEndedBy {
359
+ type: EndedByType;
360
+ /** userId quando `type = user`. */
361
+ id?: string;
362
+ /** Ex.: `doctor_end`, `reconnect_timeout`, `appointment_cancelled`, `unused_expired`, `emergency_kill`. */
363
+ reason: string;
364
+ }
365
+
366
+ /** Snapshot do entitlement no momento da criação — o gate vivo continua sendo `tenant.modules`. */
367
+ export interface ITeleconsultationEntitlementSnapshot {
368
+ version: number;
369
+ capabilities: Record<string, boolean | number | string>;
370
+ }
371
+
372
+ export interface ITeleconsultationEventActor {
373
+ type: TeleconsultationEventActorType;
374
+ /** userId, participantId ou id opaco do provider — nunca nome/telefone. */
375
+ id?: string;
376
+ }
377
+
378
+ /** Metadata allowlisted — sem PII e sem conteúdo médico. */
379
+ export type TeleconsultationEventMetadata = Record<string, string | number | boolean>;
380
+
381
+ // ======================================================
382
+ // Documentos (por tenant)
383
+ // ======================================================
384
+
385
+ /** `.../teleconsultations/{teleconsultationId}` (data-model §1). */
386
+ export interface ITeleconsultation extends IFireDoc {
387
+ calendar_id: string;
388
+ appointment_id: string;
389
+ patient_id: string;
390
+ scheduled_professional_id?: string;
391
+ /** Claim transacional (D22): primeiro elegível vence; reassignment exige `configure` + motivo + evento. */
392
+ claimed_host_user_id?: string;
393
+ claimed_host_at?: Date | null;
394
+ mode: TeleconsultationMode;
395
+ status: TeleconsultationStatus;
396
+ waiting_policy: WaitingPolicy;
397
+ scheduled_start_at: Date;
398
+ scheduled_end_at: Date;
399
+ doctor_access_from: Date;
400
+ patient_access_from: Date;
401
+ unused_expires_at: Date;
402
+ reconnect_until?: Date | null;
403
+ started_at?: Date | null;
404
+ ended_at?: Date | null;
405
+ ended_by?: ITeleconsultationEndedBy | null;
406
+ provider: TeleconsultationProvider;
407
+ /** Id opaco do meeting no provider — nunca vai em external link nem no SPA. */
408
+ provider_meeting_id?: string;
409
+ active_provider_session_id?: string;
410
+ recording_status: RecordingStatus;
411
+ transcription_status: TranscriptionStatus;
412
+ entitlement_snapshot: ITeleconsultationEntitlementSnapshot;
413
+ /**
414
+ * Geração no `appointment-index`: incrementa quando o appointment volta a ser
415
+ * telemedicina depois de um estado terminal (nova teleconsulta, novo id).
416
+ */
417
+ generation: number;
418
+ /** Contagem de reagendamentos (SEQUENCE do ICS). */
419
+ reschedule_count?: number;
420
+ /** Última versão/data do appointment aplicada — descarta evento de reconciliação antigo. */
421
+ last_reconciled_appointment_version?: number | string | null;
422
+ last_reconciled_at?: Date | null;
423
+ /** Concorrência otimista: todo comando de estado é transação + `version`. */
424
+ version: number;
425
+ created_at: Date;
426
+ updated_at: Date;
427
+ }
428
+
429
+ /** `.../participants/{participantId}` (data-model §2). Id lógico estável; reconexão reutiliza o mesmo participante. */
430
+ export interface ITeleconsultationParticipant extends IFireDoc {
431
+ teleconsultation_id: string;
432
+ role: ParticipantRole;
433
+ /** userId quando `role = doctor`. */
434
+ actor_user_id?: string;
435
+ /** patientId quando `role = patient`. */
436
+ patient_id?: string;
437
+ /** Id do participante no provider (pode ser renovado; o id lógico é o doc). */
438
+ provider_participant_id?: string;
439
+ /** `custom_participant_id` opaco enviado ao provider — nunca o patientId. */
440
+ opaque_custom_participant_id: string;
441
+ state: ParticipantState;
442
+ identity_level?: IdentityLevel;
443
+ admitted_at?: Date | null;
444
+ connected_at?: Date | null;
445
+ last_seen_at?: Date | null;
446
+ disconnected_at?: Date | null;
447
+ reconnect_count: number;
448
+ created_at: Date;
449
+ updated_at: Date;
450
+ }
451
+
452
+ export interface ITeleconsultationConsentEvidence {
453
+ ui_locale: string;
454
+ /** Hash da sessão pública — nunca o cookie/sid bruto. */
455
+ session_id_hash: string;
456
+ }
457
+
458
+ /** `.../consents/{consentId}` (data-model §3). Guarda versão/hash do texto, não o texto. */
459
+ export interface ITeleconsultationConsent extends IFireDoc {
460
+ teleconsultation_id: string;
461
+ subject: ConsentSubject;
462
+ decision: ConsentDecision;
463
+ terms_version: string;
464
+ /** `content_hash` do legal text vigente na solicitação. */
465
+ terms_content_hash?: string;
466
+ participant_id: string;
467
+ actor_type: ConsentActorType;
468
+ occurred_at: Date;
469
+ correlation_id: string;
470
+ /** Vincula aceite/recusa/retirada de gravação à solicitação específica (data-model §11). */
471
+ recording_request_id?: string;
472
+ evidence: ITeleconsultationConsentEvidence;
473
+ }
474
+
475
+ /** `.../artifacts/{artifactId}` (data-model §4). Download só por endpoint `artifacts_read` + URL assinada ≤ 5 min. */
476
+ export interface ITeleconsultationArtifact extends IFireDoc {
477
+ teleconsultation_id: string;
478
+ type: ArtifactType;
479
+ /** Segmento (1..n) — mais de uma sessão física do provider = vários segmentos da mesma teleconsulta. */
480
+ segment: number;
481
+ status: ArtifactStatus;
482
+ provider_artifact_id?: string;
483
+ provider_session_id?: string;
484
+ recording_request_id?: string;
485
+ storage_bucket?: string;
486
+ storage_object?: string;
487
+ content_type?: string;
488
+ size_bytes?: number;
489
+ checksum?: string;
490
+ started_at?: Date | null;
491
+ ended_at?: Date | null;
492
+ /** Retenção: o sweep deleta o objeto e só então marca `deleted` (auditoria preservada). */
493
+ expires_at?: Date | null;
494
+ error_code?: string;
495
+ created_at: Date;
496
+ updated_at: Date;
497
+ }
498
+
499
+ /** `.../events/{eventId}` (data-model §5) — append-only. */
500
+ export interface ITeleconsultationEvent extends IFireDoc {
501
+ teleconsultation_id: string;
502
+ type: TeleconsultationEventType;
503
+ occurred_at: Date;
504
+ actor: ITeleconsultationEventActor;
505
+ source: TeleconsultationEventSource;
506
+ correlation_id: string;
507
+ causation_id?: string;
508
+ idempotency_key?: string;
509
+ from_status?: TeleconsultationStatus | string;
510
+ to_status?: TeleconsultationStatus | string;
511
+ metadata?: TeleconsultationEventMetadata;
512
+ }
513
+
514
+ /**
515
+ * `.../webhook-events/{providerEventId}` (data-model §6). Dedup + outbox durável:
516
+ * a recepção grava `pending` ANTES do 2xx; o worker faz claim, processa
517
+ * idempotentemente e marca `completed`. Sem payload bruto — só campos allowlisted.
518
+ */
519
+ export interface ITeleconsultationWebhookEvent extends IFireDoc {
520
+ teleconsultation_id?: string;
521
+ provider: TeleconsultationProvider;
522
+ /** Id do evento no provider (= doc id; ex.: `rtk-uuid`). */
523
+ provider_event_id: string;
524
+ event_type: string;
525
+ /** Hash do corpo bruto (integridade/dedup) — nunca o corpo. */
526
+ payload_hash?: string;
527
+ /** Campos allowlisted do payload (ids opacos, status) — sem PII. */
528
+ sanitized_payload?: Record<string, string | number | boolean | null>;
529
+ status: WebhookEventStatus;
530
+ received_at: Date;
531
+ claimed_at?: Date | null;
532
+ claimed_by?: string;
533
+ lease_until?: Date | null;
534
+ processed_at?: Date | null;
535
+ attempt: number;
536
+ next_attempt_at?: Date | null;
537
+ result?: string;
538
+ error_code?: string;
539
+ correlation_id: string;
540
+ /** TTL nativo do Firestore. */
541
+ expires_at: Date;
542
+ }
543
+
544
+ /** `.../access-grants/{grantId}` (data-model §7). Segredo nunca persistido bruto. */
545
+ export interface ITeleconsultationAccessGrant extends IFireDoc {
546
+ teleconsultation_id: string;
547
+ role: ParticipantRole;
548
+ kind: AccessGrantKind;
549
+ /** HMAC (chave rotacionável) do segredo; comparação constant-time. */
550
+ secret_hash: string;
551
+ /** Hash do destinatário (telefone/e-mail) — evita reenvio para contato diferente do agendamento. */
552
+ recipient_ref_hash?: string;
553
+ actor_user_id?: string;
554
+ participant_id?: string;
555
+ state: AccessGrantState;
556
+ expires_at: Date;
557
+ consumed_at?: Date | null;
558
+ revoked_at?: Date | null;
559
+ revoked_by?: string;
560
+ last_used_at?: Date | null;
561
+ failed_attempts: number;
562
+ failed_window_started_at?: Date | null;
563
+ blocked_until?: Date | null;
564
+ /** `launch_code` filho de `qr_capability`; `browser_session` filho do code/capability. */
565
+ parent_grant_id?: string;
566
+ /** Canal do envio que originou o grant (`qr_capability`). */
567
+ channel_kind?: AccessSendChannelKind;
568
+ /** Doc `quick-reply-actions/{actionId}` ligado ao grant — cancelado no revoke. */
569
+ quick_reply_action_id?: string;
570
+ version: number;
571
+ created_at: Date;
572
+ updated_at: Date;
573
+ }
574
+
575
+ /**
576
+ * `.../appointment-index/{calendarId}__{appointmentId}` (plan §2). Unicidade da
577
+ * teleconsulta ativa por appointment; o reconciliador transaciona sobre este doc.
578
+ */
579
+ export interface ITelemedicineAppointmentIndex extends IFireDoc {
580
+ calendar_id: string;
581
+ appointment_id: string;
582
+ active_teleconsultation_id: string | null;
583
+ generation: number;
584
+ last_reconciled_appointment_version?: number | string | null;
585
+ updated_at: Date;
586
+ }
587
+
588
+ // ======================================================
589
+ // Documentos de plataforma (`platform/evo-telemedicine/**`, só backend)
590
+ // ======================================================
591
+
592
+ /**
593
+ * `platform/evo-telemedicine/legal-texts/{subject}-{locale}-{version}` (data-model §9).
594
+ * Publish explícito, nunca overwrite. NÃO estende `IFireGlobalDoc`: aqui `version`
595
+ * é a versão do TEXTO (string, ex.: `v1`), não o contador de concorrência.
596
+ */
597
+ export interface ITelemedicineLegalText {
598
+ readonly id: string;
599
+ subject: ConsentSubject;
600
+ locale: string;
601
+ version: string;
602
+ title: string;
603
+ /** HTML sanitizado; placeholders allowlisted (`{{clinic_name}}`, `{{clinic_contact}}`). */
604
+ content_html: string;
605
+ content_hash: string;
606
+ published_at?: Date | null;
607
+ effective_at?: Date | null;
608
+ status: LegalTextStatus;
609
+ placeholders?: string[];
610
+ created_at?: Date | null;
611
+ updated_at?: Date | null;
612
+ }
613
+
614
+ /** `platform/evo-telemedicine/config/rollout` (plan §2). Deny se ausente; cache 30 s. */
615
+ export interface ITelemedicineRollout extends IFireGlobalDoc {
616
+ create_enabled: boolean;
617
+ join_enabled: boolean;
618
+ recording_enabled: boolean;
619
+ /** Só pode ser `true` em produção após o gate de D12. */
620
+ transcription_enabled: boolean;
621
+ /** Bloqueia tudo, inclusive consultas ao vivo (D19). */
622
+ emergency_kill: boolean;
623
+ updated_at: Date;
624
+ updated_by: string;
625
+ }
626
+
627
+ /** `platform/evo-telemedicine/config/transcription` (plan F9). */
628
+ export interface ITelemedicineTranscriptionConfig extends IFireGlobalDoc {
629
+ provider: TranscriptionProvider;
630
+ /** `realtimekit` com `false` só funciona em staging. */
631
+ production_gate_approved: boolean;
632
+ approved_by?: string;
633
+ approved_at?: Date | null;
634
+ }
635
+
636
+ /** `platform/evo-telemedicine/grant-index/{hmac(cap)}` (plan §2). Só resolve o path; o grant tenant-scoped é a verdade. */
637
+ export interface ITelemedicineGrantIndex extends IFireGlobalDoc {
638
+ tenant: string;
639
+ teleconsultation_id: string;
640
+ grant_id: string;
641
+ /** TTL nativo. */
642
+ expires_at: Date;
643
+ }
644
+
645
+ // ======================================================
646
+ // Auditoria de entitlement (`.../entitlement-audit/{id}`, data-model §8)
647
+ // ======================================================
648
+
649
+ export const EntitlementAuditActionEnum = {
650
+ Set: "set",
651
+ Override: "override",
652
+ Revoke: "revoke",
653
+ } as const;
654
+ export type EntitlementAuditAction =
655
+ (typeof EntitlementAuditActionEnum)[keyof typeof EntitlementAuditActionEnum];
656
+
657
+ export interface IEntitlementAuditActor {
658
+ type: "user" | "system" | "script";
659
+ id?: string;
660
+ }
661
+
662
+ /** Fora do doc do tenant, como pede o data-model. `before/after` são a projeção sanitizada (sem preço/contrato). */
663
+ export interface ITelemedicineEntitlementAudit extends IFireDoc {
664
+ module: string;
665
+ action: EntitlementAuditAction;
666
+ before?: ITenantModuleEntitlement | null;
667
+ after: ITenantModuleEntitlement;
668
+ actor: IEntitlementAuditActor;
669
+ reason: string;
670
+ occurred_at: Date;
671
+ correlation_id?: string;
672
+ }
673
+
674
+ // ======================================================
675
+ // Códigos de erro (plan §4 — `{ ok: false, error: '<CODE>', message? }` com status estável)
676
+ // ======================================================
677
+
678
+ export const TelemedicineErrorCode = {
679
+ /** 403 — entitlement ausente/inativo (sem bypass de super admin). */
680
+ TenantModuleDisabled: "TENANT_MODULE_DISABLED",
681
+ /** 403 — kill switch operacional (`create|join|recording|transcription`). */
682
+ RolloutDisabled: "ROLLOUT_DISABLED",
683
+ /** 403 — `emergency_kill` ligado. */
684
+ EmergencyKill: "EMERGENCY_KILL",
685
+ /** 403 — RBAC. */
686
+ PermissionDenied: "PERMISSION_DENIED",
687
+ /** 403 — capability do entitlement ausente/false (`recording`, `transcription`, `auto_admit`). */
688
+ CapabilityDisabled: "CAPABILITY_DISABLED",
689
+ /** 404 — teleconsulta/artefato/grant inexistente (ou genérico, em rota pública). */
690
+ NotFound: "NOT_FOUND",
691
+ /** 409 — fora da janela (antes de `*_access_from` ou após `unused_expires_at`). */
692
+ AccessWindowClosed: "ACCESS_WINDOW_CLOSED",
693
+ /** 409 — outro médico já fez o claim de host. */
694
+ HostClaimedByOther: "HOST_CLAIMED_BY_OTHER",
695
+ /** 409 — comando exige médico claimant presente (admit/end/recording). */
696
+ HostRequired: "HOST_REQUIRED",
697
+ /** 409 — termos/privacidade (ou gravação) não aceitos na versão vigente. */
698
+ ConsentRequired: "CONSENT_REQUIRED",
699
+ /** 409 — paciente ainda não verificou identidade. */
700
+ IdentityRequired: "IDENTITY_REQUIRED",
701
+ /** 401 — verificação de identidade falhou (erro neutro: não diz qual campo). */
702
+ IdentityVerificationFailed: "IDENTITY_VERIFICATION_FAILED",
703
+ /** 429 — grant bloqueado por excesso de falhas. */
704
+ GrantBlocked: "GRANT_BLOCKED",
705
+ /** 401 — grant/code inválido, consumido, expirado ou revogado (genérico por design). */
706
+ GrantInvalid: "GRANT_INVALID",
707
+ /** 401 — cookie `__session` ausente/inválido/expirado. */
708
+ SessionInvalid: "SESSION_INVALID",
709
+ /** 410 — estado terminal (`ended|cancelled|expired`). */
710
+ TeleconsultationTerminal: "TELECONSULTATION_TERMINAL",
711
+ /** 409 — transição de estado não permitida (spec §10). */
712
+ InvalidTransition: "INVALID_TRANSITION",
713
+ /** 409 — `expected_version` divergente. */
714
+ VersionConflict: "VERSION_CONFLICT",
715
+ /** 409 — política de espera só muda até o paciente entrar. */
716
+ WaitingPolicyLocked: "WAITING_POLICY_LOCKED",
717
+ /** 409 — já existe gravação ativa/pendente. */
718
+ RecordingActive: "RECORDING_ACTIVE",
719
+ /** 502 — provider falhou ao gravar (não derruba a consulta). */
720
+ RecordingFailed: "RECORDING_FAILED",
721
+ /** 403 — transcrição em produção sem gate aprovado (D12). */
722
+ TranscriptionGateClosed: "TRANSCRIPTION_GATE_CLOSED",
723
+ /** 409 — artefato não está `ready`. */
724
+ ArtifactNotReady: "ARTIFACT_NOT_READY",
725
+ /** 502 — erro do provider de mídia. */
726
+ ProviderError: "PROVIDER_ERROR",
727
+ /** 429 — rate limit por IP-hash/grant. */
728
+ RateLimited: "RATE_LIMITED",
729
+ /** 400 — body/params inválidos. */
730
+ ValidationError: "VALIDATION_ERROR",
731
+ } as const;
732
+
733
+ export type TelemedicineErrorCode =
734
+ (typeof TelemedicineErrorCode)[keyof typeof TelemedicineErrorCode];
735
+
736
+ export interface ITelemedicineErrorResponse {
737
+ ok: false;
738
+ error: TelemedicineErrorCode;
739
+ message?: string;
740
+ error_id?: string;
741
+ }
742
+
743
+ // ======================================================
744
+ // DTOs públicos — HTTP (plan §4). Datas em ISO 8601 (`string`).
745
+ // ======================================================
746
+
747
+ export interface ITeleconsultationParticipantDto {
748
+ participant_id: string;
749
+ role: ParticipantRole;
750
+ state: ParticipantState;
751
+ identity_level?: IdentityLevel;
752
+ admitted_at?: string | null;
753
+ connected_at?: string | null;
754
+ last_seen_at?: string | null;
755
+ disconnected_at?: string | null;
756
+ reconnect_count: number;
757
+ }
758
+
759
+ /** Estado do CTA de acesso do paciente, para a recepção — destinatário só mascarado. */
760
+ export interface ITeleconsultationAccessSummaryDto {
761
+ grant_id?: string;
762
+ grant_state?: AccessGrantState;
763
+ channel_kind?: AccessSendChannelKind;
764
+ /** Ex.: `+55 11 9****-1234`, `l***@dominio.com`. */
765
+ recipient_masked?: string;
766
+ last_sent_at?: string | null;
767
+ expires_at?: string | null;
768
+ }
769
+
770
+ /** `GET /v1/appointments/:calendarId/:appointmentId/teleconsultation` (telemed_admin). */
771
+ export interface ITeleconsultationSummaryDto {
772
+ id: string;
773
+ calendar_id: string;
774
+ appointment_id: string;
775
+ patient_id: string;
776
+ scheduled_professional_id?: string;
777
+ claimed_host_user_id?: string;
778
+ claimed_host_at?: string | null;
779
+ mode: TeleconsultationMode;
780
+ status: TeleconsultationStatus;
781
+ waiting_policy: WaitingPolicy;
782
+ scheduled_start_at: string;
783
+ scheduled_end_at: string;
784
+ doctor_access_from: string;
785
+ patient_access_from: string;
786
+ unused_expires_at: string;
787
+ reconnect_until?: string | null;
788
+ started_at?: string | null;
789
+ ended_at?: string | null;
790
+ ended_by?: ITeleconsultationEndedBy | null;
791
+ recording_status: RecordingStatus;
792
+ transcription_status: TranscriptionStatus;
793
+ entitlement_snapshot: ITeleconsultationEntitlementSnapshot;
794
+ /** Capabilities EFETIVAS agora (entitlement vivo AND rollout): `recording`, `transcription`, `auto_admit`. */
795
+ capabilities_now: Record<string, boolean>;
796
+ generation: number;
797
+ version: number;
798
+ participants?: ITeleconsultationParticipantDto[];
799
+ access?: ITeleconsultationAccessSummaryDto;
800
+ }
801
+
802
+ /** `POST /v1/teleconsultations/:id/launch` → `{ launch_url }` (fragment `#code=`; SPA faz o exchange). */
803
+ export interface ILaunchResponseDto {
804
+ ok: true;
805
+ launch_url: string;
806
+ /** Expiração do code one-time (5 min). */
807
+ expires_at: string;
808
+ claimed_host_user_id: string;
809
+ }
810
+
811
+ export interface IRoomSessionWindowDto {
812
+ scheduled_start_at: string;
813
+ scheduled_end_at: string;
814
+ doctor_access_from: string;
815
+ patient_access_from: string;
816
+ unused_expires_at: string;
817
+ reconnect_until?: string | null;
818
+ }
819
+
820
+ export interface IRoomConsentRefDto {
821
+ subject: ConsentSubject;
822
+ terms_version: string;
823
+ }
824
+
825
+ /**
826
+ * `GET /api/room/v1/session` — estado SANITIZADO: sem tenant id, sem ids do
827
+ * provider; nome do profissional/clínica só depois da verificação.
828
+ */
829
+ export interface IRoomSessionDto {
830
+ ok: true;
831
+ role: ParticipantRole;
832
+ participant_id: string;
833
+ participant_state: ParticipantState;
834
+ teleconsultation_status: TeleconsultationStatus;
835
+ mode: TeleconsultationMode;
836
+ waiting_policy: WaitingPolicy;
837
+ window: IRoomSessionWindowDto;
838
+ identity_level: IdentityLevel;
839
+ /** Paciente precisa passar por `POST /identity/verify` antes do join. */
840
+ identity_required: boolean;
841
+ recording_status: RecordingStatus;
842
+ transcription_status: TranscriptionStatus;
843
+ /** Textos vigentes que ainda precisam de aceite antes do join. */
844
+ required_consents: IRoomConsentRefDto[];
845
+ accepted_consents: IRoomConsentRefDto[];
846
+ /** Capabilities efetivas para esta sessão (entitlement AND rollout AND papel). */
847
+ capabilities: Record<string, boolean>;
848
+ professional_display_name?: string;
849
+ clinic_display_name?: string;
850
+ /** Expiração do access da sessão (15 min, rotativo via `/session/refresh`). */
851
+ session_expires_at: string;
852
+ server_time: string;
853
+ }
854
+
855
+ /** `POST /api/room/v1/join` — token do provider fica em MEMÓRIA do SPA; nunca em storage. */
856
+ export interface IRoomJoinDto {
857
+ ok: true;
858
+ provider_token: string;
859
+ /** Id opaco do meeting (não é o `provider_meeting_id`). */
860
+ meeting_id_opaque: string;
861
+ preset: string;
862
+ participant_id: string;
863
+ custom_participant_id: string;
864
+ /** true quando o paciente cai na waiting room (`manual_admit` ou médico ausente). */
865
+ waiting: boolean;
866
+ /** Expiração do token do provider, quando conhecida. */
867
+ token_expires_at?: string;
868
+ }
869
+
870
+ /** `POST /api/room/v1/admit` — o SPA só chama `acceptWaitingRoomRequest` DEPOIS de receber isto. */
871
+ export interface IAdmissionGrantDto {
872
+ ok: true;
873
+ participant_id: string;
874
+ provider_participant_id: string;
875
+ admitted_at: string;
876
+ teleconsultation_status: TeleconsultationStatus;
877
+ }
878
+
879
+ export interface ITeleconsultationArtifactDto {
880
+ id: string;
881
+ type: ArtifactType;
882
+ segment: number;
883
+ status: ArtifactStatus;
884
+ content_type?: string;
885
+ size_bytes?: number;
886
+ started_at?: string | null;
887
+ ended_at?: string | null;
888
+ expires_at?: string | null;
889
+ error_code?: string;
890
+ created_at: string;
891
+ }
892
+
893
+ /** `POST /v1/teleconsultations/:id/artifacts/:artifactId/signed-url` — URL ≤ 5 min; evento `artifact.accessed`. */
894
+ export interface ISignedUrlResponseDto {
895
+ ok: true;
896
+ url: string;
897
+ expires_at: string;
898
+ }
899
+
900
+ export interface ITeleconsultationEventDto {
901
+ id: string;
902
+ type: TeleconsultationEventType;
903
+ occurred_at: string;
904
+ actor: ITeleconsultationEventActor;
905
+ source: TeleconsultationEventSource;
906
+ correlation_id: string;
907
+ causation_id?: string;
908
+ from_status?: string;
909
+ to_status?: string;
910
+ metadata?: TeleconsultationEventMetadata;
911
+ }
912
+
913
+ /** `GET /v1/teleconsultations/:id/events` — paginação por cursor. */
914
+ export interface ITeleconsultationEventsPageDto {
915
+ ok: true;
916
+ events: ITeleconsultationEventDto[];
917
+ next_cursor?: string;
918
+ }
919
+
920
+ export interface ITelemedicineLegalTextDto {
921
+ subject: ConsentSubject;
922
+ locale: string;
923
+ version: string;
924
+ title: string;
925
+ content_html: string;
926
+ content_hash: string;
927
+ effective_at?: string | null;
928
+ }
929
+
930
+ /** `GET /api/room/v1/legal-texts`. */
931
+ export interface ILegalTextsResponseDto {
932
+ ok: true;
933
+ texts: ITelemedicineLegalTextDto[];
934
+ }
935
+
936
+ /** `POST /api/room/v1/identity/verify` — sucesso; falha é `IDENTITY_VERIFICATION_FAILED` neutro. */
937
+ export interface IIdentityVerifyResponseDto {
938
+ ok: true;
939
+ identity_level: IdentityLevel;
940
+ }
941
+
942
+ /** `POST /v1/teleconsultations/:id/access/send`. */
943
+ export interface IAccessSendResponseDto {
944
+ ok: true;
945
+ grant_id: string;
946
+ channel_kind: AccessSendChannelKind;
947
+ recipient_masked?: string;
948
+ expires_at: string;
949
+ /** Só para `channel_kind = manual` (smoke): URL mascarada, nunca a capability inteira. */
950
+ url_masked?: string;
951
+ /** Id da task `telemedicine_join.send` criada, quando houver canal. */
952
+ task_id?: string;
953
+ }
954
+
955
+ // ======================================================
956
+ // Comandos (request bodies). `tenant` sempre do contexto, nunca do body.
957
+ // ======================================================
958
+
959
+ export interface IWaitingPolicyCommand {
960
+ waiting_policy: WaitingPolicy;
961
+ expected_version?: number;
962
+ }
963
+
964
+ export interface IReassignHostCommand {
965
+ to_user_id: string;
966
+ reason: string;
967
+ expected_version?: number;
968
+ }
969
+
970
+ export interface IAccessSendCommand {
971
+ channel_kind: AccessSendChannelKind;
972
+ /** Revoga a capability anterior antes de emitir a nova. */
973
+ resend?: boolean;
974
+ }
975
+
976
+ export interface IAccessRevokeCommand {
977
+ /** Ausente = todos os grants ativos do paciente na teleconsulta. */
978
+ grant_id?: string;
979
+ reason: string;
980
+ }
981
+
982
+ export interface IEndCommand {
983
+ reason?: string;
984
+ expected_version?: number;
985
+ }
986
+
987
+ /** `POST /api/room/v1/exchange` — code one-time do fragment `#code=`. */
988
+ export interface IExchangeCommand {
989
+ code: string;
990
+ }
991
+
992
+ /** `POST /api/room/v1/identity/verify` — DOB normalizada `YYYY-MM-DD`; comparação exata após normalização. */
993
+ export interface IIdentityVerifyCommand {
994
+ display_name: string;
995
+ birth_date?: string;
996
+ }
997
+
998
+ /** `POST /api/room/v1/consents` — termos/privacidade (gravação usa `IRecordingConsentCommand`). */
999
+ export interface IConsentCommand {
1000
+ subject: ConsentSubject;
1001
+ terms_version: string;
1002
+ decision: Extract<ConsentDecision, "accepted" | "declined">;
1003
+ }
1004
+
1005
+ /** `POST /api/room/v1/presence`. */
1006
+ export interface IPresenceCommand {
1007
+ state: PresenceState;
1008
+ provider_participant_id?: string;
1009
+ }
1010
+
1011
+ /** `POST /api/room/v1/admit` e `/reject`. */
1012
+ export interface IAdmitCommand {
1013
+ participant_id: string;
1014
+ }
1015
+
1016
+ export interface IRejectCommand {
1017
+ participant_id: string;
1018
+ reason?: string;
1019
+ }
1020
+
1021
+ /** `POST /api/room/v1/recording/request` (médico com `record`) — idempotente por `recording_request_id`. */
1022
+ export interface IRecordingRequestCommand {
1023
+ recording_request_id: string;
1024
+ }
1025
+
1026
+ /** `POST /api/room/v1/recording/accept|decline|withdraw` (paciente) — versão do texto = a da solicitação. */
1027
+ export interface IRecordingConsentCommand {
1028
+ recording_request_id: string;
1029
+ terms_version: string;
1030
+ }
1031
+
1032
+ /** `POST /api/room/v1/recording/stop` (médico) — repetível. */
1033
+ export interface IRecordingStopCommand {
1034
+ recording_request_id?: string;
1035
+ }
1036
+
1037
+ export interface ITelemetryEvent {
1038
+ name: string;
1039
+ at: string;
1040
+ attrs?: Record<string, string | number | boolean>;
1041
+ }
1042
+
1043
+ /** `POST /api/room/v1/telemetry` — nomes allowlisted no backend; sem PII. */
1044
+ export interface ITelemetryCommand {
1045
+ events: ITelemetryEvent[];
1046
+ }