@medalsocial/sdk 1.2.1 → 1.4.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.
@@ -8,6 +8,25 @@ interface ClientConfig {
8
8
  timeout: number;
9
9
  userAgent: string;
10
10
  }
11
+ /** Per-request options for write operations. */
12
+ interface RequestOptions {
13
+ /**
14
+ * Idempotency key sent as the `Idempotency-Key` header. Retries with the
15
+ * same key return the original result instead of repeating the operation.
16
+ * Required by some endpoints for capability-scoped tokens (e.g. helpdesk
17
+ * replies, webhook creation).
18
+ */
19
+ idempotencyKey?: string;
20
+ /**
21
+ * Capability confirmation token sent as the `X-Capability-Confirmation`
22
+ * header. Required alongside `idempotencyKey` when a token granted a
23
+ * capability-style scope directly (e.g. `helpdesk.webhook.manage`) executes
24
+ * a confirmable write route. Obtain one from
25
+ * `POST /api/v1/capability-confirmations`. API keys with legacy scopes do
26
+ * not need it.
27
+ */
28
+ capabilityConfirmation?: string;
29
+ }
11
30
  /**
12
31
  * Low-level HTTP client used by all resource classes.
13
32
  * Handles authentication, retries, timeout, and error parsing.
@@ -19,11 +38,12 @@ declare class BaseClient {
19
38
  /** Execute an authenticated GET request and return the parsed JSON body. */
20
39
  get<T>(path: string, params?: Record<string, string | undefined>): Promise<T>;
21
40
  /** Execute an authenticated POST request with a JSON body. */
22
- post<T>(path: string, body?: unknown): Promise<T>;
41
+ post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T>;
23
42
  /** Execute an authenticated PATCH request with a JSON body. */
24
- patch<T>(path: string, body: unknown): Promise<T>;
43
+ patch<T>(path: string, body: unknown, options?: RequestOptions): Promise<T>;
25
44
  /** Execute an authenticated DELETE request. */
26
- delete<T>(path: string): Promise<T>;
45
+ delete<T>(path: string, options?: RequestOptions): Promise<T>;
46
+ private writeHeaders;
27
47
  private buildUrl;
28
48
  private request;
29
49
  }
@@ -288,10 +308,21 @@ interface SendEmailInput {
288
308
  fallback_locale?: string;
289
309
  variables?: Record<string, string>;
290
310
  contact_id?: string;
311
+ /** Body-level idempotency key (alternative to the `Idempotency-Key` header). */
312
+ idempotency_key?: string;
313
+ /** Also send a `[Copy]` of the email to this address. */
314
+ copy_to?: string;
315
+ /** Reply-To for the copy (defaults to the primary recipient). */
316
+ copy_reply_to?: string;
291
317
  }
292
318
  /** Result returned after queuing a transactional email send (HTTP 202). */
293
319
  interface EmailSendResult {
294
- id: string;
320
+ /** Email send id — poll `emails.get(id)` with it to track delivery. */
321
+ id: string | null;
322
+ /** Send id of the `copy_to` copy, or `null` when no copy was requested. */
323
+ copy_id: string | null;
324
+ /** CRM contact linked to the send, or `null`. */
325
+ contact_id: string | null;
295
326
  status: string;
296
327
  }
297
328
  /** Full record for a sent email, including delivery timestamps. */
@@ -321,12 +352,23 @@ interface BatchSendInput {
321
352
  variables?: Record<string, string>;
322
353
  }[];
323
354
  }
355
+ /** Per-recipient outcome of a batch send, in request order. */
356
+ interface BatchSendRecipientResult {
357
+ email: string;
358
+ /** Email send id — poll `emails.get(id)` with it. `null` when not queued. */
359
+ id: string | null;
360
+ status: "queued" | "failed";
361
+ /** Failure reason for recipients that were not queued. */
362
+ error: string | null;
363
+ }
324
364
  /** Summary returned after queuing a batch email send. */
325
365
  interface BatchSendSummary {
326
366
  batch_id: string;
327
367
  total: number;
328
368
  queued: number;
329
369
  failed: number;
370
+ /** Per-recipient outcome, in request order. */
371
+ results: BatchSendRecipientResult[];
330
372
  }
331
373
  /** @deprecated Use `BatchSendSummary` for `emails.batch()` responses. */
332
374
  type BatchSendResult = BatchSendSummary;
