@hipnation-truth/sdk 0.15.3 → 0.16.0

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.
package/dist/react.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { ConvexReactClient } from 'convex/react';
4
+ import * as convex_browser from 'convex/browser';
5
+ import { ConvexHttpClient } from 'convex/browser';
4
6
 
5
7
  /**
6
8
  * React hooks for Truth SDK — reactive conversation + message data.
@@ -437,7 +439,7 @@ declare function useAppointment(id: string): any;
437
439
  * Subscribe to an appointment by its Elation ID.
438
440
  */
439
441
  declare function useAppointmentByElationId(elationId: string): any;
440
- interface Physician {
442
+ interface Physician$1 {
441
443
  _id: string;
442
444
  elationId: number;
443
445
  firstName?: string;
@@ -666,6 +668,272 @@ interface UseNotificationsResult {
666
668
  }
667
669
  declare function useNotifications(options: UseNotificationsOptions): UseNotificationsResult;
668
670
 
671
+ /**
672
+ * React hook for patient family-member lookup — Truth SDK.
673
+ *
674
+ * Replaces the CommHub Hasura `useFamilyMembersQuery` (backed by
675
+ * `queries/family_members.graphql`). Queries the Convex
676
+ * `patients:listFamilyMembers` function, which returns the set of
677
+ * patients that share a `familyId` OR share at least one phone number
678
+ * with the reference patient, excluding the reference patient themselves.
679
+ *
680
+ * Arguments mirror the Hasura `FamilyMembers` GraphQL query variables:
681
+ * - `familyId` → `$familyId` (Hint family/account id)
682
+ * - `phoneNumbers` → `$phoneNumbers` (patient's phone numbers)
683
+ * - `excludeHintId` → `$excludePatientId` (skip the reference patient)
684
+ *
685
+ * Must be used within a `<TruthProvider />`.
686
+ *
687
+ * @example
688
+ * ```tsx
689
+ * import { usePatientFamilyMembers } from '@hipnation-truth/sdk/react/patient-family';
690
+ *
691
+ * function FamilyPanel({ hintId, familyId, phones }: Props) {
692
+ * const { data: members, loading } = usePatientFamilyMembers({
693
+ * familyId,
694
+ * phoneNumbers: phones,
695
+ * excludeHintId: hintId,
696
+ * });
697
+ * if (loading) return <Spinner />;
698
+ * return members?.map((m) => <div key={m._id}>{m.firstName} {m.lastName}</div>);
699
+ * }
700
+ * ```
701
+ */
702
+
703
+ /**
704
+ * Shape of a single patient row returned by `patients:listFamilyMembers`.
705
+ * Mirrors the fields CommHub's `FamilyMembersQuery` selected from Hasura:
706
+ * - `id` → `_id` (Convex document id)
707
+ * - `name` → `firstName + " " + lastName`
708
+ * - `hint_id` → `hintId`
709
+ * - `elation_id` → `elationId`
710
+ * - `family_id` → `familyId`
711
+ * - `phone_numbers` → `phones[].number`
712
+ */
713
+ interface FamilyMemberRow {
714
+ _id: string;
715
+ _creationTime: number;
716
+ firstName: string;
717
+ lastName: string;
718
+ elationId?: string;
719
+ hintId?: string;
720
+ familyId?: string;
721
+ phones: Array<{
722
+ type?: string;
723
+ number: string;
724
+ }>;
725
+ membershipStatus?: string;
726
+ sources: string[];
727
+ lastSyncedAt: string;
728
+ }
729
+ /** Arguments for `usePatientFamilyMembers`. */
730
+ interface UsePatientFamilyMembersInput {
731
+ /**
732
+ * Hint family/account id — when present, all patients sharing this id
733
+ * are returned (matches the Hasura `family_id` column).
734
+ *
735
+ * BACKFILL NEEDED: `familyId` was added to the Convex `patients` schema
736
+ * in this PR. Existing patient rows will return empty for this branch
737
+ * until the Hint patient upsert path (upsertFromHint / basic-refresh)
738
+ * is updated to write this field, or a one-time backfill script runs.
739
+ * The phone-number fallback continues to work in the interim.
740
+ */
741
+ familyId?: string | null;
742
+ /**
743
+ * Patient's phone numbers — patients sharing at least one phone number
744
+ * are included as family members (mirrors Hasura `phone_numbers._overlap`).
745
+ */
746
+ phoneNumbers?: string[] | null;
747
+ /**
748
+ * Hint patient ID of the reference patient to exclude from results
749
+ * (mirrors Hasura `$excludePatientId`).
750
+ */
751
+ excludeHintId?: string | null;
752
+ }
753
+ /**
754
+ * Subscribe to family members of a patient in real time.
755
+ *
756
+ * Returns all patients that share the same `familyId` OR share at least one
757
+ * phone number with the reference patient, sorted by name. The reference
758
+ * patient is excluded via `excludeHintId`.
759
+ *
760
+ * Pass `undefined` (or an object where all fields are undefined/null) to
761
+ * skip the query — returns `{ data: undefined, loading: false }`.
762
+ *
763
+ * @param input - Query inputs. The query is skipped if `input` is undefined
764
+ * or all fields are falsy.
765
+ */
766
+ declare function usePatientFamilyMembers(input: UsePatientFamilyMembersInput | undefined): UseQueryResult<FamilyMemberRow[]>;
767
+
768
+ /**
769
+ * React hook for Truth SDK — reactive patient search.
770
+ *
771
+ * Wraps the `patients:search` Convex query so CommHub consumers
772
+ * (and other frontends) can search patients by name, phone, or email
773
+ * with live-updating results and optional office scoping, without
774
+ * managing Convex subscriptions themselves.
775
+ *
776
+ * **Hook contract:** returns `UseQueryResult<PatientSearchResult[]>`.
777
+ * - `data` is `undefined` while loading or when the query is skipped
778
+ * (empty `query` string or `query === undefined`).
779
+ * - `loading` is `true` only while a real subscription is in-flight.
780
+ * - `error` is reserved for SDK-side validation; Convex query errors
781
+ * propagate as React errors and should be caught with an error
782
+ * boundary.
783
+ *
784
+ * **Backed by `patients:search`** — added in agent-A-search.
785
+ * Multi-word queries split on whitespace; every token must match at
786
+ * least one of firstName / lastName / email / phone digits.
787
+ *
788
+ * Must be used within `<TruthProvider />` (see `./provider`).
789
+ *
790
+ * @example
791
+ * ```tsx
792
+ * import { usePatientSearch } from '@hipnation-truth/sdk/react/patient-search';
793
+ *
794
+ * function PatientPicker({ officeId }: { officeId?: string }) {
795
+ * const [query, setQuery] = useState('');
796
+ * const { data: patients, loading } = usePatientSearch({ query, officeId });
797
+ * return (
798
+ * <>
799
+ * <input value={query} onChange={e => setQuery(e.target.value)} />
800
+ * {loading && <Spinner />}
801
+ * {patients?.map(p => <PatientRow key={p.id} patient={p} />)}
802
+ * </>
803
+ * );
804
+ * }
805
+ * ```
806
+ */
807
+
808
+ /**
809
+ * A single patient result from `patients:search`.
810
+ *
811
+ * Fields are normalised across the `patients` + `hintPatients` tables
812
+ * so callers get a flat, consistent object regardless of which source
813
+ * contributed the match.
814
+ */
815
+ interface PatientSearchResult {
816
+ /** Convex `_id` of the matching `patients` or `hintPatients` row. */
817
+ id: string;
818
+ /** Hint patient id — present when the row originates from Hint. */
819
+ hintId: string | undefined;
820
+ firstName: string;
821
+ lastName: string;
822
+ /** ISO date string (YYYY-MM-DD) — undefined when not available. */
823
+ dob: string | undefined;
824
+ /** Email addresses associated with the patient. */
825
+ emails: string[];
826
+ /**
827
+ * Raw phone strings from the source table. Callers should
828
+ * strip non-digits for dialling; the first entry is the primary
829
+ * contact number.
830
+ */
831
+ phones: string[];
832
+ /** Hint office id — present for Hint-sourced results. */
833
+ officeId: string | undefined;
834
+ /** Human-readable office name — present when officeId is set. */
835
+ officeName: string | undefined;
836
+ }
837
+ interface UsePatientSearchOptions {
838
+ /**
839
+ * The search term entered by the user. Multi-word queries are split
840
+ * on whitespace and every token must match. Pass `""` or `undefined`
841
+ * to skip the query and get `{ data: undefined, loading: false }`.
842
+ */
843
+ query: string | undefined;
844
+ /**
845
+ * When supplied, restricts results to patients whose authoritative
846
+ * Hint office id matches. Maps to the `officeId` arg on the Convex
847
+ * query and uses the `hintPatients.by_officeId` index path.
848
+ */
849
+ officeId?: string | null;
850
+ /**
851
+ * `"fuzzy"` (default) uses Convex full-text search indexes for name
852
+ * queries; `"exact"` uses a bounded scan-filter only. Most callers
853
+ * should leave this at the default.
854
+ */
855
+ mode?: "fuzzy" | "exact";
856
+ /** Maximum number of results to return (server caps at 200). */
857
+ limit?: number;
858
+ }
859
+ /**
860
+ * Subscribe to a live patient search backed by `patients:search` on the
861
+ * Truth Convex deployment.
862
+ *
863
+ * Results update reactively whenever the underlying `patients` or
864
+ * `hintPatients` tables change — no polling required.
865
+ *
866
+ * @param options - Search options (see `UsePatientSearchOptions`).
867
+ * @returns `UseQueryResult<PatientSearchResult[]>` — always defined
868
+ * once a non-empty `query` is provided and the subscription resolves.
869
+ */
870
+ declare function usePatientSearch(options: UsePatientSearchOptions): UseQueryResult<PatientSearchResult[]>;
871
+
872
+ /**
873
+ * Patient interfaces for the Truth SDK.
874
+ */
875
+ /**
876
+ * Normalized patient record from the Truth platform.
877
+ */
878
+ interface Patient {
879
+ /** Truth platform patient ID */
880
+ id: string;
881
+ /** Elation EHR patient ID (if linked) */
882
+ elationId?: string;
883
+ /** Hint EHR patient ID (if linked) */
884
+ hintId?: string;
885
+ /** Patient first name */
886
+ firstName: string;
887
+ /** Patient last name */
888
+ lastName: string;
889
+ /** Date of birth (ISO 8601 date string, e.g. "1990-01-15") */
890
+ dateOfBirth?: string;
891
+ /** Patient sex */
892
+ sex?: string;
893
+ /** Primary email address */
894
+ email?: string;
895
+ /** Primary phone number */
896
+ phone?: string;
897
+ /** Patient status in the system */
898
+ status: string;
899
+ /** Associated organization/tenant ID */
900
+ tenantId: string;
901
+ /** ISO 8601 timestamp of record creation */
902
+ createdAt: string;
903
+ /** ISO 8601 timestamp of last update */
904
+ updatedAt: string;
905
+ }
906
+ /**
907
+ * Options for listing patients.
908
+ */
909
+ interface PatientListOptions {
910
+ /** Search patients by name, email, or phone */
911
+ search?: string;
912
+ /** Maximum number of results to return */
913
+ limit?: number;
914
+ /** Cursor for pagination */
915
+ cursor?: string;
916
+ }
917
+
918
+ /**
919
+ * Bulk patient lookup by Convex ids.
920
+ *
921
+ * Backs the inbox card render in CommHub — one reactive subscription
922
+ * resolves names + office + EHR ids for the entire visible list
923
+ * instead of N per-row queries.
924
+ *
925
+ * Must be used within `<TruthProvider />`.
926
+ */
927
+
928
+ /**
929
+ * Subscribe to a list of patients by their Convex ids. Returns a
930
+ * `Record<patientId, Patient>` for O(1) lookup at render time.
931
+ *
932
+ * Pass an empty array (or `undefined`) to skip the query. Missing ids
933
+ * are absent from the map (silently dropped server-side).
934
+ */
935
+ declare function usePatientsByIds(ids: string[] | null | undefined): UseQueryResult<Record<string, Patient>>;
936
+
669
937
  interface TruthProviderProps {
670
938
  /** Truth environment — determines which Convex deployment to connect to */
671
939
  environment?: string;
@@ -693,6 +961,16 @@ interface Reminder {
693
961
  triggeredAt?: string;
694
962
  cancelledAt?: string;
695
963
  }
964
+ interface ScheduleReminderInput {
965
+ conversationId: string;
966
+ remindAt: string | Date;
967
+ note?: string;
968
+ createdBy: string;
969
+ }
970
+ interface ScheduleReminderResult {
971
+ reminderId: string;
972
+ remindAt: string;
973
+ }
696
974
 
697
975
  /**
698
976
  * React hooks for conversation reminders — bulk lookup keyed by
@@ -1004,6 +1282,52 @@ interface TrackOptions {
1004
1282
  occurredAt?: string;
1005
1283
  }
1006
1284
 
1285
+ /**
1286
+ * Configuration interfaces for the Truth SDK.
1287
+ */
1288
+ /**
1289
+ * Environment options for the Truth platform.
1290
+ */
1291
+ declare const ENVIRONMENTS: {
1292
+ readonly local: "local";
1293
+ readonly staging: "staging";
1294
+ readonly stg: "stg";
1295
+ readonly sandbox: "sandbox";
1296
+ readonly uat: "uat";
1297
+ readonly production: "production";
1298
+ };
1299
+ type Environment = (typeof ENVIRONMENTS)[keyof typeof ENVIRONMENTS];
1300
+ /**
1301
+ * Configuration for initializing a TruthClient.
1302
+ */
1303
+ interface TruthClientConfig {
1304
+ /** API key for authenticating with the Truth platform (e.g. "hn_live_...") */
1305
+ apiKey: string;
1306
+ /** Target environment */
1307
+ environment: Environment;
1308
+ /** Override the default Convex URL for data access */
1309
+ convexUrl?: string;
1310
+ /** Event source identifier (e.g. "communication-hub.backend") */
1311
+ source?: string;
1312
+ /** Git SHA or version string for event source versioning */
1313
+ sourceVersion?: string;
1314
+ /** Default tenant (organization) ID for events */
1315
+ tenantId?: string;
1316
+ /** Number of events to buffer before flushing (default: 25) */
1317
+ batchSize?: number;
1318
+ /** Interval in milliseconds between automatic flushes (default: 5000) */
1319
+ flushIntervalMs?: number;
1320
+ /** Base URL for the Truth API (overrides environment-based default) */
1321
+ apiBaseUrl?: string;
1322
+ /**
1323
+ * Auto-register the service worker and subscribe to web push on init.
1324
+ * Only applies in browser environments with Push API support.
1325
+ * Default: true.
1326
+ */
1327
+ autoInitServiceWorker?: boolean;
1328
+ /** Path to the service worker file. Default: "/truth-sw.js" */
1329
+ serviceWorkerPath?: string;
1330
+ }
1007
1331
  /**
1008
1332
  * Actor context attached to tracked events.
1009
1333
  */
@@ -1011,6 +1335,14 @@ interface ActorContext {
1011
1335
  actorId: string;
1012
1336
  actorType: "user" | "system" | "webhook";
1013
1337
  }
1338
+ /**
1339
+ * Paginated result wrapper for list operations.
1340
+ */
1341
+ interface PaginatedResult<T> {
1342
+ data: T[];
1343
+ cursor: string | null;
1344
+ hasMore: boolean;
1345
+ }
1014
1346
 
1015
1347
  interface TruthTrackingContextValue {
1016
1348
  track: <T extends EventType>(eventType: T, payload: EventPayloadMap[T], options?: TrackOptions) => void;
@@ -1036,6 +1368,66 @@ declare function TruthTrackingProvider({ environment, source, sourceVersion, ten
1036
1368
  */
1037
1369
  declare function useTruth(): TruthTrackingContextValue;
1038
1370
 
1371
+ /**
1372
+ * UserSettingsResource — typed client for the Truth `userSettings` Convex
1373
+ * table. Exposes the one mutation CommHub's Settings screen needs:
1374
+ * `updateNotifications({ userId, notificationsEnabled })`.
1375
+ *
1376
+ * Server-side / imperative use:
1377
+ * truth.userSettings.updateNotifications({ userId, notificationsEnabled: true })
1378
+ *
1379
+ * Reactive React use lives in `@hipnation-truth/sdk/react` via
1380
+ * `useUserSettings`.
1381
+ */
1382
+
1383
+ interface UserSettings {
1384
+ _id: string;
1385
+ userId: string;
1386
+ notificationsEnabled: boolean;
1387
+ createdAt: string;
1388
+ updatedAt: string;
1389
+ }
1390
+ interface UpdateNotificationsInput {
1391
+ userId: string;
1392
+ notificationsEnabled: boolean;
1393
+ }
1394
+ interface UpdateNotificationsResult {
1395
+ action: "inserted" | "updated";
1396
+ id: string;
1397
+ }
1398
+ declare class UserSettingsResource {
1399
+ private readonly convex;
1400
+ constructor(convex: ConvexHttpClient);
1401
+ /**
1402
+ * Upsert notification preferences for a user. Creates the row if it
1403
+ * doesn't exist; patches `notificationsEnabled` + `updatedAt` otherwise.
1404
+ */
1405
+ updateNotifications(input: UpdateNotificationsInput): Promise<UpdateNotificationsResult>;
1406
+ }
1407
+
1408
+ /**
1409
+ * useUserSettings — reactive hook for per-user notification preferences
1410
+ * stored in the Truth `userSettings` Convex table.
1411
+ *
1412
+ * Must be used within `<TruthProvider />` (see `./provider`).
1413
+ *
1414
+ * @example
1415
+ * ```tsx
1416
+ * const { data: settings, loading } = useUserSettings(userId);
1417
+ * // settings?.notificationsEnabled — boolean or undefined (no row yet)
1418
+ * ```
1419
+ */
1420
+
1421
+ /**
1422
+ * Subscribe to the `userSettings` row for `userId`. Returns `null` when
1423
+ * no row exists (treat as all-defaults). Returns `undefined` while the
1424
+ * Convex query is in-flight.
1425
+ *
1426
+ * Pass `null` or `undefined` as `userId` to skip the query (e.g. while
1427
+ * auth is loading).
1428
+ */
1429
+ declare function useUserSettings(userId: string | null | undefined): UseQueryResult<UserSettings | null>;
1430
+
1039
1431
  /**
1040
1432
  * Appointment interfaces for the Truth SDK.
1041
1433
  */
@@ -1093,49 +1485,1056 @@ interface AppointmentListOptions {
1093
1485
  }
1094
1486
 
1095
1487
  /**
1096
- * Patient interfaces for the Truth SDK.
1488
+ * AppointmentResource provides data access to normalized appointment records
1489
+ * backed by Convex.
1097
1490
  */
1491
+
1492
+ declare class AppointmentResource {
1493
+ private readonly convex;
1494
+ constructor(convexClient: ConvexHttpClient);
1495
+ /**
1496
+ * Get an appointment by its Truth platform ID.
1497
+ */
1498
+ get(id: string): Promise<Appointment | null>;
1499
+ /**
1500
+ * List appointments with optional filters, pagination, and limit.
1501
+ */
1502
+ list(options?: AppointmentListOptions): Promise<PaginatedResult<Appointment>>;
1503
+ }
1504
+
1098
1505
  /**
1099
- * Normalized patient record from the Truth platform.
1506
+ * AttachmentsResource presigned S3 upload/download + Convex metadata.
1507
+ *
1508
+ * Replaces CommHub's base64-in-Postgres attachment flow:
1509
+ * 1. client.attachments.createUploadUrl(...) → presigned PUT URL
1510
+ * 2. Client PUTs bytes directly to S3
1511
+ * 3. client.attachments.record(...) → writes Convex metadata
1512
+ * 4. client.attachments.getDownloadUrl(s3Key) → presigned GET URL
1100
1513
  */
1101
- interface Patient {
1102
- /** Truth platform patient ID */
1514
+
1515
+ interface CreateUploadUrlInput {
1516
+ fileName: string;
1517
+ mimeType: string;
1518
+ size: number;
1519
+ conversationId?: string;
1520
+ }
1521
+ interface CreateUploadUrlResult {
1522
+ uploadUrl: string;
1523
+ s3Key: string;
1524
+ expiresIn: number;
1525
+ applicationId: string | null;
1526
+ }
1527
+ interface GetDownloadUrlResult {
1528
+ url: string;
1529
+ expiresIn: number;
1530
+ }
1531
+ interface RecordAttachmentInput {
1532
+ s3Key: string;
1533
+ fileName: string;
1534
+ mimeType: string;
1535
+ size: number;
1536
+ conversationId?: string;
1537
+ uploadedBy: string;
1538
+ }
1539
+ interface Attachment {
1540
+ _id: string;
1541
+ s3Key: string;
1542
+ fileName: string;
1543
+ mimeType: string;
1544
+ size: number;
1545
+ conversationId?: string;
1546
+ uploadedBy: string;
1547
+ createdAt: string;
1548
+ }
1549
+ declare class AttachmentsResource {
1550
+ private readonly baseUrl;
1551
+ private readonly apiKey;
1552
+ private readonly convex;
1553
+ constructor(apiBaseUrl: string, apiKey: string, convexClient: ConvexHttpClient);
1554
+ private post;
1555
+ createUploadUrl(input: CreateUploadUrlInput): Promise<CreateUploadUrlResult>;
1556
+ getDownloadUrl(s3Key: string, expiresIn?: number): Promise<GetDownloadUrlResult>;
1557
+ record(input: RecordAttachmentInput): Promise<{
1558
+ attachmentId: string;
1559
+ s3Key: string;
1560
+ }>;
1561
+ get(attachmentId: string): Promise<Attachment | null>;
1562
+ listByConversation(conversationId: string): Promise<Attachment[]>;
1563
+ /**
1564
+ * One-shot upload: presign → PUT to S3 → record in Convex → return a
1565
+ * 7-day signed download URL ready to embed in an outbound SMS. Caller
1566
+ * passes the resulting `downloadUrl` to `messages.dialpad.sendSms` (or
1567
+ * the new `messages.sendAttachmentMessage`) to actually deliver it.
1568
+ *
1569
+ * Replaces CommHub's NestJS `/send-attachment` controller in a single
1570
+ * SDK call — no base64 round-trip, no legacy `/attachments/:id/download`
1571
+ * REST endpoint required.
1572
+ */
1573
+ upload(input: {
1574
+ file: Blob | ArrayBuffer | Uint8Array;
1575
+ fileName: string;
1576
+ mimeType: string;
1577
+ size: number;
1578
+ conversationId?: string;
1579
+ uploadedBy: string;
1580
+ /** Download URL TTL in seconds. Default 7 days. */
1581
+ downloadExpiresIn?: number;
1582
+ }): Promise<{
1583
+ attachmentId: string;
1584
+ s3Key: string;
1585
+ downloadUrl: string;
1586
+ }>;
1587
+ }
1588
+
1589
+ /**
1590
+ * ConversationsResource — write methods that hang off a specific
1591
+ * conversation: notes, tasks, outbound messages.
1592
+ *
1593
+ * Replaces CommHub's `truthConversationApi.ts` raw-fetch wrappers so
1594
+ * the frontend goes through a typed SDK surface instead of building
1595
+ * URLs by hand.
1596
+ *
1597
+ * All methods proxy the matching oRPC procedures (`/conversations/*`)
1598
+ * via Truth's application-key auth. Errors surface as
1599
+ * `ConversationsError` with a status code so callers can distinguish
1600
+ * transport failures (status=0) from API rejections (status>=400).
1601
+ */
1602
+ /** Address a conversation by either Convex `_id` or the phonePair. */
1603
+ interface ConversationAddress {
1604
+ conversationId?: string;
1605
+ phonePair?: string;
1606
+ }
1607
+ type ConversationTaskStatus = "pending" | "completed";
1608
+ type ConversationTaskPriority = "high" | "medium" | "low";
1609
+ interface CreateConversationNoteInput extends ConversationAddress {
1610
+ eventId?: string;
1611
+ author: string;
1612
+ content: string;
1613
+ status?: string;
1614
+ type?: string;
1615
+ }
1616
+ interface CreateConversationTaskInput extends ConversationAddress {
1617
+ eventId?: string;
1618
+ author: string;
1619
+ description: string;
1620
+ status?: ConversationTaskStatus;
1621
+ priority?: ConversationTaskPriority;
1622
+ assignee?: string;
1623
+ type?: string;
1624
+ }
1625
+ interface SetConversationTaskStatusInput {
1626
+ taskId: string;
1627
+ status: ConversationTaskStatus;
1628
+ resolvedBy?: string;
1629
+ }
1630
+ interface SendConversationMessageInput {
1631
+ fromNumber: string;
1632
+ toNumber: string;
1633
+ message?: string;
1634
+ media?: string;
1635
+ }
1636
+ interface SendConversationMessageResult {
1637
+ id: number;
1638
+ messageStatus: string;
1639
+ direction: string;
1640
+ }
1641
+ declare class ConversationNotesSubresource {
1642
+ private readonly post;
1643
+ constructor(post: <T>(path: string, body: unknown) => Promise<T>);
1644
+ /** Create a note on a conversation (addressed by id or phonePair). */
1645
+ create(input: CreateConversationNoteInput): Promise<{
1646
+ id: string;
1647
+ }>;
1648
+ }
1649
+ declare class ConversationTasksSubresource {
1650
+ private readonly post;
1651
+ constructor(post: <T>(path: string, body: unknown) => Promise<T>);
1652
+ /** Create a task on a conversation. */
1653
+ create(input: CreateConversationTaskInput): Promise<{
1654
+ id: string;
1655
+ }>;
1656
+ /** Mark a task pending or completed. */
1657
+ setStatus(input: SetConversationTaskStatusInput): Promise<{
1658
+ ok: true;
1659
+ }>;
1660
+ }
1661
+ declare class ConversationMessagesSubresource {
1662
+ private readonly post;
1663
+ constructor(post: <T>(path: string, body: unknown) => Promise<T>);
1664
+ /**
1665
+ * Send an outbound SMS / MMS via the typed `sendMessage` oRPC procedure.
1666
+ *
1667
+ * Prefer this over the generic Dialpad proxy (`messages.dialpad.sendSms`)
1668
+ * — this hits the validated Truth route and consistently surfaces
1669
+ * `ConversationsError` on failure.
1670
+ */
1671
+ send(input: SendConversationMessageInput): Promise<SendConversationMessageResult>;
1672
+ }
1673
+ declare class ConversationsResource {
1674
+ readonly notes: ConversationNotesSubresource;
1675
+ readonly tasks: ConversationTasksSubresource;
1676
+ readonly messages: ConversationMessagesSubresource;
1677
+ /** 30s upstream timeout — matches NotesResource for consistency. */
1678
+ private static readonly REQUEST_TIMEOUT_MS;
1679
+ private readonly baseUrl;
1680
+ private readonly apiKey;
1681
+ private readonly convex;
1682
+ constructor(apiBaseUrl: string, apiKey: string, convex?: convex_browser.ConvexHttpClient);
1683
+ /**
1684
+ * Mark a conversation read for the calling user (zeroes unreadCount,
1685
+ * stamps `lastReadAt`). Calls the public Convex `markRead` mutation
1686
+ * which enforces self-tenancy on the auth identity.
1687
+ */
1688
+ markRead(input: {
1689
+ conversationId: string;
1690
+ userId: string;
1691
+ }): Promise<{
1692
+ ok: true;
1693
+ }>;
1694
+ /**
1695
+ * Mark a conversation unread for the calling user (sets unreadCount=1).
1696
+ */
1697
+ markUnread(input: {
1698
+ conversationId: string;
1699
+ userId: string;
1700
+ }): Promise<{
1701
+ updated: true;
1702
+ }>;
1703
+ /**
1704
+ * Zero unread on every conversation the user has reads for, except
1705
+ * those whose `providerPhone` is in `excludedProviderPhones`.
1706
+ */
1707
+ clearAllUnread(input: {
1708
+ userId: string;
1709
+ excludedProviderPhones?: string[];
1710
+ }): Promise<{
1711
+ cleared: number;
1712
+ }>;
1713
+ private postRequest;
1714
+ }
1715
+
1716
+ /**
1717
+ * Dialpad resource — typed access to Truth's Dialpad proxy endpoints.
1718
+ *
1719
+ * @example
1720
+ * ```ts
1721
+ * const sms = await truth.messages.dialpad.sendSms({ from_number, to_number, message });
1722
+ * const call = await truth.messages.dialpad.initiateCall(userId, phoneNumber);
1723
+ * await truth.messages.dialpad.hangupCall(callId);
1724
+ * const url = await truth.messages.dialpad.authenticateVoicemail(voicemailLink);
1725
+ * ```
1726
+ */
1727
+ interface SendSmsParams {
1728
+ from_number: string;
1729
+ to_number: string;
1730
+ message?: string;
1731
+ media?: string | string[];
1732
+ }
1733
+ interface SendSmsResponse {
1103
1734
  id: string;
1104
- /** Elation EHR patient ID (if linked) */
1105
- elationId?: string;
1106
- /** Hint EHR patient ID (if linked) */
1735
+ message_status: string;
1736
+ [key: string]: unknown;
1737
+ }
1738
+ interface InitiateCallResponse {
1739
+ call_id: number;
1740
+ [key: string]: unknown;
1741
+ }
1742
+ interface CallStatusResponse {
1743
+ state: string;
1744
+ [key: string]: unknown;
1745
+ }
1746
+ interface DialpadUser {
1747
+ id: number;
1748
+ emails: string[];
1749
+ first_name?: string;
1750
+ last_name?: string;
1751
+ [key: string]: unknown;
1752
+ }
1753
+ interface DialpadNumberInfo {
1754
+ user_id?: number;
1755
+ type?: string;
1756
+ [key: string]: unknown;
1757
+ }
1758
+ declare class DialpadResource {
1759
+ private readonly baseUrl;
1760
+ private readonly apiKey;
1761
+ constructor(apiBaseUrl: string, apiKey: string);
1762
+ /**
1763
+ * Send an SMS or MMS message via Dialpad.
1764
+ */
1765
+ sendSms(params: SendSmsParams): Promise<SendSmsResponse>;
1766
+ /**
1767
+ * Initiate an outbound call from a Dialpad user to a phone number.
1768
+ */
1769
+ initiateCall(userId: number, phoneNumber: string): Promise<InitiateCallResponse>;
1770
+ /**
1771
+ * Hang up an active call.
1772
+ */
1773
+ hangupCall(callId: number): Promise<void>;
1774
+ /**
1775
+ * Alias for `hangupCall` — mirrors the CommHub `endCall` action name so
1776
+ * the SDK swap is a direct rename.
1777
+ */
1778
+ endCall(callId: number): Promise<void>;
1779
+ /**
1780
+ * Send an MMS with a pre-uploaded attachment. Takes the S3 key returned
1781
+ * by `client.attachments.createUploadUrl(...)` + PUT, fetches a short-
1782
+ * lived download URL, and hands it to Dialpad as `media[]`.
1783
+ *
1784
+ * Replaces CommHub's `sendAttachment` Hasura Action (which stored bytes
1785
+ * as base64 in Postgres and served them through a GET endpoint).
1786
+ */
1787
+ sendAttachmentWithUrl(params: {
1788
+ from_number: string;
1789
+ to_number: string;
1790
+ mediaUrl: string;
1791
+ message?: string;
1792
+ }): Promise<SendSmsResponse>;
1793
+ /**
1794
+ * Get the status of a call.
1795
+ */
1796
+ getCallStatus(callId: number): Promise<CallStatusResponse | null>;
1797
+ /**
1798
+ * Get a Dialpad user by their user ID.
1799
+ */
1800
+ getUser(userId: string | number): Promise<DialpadUser | null>;
1801
+ /**
1802
+ * Find a Dialpad user by email.
1803
+ */
1804
+ getUserByEmail(email: string): Promise<DialpadUser | null>;
1805
+ /**
1806
+ * Find a Dialpad user by phone number.
1807
+ */
1808
+ getUserByPhoneNumber(phoneNumber: string): Promise<DialpadUser | null>;
1809
+ /**
1810
+ * Get information about a Dialpad phone number.
1811
+ */
1812
+ getNumberInfo(phoneNumber: string): Promise<DialpadNumberInfo | null>;
1813
+ /**
1814
+ * Authenticate a voicemail download URL. Truth appends the Dialpad API key,
1815
+ * follows redirects, and returns the clean URL for client-side playback.
1816
+ */
1817
+ authenticateVoicemail(voicemailLink: string): Promise<string | null>;
1818
+ private get;
1819
+ private post;
1820
+ private put;
1821
+ }
1822
+ declare class MessagesResource {
1823
+ readonly dialpad: DialpadResource;
1824
+ private readonly baseUrl;
1825
+ private readonly apiKey;
1826
+ constructor(apiBaseUrl: string, apiKey: string);
1827
+ /**
1828
+ * Get an authenticated URL for a Dialpad voicemail recording.
1829
+ *
1830
+ * Replaces CommHub's `getAuthenticatedVoicemailUrl` Hasura mutation
1831
+ * (which proxied to the legacy NestJS backend). Truth appends the
1832
+ * Dialpad API key, follows redirects, strips the key from the final
1833
+ * URL, and returns it for client-side audio playback.
1834
+ *
1835
+ * @param voicemailLink The raw Dialpad voicemail download URL (value
1836
+ * of the `voicemail_link` field on the call event row).
1837
+ * @returns An object with `url` (clean playback URL) and `expiresAt`
1838
+ * (ISO-8601 timestamp, ~5 min from request time, informational only).
1839
+ */
1840
+ getVoicemailUrl(voicemailLink: string): Promise<{
1841
+ url: string;
1842
+ expiresAt: string;
1843
+ }>;
1844
+ /**
1845
+ * End a Dialpad call via the typed oRPC procedure (POST hangup, with
1846
+ * 404→`alreadyEnded` so the UI doesn't flash an error on a natural
1847
+ * race). Replaces CommHub's `useEndCallMutation` Hasura action.
1848
+ *
1849
+ * Prefer this over `messages.dialpad.endCall` which goes through the
1850
+ * generic proxy (PUT, no 404 handling).
1851
+ */
1852
+ endCall(callId: number | string): Promise<{
1853
+ ok: true;
1854
+ alreadyEnded: boolean;
1855
+ }>;
1856
+ }
1857
+
1858
+ /**
1859
+ * EHR proxy resource — typed access to Truth's EHR proxy endpoints.
1860
+ *
1861
+ * Provides per-provider proxy methods so consumers never construct
1862
+ * proxy URLs manually.
1863
+ *
1864
+ * @example
1865
+ * ```ts
1866
+ * const patient = await truth.ehr.elation.get('/patients/123/');
1867
+ * const notes = await truth.ehr.elation.post('/non_visit_notes/', noteData);
1868
+ * const hintPatient = await truth.ehr.hint.get('/provider/patients/456');
1869
+ * ```
1870
+ */
1871
+ declare class EhrProviderProxy {
1872
+ private readonly baseUrl;
1873
+ private readonly provider;
1874
+ constructor(apiBaseUrl: string, provider: string);
1875
+ /**
1876
+ * GET request to the EHR proxy.
1877
+ * @param path — path relative to the provider root (e.g., "/patients/123/")
1878
+ * @param params — optional query parameters
1879
+ */
1880
+ get<T = unknown>(path: string, params?: Record<string, unknown>): Promise<T>;
1881
+ /**
1882
+ * POST request to the EHR proxy.
1883
+ */
1884
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
1885
+ /**
1886
+ * PUT request to the EHR proxy.
1887
+ */
1888
+ put<T = unknown>(path: string, body?: unknown): Promise<T>;
1889
+ /**
1890
+ * PATCH request to the EHR proxy.
1891
+ */
1892
+ patch<T = unknown>(path: string, body?: unknown): Promise<T>;
1893
+ /**
1894
+ * DELETE request to the EHR proxy.
1895
+ */
1896
+ delete<T = unknown>(path: string): Promise<T>;
1897
+ }
1898
+ declare class EhrResource {
1899
+ /** Elation EHR proxy */
1900
+ readonly elation: EhrProviderProxy;
1901
+ /** Hint Health proxy */
1902
+ readonly hint: EhrProviderProxy;
1903
+ constructor(apiBaseUrl: string);
1904
+ }
1905
+
1906
+ /**
1907
+ * NotesResource — push formatted notes to Elation.
1908
+ *
1909
+ * Caller assembles the note body (text + bullets). Truth owns the Elation
1910
+ * OAuth token and the HTTP call so applications can drop Elation
1911
+ * credentials from their own config.
1912
+ */
1913
+ interface NonVisitNoteBullet {
1914
+ text: string;
1915
+ category?: string;
1916
+ }
1917
+ interface PushNoteToElationInput {
1918
+ patient: number;
1919
+ physician: number;
1920
+ practice: number;
1921
+ note: {
1922
+ text: string;
1923
+ type?: string;
1924
+ };
1925
+ document_date?: string;
1926
+ chart_date?: string;
1927
+ bullets?: NonVisitNoteBullet[];
1928
+ }
1929
+ interface PushNoteToElationResult {
1930
+ success: boolean;
1931
+ elationNoteId: number | null;
1932
+ }
1933
+ interface PushConversationToElationInput {
1934
+ /** One-of: conversationId (Convex) or phonePair. */
1935
+ conversationId?: string;
1936
+ phonePair?: string;
1937
+ /** Actor user id — recorded on the audit row. */
1938
+ initiatedBy: string;
1939
+ /** Pre-assembled note body (transcript-style text). */
1940
+ noteText: string;
1941
+ bullets?: NonVisitNoteBullet[];
1942
+ notesPushed?: number;
1943
+ messagesPushed?: number;
1944
+ tasksPushed?: number;
1945
+ }
1946
+ interface PushConversationToElationResult {
1947
+ success: true;
1948
+ batchId: string;
1949
+ elationNoteId: number | null;
1950
+ notesPushed: number;
1951
+ messagesPushed: number;
1952
+ tasksPushed: number;
1953
+ }
1954
+ declare class NotesResource {
1955
+ private readonly baseUrl;
1956
+ private readonly apiKey;
1957
+ constructor(apiBaseUrl: string, apiKey: string);
1958
+ /** 30s upstream timeout — Elation API has occasional slow hops; we
1959
+ * don't want a pending mutation to hold the UI thread indefinitely. */
1960
+ private static readonly REQUEST_TIMEOUT_MS;
1961
+ private post;
1962
+ /**
1963
+ * Low-level — caller has already resolved Elation patient / physician
1964
+ * / practice. Use `pushConversationToElation` if you only have a
1965
+ * conversation handle.
1966
+ */
1967
+ pushToElation(input: PushNoteToElationInput): Promise<PushNoteToElationResult>;
1968
+ /**
1969
+ * Orchestrator — pass a conversation handle + pre-assembled note text.
1970
+ * Truth resolves the linked patient via Convex, looks up
1971
+ * physician/practice in Elation, posts the note, and writes an audit
1972
+ * row to `elationSyncEvents`. Replaces CommHub's `pushNotesToElation`
1973
+ * Hasura action.
1974
+ */
1975
+ pushConversationToElation(input: PushConversationToElationInput): Promise<PushConversationToElationResult>;
1976
+ }
1977
+
1978
+ /**
1979
+ * NotificationsResource — wraps Truth's /api/notifications/* endpoints.
1980
+ *
1981
+ * Server-side use (for example from a CommHub backend job or
1982
+ * another service): `truth.notifications.send({ userId, title, body })`.
1983
+ *
1984
+ * Client-side React usage lives in `@hipnation-truth/sdk/react` via
1985
+ * the `useNotifications` hook which mirrors `expo-notifications`.
1986
+ */
1987
+ type NotificationPlatform = "ios" | "android" | "web";
1988
+ interface RegisterDeviceInput {
1989
+ userId: string;
1990
+ platform: NotificationPlatform;
1991
+ nativeToken?: string;
1992
+ webPushSubscription?: {
1993
+ endpoint: string;
1994
+ keys: {
1995
+ p256dh: string;
1996
+ auth: string;
1997
+ };
1998
+ };
1999
+ appVersion?: string;
2000
+ osVersion?: string;
2001
+ locale?: string;
2002
+ timezone?: string;
2003
+ /**
2004
+ * iOS only — which APNs endpoint the `nativeToken` was issued
2005
+ * against. The token bytes don't carry this; it's determined by
2006
+ * the build's `aps-environment` entitlement
2007
+ * (`development` ⇒ sandbox, `production` ⇒ production). When the
2008
+ * consumer is an Expo app, detect via
2009
+ * `Application.getIosPushNotificationServiceEnvironmentAsync()`
2010
+ * from `expo-application` and pass the normalised value here.
2011
+ * Omitting this falls back to the application's default
2012
+ * `pushConfig.ios.environment` server-side.
2013
+ */
2014
+ apnsEnvironment?: "sandbox" | "production";
2015
+ }
2016
+ interface RegisterDeviceResult {
2017
+ deviceId: string;
2018
+ action: "inserted" | "updated";
2019
+ snsEndpointArn?: string;
2020
+ }
2021
+ interface UnregisterDeviceInput {
2022
+ nativeToken?: string;
2023
+ deviceId?: string;
2024
+ }
2025
+ interface SendNotificationInput {
2026
+ userId: string;
2027
+ title: string;
2028
+ body: string;
2029
+ data?: Record<string, unknown>;
2030
+ badge?: number;
2031
+ sound?: string;
2032
+ }
2033
+ interface SendNotificationResult {
2034
+ delivered: number;
2035
+ failed?: number;
2036
+ suppressed?: boolean;
2037
+ suppressionReason?: string;
2038
+ }
2039
+ interface NotificationPreferences {
2040
+ channels: {
2041
+ sms: boolean;
2042
+ push: boolean;
2043
+ email: boolean;
2044
+ inApp: boolean;
2045
+ };
2046
+ quietHours?: {
2047
+ enabled: boolean;
2048
+ start: string;
2049
+ end: string;
2050
+ timezone: string;
2051
+ };
2052
+ doNotDisturbUntil?: string;
2053
+ updatedAt: string;
2054
+ }
2055
+ interface UpdatePreferencesInput {
2056
+ userId: string;
2057
+ channels?: NotificationPreferences["channels"];
2058
+ quietHours?: NotificationPreferences["quietHours"];
2059
+ doNotDisturbUntil?: string | null;
2060
+ }
2061
+ interface ScheduleNotificationInput extends SendNotificationInput {
2062
+ /** ISO 8601 timestamp; must be strictly in the future. */
2063
+ scheduledAt: string;
2064
+ }
2065
+ interface ScheduleNotificationResult {
2066
+ jobId: string;
2067
+ scheduledAt: string;
2068
+ }
2069
+ interface CancelScheduledNotificationResult {
2070
+ cancelled: boolean;
2071
+ reason?: string;
2072
+ status?: string;
2073
+ }
2074
+ type ScheduledJobStatus = "pending" | "executed" | "cancelled" | "failed";
2075
+ interface ScheduledNotification {
2076
+ jobId: string;
2077
+ userId: string;
2078
+ title: string;
2079
+ body: string;
2080
+ data?: unknown;
2081
+ badge?: number;
2082
+ sound?: string;
2083
+ scheduledAt: string;
2084
+ status: ScheduledJobStatus;
2085
+ resultHistoryId?: string;
2086
+ errorMessage?: string;
2087
+ createdAt: string;
2088
+ }
2089
+ interface PushEventPayload {
2090
+ title: string;
2091
+ body: string;
2092
+ data?: unknown;
2093
+ }
2094
+ declare class NotificationsResource {
2095
+ private readonly baseUrl;
2096
+ private readonly apiKey;
2097
+ constructor(apiBaseUrl: string, apiKey: string);
2098
+ private post;
2099
+ private get;
2100
+ private delete;
2101
+ /**
2102
+ * Register a device (or refresh its metadata) for push delivery.
2103
+ * Safe to call repeatedly — the server dedupes by native token.
2104
+ */
2105
+ registerDevice(input: RegisterDeviceInput): Promise<RegisterDeviceResult>;
2106
+ /** Revoke a device — on sign-out or when the OS reports an invalid token. */
2107
+ unregisterDevice(input: UnregisterDeviceInput): Promise<{
2108
+ revoked: boolean;
2109
+ }>;
2110
+ /**
2111
+ * Send a push notification to every active device belonging to
2112
+ * `userId`. Honors the user's notificationPreferences (quiet hours,
2113
+ * DND, channel off) before publishing.
2114
+ */
2115
+ send(input: SendNotificationInput): Promise<SendNotificationResult>;
2116
+ /** Read a user's notification preferences. Returns defaults when no row exists. */
2117
+ getPreferences(userId: string): Promise<NotificationPreferences>;
2118
+ updatePreferences(input: UpdatePreferencesInput): Promise<{
2119
+ ok: boolean;
2120
+ }>;
2121
+ /**
2122
+ * Schedule a future push notification. Convex's native scheduler
2123
+ * fires the send at `scheduledAt` and runs the same delivery
2124
+ * pipeline as `send()` (preferences, devices, history audit).
2125
+ *
2126
+ * Throws `NotificationsError` with status 400 if `scheduledAt` is
2127
+ * not strictly in the future.
2128
+ */
2129
+ schedule(input: ScheduleNotificationInput): Promise<ScheduleNotificationResult>;
2130
+ /**
2131
+ * Cancel a pending scheduled notification. Returns `cancelled: false`
2132
+ * (no error) if the job has already executed, was previously
2133
+ * cancelled, or no longer exists — `reason` describes which case.
2134
+ */
2135
+ cancelScheduled(jobId: string): Promise<CancelScheduledNotificationResult>;
2136
+ /**
2137
+ * List scheduled notifications for a user — pending, executed,
2138
+ * cancelled, or failed. Most-recent first. Default limit 100.
2139
+ */
2140
+ listScheduled(userId: string, options?: {
2141
+ limit?: number;
2142
+ }): Promise<ScheduledNotification[]>;
2143
+ getVapidKey(): Promise<string | null>;
2144
+ onPushReceived(callback: (payload: PushEventPayload) => void): () => void;
2145
+ onPushTapped(callback: (payload: PushEventPayload) => void): () => void;
2146
+ }
2147
+
2148
+ /**
2149
+ * PatientDetailsResource — merged Hint + Elation patient lookups.
2150
+ *
2151
+ * Backed by the Truth API at /api/patients/details/*, authenticated with
2152
+ * X-API-Key. Replaces CommHub's getPatientDetails / getPatientBasicDetails
2153
+ * / getPatientMedicalDetails actions.
2154
+ */
2155
+ interface PatientDetailsInput {
1107
2156
  hintId?: string;
1108
- /** Patient first name */
1109
- firstName: string;
1110
- /** Patient last name */
1111
- lastName: string;
1112
- /** Date of birth (ISO 8601 date string, e.g. "1990-01-15") */
1113
- dateOfBirth?: string;
1114
- /** Patient sex */
1115
- sex?: string;
1116
- /** Primary email address */
2157
+ elationId?: string;
2158
+ }
2159
+ interface PatientDetailsResult {
2160
+ hint: unknown | null;
2161
+ elation: unknown | null;
2162
+ resolvedElationId: string | null;
2163
+ }
2164
+ interface PatientBasicDetailsResult {
2165
+ hint: unknown | null;
2166
+ elationId: string | null;
2167
+ }
2168
+ interface PatientMedicalDetailsResult {
2169
+ problems: unknown | null;
2170
+ medications: unknown | null;
2171
+ allergies: unknown | null;
2172
+ appointments: unknown | null;
2173
+ }
2174
+ declare class PatientDetailsResource {
2175
+ private readonly baseUrl;
2176
+ private readonly apiKey;
2177
+ constructor(apiBaseUrl: string, apiKey: string);
2178
+ private post;
2179
+ get(input: PatientDetailsInput): Promise<PatientDetailsResult>;
2180
+ getBasic(input: PatientDetailsInput): Promise<PatientBasicDetailsResult>;
2181
+ getMedical(elationId: string): Promise<PatientMedicalDetailsResult>;
2182
+ /**
2183
+ * Trigger a server-side refresh of the patient's Elation medical
2184
+ * records (medications, problems, allergies, appointments) into the
2185
+ * Convex cache. Fire-and-forget — the UI should read via the Convex-
2186
+ * reactive `usePatientMedical` hook.
2187
+ */
2188
+ refreshMedical(elationId: number): Promise<{
2189
+ totals: {
2190
+ medications: number;
2191
+ problems: number;
2192
+ allergies: number;
2193
+ appointments: number;
2194
+ };
2195
+ }>;
2196
+ }
2197
+
2198
+ /**
2199
+ * PatientResource provides data access to normalized patient records
2200
+ * backed by Convex.
2201
+ */
2202
+
2203
+ declare class PatientResource {
2204
+ private readonly convex;
2205
+ constructor(convexClient: ConvexHttpClient);
2206
+ /**
2207
+ * Get a patient by their Truth platform ID.
2208
+ */
2209
+ get(id: string): Promise<Patient | null>;
2210
+ /**
2211
+ * Get a patient by their Elation EHR ID.
2212
+ */
2213
+ getByElationId(elationId: string): Promise<Patient | null>;
2214
+ /**
2215
+ * Get a patient by their Hint EHR ID.
2216
+ */
2217
+ getByHintId(hintId: string): Promise<Patient | null>;
2218
+ /**
2219
+ * List patients with optional search, pagination, and limit.
2220
+ */
2221
+ list(options?: PatientListOptions): Promise<PaginatedResult<Patient>>;
2222
+ }
2223
+
2224
+ /**
2225
+ * PhysiciansResource — Convex-backed physician lookups.
2226
+ *
2227
+ * Populated from Elation by Truth's daily PhysiciansBackfillCron. Replaces
2228
+ * per-physician Elation HTTP hops with a single Convex batch query.
2229
+ */
2230
+
2231
+ interface Physician {
2232
+ _id: string;
2233
+ elationId: number;
2234
+ firstName?: string;
2235
+ lastName?: string;
2236
+ npi?: string;
2237
+ credentials?: string;
2238
+ specialties?: string[];
2239
+ practice?: number;
1117
2240
  email?: string;
1118
- /** Primary phone number */
1119
2241
  phone?: string;
1120
- /** Patient status in the system */
1121
- status: string;
1122
- /** Associated organization/tenant ID */
1123
- tenantId: string;
1124
- /** ISO 8601 timestamp of record creation */
2242
+ lastSyncedAt: string;
2243
+ }
2244
+ declare class PhysiciansResource {
2245
+ private readonly convex;
2246
+ constructor(convex: ConvexHttpClient);
2247
+ /**
2248
+ * Resolve a batch of physicians by Elation IDs. Missing ids are dropped.
2249
+ */
2250
+ getByElationIds(ids: number[]): Promise<Physician[]>;
2251
+ getByElationId(id: number): Promise<Physician | null>;
2252
+ listByPractice(practice: number, limit?: number): Promise<Physician[]>;
2253
+ }
2254
+
2255
+ /**
2256
+ * RemindersResource — schedule, cancel, and list conversation reminders.
2257
+ *
2258
+ * Backed by Convex mutations/queries (durable scheduled functions) — replaces
2259
+ * the in-memory setTimeout scheduler that used to live in CommHub's NestJS
2260
+ * backend.
2261
+ */
2262
+
2263
+ declare class RemindersResource {
2264
+ private readonly convex;
2265
+ constructor(convexClient: ConvexHttpClient);
2266
+ /**
2267
+ * Schedule a reminder to fire at `remindAt`. Returns the reminder id,
2268
+ * which callers should store if they may want to cancel it later.
2269
+ */
2270
+ schedule(input: ScheduleReminderInput): Promise<ScheduleReminderResult>;
2271
+ /**
2272
+ * Cancel a pending reminder. No-op if the reminder has already fired or
2273
+ * been cancelled.
2274
+ */
2275
+ cancel(reminderId: string, cancelledBy: string): Promise<{
2276
+ reminderId: string;
2277
+ status: string;
2278
+ }>;
2279
+ /**
2280
+ * List reminders for a conversation (most recent first).
2281
+ */
2282
+ listByConversation(conversationId: string): Promise<Reminder[]>;
2283
+ }
2284
+
2285
+ type TaskStatus = "open" | "in_progress" | "resolved" | "cancelled";
2286
+ interface Task {
2287
+ _id: string;
2288
+ eventId?: string;
2289
+ assignedTo: string;
2290
+ createdBy: string;
2291
+ title: string;
2292
+ description?: string;
2293
+ data?: unknown;
2294
+ status: TaskStatus;
2295
+ resolvedBy?: string;
2296
+ resolvedAt?: string;
1125
2297
  createdAt: string;
1126
- /** ISO 8601 timestamp of last update */
1127
2298
  updatedAt: string;
1128
2299
  }
2300
+ interface CreateTaskInput {
2301
+ eventId?: string;
2302
+ assignedTo: string;
2303
+ createdBy: string;
2304
+ title: string;
2305
+ description?: string;
2306
+ data?: unknown;
2307
+ }
2308
+ interface UpdateTaskStatusInput {
2309
+ taskId: string;
2310
+ status: TaskStatus;
2311
+ resolvedBy?: string;
2312
+ data?: unknown;
2313
+ }
2314
+
1129
2315
  /**
1130
- * Options for listing patients.
2316
+ * TasksResource create, update status, list, get tasks.
2317
+ *
2318
+ * Backed by Convex. Replaces CommHub's createTaskWithNotification +
2319
+ * updateTaskStatusWithNotification actions. Push notification side-effect
2320
+ * is emitted downstream via Kinesis.
1131
2321
  */
1132
- interface PatientListOptions {
1133
- /** Search patients by name, email, or phone */
1134
- search?: string;
1135
- /** Maximum number of results to return */
1136
- limit?: number;
1137
- /** Cursor for pagination */
1138
- cursor?: string;
2322
+
2323
+ declare class TasksResource {
2324
+ private readonly convex;
2325
+ constructor(convexClient: ConvexHttpClient);
2326
+ create(input: CreateTaskInput): Promise<{
2327
+ taskId: string;
2328
+ status: TaskStatus;
2329
+ }>;
2330
+ updateStatus(input: UpdateTaskStatusInput): Promise<{
2331
+ taskId: string;
2332
+ status: TaskStatus;
2333
+ previousStatus: TaskStatus;
2334
+ }>;
2335
+ get(taskId: string): Promise<Task | null>;
2336
+ listByAssignee(assignedTo: string, options?: {
2337
+ status?: TaskStatus;
2338
+ limit?: number;
2339
+ }): Promise<Task[]>;
2340
+ listOpen(limit?: number): Promise<Task[]>;
2341
+ }
2342
+
2343
+ interface TranslationResult {
2344
+ translatedText: string;
2345
+ detectedLanguage?: {
2346
+ language: string;
2347
+ score: number;
2348
+ };
2349
+ sourceLanguage: string;
2350
+ targetLanguage: string;
2351
+ }
2352
+ interface DetectionResult {
2353
+ language: string;
2354
+ score: number;
2355
+ isTranslationSupported: boolean;
2356
+ isTransliterationSupported: boolean;
2357
+ }
2358
+ interface TranslateTextInput {
2359
+ text: string;
2360
+ targetLanguage: string;
2361
+ sourceLanguage?: string;
2362
+ }
2363
+ interface TranslateBatchInput {
2364
+ texts: string[];
2365
+ targetLanguage: string;
2366
+ sourceLanguage?: string;
1139
2367
  }
1140
2368
 
1141
- export { ACTIVE_CALL_STATES, type Appointment, type AppointmentListOptions, CONNECTED_CALL_STATES, type ConversationListItem, type ConversationMessage, type ConversationMessageRow, type ConversationNoteRow, type ConversationRow, type ConversationTaskForUserRow, type ConversationTaskRow, type DialpadCallLogRow, type DialpadCallRow, DialpadCallState, type EventPayloadMap, type EventType, type Patient, type PatientListOptions, type PermissionStatus, type Physician, RINGING_CALL_STATES, TERMINAL_CALL_STATES, type TrackOptions, TruthProvider, type TruthProviderProps, type TruthTrackingContextValue, TruthTrackingProvider, type TruthTrackingProviderProps, type UseActiveCallsOptions, type UseAppointmentListOptions, type UseConversationMessagesOptions, type UseConversationsFilters, type UseMessagesOptions, type UseNotificationsOptions, type UseNotificationsResult, type UsePatientBasicOptions, type UsePatientBasicResult, type UsePatientListOptions, type UsePatientMedicalOptions, type UsePatientPhotoOptions, type UseQueryResult, useActiveCalls, useAppointment, useAppointmentByElationId, useAppointments, useConversationByPhonePair, useConversationMessages, useConversationNotes, useConversationNotesByPhonePair, useConversationTasks, useConversationTasksByPhonePair, useConversationTasksForUser, useConversations, useDialpadCallByCallId, useDialpadCallLog, useDialpadCallsForConversation, useMessages, useNotifications, usePatient, usePatientBasic, usePatientByElationId, usePatientByHintId, usePatientMedical, usePatientPhoto, usePatients, usePharmacyByNcpdpId, usePhysicianByElationId, usePhysiciansByElationIds, useRemindersForConversations, useTruth, useUnreadCount };
2369
+ /**
2370
+ * TranslationResource — Azure Translator proxy through Truth API.
2371
+ *
2372
+ * All three operations are HTTP calls to Truth's oRPC endpoints at
2373
+ * `/api/translation/*`, authenticated with the same X-API-Key as the
2374
+ * event tracker.
2375
+ */
2376
+
2377
+ declare class TranslationResource {
2378
+ private readonly baseUrl;
2379
+ private readonly apiKey;
2380
+ constructor(apiBaseUrl: string, apiKey: string);
2381
+ private post;
2382
+ translate(input: TranslateTextInput): Promise<TranslationResult>;
2383
+ translateBatch(input: TranslateBatchInput): Promise<TranslationResult[]>;
2384
+ detect(text: string): Promise<DetectionResult>;
2385
+ }
2386
+
2387
+ /**
2388
+ * TruthClient -- main entry point for the @hipnation-truth/sdk package.
2389
+ *
2390
+ * Provides:
2391
+ * - `.patients` Resource-based patient data access (Convex-backed)
2392
+ * - `.appointments` Resource-based appointment data access (Convex-backed)
2393
+ * - `.ehr` EHR proxy access (Elation, Hint)
2394
+ * - `.messages` Messaging proxy access (Dialpad)
2395
+ * - `.track()` Fire-and-forget event tracking (batched HTTP -> Truth API)
2396
+ * - `.identify()` Set default actor context for subsequent events
2397
+ * - `.flush()` Force flush of buffered events (for graceful shutdown)
2398
+ */
2399
+
2400
+ declare class TruthClient {
2401
+ /** Patient data resource (Convex-backed) */
2402
+ readonly patients: PatientResource;
2403
+ /** Appointment data resource (Convex-backed) */
2404
+ readonly appointments: AppointmentResource;
2405
+ /** EHR proxy — typed access to Elation and Hint APIs through Truth */
2406
+ readonly ehr: EhrResource;
2407
+ /** Messaging proxy — typed access to Dialpad APIs through Truth */
2408
+ readonly messages: MessagesResource;
2409
+ /** Conversation reminders (Convex-backed, durable scheduler) */
2410
+ readonly reminders: RemindersResource;
2411
+ /** Translation (Azure Translator proxy) */
2412
+ readonly translation: TranslationResource;
2413
+ /** Tasks (Convex-backed) */
2414
+ readonly tasks: TasksResource;
2415
+ /** Patient details — merged Hint + Elation lookups via Truth API */
2416
+ readonly patientDetails: PatientDetailsResource;
2417
+ /** Attachments — presigned S3 upload/download + Convex metadata */
2418
+ readonly attachments: AttachmentsResource;
2419
+ /** Notes — push formatted notes to Elation */
2420
+ readonly notes: NotesResource;
2421
+ /** Physicians (Convex-backed cache from Elation) */
2422
+ readonly physicians: PhysiciansResource;
2423
+ /** Push / web notifications (AWS End User Messaging) */
2424
+ readonly notifications: NotificationsResource;
2425
+ /** Conversation-tied writes — notes, tasks, outbound messages. */
2426
+ readonly conversations: ConversationsResource;
2427
+ /** User settings — notification preferences (Convex-backed) */
2428
+ readonly userSettings: UserSettingsResource;
2429
+ private readonly convex;
2430
+ private readonly tracker;
2431
+ private _vapidPublicKey;
2432
+ private _webPushReady;
2433
+ private readonly _serviceWorkerPath;
2434
+ constructor(config: TruthClientConfig);
2435
+ get vapidPublicKey(): string | null;
2436
+ get webPushReady(): Promise<void> | null;
2437
+ private initWebPush;
2438
+ /**
2439
+ * The resolved Truth API base URL for this environment.
2440
+ * Use this when making HTTP calls to Truth's proxy endpoints
2441
+ * (e.g., EHR proxy, messages proxy).
2442
+ *
2443
+ * @example
2444
+ * ```ts
2445
+ * const url = `${truth.apiBaseUrl}/api/ehr/elation/patients/123`;
2446
+ * ```
2447
+ */
2448
+ get apiBaseUrl(): string;
2449
+ /**
2450
+ * Track an event. Fire-and-forget -- the event is buffered internally
2451
+ * and flushed in batches to the Truth API.
2452
+ *
2453
+ * @example
2454
+ * ```ts
2455
+ * truth.track('conversation.message_sent.v1', {
2456
+ * channel: 'sms',
2457
+ * direction: 'outbound',
2458
+ * message_chars: 140,
2459
+ * has_attachment: false,
2460
+ * provider_system: 'dialpad',
2461
+ * });
2462
+ * ```
2463
+ */
2464
+ track<T extends EventType>(eventType: T, payload: EventPayloadMap[T], options?: TrackOptions): void;
2465
+ /**
2466
+ * Set the default actor context for all subsequent tracked events.
2467
+ * Can be overridden per-event via TrackOptions.
2468
+ *
2469
+ * @example
2470
+ * ```ts
2471
+ * truth.identify('user_123', 'user');
2472
+ * ```
2473
+ */
2474
+ identify(actorId: string, actorType: ActorContext["actorType"]): void;
2475
+ /**
2476
+ * Flush all buffered events immediately. Returns a Promise that resolves
2477
+ * when the flush completes. Use this for graceful shutdown.
2478
+ *
2479
+ * @example
2480
+ * ```ts
2481
+ * process.on('SIGTERM', async () => {
2482
+ * await truth.flush();
2483
+ * process.exit(0);
2484
+ * });
2485
+ * ```
2486
+ */
2487
+ flush(): Promise<void>;
2488
+ /**
2489
+ * Gracefully shut down the client. Flushes all pending events and
2490
+ * releases resources.
2491
+ */
2492
+ destroy(): Promise<void>;
2493
+ }
2494
+
2495
+ /**
2496
+ * React hook for fetching an authenticated Dialpad voicemail URL.
2497
+ *
2498
+ * Provides an imperative `fetchUrl` callback that wraps
2499
+ * `client.messages.getVoicemailUrl()` in React state so CommHub's
2500
+ * `AudioPlayer` can replace its urql `useMutation` with a single hook
2501
+ * call, without adding a query library dependency.
2502
+ *
2503
+ * The TruthClient instance is passed directly so the hook is testable
2504
+ * and doesn't require a global singleton or React context.
2505
+ *
2506
+ * @example
2507
+ * ```tsx
2508
+ * import { useVoicemailUrl } from '@hipnation-truth/sdk/react/voicemail';
2509
+ * import { getTruthClient } from '@/lib/truthClient';
2510
+ *
2511
+ * function AudioPlayer({ uri }: { uri: string }) {
2512
+ * const { fetchUrl, url, isLoading, error } = useVoicemailUrl(getTruthClient());
2513
+ *
2514
+ * const handlePlay = async () => {
2515
+ * const authenticated = await fetchUrl(uri);
2516
+ * if (authenticated) { ... }
2517
+ * };
2518
+ * }
2519
+ * ```
2520
+ */
2521
+
2522
+ interface UseVoicemailUrlResult {
2523
+ /** Trigger the URL fetch. Resolves with the clean playback URL or null on error. */
2524
+ fetchUrl: (voicemailLink: string) => Promise<string | null>;
2525
+ /** The most recently fetched URL (null until first successful fetch). */
2526
+ url: string | null;
2527
+ /** True while the fetch is in-flight. */
2528
+ isLoading: boolean;
2529
+ /** Error message from the last failed fetch, or null. */
2530
+ error: string | null;
2531
+ }
2532
+ /**
2533
+ * Provides an imperative `fetchUrl` callback that authenticates a Dialpad
2534
+ * voicemail link via the Truth SDK `messages.getVoicemailUrl()` method.
2535
+ *
2536
+ * @param client The TruthClient instance (e.g., from `getTruthClient()`).
2537
+ */
2538
+ declare function useVoicemailUrl(client: TruthClient): UseVoicemailUrlResult;
2539
+
2540
+ export { ACTIVE_CALL_STATES, type Appointment, type AppointmentListOptions, CONNECTED_CALL_STATES, type ConversationListItem, type ConversationMessage, type ConversationMessageRow, type ConversationNoteRow, type ConversationRow, type ConversationTaskForUserRow, type ConversationTaskRow, type DialpadCallLogRow, type DialpadCallRow, DialpadCallState, type EventPayloadMap, type EventType, type FamilyMemberRow, type Patient, type PatientListOptions, type PatientSearchResult, type PermissionStatus, type Physician$1 as Physician, RINGING_CALL_STATES, TERMINAL_CALL_STATES, type TrackOptions, TruthProvider, type TruthProviderProps, type TruthTrackingContextValue, TruthTrackingProvider, type TruthTrackingProviderProps, type UseActiveCallsOptions, type UseAppointmentListOptions, type UseConversationMessagesOptions, type UseConversationsFilters, type UseMessagesOptions, type UseNotificationsOptions, type UseNotificationsResult, type UsePatientBasicOptions, type UsePatientBasicResult, type UsePatientFamilyMembersInput, type UsePatientListOptions, type UsePatientMedicalOptions, type UsePatientPhotoOptions, type UsePatientSearchOptions, type UseQueryResult, useActiveCalls, useAppointment, useAppointmentByElationId, useAppointments, useConversationByPhonePair, useConversationMessages, useConversationNotes, useConversationNotesByPhonePair, useConversationTasks, useConversationTasksByPhonePair, useConversationTasksForUser, useConversations, useDialpadCallByCallId, useDialpadCallLog, useDialpadCallsForConversation, useMessages, useNotifications, usePatient, usePatientBasic, usePatientByElationId, usePatientByHintId, usePatientFamilyMembers, usePatientMedical, usePatientPhoto, usePatientSearch, usePatients, usePatientsByIds, usePharmacyByNcpdpId, usePhysicianByElationId, usePhysiciansByElationIds, useRemindersForConversations, useTruth, useUnreadCount, useUserSettings, useVoicemailUrl };