@medalsocial/sdk 1.3.0 → 1.5.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.
@@ -48,6 +48,78 @@ declare class BaseClient {
48
48
  private request;
49
49
  }
50
50
 
51
+ /** Lifecycle status of a hosted connect link. */
52
+ type ConnectLinkStatus = "pending" | "consumed" | "expired" | "revoked";
53
+ /** Lifecycle state of a channel connection. */
54
+ type ChannelConnectionState = "connecting" | "active" | "disconnected" | "disabled";
55
+ /** Input for minting a hosted connect link. */
56
+ interface CreateConnectLinkInput {
57
+ /** Channel type to connect (e.g. `telegram_inbox`). */
58
+ channel_type: string;
59
+ /** Display label shown on the hosted connect page (max 100 characters). */
60
+ label?: string;
61
+ /** URL the hosted page redirects to after a successful connect — must be https. */
62
+ redirect_url?: string;
63
+ }
64
+ /** Result of minting a connect link (HTTP 201). */
65
+ interface ConnectLinkCreateResult {
66
+ id: string;
67
+ /**
68
+ * The single-use hosted connect URL containing the one-time link token.
69
+ * **Present ONLY in the live create response** — an idempotent replay
70
+ * (retrying with the same `Idempotency-Key`) returns the link WITHOUT `url`.
71
+ * If you lose it, revoke the link and mint a new one; the token can never
72
+ * be retrieved again.
73
+ */
74
+ url?: string;
75
+ channel_type: string;
76
+ label: string | null;
77
+ status: ConnectLinkStatus;
78
+ /** Unix timestamp in milliseconds when the link expires. */
79
+ expires_at: number;
80
+ }
81
+ /** A hosted connect link (list view — tokens are never returned). */
82
+ interface ConnectLink {
83
+ id: string;
84
+ channel_type: string;
85
+ label: string | null;
86
+ status: ConnectLinkStatus;
87
+ /** Stable ref of the connection created by consuming this link, or `null`. */
88
+ consumed_connection_ref: string | null;
89
+ /** Unix timestamp in milliseconds. */
90
+ expires_at: number;
91
+ /** Unix timestamp in milliseconds. */
92
+ created_at: number;
93
+ }
94
+ /** Filters for listing connect links. */
95
+ interface ListConnectLinksOptions {
96
+ channel_type?: string;
97
+ status?: ConnectLinkStatus;
98
+ }
99
+ /** Result of revoking a connect link. */
100
+ interface ConnectLinkRevokeResult {
101
+ id: string;
102
+ status: "revoked";
103
+ }
104
+ /** A channel connection attached to the workspace (generic, channel-agnostic shape). */
105
+ interface ChannelConnection {
106
+ id: string;
107
+ channel_type: string;
108
+ label: string | null;
109
+ state: ChannelConnectionState;
110
+ /** Privacy-preserving identity handle (e.g. a masked phone number). */
111
+ masked_identity: string;
112
+ /** Unix timestamp in milliseconds, or `null` if never active. */
113
+ last_activity_at: number | null;
114
+ /** Linked helpdesk channel connection ID, or `null`. */
115
+ helpdesk_connection_id: string | null;
116
+ }
117
+ /** Result of disconnecting a channel connection. */
118
+ interface ChannelConnectionDisconnectResult {
119
+ id: string;
120
+ state: "disconnected";
121
+ }
122
+
51
123
  /** Successful API response wrapper */