@@ -387,11 +429,17 @@ declare class Emails {
387
429
  private client;
388
430
  readonly templates: EmailTemplates;
389
431
  constructor(client: BaseClient);
390
- /** Send a transactional email using a template. Returns a queued job ID (HTTP 202). */
432
+ /**
433
+ * Send a transactional email using a template (HTTP 202). The returned `id`
434
+ * is an email send id — poll `emails.get(id)` with it to track delivery.
435
+ */
391
436
  send(input: SendEmailInput): Promise<ApiResponse<EmailSendResult>>;
392
437
  /** Get the delivery status of a sent email. */
393
438
  get(id: string): Promise<ApiResponse<EmailSend>>;
394
- /** Send the same template to multiple recipients (max 100). Returns HTTP 202. */
439
+ /**
440
+ * Send the same template to multiple recipients (max 100, HTTP 202). Each
441
+ * queued recipient gets its own send id in `results` for `emails.get(id)`.
442
+ */
395
443
  batch(input: BatchSendInput): Promise<ApiResponse<BatchSendSummary>>;
396
444
  }
397
445
 
@@ -484,6 +532,122 @@ declare class Gdpr {
484
532
  }>;
485
533
  }
486
534
 
535
+ /** Lifecycle status of a helpdesk conversation. */
536
+ type ConversationStatus = "open" | "snoozed" | "closed";
537
+ /** Who authored a helpdesk message. */
538
+ type MessageAuthorType = "visitor" | "operator" | "ai" | "system";
539
+ /** Kind of helpdesk message. `note` is operator-internal and never delivered to the customer. */
540
+ type HelpdeskMessageType = "chat" | "email" | "note";
541
+ /** A helpdesk conversation across any connected channel (widget, email, social DMs, …). */
542
+ interface Conversation {
543
+ id: string;
544
+ /** Channel type, e.g. 'widget', 'instagram', 'messenger', 'whatsapp', 'email'. */
545
+ channel: string;
546
+ channel_connection_id: string | null;
547
+ status: ConversationStatus;
548
+ subject: string | null;
549
+ assignee_user_id: string | null;
550
+ contact_id: string | null;
551
+ visitor_name: string | null;
552
+ visitor_email: string | null;
553
+ external_conversation_id: string | null;
554
+ channel_account_id: string | null;
555
+ message_count: number;
556
+ unread_for_operator: number;
557
+ /** Unix timestamp in milliseconds. */
558
+ last_message_at: number;
559
+ last_message_preview: string | null;
560
+ last_message_author_type: MessageAuthorType | null;
561
+ /** Unix timestamp in milliseconds. */
562
+ created_at: number;
563
+ /** Unix timestamp in milliseconds. */
564
+ updated_at: number;
565
+ }
566
+ /** A single message inside a helpdesk conversation. */
567
+ interface ConversationMessage {
568
+ id: string;
569
+ conversation_id: string;
570
+ author_type: MessageAuthorType;
571
+ message_type: HelpdeskMessageType;
572
+ author_user_id: string | null;
573
+ author_name: string | null;
574
+ body: string;
575
+ /** Unix timestamp in milliseconds. */
576
+ created_at: number;
577
+ }
578
+ /** Filters for listing/searching helpdesk conversations. */
579
+ interface ListConversationsOptions extends PaginationOptions {
580
+ status?: ConversationStatus;
581
+ /** Only conversations assigned to this user. */
582
+ assignee_user_id?: string;
583
+ /** Match against visitor name/email. */
584
+ requester?: string;
585
+ /** Free-text search query. */
586
+ query?: string;
587
+ /** Only conversations on these channels (serialized as CSV). */
588
+ channels?: string[];
589
+ }
590
+ /** Input for updating a conversation's status and/or assignee. At least one field is required. */
591
+ interface UpdateConversationInput {
592
+ status?: ConversationStatus;
593
+ /** User ID to assign, or `null` to unassign. */
594
+ assignee_user_id?: string | null;
595
+ }
596
+ /** Result of a conversation update. */
597
+ interface ConversationUpdateResult {
598
+ id: string;
599
+ status: ConversationStatus;
600
+ assignee_user_id: string | null;
601
+ }
602
+ /** Input for sending an operator reply (or internal note) into a conversation. */
603
+ interface CreateReplyInput {
604
+ conversation_id: string;
605
+ /** Message body (max 20,000 characters). */
606
+ body: string;
607
+ /** `note` = operator-internal note (not delivered to the customer). Default `chat`. */
608
+ message_type?: "chat" | "note";
609
+ /** Agent display name for bridged replies (shown in widget + inbox). */
610
+ author_name?: string;
611
+ }
612
+ /** Result returned after creating a reply (HTTP 201). */
613
+ interface ReplyCreateResult {
614
+ id: string;
615
+ conversation_id: string;
616
+ status: string;
617
+ }
618
+
619
+ /** Browse and manage helpdesk conversations. */
620
+ declare class HelpdeskConversations {
621
+ private client;
622
+ constructor(client: BaseClient);
623
+ /** List/search conversations with cursor-based pagination and optional filters. */
624
+ list(options?: ListConversationsOptions): Promise<PaginatedResponse<Conversation>>;
625
+ /** Get a conversation by ID. */
626
+ get(id: string): Promise<ApiResponse<Conversation>>;
627
+ /** Update a conversation's status and/or assignee (pass `assignee_user_id: null` to unassign). */
628
+ update(id: string, input: UpdateConversationInput, options?: RequestOptions): Promise<ApiResponse<ConversationUpdateResult>>;
629
+ /** Read a conversation's messages with cursor-based pagination. */
630
+ messages(id: string, options?: PaginationOptions): Promise<PaginatedResponse<ConversationMessage>>;
631
+ }
632
+ /** Send operator replies (or internal notes) into conversations. */
633
+ declare class HelpdeskReplies {
634
+ private client;
635
+ constructor(client: BaseClient);
636
+ /**
637
+ * Send an operator reply or internal note. Returns HTTP 201.
638
+ *
639
+ * Pass an `idempotencyKey` so retried requests do not create duplicate
640
+ * messages — it is REQUIRED for capability-scoped tokens.
641
+ */
642
+ create(input: CreateReplyInput, options?: RequestOptions): Promise<ApiResponse<ReplyCreateResult>>;
643
+ }
644
+ /** Helpdesk bridge — read conversations, reply, and manage assignment/status. */
645
+ declare class Helpdesk {
646
+ readonly conversations: HelpdeskConversations;
647
+ readonly replies: HelpdeskReplies;
648
+ constructor(client: BaseClient);
649
+ }
650
+
487
651
  /** A post in the workspace (list view). */
