@medalsocial/sdk 1.2.1 → 1.3.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/README.md +131 -1
- package/dist/openapi/medal-social.openapi.json +1257 -86
- package/dist/src/index.d.mts +437 -4
- package/dist/src/index.d.ts +437 -4
- package/dist/src/index.js +224 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.mjs +218 -7
- package/dist/src/index.mjs.map +1 -1
- package/dist/src/openapi.generated.d.mts +674 -0
- package/dist/src/openapi.generated.d.ts +674 -0
- package/dist/src/openapi.generated.js.map +1 -1
- package/openapi/medal-social.openapi.yaml +710 -0
- package/package.json +14 -14
package/dist/src/index.d.mts
CHANGED
|
@@ -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
|
}
|
|
@@ -484,6 +504,122 @@ declare class Gdpr {
|
|
|
484
504
|
}>;
|
|
485
505
|
}
|
|
486
506
|
|
|
507
|
+
/** Lifecycle status of a helpdesk conversation. */
|
|
508
|
+
type ConversationStatus = "open" | "snoozed" | "closed";
|
|
509
|
+
/** Who authored a helpdesk message. */
|
|
510
|
+
type MessageAuthorType = "visitor" | "operator" | "ai" | "system";
|
|
511
|
+
/** Kind of helpdesk message. `note` is operator-internal and never delivered to the customer. */
|
|
512
|
+
type HelpdeskMessageType = "chat" | "email" | "note";
|
|
513
|
+
/** A helpdesk conversation across any connected channel (widget, email, social DMs, …). */
|
|
514
|
+
interface Conversation {
|
|
515
|
+
id: string;
|
|
516
|
+
/** Channel type, e.g. 'widget', 'instagram', 'messenger', 'whatsapp', 'email'. */
|
|
517
|
+
channel: string;
|
|
518
|
+
channel_connection_id: string | null;
|
|
519
|
+
status: ConversationStatus;
|
|
520
|
+
subject: string | null;
|
|
521
|
+
assignee_user_id: string | null;
|
|
522
|
+
contact_id: string | null;
|
|
523
|
+
visitor_name: string | null;
|
|
524
|
+
visitor_email: string | null;
|
|
525
|
+
external_conversation_id: string | null;
|
|
526
|
+
channel_account_id: string | null;
|
|
527
|
+
message_count: number;
|
|
528
|
+
unread_for_operator: number;
|
|
529
|
+
/** Unix timestamp in milliseconds. */
|
|
530
|
+
last_message_at: number;
|
|
531
|
+
last_message_preview: string | null;
|
|
532
|
+
last_message_author_type: MessageAuthorType | null;
|
|
533
|
+
/** Unix timestamp in milliseconds. */
|
|
534
|
+
created_at: number;
|
|
535
|
+
/** Unix timestamp in milliseconds. */
|
|
536
|
+
updated_at: number;
|
|
537
|
+
}
|
|
538
|
+
/** A single message inside a helpdesk conversation. */
|
|
539
|
+
interface ConversationMessage {
|
|
540
|
+
id: string;
|
|
541
|
+
conversation_id: string;
|
|
542
|
+
author_type: MessageAuthorType;
|
|
543
|
+
message_type: HelpdeskMessageType;
|
|
544
|
+
author_user_id: string | null;
|
|
545
|
+
author_name: string | null;
|
|
546
|
+
body: string;
|
|
547
|
+
/** Unix timestamp in milliseconds. */
|
|
548
|
+
created_at: number;
|
|
549
|
+
}
|
|
550
|
+
/** Filters for listing/searching helpdesk conversations. */
|
|
551
|
+
interface ListConversationsOptions extends PaginationOptions {
|
|
552
|
+
status?: ConversationStatus;
|
|
553
|
+
/** Only conversations assigned to this user. */
|
|
554
|
+
assignee_user_id?: string;
|
|
555
|
+
/** Match against visitor name/email. */
|
|
556
|
+
requester?: string;
|
|
557
|
+
/** Free-text search query. */
|
|
558
|
+
query?: string;
|
|
559
|
+
/** Only conversations on these channels (serialized as CSV). */
|
|
560
|
+
channels?: string[];
|
|
561
|
+
}
|
|
562
|
+
/** Input for updating a conversation's status and/or assignee. At least one field is required. */
|
|
563
|
+
interface UpdateConversationInput {
|
|
564
|
+
status?: ConversationStatus;
|
|
565
|
+
/** User ID to assign, or `null` to unassign. */
|
|
566
|
+
assignee_user_id?: string | null;
|
|
567
|
+
}
|
|
568
|
+
/** Result of a conversation update. */
|
|
569
|
+
interface ConversationUpdateResult {
|
|
570
|
+
id: string;
|
|
571
|
+
status: ConversationStatus;
|
|
572
|
+
assignee_user_id: string | null;
|
|
573
|
+
}
|
|
574
|
+
/** Input for sending an operator reply (or internal note) into a conversation. */
|
|
575
|
+
interface CreateReplyInput {
|
|
576
|
+
conversation_id: string;
|
|
577
|
+
/** Message body (max 20,000 characters). */
|
|
578
|
+
body: string;
|
|
579
|
+
/** `note` = operator-internal note (not delivered to the customer). Default `chat`. */
|
|
580
|
+
message_type?: "chat" | "note";
|
|
581
|
+
/** Agent display name for bridged replies (shown in widget + inbox). */
|
|
582
|
+
author_name?: string;
|
|
583
|
+
}
|
|
584
|
+
/** Result returned after creating a reply (HTTP 201). */
|
|
585
|
+
interface ReplyCreateResult {
|
|
586
|
+
id: string;
|
|
587
|
+
conversation_id: string;
|
|
588
|
+
status: string;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Browse and manage helpdesk conversations. */
|
|
592
|
+
declare class HelpdeskConversations {
|
|
593
|
+
private client;
|
|
594
|
+
constructor(client: BaseClient);
|
|
595
|
+
/** List/search conversations with cursor-based pagination and optional filters. */
|
|
596
|
+
list(options?: ListConversationsOptions): Promise<PaginatedResponse<Conversation>>;
|
|
597
|
+
/** Get a conversation by ID. */
|
|
598
|
+
get(id: string): Promise<ApiResponse<Conversation>>;
|
|
599
|
+
/** Update a conversation's status and/or assignee (pass `assignee_user_id: null` to unassign). */
|
|
600
|
+
update(id: string, input: UpdateConversationInput, options?: RequestOptions): Promise<ApiResponse<ConversationUpdateResult>>;
|
|
601
|
+
/** Read a conversation's messages with cursor-based pagination. */
|
|
602
|
+
messages(id: string, options?: PaginationOptions): Promise<PaginatedResponse<ConversationMessage>>;
|
|
603
|
+
}
|
|
604
|
+
/** Send operator replies (or internal notes) into conversations. */
|
|
605
|
+
declare class HelpdeskReplies {
|
|
606
|
+
private client;
|
|
607
|
+
constructor(client: BaseClient);
|
|
608
|
+
/**
|
|
609
|
+
* Send an operator reply or internal note. Returns HTTP 201.
|
|
610
|
+
*
|
|
611
|
+
* Pass an `idempotencyKey` so retried requests do not create duplicate
|
|
612
|
+
* messages — it is REQUIRED for capability-scoped tokens.
|
|
613
|
+
*/
|
|
614
|
+
create(input: CreateReplyInput, options?: RequestOptions): Promise<ApiResponse<ReplyCreateResult>>;
|
|
615
|
+
}
|
|
616
|
+
/** Helpdesk bridge — read conversations, reply, and manage assignment/status. */
|
|
617
|
+
declare class Helpdesk {
|
|
618
|
+
readonly conversations: HelpdeskConversations;
|
|
619
|
+
readonly replies: HelpdeskReplies;
|
|
620
|
+
constructor(client: BaseClient);
|
|
621
|
+
}
|
|
622
|
+
|
|
487
623
|
/** A post in the workspace (list view). */
|
|
488
624
|
interface Post {
|
|
489
625
|
id: string;
|
|
@@ -590,6 +726,129 @@ declare class Posts {
|
|
|
590
726
|
channels(): Promise<ApiResponse<Channel[]>>;
|
|
591
727
|
}
|
|
592
728
|
|
|
729
|
+
/** A webhook endpoint registered in the workspace. */
|
|
730
|
+
interface WebhookEndpoint {
|
|
731
|
+
id: string;
|
|
732
|
+
name: string;
|
|
733
|
+
/** Destination URL (must be https). */
|
|
734
|
+
url: string;
|
|
735
|
+
enabled: boolean;
|
|
736
|
+
/** Subscribed event types. Empty array = all events. */
|
|
737
|
+
event_types: string[];
|
|
738
|
+
/** Channel-type filter (e.g. ['widget', 'whatsapp']), or `null` for all channels. */
|
|
739
|
+
channels: string[] | null;
|
|
740
|
+
/** Channel-connection filter, or `null` for all connections. */
|
|
741
|
+
channel_connection_ids: string[] | null;
|
|
742
|
+
/** Last 4 characters of the signing secret, for identification. */
|
|
743
|
+
secret_last4: string;
|
|
744
|
+
consecutive_failures: number;
|
|
745
|
+
/** Unix timestamps in milliseconds, or `null` if never. */
|
|
746
|
+
last_delivery_at: number | null;
|
|
747
|
+
last_success_at: number | null;
|
|
748
|
+
last_error_at: number | null;
|
|
749
|
+
last_error: string | null;
|
|
750
|
+
created_at: number;
|
|
751
|
+
updated_at: number;
|
|
752
|
+
/**
|
|
753
|
+
* Full signing secret (`whsec_…`) — present ONLY in the `create` response.
|
|
754
|
+
* It is returned exactly once and can never be retrieved again. Store it
|
|
755
|
+
* securely immediately; you need it to verify delivery signatures.
|
|
756
|
+
*/
|
|
757
|
+
secret?: string;
|
|
758
|
+
}
|
|
759
|
+
/** Input for creating a webhook endpoint. */
|
|
760
|
+
interface CreateWebhookInput {
|
|
761
|
+
/** Display name (max 100 characters). */
|
|
762
|
+
name: string;
|
|
763
|
+
/** Destination URL — must be https. */
|
|
764
|
+
url: string;
|
|
765
|
+
/** Event types to subscribe to (e.g. 'helpdesk.message_received'). Empty = all. */
|
|
766
|
+
event_types: string[];
|
|
767
|
+
/** Restrict to these channel types (e.g. ['widget', 'whatsapp']). */
|
|
768
|
+
channels?: string[];
|
|
769
|
+
/** Restrict to these channel connection IDs. */
|
|
770
|
+
channel_connection_ids?: string[];
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Input for updating a webhook endpoint. Only provided fields change.
|
|
774
|
+
* Pass `null` for `channels` or `channel_connection_ids` to CLEAR an existing
|
|
775
|
+
* filter (deliver for all channels / all accounts again); omitting the field
|
|
776
|
+
* leaves the current filter unchanged.
|
|
777
|
+
*/
|
|
778
|
+
interface UpdateWebhookInput {
|
|
779
|
+
name?: string;
|
|
780
|
+
url?: string;
|
|
781
|
+
event_types?: string[];
|
|
782
|
+
channels?: string[] | null;
|
|
783
|
+
channel_connection_ids?: string[] | null;
|
|
784
|
+
enabled?: boolean;
|
|
785
|
+
}
|
|
786
|
+
/** Result of deleting a webhook endpoint. */
|
|
787
|
+
interface WebhookDeleteResult {
|
|
788
|
+
id: string;
|
|
789
|
+
status: string;
|
|
790
|
+
}
|
|
791
|
+
/** A delivery attempt record for a webhook endpoint. */
|
|
792
|
+
interface WebhookDelivery {
|
|
793
|
+
id: string;
|
|
794
|
+
event_type: string;
|
|
795
|
+
status: "pending" | "delivered" | "dead_letter";
|
|
796
|
+
attempt_count: number;
|
|
797
|
+
/** Unix timestamp in milliseconds of the next retry, or `null`. */
|
|
798
|
+
next_attempt_at: number | null;
|
|
799
|
+
response_status: number | null;
|
|
800
|
+
duration_ms: number | null;
|
|
801
|
+
last_error: string | null;
|
|
802
|
+
/** Unix timestamp in milliseconds, or `null` if not delivered. */
|
|
803
|
+
delivered_at: number | null;
|
|
804
|
+
created_at: number;
|
|
805
|
+
}
|
|
806
|
+
/** Options for listing recent deliveries. */
|
|
807
|
+
interface ListDeliveriesOptions {
|
|
808
|
+
limit?: number;
|
|
809
|
+
}
|
|
810
|
+
/** Result returned after queuing a test delivery (HTTP 202). */
|
|
811
|
+
interface WebhookTestResult {
|
|
812
|
+
delivery_id: string;
|
|
813
|
+
status: string;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/** Manage webhook endpoints and inspect their deliveries. */
|
|
817
|
+
declare class Webhooks {
|
|
818
|
+
private client;
|
|
819
|
+
constructor(client: BaseClient);
|
|
820
|
+
/** List all webhook endpoints in the workspace. */
|
|
821
|
+
list(): Promise<ApiResponse<WebhookEndpoint[]>>;
|
|
822
|
+
/**
|
|
823
|
+
* Create a webhook endpoint. Returns HTTP 201.
|
|
824
|
+
*
|
|
825
|
+
* **The response's `data.secret` contains the signing secret EXACTLY ONCE.**
|
|
826
|
+
* It can never be retrieved again — store it securely immediately. You need
|
|
827
|
+
* it to verify the `X-Medal-Signature` header on incoming deliveries (see
|
|
828
|
+
* `verifyWebhookSignature`).
|
|
829
|
+
*
|
|
830
|
+
* `secret` is typed optional because an idempotent replay (retrying with the
|
|
831
|
+
* same `Idempotency-Key`, `X-Idempotent-Replayed: true`) returns the existing
|
|
832
|
+
* endpoint WITHOUT the secret — handle that case (rotate if you lost it).
|
|
833
|
+
*/
|
|
834
|
+
create(input: CreateWebhookInput, options?: RequestOptions): Promise<ApiResponse<WebhookEndpoint>>;
|
|
835
|
+
/** Get a webhook endpoint by ID. */
|
|
836
|
+
get(id: string): Promise<ApiResponse<WebhookEndpoint>>;
|
|
837
|
+
/** Update a webhook endpoint (name, url, event types, filters, enabled). */
|
|
838
|
+
update(id: string, input: UpdateWebhookInput, options?: RequestOptions): Promise<ApiResponse<WebhookEndpoint>>;
|
|
839
|
+
/**
|
|
840
|
+
* Permanently delete a webhook endpoint (stops all outbound deliveries).
|
|
841
|
+
* Capability-scoped tokens must pass `idempotencyKey` — the API requires
|
|
842
|
+
* `Idempotency-Key` + `X-Capability-Confirmation` for direct capability
|
|
843
|
+
* grants on this route. API keys with legacy scopes may omit it.
|
|
844
|
+
*/
|
|
845
|
+
delete(id: string, options?: RequestOptions): Promise<ApiResponse<WebhookDeleteResult>>;
|
|
846
|
+
/** List recent deliveries for an endpoint (most recent first). */
|
|
847
|
+
deliveries(id: string, options?: ListDeliveriesOptions): Promise<ApiResponse<WebhookDelivery[]>>;
|
|
848
|
+
/** Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202. */
|
|
849
|
+
test(id: string): Promise<ApiResponse<WebhookTestResult>>;
|
|
850
|
+
}
|
|
851
|
+
|
|
593
852
|
/** A Medal Social workspace accessible to the authenticated credential. */
|
|
594
853
|
interface Workspace {
|
|
595
854
|
id: string;
|
|
@@ -606,6 +865,178 @@ declare class Workspaces {
|
|
|
606
865
|
list(): Promise<ApiResponse<Workspace[]>>;
|
|
607
866
|
}
|
|
608
867
|
|
|
868
|
+
/**
|
|
869
|
+
* Webhook event types and signature verification for the Medal Social
|
|
870
|
+
* outbound webhook bridge.
|
|
871
|
+
*
|
|
872
|
+
* Every delivery is an HTTP POST with headers:
|
|
873
|
+
* - `X-Medal-Timestamp` — Unix milliseconds when the request was signed
|
|
874
|
+
* - `X-Medal-Signature` — `sha256=<base64(HMAC-SHA256("{timestamp}.{rawBody}", secret))>`
|
|
875
|
+
* - `X-Medal-Event` — the event type
|
|
876
|
+
* - `X-Medal-Delivery-Id` / `Idempotency-Key` — unique delivery ID (deduplicate on this)
|
|
877
|
+
*
|
|
878
|
+
* Use {@link verifyWebhookSignature} to authenticate a delivery and get the
|
|
879
|
+
* parsed, typed event back. Uses Web Crypto (`crypto.subtle`) so it works in
|
|
880
|
+
* Node.js 18+, Deno, Bun, Cloudflare Workers, and browsers.
|
|
881
|
+
*/
|
|
882
|
+
/** Snapshot of a conversation included in every helpdesk webhook event. */
|
|
883
|
+
interface WebhookConversationSnapshot {
|
|
884
|
+
id: string;
|
|
885
|
+
channel: string;
|
|
886
|
+
channelConnectionId: string | null;
|
|
887
|
+
status: string;
|
|
888
|
+
subject: string | null;
|
|
889
|
+
assigneeUserId: string | null;
|
|
890
|
+
contactId: string | null;
|
|
891
|
+
visitorName: string | null;
|
|
892
|
+
visitorEmail: string | null;
|
|
893
|
+
externalConversationId: string | null;
|
|
894
|
+
channelAccountId: string | null;
|
|
895
|
+
messageCount: number;
|
|
896
|
+
/** Unix timestamp in milliseconds. */
|
|
897
|
+
lastMessageAt: number;
|
|
898
|
+
/** Unix timestamp in milliseconds. */
|
|
899
|
+
createdAt: number;
|
|
900
|
+
}
|
|
901
|
+
/** Snapshot of a message included in helpdesk message events. */
|
|
902
|
+
interface WebhookMessageSnapshot {
|
|
903
|
+
id: string;
|
|
904
|
+
authorType: "visitor" | "operator" | "ai" | "system";
|
|
905
|
+
messageType: "chat" | "email" | "note";
|
|
906
|
+
body: string;
|
|
907
|
+
authorUserId: string | null;
|
|
908
|
+
authorName: string | null;
|
|
909
|
+
externalMessageId: string | null;
|
|
910
|
+
deliveryStatus: string | null;
|
|
911
|
+
deliveryError: string | null;
|
|
912
|
+
/** Unix timestamp in milliseconds. */
|
|
913
|
+
createdAt: number;
|
|
914
|
+
}
|
|
915
|
+
/** Fields present in the `data` of every helpdesk event. */
|
|
916
|
+
interface HelpdeskEventData {
|
|
917
|
+
/** Channel type at the top level, for quick filtering. */
|
|
918
|
+
channel: string;
|
|
919
|
+
channelConnectionId: string | null;
|
|
920
|
+
conversation: WebhookConversationSnapshot;
|
|
921
|
+
}
|
|
922
|
+
/** Envelope fields shared by all webhook events. */
|
|
923
|
+
interface WebhookEventBase {
|
|
924
|
+
/** Unique delivery/event ID — use for deduplication. */
|
|
925
|
+
id: string;
|
|
926
|
+
/** Unix timestamp in milliseconds when the event was created. */
|
|
927
|
+
created_at: number;
|
|
928
|
+
workspace_id: string;
|
|
929
|
+
}
|
|
930
|
+
/** A new conversation was created. */
|
|
931
|
+
interface ConversationCreatedEvent extends WebhookEventBase {
|
|
932
|
+
type: "helpdesk.conversation_created";
|
|
933
|
+
data: HelpdeskEventData;
|
|
934
|
+
}
|
|
935
|
+
/** A conversation was assigned or unassigned. */
|
|
936
|
+
interface ConversationAssignedEvent extends WebhookEventBase {
|
|
937
|
+
type: "helpdesk.conversation_assigned";
|
|
938
|
+
data: HelpdeskEventData & {
|
|
939
|
+
assigneeUserId: string | null;
|
|
940
|
+
previousAssigneeUserId: string | null;
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
/** A conversation's status changed (open / snoozed / closed). */
|
|
944
|
+
interface ConversationStatusChangedEvent extends WebhookEventBase {
|
|
945
|
+
type: "helpdesk.conversation_status_changed";
|
|
946
|
+
data: HelpdeskEventData & {
|
|
947
|
+
status: string;
|
|
948
|
+
previousStatus: string;
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
/** A message arrived from the visitor/customer. */
|
|
952
|
+
interface MessageReceivedEvent extends WebhookEventBase {
|
|
953
|
+
type: "helpdesk.message_received";
|
|
954
|
+
data: HelpdeskEventData & {
|
|
955
|
+
message: WebhookMessageSnapshot;
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
/** A message was sent by an operator, AI, or the system. */
|
|
959
|
+
interface MessageSentEvent extends WebhookEventBase {
|
|
960
|
+
type: "helpdesk.message_sent";
|
|
961
|
+
data: HelpdeskEventData & {
|
|
962
|
+
message: WebhookMessageSnapshot;
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
/** The delivery status of an outbound message changed (sent / delivered / failed …). */
|
|
966
|
+
interface MessageDeliveryUpdatedEvent extends WebhookEventBase {
|
|
967
|
+
type: "helpdesk.message_delivery_updated";
|
|
968
|
+
data: HelpdeskEventData & {
|
|
969
|
+
message: WebhookMessageSnapshot;
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
/** A `test.ping` delivery queued via `medal.webhooks.test(id)`. Carries sample data. */
|
|
973
|
+
interface TestPingEvent extends WebhookEventBase {
|
|
974
|
+
type: "test.ping";
|
|
975
|
+
data: Record<string, unknown>;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Discriminated union of all webhook events, keyed on `type`.
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```ts
|
|
982
|
+
* switch (event.type) {
|
|
983
|
+
* case 'helpdesk.message_received':
|
|
984
|
+
* console.log(event.data.message.body);
|
|
985
|
+
* break;
|
|
986
|
+
* case 'helpdesk.conversation_status_changed':
|
|
987
|
+
* console.log(event.data.previousStatus, '→', event.data.status);
|
|
988
|
+
* break;
|
|
989
|
+
* }
|
|
990
|
+
* ```
|
|
991
|
+
*/
|
|
992
|
+
type WebhookEvent = ConversationCreatedEvent | ConversationAssignedEvent | ConversationStatusChangedEvent | MessageReceivedEvent | MessageSentEvent | MessageDeliveryUpdatedEvent | TestPingEvent;
|
|
993
|
+
/** Machine-readable reason a webhook verification failed. */
|
|
994
|
+
type WebhookVerificationErrorCode = "malformed_header" | "timestamp_out_of_tolerance" | "invalid_signature" | "invalid_payload";
|
|
995
|
+
/** Thrown by {@link verifyWebhookSignature} when a delivery cannot be authenticated. */
|
|
996
|
+
declare class WebhookVerificationError extends Error {
|
|
997
|
+
readonly code: WebhookVerificationErrorCode;
|
|
998
|
+
constructor(code: WebhookVerificationErrorCode, message: string);
|
|
999
|
+
}
|
|
1000
|
+
/** Input for {@link verifyWebhookSignature}. */
|
|
1001
|
+
interface VerifyWebhookSignatureInput {
|
|
1002
|
+
/** The RAW request body string, exactly as received (do not re-serialize parsed JSON). */
|
|
1003
|
+
payload: string;
|
|
1004
|
+
/** Value of the `X-Medal-Timestamp` header (Unix milliseconds). */
|
|
1005
|
+
timestamp: string;
|
|
1006
|
+
/** Value of the `X-Medal-Signature` header (`sha256=<base64>`). */
|
|
1007
|
+
signature: string;
|
|
1008
|
+
/** The endpoint signing secret (`whsec_…`) returned once at creation time. */
|
|
1009
|
+
secret: string;
|
|
1010
|
+
/** Max allowed clock skew between now and the signed timestamp. Default 5 minutes. */
|
|
1011
|
+
toleranceMs?: number;
|
|
1012
|
+
}
|
|
1013
|
+
/** Default allowed clock skew for webhook verification (5 minutes). */
|
|
1014
|
+
declare const DEFAULT_WEBHOOK_TOLERANCE_MS: number;
|
|
1015
|
+
/**
|
|
1016
|
+
* Verify a webhook delivery's signature and timestamp, then return the parsed
|
|
1017
|
+
* typed event.
|
|
1018
|
+
*
|
|
1019
|
+
* Recomputes `HMAC-SHA256("{timestamp}.{payload}", secret)` with Web Crypto
|
|
1020
|
+
* and compares it against the signature in constant time. Deliveries whose
|
|
1021
|
+
* timestamp deviates from the current time by more than `toleranceMs`
|
|
1022
|
+
* (default 5 minutes) are rejected to prevent replay attacks.
|
|
1023
|
+
*
|
|
1024
|
+
* @throws {WebhookVerificationError} if the headers are malformed, the
|
|
1025
|
+
* timestamp is outside the tolerance window, the signature does not match,
|
|
1026
|
+
* or the payload is not valid JSON.
|
|
1027
|
+
*
|
|
1028
|
+
* @example
|
|
1029
|
+
* ```ts
|
|
1030
|
+
* const event = await verifyWebhookSignature({
|
|
1031
|
+
* payload: rawBody,
|
|
1032
|
+
* timestamp: req.headers['x-medal-timestamp'],
|
|
1033
|
+
* signature: req.headers['x-medal-signature'],
|
|
1034
|
+
* secret: process.env.MEDAL_WEBHOOK_SECRET,
|
|
1035
|
+
* });
|
|
1036
|
+
* ```
|
|
1037
|
+
*/
|
|
1038
|
+
declare function verifyWebhookSignature(input: VerifyWebhookSignatureInput): Promise<WebhookEvent>;
|
|
1039
|
+
|
|
609
1040
|
/** Options for configuring the {@link Medal} client. */
|
|
610
1041
|
interface MedalOptions {
|
|
611
1042
|
/** Override the base URL (defaults to https://io.medalsocial.com). */
|
|
@@ -670,7 +1101,9 @@ declare class Medal {
|
|
|
670
1101
|
readonly contacts: Contacts;
|
|
671
1102
|
readonly deals: Deals;
|
|
672
1103
|
readonly gdpr: Gdpr;
|
|
1104
|
+
readonly helpdesk: Helpdesk;
|
|
673
1105
|
readonly posts: Posts;
|
|
1106
|
+
readonly webhooks: Webhooks;
|
|
674
1107
|
readonly workspaces: Workspaces;
|
|
675
1108
|
constructor(token: string, options?: MedalOptions);
|
|
676
1109
|
}
|
|
@@ -678,4 +1111,4 @@ declare class Medal {
|
|
|
678
1111
|
/** Convenience factory — equivalent to `new Medal(apiKey, options)`. */
|
|
679
1112
|
declare function createMedalClient(apiKey: string, options?: MedalOptions): Medal;
|
|
680
1113
|
|
|
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 };
|
|
1114
|
+
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 };
|