52
124
  interface ApiResponse<T> {
53
125
  data: T;
@@ -73,6 +145,52 @@ interface PaginationOptions {
73
145
  cursor?: string;
74
146
  }
75
147
 
148
+ /** Mint, list, and revoke hosted connect links. */
149
+ declare class ChannelConnectLinks {
150
+ private client;
151
+ constructor(client: BaseClient);
152
+ /**
153
+ * Mint a single-use hosted connect link. Returns HTTP 201.
154
+ *
155
+ * **The response's `data.url` contains the one-time link token EXACTLY
156
+ * ONCE.** Send it to the person who should connect their account — an
157
+ * idempotent replay (same `Idempotency-Key`) returns the link WITHOUT
158
+ * `url`, so store it immediately (or revoke and mint a new link if lost).
159
+ *
160
+ * Requires the `channel.connect.manage` scope; OAuth callers additionally
161
+ * need the workspace `admin` role.
162
+ */
163
+ create(input: CreateConnectLinkInput, options?: RequestOptions): Promise<ApiResponse<ConnectLinkCreateResult>>;
164
+ /** List the workspace's connect links (tokens are never returned). */
165
+ list(options?: ListConnectLinksOptions): Promise<ApiResponse<ConnectLink[]>>;
166
+ /** Revoke a pending connect link so it can no longer be consumed. */
167
+ revoke(id: string, options?: RequestOptions): Promise<ApiResponse<ConnectLinkRevokeResult>>;
168
+ }
169
+ /** List and disconnect the workspace's channel connections. */
170
+ declare class ChannelConnections {
171
+ private client;
172
+ constructor(client: BaseClient);
173
+ /** List the workspace's channel connections (generic, channel-agnostic shape). */
174
+ list(): Promise<ApiResponse<ChannelConnection[]>>;
175
+ /**
176
+ * Disconnect a connected channel account (best-effort platform logout, then
177
+ * local revoke). Emits a `helpdesk.channel_disconnected` webhook event with
178
+ * `reason: "api_disconnect"` if the account was previously connected.
179
+ */
180
+ disconnect(id: string, options?: RequestOptions): Promise<ApiResponse<ChannelConnectionDisconnectResult>>;
181
+ }
182
+ /**
183
+ * Partner channel connect — mint hosted connect links that let an external
184
+ * person (no Medal account required) attach a channel account (e.g.
185
+ * `telegram_inbox`) to the workspace's helpdesk, and manage the resulting
186
+ * connections.
187
+ */
188
+ declare class Channels {
189
+ readonly connectLinks: ChannelConnectLinks;
190
+ readonly connections: ChannelConnections;
191
+ constructor(client: BaseClient);
192
+ }
193
+
76
194
  /** A contact in the workspace CRM. */
77
195
  interface Contact {
78
196
  id: string;
@@ -308,10 +426,21 @@ interface SendEmailInput {
308
426
  fallback_locale?: string;
309
427
  variables?: Record<string, string>;
310
428
  contact_id?: string;
429
+ /** Body-level idempotency key (alternative to the `Idempotency-Key` header). */
430
+ idempotency_key?: string;
431
+ /** Also send a `[Copy]` of the email to this address. */
432
+ copy_to?: string;
433
+ /** Reply-To for the copy (defaults to the primary recipient). */
434
+ copy_reply_to?: string;
311
435
  }
312
436
  /** Result returned after queuing a transactional email send (HTTP 202). */
313
437
  interface EmailSendResult {
314
- id: string;
438
+ /** Email send id — poll `emails.get(id)` with it to track delivery. */
439
+ id: string | null;
440
+ /** Send id of the `copy_to` copy, or `null` when no copy was requested. */
441
+ copy_id: string | null;
442
+ /** CRM contact linked to the send, or `null`. */
443
+ contact_id: string | null;
315
444
  status: string;
316
445
  }
317
446
  /** Full record for a sent email, including delivery timestamps. */
@@ -341,12 +470,23 @@ interface BatchSendInput {
341
470
  variables?: Record<string, string>;
342
471
  }[];
343
472
  }
473
+ /** Per-recipient outcome of a batch send, in request order. */
474
+ interface BatchSendRecipientResult {
475
+ email: string;
476
+ /** Email send id — poll `emails.get(id)` with it. `null` when not queued. */
477
+ id: string | null;
478
+ status: "queued" | "failed";
479
+ /** Failure reason for recipients that were not queued. */
480
+ error: string | null;
481
+ }
344
482
  /** Summary returned after queuing a batch email send. */
345
483
  interface BatchSendSummary {
346
484
  batch_id: string;
347
485
  total: number;
348
486
  queued: number;
349
487
  failed: number;
488
+ /** Per-recipient outcome, in request order. */
489
+ results: BatchSendRecipientResult[];
350
490
  }
351
491
  /** @deprecated Use `BatchSendSummary` for `emails.batch()` responses. */
352
492
  type BatchSendResult = BatchSendSummary;
@@ -407,11 +547,17 @@ declare class Emails {
407
547
  private client;
408
548
  readonly templates: EmailTemplates;
409
549
  constructor(client: BaseClient);
410
- /** Send a transactional email using a template. Returns a queued job ID (HTTP 202). */
550
+ /**
551
+ * Send a transactional email using a template (HTTP 202). The returned `id`
552
+ * is an email send id — poll `emails.get(id)` with it to track delivery.
553
+ */
411
554
  send(input: SendEmailInput): Promise<ApiResponse<EmailSendResult>>;
412
555
  /** Get the delivery status of a sent email. */
413
556
  get(id: string): Promise<ApiResponse<EmailSend>>;
414
- /** Send the same template to multiple recipients (max 100). Returns HTTP 202. */
557
+ /**
558
+ * Send the same template to multiple recipients (max 100, HTTP 202). Each
559
+ * queued recipient gets its own send id in `results` for `emails.get(id)`.
560
+ */
415
561
  batch(input: BatchSendInput): Promise<ApiResponse<BatchSendSummary>>;
416
562
  }
417
563
 
@@ -969,6 +1115,38 @@ interface MessageDeliveryUpdatedEvent extends WebhookEventBase {
969
1115
  message: WebhookMessageSnapshot;
970
1116
  };
971
1117
  }