488
652
  interface Post {
489
653
  id: string;
@@ -590,6 +754,129 @@ declare class Posts {
590
754
  channels(): Promise<ApiResponse<Channel[]>>;
591
755
  }
592
756
 
757
+ /** A webhook endpoint registered in the workspace. */
758
+ interface WebhookEndpoint {
759
+ id: string;
760
+ name: string;
761
+ /** Destination URL (must be https). */
762
+ url: string;
763
+ enabled: boolean;
764
+ /** Subscribed event types. Empty array = all events. */
765
+ event_types: string[];
766
+ /** Channel-type filter (e.g. ['widget', 'whatsapp']), or `null` for all channels. */
767
+ channels: string[] | null;
768
+ /** Channel-connection filter, or `null` for all connections. */
769
+ channel_connection_ids: string[] | null;
770
+ /** Last 4 characters of the signing secret, for identification. */
771
+ secret_last4: string;
772
+ consecutive_failures: number;
773
+ /** Unix timestamps in milliseconds, or `null` if never. */
774
+ last_delivery_at: number | null;
775
+ last_success_at: number | null;
776
+ last_error_at: number | null;
777
+ last_error: string | null;
778
+ created_at: number;
779
+ updated_at: number;
780
+ /**
781
+ * Full signing secret (`whsec_…`) — present ONLY in the `create` response.
782
+ * It is returned exactly once and can never be retrieved again. Store it
783
+ * securely immediately; you need it to verify delivery signatures.
784
+ */
785
+ secret?: string;
786
+ }
787
+ /** Input for creating a webhook endpoint. */
788
+ interface CreateWebhookInput {
789
+ /** Display name (max 100 characters). */
790
+ name: string;
791
+ /** Destination URL — must be https. */
792
+ url: string;
793
+ /** Event types to subscribe to (e.g. 'helpdesk.message_received'). Empty = all. */
794
+ event_types: string[];
795
+ /** Restrict to these channel types (e.g. ['widget', 'whatsapp']). */
796
+ channels?: string[];
797
+ /** Restrict to these channel connection IDs. */
798
+ channel_connection_ids?: string[];
799
+ }
800
+ /**
801
+ * Input for updating a webhook endpoint. Only provided fields change.
802
+ * Pass `null` for `channels` or `channel_connection_ids` to CLEAR an existing
803
+ * filter (deliver for all channels / all accounts again); omitting the field
804
+ * leaves the current filter unchanged.
805
+ */
806
+ interface UpdateWebhookInput {
807
+ name?: string;
808
+ url?: string;
809
+ event_types?: string[];
810
+ channels?: string[] | null;
811
+ channel_connection_ids?: string[] | null;
812
+ enabled?: boolean;
813
+ }
814
+ /** Result of deleting a webhook endpoint. */
815
+ interface WebhookDeleteResult {
816
+ id: string;
817
+ status: string;
818
+ }
819
+ /** A delivery attempt record for a webhook endpoint. */
820
+ interface WebhookDelivery {
821
+ id: string;
822
+ event_type: string;
823
+ status: "pending" | "delivered" | "dead_letter";
824
+ attempt_count: number;
825
+ /** Unix timestamp in milliseconds of the next retry, or `null`. */
826
+ next_attempt_at: number | null;
827
+ response_status: number | null;
828
+ duration_ms: number | null;
829
+ last_error: string | null;
830
+ /** Unix timestamp in milliseconds, or `null` if not delivered. */
831
+ delivered_at: number | null;
832
+ created_at: number;
833
+ }
834
+ /** Options for listing recent deliveries. */
835
+ interface ListDeliveriesOptions {
836
+ limit?: number;
837
+ }
838
+ /** Result returned after queuing a test delivery (HTTP 202). */
839
+ interface WebhookTestResult {
840
+ delivery_id: string;
841
+ status: string;
842
+ }
843
+
844
+ /** Manage webhook endpoints and inspect their deliveries. */
845
+ declare class Webhooks {
846
+ private client;
847
+ constructor(client: BaseClient);
848
+ /** List all webhook endpoints in the workspace. */
849
+ list(): Promise<ApiResponse<WebhookEndpoint[]>>;
850
+ /**
851
+ * Create a webhook endpoint. Returns HTTP 201.
852
+ *
853
+ * **The response's `data.secret` contains the signing secret EXACTLY ONCE.**
854
+ * It can never be retrieved again — store it securely immediately. You need
855
+ * it to verify the `X-Medal-Signature` header on incoming deliveries (see
856
+ * `verifyWebhookSignature`).
857
+ *
858
+ * `secret` is typed optional because an idempotent replay (retrying with the
859
+ * same `Idempotency-Key`, `X-Idempotent-Replayed: true`) returns the existing
860
+ * endpoint WITHOUT the secret — handle that case (rotate if you lost it).
861
+ */
862
+ create(input: CreateWebhookInput, options?: RequestOptions): Promise<ApiResponse<WebhookEndpoint>>;
863
+ /** Get a webhook endpoint by ID. */
864
+ get(id: string): Promise<ApiResponse<WebhookEndpoint>>;
865
+ /** Update a webhook endpoint (name, url, event types, filters, enabled). */
866
+ update(id: string, input: UpdateWebhookInput, options?: RequestOptions): Promise<ApiResponse<WebhookEndpoint>>;
867
+ /**
868
+ * Permanently delete a webhook endpoint (stops all outbound deliveries).
869
+ * Capability-scoped tokens must pass `idempotencyKey` — the API requires
870
+ * `Idempotency-Key` + `X-Capability-Confirmation` for direct capability
871
+ * grants on this route. API keys with legacy scopes may omit it.
872
+ */
873
+ delete(id: string, options?: RequestOptions): Promise<ApiResponse<WebhookDeleteResult>>;
874
+ /** List recent deliveries for an endpoint (most recent first). */
875
+ deliveries(id: string, options?: ListDeliveriesOptions): Promise<ApiResponse<WebhookDelivery[]>>;
876
+ /** Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202. */
877
+ test(id: string): Promise<ApiResponse<WebhookTestResult>>;
878
+ }
879
+
593
880
  /** A Medal Social workspace accessible to the authenticated credential. */
594
881
  interface Workspace {
595
882
  id: string;
@@ -606,6 +893,178 @@ declare class Workspaces {
606
893
  list(): Promise<ApiResponse<Workspace[]>>;
607
894
  }
608
895
 
896
+ /**
897
+ * Webhook event types and signature verification for the Medal Social
898
+ * outbound webhook bridge.
899
+ *
900
+ * Every delivery is an HTTP POST with headers:
901
+ * - `X-Medal-Timestamp` — Unix milliseconds when the request was signed
902
+ * - `X-Medal-Signature` — `sha256=<base64(HMAC-SHA256("{timestamp}.{rawBody}", secret))>`
903
+ * - `X-Medal-Event` — the event type
904
+ * - `X-Medal-Delivery-Id` / `Idempotency-Key` — unique delivery ID (deduplicate on this)
905
+ *
906
+ * Use {@link verifyWebhookSignature} to authenticate a delivery and get the
907
+ * parsed, typed event back. Uses Web Crypto (`crypto.subtle`) so it works in
908
+ * Node.js 18+, Deno, Bun, Cloudflare Workers, and browsers.
909
+ */
910
+ /** Snapshot of a conversation included in every helpdesk webhook event. */
911
+ interface WebhookConversationSnapshot {
912
+ id: string;
913
+ channel: string;
914
+ channelConnectionId: string | null;
915
+ status: string;
916
+ subject: string | null;
917
+ assigneeUserId: string | null;
918
+ contactId: string | null;
919
+ visitorName: string | null;
920
+ visitorEmail: string | null;
921
+ externalConversationId: string | null;
922
+ channelAccountId: string | null;
923
+ messageCount: number;
924
+ /** Unix timestamp in milliseconds. */
925
+ lastMessageAt: number;
926
+ /** Unix timestamp in milliseconds. */
927
+ createdAt: number;
928
+ }
929
+ /** Snapshot of a message included in helpdesk message events. */
930
+ interface WebhookMessageSnapshot {
931
+ id: string;
932
+ authorType: "visitor" | "operator" | "ai" | "system";
933
+ messageType: "chat" | "email" | "note";
934
+ body: string;
935
+ authorUserId: string | null;
936
+ authorName: string | null;
937
+ externalMessageId: string | null;
938
+ deliveryStatus: string | null;
939
+ deliveryError: string | null;
940
+ /** Unix timestamp in milliseconds. */
941
+ createdAt: number;
942
+ }
943
+ /** Fields present in the `data` of every helpdesk event. */
944
+ interface HelpdeskEventData {
945
+ /** Channel type at the top level, for quick filtering. */
946
+ channel: string;
947
+ channelConnectionId: string | null;
948
+ conversation: WebhookConversationSnapshot;
949
+ }
950
+ /** Envelope fields shared by all webhook events. */
951
+ interface WebhookEventBase {
952
+ /** Unique delivery/event ID — use for deduplication. */
953
+ id: string;
954
+ /** Unix timestamp in milliseconds when the event was created. */
955
+ created_at: number;
956
+ workspace_id: string;
957
+ }
958
+ /** A new conversation was created. */
959
+ interface ConversationCreatedEvent extends WebhookEventBase {
960
+ type: "helpdesk.conversation_created";
961
+ data: HelpdeskEventData;
962
+ }
963
+ /** A conversation was assigned or unassigned. */
964
+ interface ConversationAssignedEvent extends WebhookEventBase {
965
+ type: "helpdesk.conversation_assigned";
966
+ data: HelpdeskEventData & {
967
+ assigneeUserId: string | null;
968
+ previousAssigneeUserId: string | null;
969
+ };
970
+ }
971
+ /** A conversation's status changed (open / snoozed / closed). */
972
+ interface ConversationStatusChangedEvent extends WebhookEventBase {
973
+ type: "helpdesk.conversation_status_changed";
974
+ data: HelpdeskEventData & {
975
+ status: string;
976
+ previousStatus: string;
977
+ };
978
+ }
979
+ /** A message arrived from the visitor/customer. */
980
+ interface MessageReceivedEvent extends WebhookEventBase {
981
+ type: "helpdesk.message_received";
982
+ data: HelpdeskEventData & {
983
+ message: WebhookMessageSnapshot;
984
+ };
985
+ }
986
+ /** A message was sent by an operator, AI, or the system. */
987
+ interface MessageSentEvent extends WebhookEventBase {
988
+ type: "helpdesk.message_sent";
989
+ data: HelpdeskEventData & {
990
+ message: WebhookMessageSnapshot;
991
+ };
992
+ }
993
+ /** The delivery status of an outbound message changed (sent / delivered / failed …). */
994
+ interface MessageDeliveryUpdatedEvent extends WebhookEventBase {
995
+ type: "helpdesk.message_delivery_updated";
996
+ data: HelpdeskEventData & {
997
+ message: WebhookMessageSnapshot;
998
+ };
999
+ }
1000
+ /** A `test.ping` delivery queued via `medal.webhooks.test(id)`. Carries sample data. */
1001
+ interface TestPingEvent extends WebhookEventBase {
1002
+ type: "test.ping";
1003
+ data: Record<string, unknown>;
1004
+ }
1005
+ /**
1006
+ * Discriminated union of all webhook events, keyed on `type`.
1007
+ *
1008
+ * @example
1009
+ * ```ts
1010
+ * switch (event.type) {
1011
+ * case 'helpdesk.message_received':
1012
+ * console.log(event.data.message.body);
1013
+ * break;
1014
+ * case 'helpdesk.conversation_status_changed':
1015
+ * console.log(event.data.previousStatus, '→', event.data.status);
1016
+ * break;
1017
+ * }
1018
+ * ```
1019
+ */
1020
+ type WebhookEvent = ConversationCreatedEvent | ConversationAssignedEvent | ConversationStatusChangedEvent | MessageReceivedEvent | MessageSentEvent | MessageDeliveryUpdatedEvent | TestPingEvent;
1021
+ /** Machine-readable reason a webhook verification failed. */
1022
+ type WebhookVerificationErrorCode = "malformed_header" | "timestamp_out_of_tolerance" | "invalid_signature" | "invalid_payload";
1023
+ /** Thrown by {@link verifyWebhookSignature} when a delivery cannot be authenticated. */
1024
+ declare class WebhookVerificationError extends Error {
1025
+ readonly code: WebhookVerificationErrorCode;
1026
+ constructor(code: WebhookVerificationErrorCode, message: string);
1027
+ }
1028
+ /** Input for {@link verifyWebhookSignature}. */
1029
+ interface VerifyWebhookSignatureInput {
1030
+ /** The RAW request body string, exactly as received (do not re-serialize parsed JSON). */
1031
+ payload: string;
1032
+ /** Value of the `X-Medal-Timestamp` header (Unix milliseconds). */
1033
+ timestamp: string;
1034
+ /** Value of the `X-Medal-Signature` header (`sha256=<base64>`). */
1035
+ signature: string;
1036
+ /** The endpoint signing secret (`whsec_…`) returned once at creation time. */
1037
+ secret: string;
1038
+ /** Max allowed clock skew between now and the signed timestamp. Default 5 minutes. */
1039
+ toleranceMs?: number;
1040
+ }
1041
+ /** Default allowed clock skew for webhook verification (5 minutes). */
1042
+ declare const DEFAULT_WEBHOOK_TOLERANCE_MS: number;
1043
+ /**
1044
+ * Verify a webhook delivery's signature and timestamp, then return the parsed
1045
+ * typed event.
1046
+ *
1047
+ * Recomputes `HMAC-SHA256("{timestamp}.{payload}", secret)` with Web Crypto
1048
+ * and compares it against the signature in constant time. Deliveries whose
1049
+ * timestamp deviates from the current time by more than `toleranceMs`
1050
+ * (default 5 minutes) are rejected to prevent replay attacks.
1051
+ *
1052
+ * @throws {WebhookVerificationError} if the headers are malformed, the
1053
+ * timestamp is outside the tolerance window, the signature does not match,
1054
+ * or the payload is not valid JSON.
1055
+ *
1056
+ * @example
1057
+ * ```ts
1058
+ * const event = await verifyWebhookSignature({
1059
+ * payload: rawBody,
1060
+ * timestamp: req.headers['x-medal-timestamp'],
1061
+ * signature: req.headers['x-medal-signature'],
1062
+ * secret: process.env.MEDAL_WEBHOOK_SECRET,
1063
+ * });
1064
+ * ```
1065
+ */
1066
+ declare function verifyWebhookSignature(input: VerifyWebhookSignatureInput): Promise<WebhookEvent>;
1067
+
609
1068
  /** Options for configuring the {@link Medal} client. */
610
1069
  interface MedalOptions {
611
1070
  /** Override the base URL (defaults to https://io.medalsocial.com). */
@@ -670,7 +1129,9 @@ declare class Medal {
670
1129
  readonly contacts: Contacts;
671
1130
  readonly deals: Deals;
672
1131
  readonly gdpr: Gdpr;
1132
+ readonly helpdesk: Helpdesk;
673
1133
  readonly posts: Posts;
1134
+ readonly webhooks: Webhooks;
674
1135
  readonly workspaces: Workspaces;
675
1136
  constructor(token: string, options?: MedalOptions);
676
1137
  }
@@ -678,4 +1139,4 @@ declare class Medal {
678
1139
  /** Convenience factory — equivalent to `new Medal(apiKey, options)`. */
679
1140
  declare function createMedalClient(apiKey: string, options?: MedalOptions): Medal;
680
1141
 
681
- export { type Activity, type AddNoteInput, type ApiResponse, BaseClient, type BatchSendInput, type BatchSendResult, type BatchSendSummary, type Channel, type ConsentRecord, type ConsentResult, type ConsentType, type Contact, type ContactConsents, type ContactCreateResult, type ContactNoteResult, type ContactRemoveResult, type ContactStatus, type ContactUpdateResult, Contacts, type CookieCategoryConsent, type CookieConsentInput, type CreateContactInput, type CreateDealInput, type CreatePostInput, type Deal, type DealCreateResult, type DealRemoveResult, type DealStatus, type DealUpdateResult, Deals, type EmailSend, type EmailSendResult, type EmailStatus, type EmailTemplate, type EmailTemplateDetail, Emails, Gdpr, type GdprExport, type GetTemplateOptions, type ImportContactInput, type ImportContactsResult, type ListContactsOptions, type ListDealsOptions, type ListPostsOptions, Medal, MedalApiError, type MedalOptions, type PaginatedResponse, type PaginationOptions, type Post, type PostDetail, type PostType, type PostVariant, Posts, type PublishResult, type RecordConsentInput, type SchedulePostInput, type ScheduleResult, type SendEmailInput, type UpdateContactInput, type UpdateDealInput, type UpdatePostInput, type Workspace, Workspaces, createMedalClient, Medal as default };
1142
+ export { type Activity, type AddNoteInput, type ApiResponse, BaseClient, type BatchSendInput, type BatchSendResult, type BatchSendSummary, type Channel, type ConsentRecord, type ConsentResult, type ConsentType, type Contact, type ContactConsents, type ContactCreateResult, type ContactNoteResult, type ContactRemoveResult, type ContactStatus, type ContactUpdateResult, Contacts, type Conversation, type ConversationAssignedEvent, type ConversationCreatedEvent, type ConversationMessage, type ConversationStatus, type ConversationStatusChangedEvent, type ConversationUpdateResult, type CookieCategoryConsent, type CookieConsentInput, type CreateContactInput, type CreateDealInput, type CreatePostInput, type CreateReplyInput, type CreateWebhookInput, DEFAULT_WEBHOOK_TOLERANCE_MS, type Deal, type DealCreateResult, type DealRemoveResult, type DealStatus, type DealUpdateResult, Deals, type EmailSend, type EmailSendResult, type EmailStatus, type EmailTemplate, type EmailTemplateDetail, Emails, Gdpr, type GdprExport, type GetTemplateOptions, Helpdesk, type HelpdeskMessageType, type ImportContactInput, type ImportContactsResult, type ListContactsOptions, type ListConversationsOptions, type ListDealsOptions, type ListDeliveriesOptions, type ListPostsOptions, Medal, MedalApiError, type MedalOptions, type MessageAuthorType, type MessageDeliveryUpdatedEvent, type MessageReceivedEvent, type MessageSentEvent, type PaginatedResponse, type PaginationOptions, type Post, type PostDetail, type PostType, type PostVariant, Posts, type PublishResult, type RecordConsentInput, type ReplyCreateResult, type RequestOptions, type SchedulePostInput, type ScheduleResult, type SendEmailInput, type TestPingEvent, type UpdateContactInput, type UpdateConversationInput, type UpdateDealInput, type UpdatePostInput, type UpdateWebhookInput, type VerifyWebhookSignatureInput, type WebhookConversationSnapshot, type WebhookDeleteResult, type WebhookDelivery, type WebhookEndpoint, type WebhookEvent, type WebhookMessageSnapshot, type WebhookTestResult, WebhookVerificationError, type WebhookVerificationErrorCode, Webhooks, type Workspace, Workspaces, createMedalClient, Medal as default, verifyWebhookSignature };