1118
+ /**
1119
+ * Fields present in the `data` of channel lifecycle events. Unlike message
1120
+ * events there is no conversation snapshot — the payload is channel-generic.
1121
+ * `channel` / `channelConnectionId` sit at the top level so endpoint channel
1122
+ * filters match exactly like message events.
1123
+ */
1124
+ interface WebhookChannelLifecycleData {
1125
+ /** Helpdesk channel type (e.g. `telegram`), or `null` for non-helpdesk channels. */
1126
+ channel: string | null;
1127
+ channelConnectionId: string | null;
1128
+ /** Connector channel type (e.g. `telegram_inbox`). */
1129
+ channel_type: string;
1130
+ /** Adapter-defined stable connection ref (matches `consumed_connection_ref` on the connect link). */
1131
+ connection_ref: string;
1132
+ label: string | null;
1133
+ masked_identity: string | null;
1134
+ }
1135
+ /** A channel account was connected to the workspace (e.g. via a partner connect link). */
1136
+ interface ChannelConnectedEvent extends WebhookEventBase {
1137
+ type: "helpdesk.channel_connected";
1138
+ data: WebhookChannelLifecycleData;
1139
+ }
1140
+ /** Why a channel account was disconnected. */
1141
+ type ChannelDisconnectReason = "api_disconnect" | "user_revoked" | "member_disconnect";
1142
+ /** A previously connected channel account was removed from the workspace. */
1143
+ interface ChannelDisconnectedEvent extends WebhookEventBase {
1144
+ type: "helpdesk.channel_disconnected";
1145
+ data: WebhookChannelLifecycleData & {
1146
+ /** Why the account went away. */
1147
+ reason?: ChannelDisconnectReason;
1148
+ };
1149
+ }
972
1150
  /** A `test.ping` delivery queued via `medal.webhooks.test(id)`. Carries sample data. */
973
1151
  interface TestPingEvent extends WebhookEventBase {
974
1152
  type: "test.ping";
@@ -989,7 +1167,7 @@ interface TestPingEvent extends WebhookEventBase {
989
1167
  * }
990
1168
  * ```
991
1169
  */
992
- type WebhookEvent = ConversationCreatedEvent | ConversationAssignedEvent | ConversationStatusChangedEvent | MessageReceivedEvent | MessageSentEvent | MessageDeliveryUpdatedEvent | TestPingEvent;
1170
+ type WebhookEvent = ConversationCreatedEvent | ConversationAssignedEvent | ConversationStatusChangedEvent | MessageReceivedEvent | MessageSentEvent | MessageDeliveryUpdatedEvent | ChannelConnectedEvent | ChannelDisconnectedEvent | TestPingEvent;
993
1171
  /** Machine-readable reason a webhook verification failed. */
994
1172
  type WebhookVerificationErrorCode = "malformed_header" | "timestamp_out_of_tolerance" | "invalid_signature" | "invalid_payload";
995
1173
  /** Thrown by {@link verifyWebhookSignature} when a delivery cannot be authenticated. */
@@ -1097,6 +1275,7 @@ interface MedalOptions {
1097
1275
  * ```
1098
1276
  */
1099
1277
  declare class Medal {
1278
+ readonly channels: Channels;
1100
1279
  readonly emails: Emails;
1101
1280
  readonly contacts: Contacts;
1102
1281
  readonly deals: Deals;
@@ -1111,4 +1290,4 @@ declare class Medal {
1111
1290
  /** Convenience factory — equivalent to `new Medal(apiKey, options)`. */
1112
1291
  declare function createMedalClient(apiKey: string, options?: MedalOptions): Medal;
1113
1292
 
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 };
1293
+ export { type Activity, type AddNoteInput, type ApiResponse, BaseClient, type BatchSendInput, type BatchSendResult, type BatchSendSummary, type Channel, type ChannelConnectedEvent, type ChannelConnection, type ChannelConnectionDisconnectResult, type ChannelConnectionState, type ChannelDisconnectReason, type ChannelDisconnectedEvent, Channels, type ConnectLink, type ConnectLinkCreateResult, type ConnectLinkRevokeResult, type ConnectLinkStatus, 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 CreateConnectLinkInput, 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 ListConnectLinksOptions, 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 WebhookChannelLifecycleData, 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 };
@@ -48,6 +48,78 @@ declare class BaseClient {
48
48
  private request;
49
49
  }
50
50
 
51
+ /** Lifecycle status of a hosted connect link. */
52
+ type ConnectLinkStatus = "pending" | "consumed" | "expired" | "revoked";
53
+ /** Lifecycle state of a channel connection. */
54
+ type ChannelConnectionState = "connecting" | "active" | "disconnected" | "disabled";
55
+ /** Input for minting a hosted connect link. */
56
+ interface CreateConnectLinkInput {
57
+ /** Channel type to connect (e.g. `telegram_inbox`). */
58
+ channel_type: string;
59
+ /** Display label shown on the hosted connect page (max 100 characters). */
60
+ label?: string;
61
+ /** URL the hosted page redirects to after a successful connect — must be https. */
62
+ redirect_url?: string;
63
+ }
64
+ /** Result of minting a connect link (HTTP 201). */
65
+ interface ConnectLinkCreateResult {
66
+ id: string;
67
+ /**
68
+ * The single-use hosted connect URL containing the one-time link token.
69
+ * **Present ONLY in the live create response** — an idempotent replay
70
+ * (retrying with the same `Idempotency-Key`) returns the link WITHOUT `url`.
71
+ * If you lose it, revoke the link and mint a new one; the token can never
72
+ * be retrieved again.
73
+ */
74
+ url?: string;
75
+ channel_type: string;
76
+ label: string | null;
77
+ status: ConnectLinkStatus;
78
+ /** Unix timestamp in milliseconds when the link expires. */
79
+ expires_at: number;
80
+ }
81
+ /** A hosted connect link (list view — tokens are never returned). */
82
+ interface ConnectLink {
83
+ id: string;
84
+ channel_type: string;
85
+ label: string | null;
86
+ status: ConnectLinkStatus;
87
+ /** Stable ref of the connection created by consuming this link, or `null`. */
88
+ consumed_connection_ref: string | null;
89
+ /** Unix timestamp in milliseconds. */
90
+ expires_at: number;
91
+ /** Unix timestamp in milliseconds. */
92
+ created_at: number;
93
+ }
94
+ /** Filters for listing connect links. */
95
+ interface ListConnectLinksOptions {
96
+ channel_type?: string;
97
+ status?: ConnectLinkStatus;
98
+ }
99
+ /** Result of revoking a connect link. */
100
+ interface ConnectLinkRevokeResult {
101
+ id: string;
102
+ status: "revoked";
103
+ }
104
+ /** A channel connection attached to the workspace (generic, channel-agnostic shape). */
105
+ interface ChannelConnection {
106
+ id: string;
107
+ channel_type: string;
108
+ label: string | null;
109
+ state: ChannelConnectionState;
110
+ /** Privacy-preserving identity handle (e.g. a masked phone number). */
111
+ masked_identity: string;
112
+ /** Unix timestamp in milliseconds, or `null` if never active. */
113
+ last_activity_at: number | null;
114
+ /** Linked helpdesk channel connection ID, or `null`. */
115
+ helpdesk_connection_id: string | null;
116
+ }
117
+ /** Result of disconnecting a channel connection. */
118
+ interface ChannelConnectionDisconnectResult {
119
+ id: string;
120
+ state: "disconnected";
121
+ }
122
+
51
123
  /** Successful API response wrapper */
52
124
  interface ApiResponse<T> {
53
125
  data: T;
@@ -73,6 +145,52 @@ interface PaginationOptions {
73
145
  cursor?: string;
74
146
  }
75
147
 
148
+ /** Mint, list, and revoke hosted connect links. */
149
+ declare class ChannelConnectLinks {
150
+ private client;
151
+ constructor(client: BaseClient);
152
+ /**
153
+ * Mint a single-use hosted connect link. Returns HTTP 201.
154
+ *
155
+ * **The response's `data.url` contains the one-time link token EXACTLY
156
+ * ONCE.** Send it to the person who should connect their account — an
157
+ * idempotent replay (same `Idempotency-Key`) returns the link WITHOUT
158
+ * `url`, so store it immediately (or revoke and mint a new link if lost).
159
+ *
160
+ * Requires the `channel.connect.manage` scope; OAuth callers additionally
161
+ * need the workspace `admin` role.
162
+ */
163
+ create(input: CreateConnectLinkInput, options?: RequestOptions): Promise<ApiResponse<ConnectLinkCreateResult>>;
164
+ /** List the workspace's connect links (tokens are never returned). */
165
+ list(options?: ListConnectLinksOptions): Promise<ApiResponse<ConnectLink[]>>;
166
+ /** Revoke a pending connect link so it can no longer be consumed. */
167
+ revoke(id: string, options?: RequestOptions): Promise<ApiResponse<ConnectLinkRevokeResult>>;
168
+ }
169
+ /** List and disconnect the workspace's channel connections. */
170
+ declare class ChannelConnections {
171
+ private client;
172
+ constructor(client: BaseClient);
173
+ /** List the workspace's channel connections (generic, channel-agnostic shape). */
174
+ list(): Promise<ApiResponse<ChannelConnection[]>>;
175
+ /**
176
+ * Disconnect a connected channel account (best-effort platform logout, then
177
+ * local revoke). Emits a `helpdesk.channel_disconnected` webhook event with
178
+ * `reason: "api_disconnect"` if the account was previously connected.
179
+ */
180
+ disconnect(id: string, options?: RequestOptions): Promise<ApiResponse<ChannelConnectionDisconnectResult>>;
181
+ }
182
+ /**
183
+ * Partner channel connect — mint hosted connect links that let an external
184
+ * person (no Medal account required) attach a channel account (e.g.
185
+ * `telegram_inbox`) to the workspace's helpdesk, and manage the resulting
186
+ * connections.
187
+ */
188
+ declare class Channels {
189
+ readonly connectLinks: ChannelConnectLinks;
190
+ readonly connections: ChannelConnections;
191
+ constructor(client: BaseClient);
192
+ }
193
+
76
194
  /** A contact in the workspace CRM. */
77
195
  interface Contact {
78
196
  id: string;
@@ -308,10 +426,21 @@ interface SendEmailInput {
308
426
  fallback_locale?: string;
309
427
  variables?: Record<string, string>;
310
428
  contact_id?: string;
429
+ /** Body-level idempotency key (alternative to the `Idempotency-Key` header). */
430
+ idempotency_key?: string;
431
+ /** Also send a `[Copy]` of the email to this address. */
432
+ copy_to?: string;
433
+ /** Reply-To for the copy (defaults to the primary recipient). */
434
+ copy_reply_to?: string;
311
435
  }
312
436
  /** Result returned after queuing a transactional email send (HTTP 202). */
313
437
  interface EmailSendResult {
314
- id: string;
438
+ /** Email send id — poll `emails.get(id)` with it to track delivery. */
439
+ id: string | null;
440
+ /** Send id of the `copy_to` copy, or `null` when no copy was requested. */
441
+ copy_id: string | null;
442
+ /** CRM contact linked to the send, or `null`. */
443
+ contact_id: string | null;
315
444
  status: string;
316
445
  }
317
446
  /** Full record for a sent email, including delivery timestamps. */
@@ -341,12 +470,23 @@ interface BatchSendInput {
341
470
  variables?: Record<string, string>;
342
471
  }[];
343
472
  }
473
+ /** Per-recipient outcome of a batch send, in request order. */
474
+ interface BatchSendRecipientResult {
475
+ email: string;
476
+ /** Email send id — poll `emails.get(id)` with it. `null` when not queued. */
477
+ id: string | null;
478
+ status: "queued" | "failed";
479
+ /** Failure reason for recipients that were not queued. */
480
+ error: string | null;
481
+ }
344
482
  /** Summary returned after queuing a batch email send. */
345
483
  interface BatchSendSummary {
346
484
  batch_id: string;
347
485
  total: number;
348
486
  queued: number;
349
487
  failed: number;
488
+ /** Per-recipient outcome, in request order. */
489
+ results: BatchSendRecipientResult[];
350
490
  }
351
491
  /** @deprecated Use `BatchSendSummary` for `emails.batch()` responses. */
352
492
  type BatchSendResult = BatchSendSummary;
@@ -407,11 +547,17 @@ declare class Emails {
407
547
  private client;
408
548
  readonly templates: EmailTemplates;
409
549
  constructor(client: BaseClient);
410
- /** Send a transactional email using a template. Returns a queued job ID (HTTP 202). */
550
+ /**
551
+ * Send a transactional email using a template (HTTP 202). The returned `id`
552
+ * is an email send id — poll `emails.get(id)` with it to track delivery.
553
+ */
411
554
  send(input: SendEmailInput): Promise<ApiResponse<EmailSendResult>>;
412
555
  /** Get the delivery status of a sent email. */
413
556
  get(id: string): Promise<ApiResponse<EmailSend>>;
414
- /** Send the same template to multiple recipients (max 100). Returns HTTP 202. */
557
+ /**
558
+ * Send the same template to multiple recipients (max 100, HTTP 202). Each
559
+ * queued recipient gets its own send id in `results` for `emails.get(id)`.
560
+ */
415
561
  batch(input: BatchSendInput): Promise<ApiResponse<BatchSendSummary>>;
416
562
  }
417
563
 
@@ -969,6 +1115,38 @@ interface MessageDeliveryUpdatedEvent extends WebhookEventBase {
969
1115
  message: WebhookMessageSnapshot;
970
1116
  };
971
1117
  }
1118
+ /**
1119
+ * Fields present in the `data` of channel lifecycle events. Unlike message
1120
+ * events there is no conversation snapshot — the payload is channel-generic.
1121
+ * `channel` / `channelConnectionId` sit at the top level so endpoint channel
1122
+ * filters match exactly like message events.
1123
+ */
1124
+ interface WebhookChannelLifecycleData {
1125
+ /** Helpdesk channel type (e.g. `telegram`), or `null` for non-helpdesk channels. */
1126
+ channel: string | null;
1127
+ channelConnectionId: string | null;
1128
+ /** Connector channel type (e.g. `telegram_inbox`). */
1129
+ channel_type: string;
1130
+ /** Adapter-defined stable connection ref (matches `consumed_connection_ref` on the connect link). */
1131
+ connection_ref: string;
1132
+ label: string | null;
1133
+ masked_identity: string | null;
1134
+ }
1135
+ /** A channel account was connected to the workspace (e.g. via a partner connect link). */
1136
+ interface ChannelConnectedEvent extends WebhookEventBase {
1137
+ type: "helpdesk.channel_connected";
1138
+ data: WebhookChannelLifecycleData;
1139
+ }
1140
+ /** Why a channel account was disconnected. */
1141
+ type ChannelDisconnectReason = "api_disconnect" | "user_revoked" | "member_disconnect";
1142
+ /** A previously connected channel account was removed from the workspace. */
1143
+ interface ChannelDisconnectedEvent extends WebhookEventBase {
1144
+ type: "helpdesk.channel_disconnected";
1145
+ data: WebhookChannelLifecycleData & {
1146
+ /** Why the account went away. */
1147
+ reason?: ChannelDisconnectReason;
1148
+ };
1149
+ }
972
1150
  /** A `test.ping` delivery queued via `medal.webhooks.test(id)`. Carries sample data. */
973
1151
  interface TestPingEvent extends WebhookEventBase {
974
1152
  type: "test.ping";
@@ -989,7 +1167,7 @@ interface TestPingEvent extends WebhookEventBase {
989
1167
  * }
990
1168
  * ```
991
1169
  */
992
- type WebhookEvent = ConversationCreatedEvent | ConversationAssignedEvent | ConversationStatusChangedEvent | MessageReceivedEvent | MessageSentEvent | MessageDeliveryUpdatedEvent | TestPingEvent;
1170
+ type WebhookEvent = ConversationCreatedEvent | ConversationAssignedEvent | ConversationStatusChangedEvent | MessageReceivedEvent | MessageSentEvent | MessageDeliveryUpdatedEvent | ChannelConnectedEvent | ChannelDisconnectedEvent | TestPingEvent;
993
1171
  /** Machine-readable reason a webhook verification failed. */
994
1172
  type WebhookVerificationErrorCode = "malformed_header" | "timestamp_out_of_tolerance" | "invalid_signature" | "invalid_payload";
995
1173
  /** Thrown by {@link verifyWebhookSignature} when a delivery cannot be authenticated. */
@@ -1097,6 +1275,7 @@ interface MedalOptions {
1097
1275
  * ```
1098
1276
  */
1099
1277
  declare class Medal {
1278
+ readonly channels: Channels;
1100
1279
  readonly emails: Emails;
1101
1280
  readonly contacts: Contacts;
1102
1281
  readonly deals: Deals;
@@ -1111,4 +1290,4 @@ declare class Medal {
1111
1290
  /** Convenience factory — equivalent to `new Medal(apiKey, options)`. */
1112
1291
  declare function createMedalClient(apiKey: string, options?: MedalOptions): Medal;
1113
1292
 
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 };
1293
+ export { type Activity, type AddNoteInput, type ApiResponse, BaseClient, type BatchSendInput, type BatchSendResult, type BatchSendSummary, type Channel, type ChannelConnectedEvent, type ChannelConnection, type ChannelConnectionDisconnectResult, type ChannelConnectionState, type ChannelDisconnectReason, type ChannelDisconnectedEvent, Channels, type ConnectLink, type ConnectLinkCreateResult, type ConnectLinkRevokeResult, type ConnectLinkStatus, 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 CreateConnectLinkInput, 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 ListConnectLinksOptions, 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 WebhookChannelLifecycleData, 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 };
package/dist/src/index.js CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  BaseClient: () => BaseClient,
24
+ Channels: () => Channels,
24
25
  Contacts: () => Contacts,
25
26
  DEFAULT_WEBHOOK_TOLERANCE_MS: () => DEFAULT_WEBHOOK_TOLERANCE_MS,
26
27
  Deals: () => Deals,
@@ -164,6 +165,65 @@ var BaseClient = class {
164
165
  }
165
166
  };
166
167
 
168
+ // src/resources/channels.ts
169
+ var ChannelConnectLinks = class {
170
+ constructor(client) {
171
+ this.client = client;
172
+ }
173
+ client;
174
+ /**
175
+ * Mint a single-use hosted connect link. Returns HTTP 201.
176
+ *
177
+ * **The response's `data.url` contains the one-time link token EXACTLY
178
+ * ONCE.** Send it to the person who should connect their account — an
179
+ * idempotent replay (same `Idempotency-Key`) returns the link WITHOUT
180
+ * `url`, so store it immediately (or revoke and mint a new link if lost).
181
+ *
182
+ * Requires the `channel.connect.manage` scope; OAuth callers additionally
183
+ * need the workspace `admin` role.
184
+ */
185
+ async create(input, options) {
186
+ return this.client.post("/api/v1/channels/connect-links", input, options);
187
+ }
188
+ /** List the workspace's connect links (tokens are never returned). */
189
+ async list(options) {
190
+ const params = {};
191
+ if (options?.channel_type) params.channel_type = options.channel_type;
192
+ if (options?.status) params.status = options.status;
193
+ return this.client.get("/api/v1/channels/connect-links", params);
194
+ }
195
+ /** Revoke a pending connect link so it can no longer be consumed. */
196
+ async revoke(id, options) {
197
+ return this.client.delete(`/api/v1/channels/connect-links/${encodeURIComponent(id)}`, options);
198
+ }
199
+ };
200
+ var ChannelConnections = class {
201
+ constructor(client) {
202
+ this.client = client;
203
+ }
204
+ client;
205
+ /** List the workspace's channel connections (generic, channel-agnostic shape). */
206
+ async list() {
207
+ return this.client.get("/api/v1/channels/connections");
208
+ }
209
+ /**
210
+ * Disconnect a connected channel account (best-effort platform logout, then
211
+ * local revoke). Emits a `helpdesk.channel_disconnected` webhook event with
212
+ * `reason: "api_disconnect"` if the account was previously connected.
213
+ */
214
+ async disconnect(id, options) {
215
+ return this.client.delete(`/api/v1/channels/connections/${encodeURIComponent(id)}`, options);
216
+ }
217
+ };
218
+ var Channels = class {
219
+ connectLinks;
220
+ connections;
221
+ constructor(client) {
222
+ this.connectLinks = new ChannelConnectLinks(client);
223
+ this.connections = new ChannelConnections(client);
224
+ }
225
+ };
226
+
167
227
  // src/resources/contacts.ts
168
228
  var Contacts = class {
169
229
  constructor(client) {
@@ -272,7 +332,10 @@ var Emails = class {
272
332
  }
273
333
  client;
274
334
  templates;
275
- /** Send a transactional email using a template. Returns a queued job ID (HTTP 202). */
335
+ /**
336
+ * Send a transactional email using a template (HTTP 202). The returned `id`
337
+ * is an email send id — poll `emails.get(id)` with it to track delivery.
338
+ */
276
339
  async send(input) {
277
340
  return this.client.post("/api/v1/emails", input);
278
341
  }
@@ -280,7 +343,10 @@ var Emails = class {
280
343
  async get(id) {
281
344
  return this.client.get(`/api/v1/emails/${encodeURIComponent(id)}`);
282
345
  }
283
- /** Send the same template to multiple recipients (max 100). Returns HTTP 202. */
346
+ /**
347
+ * Send the same template to multiple recipients (max 100, HTTP 202). Each
348
+ * queued recipient gets its own send id in `results` for `emails.get(id)`.
349
+ */
284
350
  async batch(input) {
285
351
  return this.client.post("/api/v1/emails/batch", input);
286
352
  }
@@ -566,6 +632,7 @@ async function verifyWebhookSignature(input) {
566
632
 
567
633
  // src/index.ts
568
634
  var Medal = class {
635
+ channels;
569
636
  emails;
570
637
  contacts;
571
638
  deals;
@@ -587,6 +654,7 @@ var Medal = class {
587
654
  timeout: options?.timeout ?? 3e4,
588
655
  userAgent: "medalsocial-sdk/1.0.0 (+https://github.com/Medal-Social/MedalSocial)"
589
656
  });
657
+ this.channels = new Channels(client);
590
658
  this.emails = new Emails(client);
591
659
  this.contacts = new Contacts(client);
592
660
  this.deals = new Deals(client);
@@ -604,6 +672,7 @@ var src_default = Medal;
604
672
  // Annotate the CommonJS export names for ESM import in node:
605
673
  0 && (module.exports = {
606
674
  BaseClient,
675
+ Channels,
607
676
  Contacts,
608
677
  DEFAULT_WEBHOOK_TOLERANCE_MS,
609
678
  Deals,