@medalsocial/sdk 1.3.0 → 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.
- package/dist/openapi/medal-social.openapi.json +114 -40
- package/dist/pilot/index.d.mts +64 -324
- package/dist/pilot/index.d.ts +64 -324
- package/dist/pilot/index.js +5 -5
- package/dist/pilot/index.js.map +1 -1
- package/dist/pilot/index.mjs +5 -5
- package/dist/pilot/index.mjs.map +1 -1
- package/dist/src/index.d.mts +31 -3
- package/dist/src/index.d.ts +31 -3
- package/dist/src/index.js +8 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.mjs +8 -2
- package/dist/src/index.mjs.map +1 -1
- package/dist/src/openapi.generated.d.mts +22 -1
- package/dist/src/openapi.generated.d.ts +22 -1
- package/dist/src/openapi.generated.js.map +1 -1
- package/openapi/medal-social.openapi.yaml +38 -3
- package/package.json +14 -14
package/dist/src/index.d.mts
CHANGED
|
@@ -308,10 +308,21 @@ interface SendEmailInput {
|
|
|
308
308
|
fallback_locale?: string;
|
|
309
309
|
variables?: Record<string, string>;
|
|
310
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;
|
|
311
317
|
}
|
|
312
318
|
/** Result returned after queuing a transactional email send (HTTP 202). */
|
|
313
319
|
interface EmailSendResult {
|
|
314
|
-
id
|
|
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;
|
|
315
326
|
status: string;
|
|
316
327
|
}
|
|
317
328
|
/** Full record for a sent email, including delivery timestamps. */
|
|
@@ -341,12 +352,23 @@ interface BatchSendInput {
|
|
|
341
352
|
variables?: Record<string, string>;
|
|
342
353
|
}[];
|
|
343
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
|
+
}
|
|
344
364
|
/** Summary returned after queuing a batch email send. */
|
|
345
365
|
interface BatchSendSummary {
|
|
346
366
|
batch_id: string;
|
|
347
367
|
total: number;
|
|
348
368
|
queued: number;
|
|
349
369
|
failed: number;
|
|
370
|
+
/** Per-recipient outcome, in request order. */
|
|
371
|
+
results: BatchSendRecipientResult[];
|
|
350
372
|
}
|
|
351
373
|
/** @deprecated Use `BatchSendSummary` for `emails.batch()` responses. */
|
|
352
374
|
type BatchSendResult = BatchSendSummary;
|
|
@@ -407,11 +429,17 @@ declare class Emails {
|
|
|
407
429
|
private client;
|
|
408
430
|
readonly templates: EmailTemplates;
|
|
409
431
|
constructor(client: BaseClient);
|
|
410
|
-
/**
|
|
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
|
+
*/
|
|
411
436
|
send(input: SendEmailInput): Promise<ApiResponse<EmailSendResult>>;
|
|
412
437
|
/** Get the delivery status of a sent email. */
|
|
413
438
|
get(id: string): Promise<ApiResponse<EmailSend>>;
|
|
414
|
-
/**
|
|
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
|
+
*/
|
|
415
443
|
batch(input: BatchSendInput): Promise<ApiResponse<BatchSendSummary>>;
|
|
416
444
|
}
|
|
417
445
|
|
package/dist/src/index.d.ts
CHANGED
|
@@ -308,10 +308,21 @@ interface SendEmailInput {
|
|
|
308
308
|
fallback_locale?: string;
|
|
309
309
|
variables?: Record<string, string>;
|
|
310
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;
|
|
311
317
|
}
|
|
312
318
|
/** Result returned after queuing a transactional email send (HTTP 202). */
|
|
313
319
|
interface EmailSendResult {
|
|
314
|
-
id
|
|
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;
|
|
315
326
|
status: string;
|
|
316
327
|
}
|
|
317
328
|
/** Full record for a sent email, including delivery timestamps. */
|
|
@@ -341,12 +352,23 @@ interface BatchSendInput {
|
|
|
341
352
|
variables?: Record<string, string>;
|
|
342
353
|
}[];
|
|
343
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
|
+
}
|
|
344
364
|
/** Summary returned after queuing a batch email send. */
|
|
345
365
|
interface BatchSendSummary {
|
|
346
366
|
batch_id: string;
|
|
347
367
|
total: number;
|
|
348
368
|
queued: number;
|
|
349
369
|
failed: number;
|
|
370
|
+
/** Per-recipient outcome, in request order. */
|
|
371
|
+
results: BatchSendRecipientResult[];
|
|
350
372
|
}
|
|
351
373
|
/** @deprecated Use `BatchSendSummary` for `emails.batch()` responses. */
|
|
352
374
|
type BatchSendResult = BatchSendSummary;
|
|
@@ -407,11 +429,17 @@ declare class Emails {
|
|
|
407
429
|
private client;
|
|
408
430
|
readonly templates: EmailTemplates;
|
|
409
431
|
constructor(client: BaseClient);
|
|
410
|
-
/**
|
|
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
|
+
*/
|
|
411
436
|
send(input: SendEmailInput): Promise<ApiResponse<EmailSendResult>>;
|
|
412
437
|
/** Get the delivery status of a sent email. */
|
|
413
438
|
get(id: string): Promise<ApiResponse<EmailSend>>;
|
|
414
|
-
/**
|
|
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
|
+
*/
|
|
415
443
|
batch(input: BatchSendInput): Promise<ApiResponse<BatchSendSummary>>;
|
|
416
444
|
}
|
|
417
445
|
|
package/dist/src/index.js
CHANGED
|
@@ -272,7 +272,10 @@ var Emails = class {
|
|
|
272
272
|
}
|
|
273
273
|
client;
|
|
274
274
|
templates;
|
|
275
|
-
/**
|
|
275
|
+
/**
|
|
276
|
+
* Send a transactional email using a template (HTTP 202). The returned `id`
|
|
277
|
+
* is an email send id — poll `emails.get(id)` with it to track delivery.
|
|
278
|
+
*/
|
|
276
279
|
async send(input) {
|
|
277
280
|
return this.client.post("/api/v1/emails", input);
|
|
278
281
|
}
|
|
@@ -280,7 +283,10 @@ var Emails = class {
|
|
|
280
283
|
async get(id) {
|
|
281
284
|
return this.client.get(`/api/v1/emails/${encodeURIComponent(id)}`);
|
|
282
285
|
}
|
|
283
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* Send the same template to multiple recipients (max 100, HTTP 202). Each
|
|
288
|
+
* queued recipient gets its own send id in `results` for `emails.get(id)`.
|
|
289
|
+
*/
|
|
284
290
|
async batch(input) {
|
|
285
291
|
return this.client.post("/api/v1/emails/batch", input);
|
|
286
292
|
}
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/types/common.ts","../../src/client.ts","../../src/resources/contacts.ts","../../src/resources/deals.ts","../../src/resources/emails.ts","../../src/resources/gdpr.ts","../../src/resources/helpdesk.ts","../../src/resources/posts.ts","../../src/resources/webhooks.ts","../../src/resources/workspaces.ts","../../src/webhook-events.ts"],"sourcesContent":["/**\n * Official TypeScript SDK for the Medal Social API.\n *\n * Provides typed access to posts, emails, contacts, deals, GDPR compliance,\n * helpdesk conversations, webhooks, and workspace management. Works in\n * Node.js, Deno, Bun, Cloudflare Workers, and modern browsers.\n *\n * @example\n * ```ts\n * import { Medal } from \"@medalsocial/sdk\";\n *\n * const medal = new Medal(\"medal_xxx\");\n * const { data: post } = await medal.posts.create({\n * content: \"Hello world!\",\n * channel_ids: [\"ch_1\"],\n * });\n * ```\n *\n * @module\n */\nimport { BaseClient } from \"./client\";\nimport { Contacts } from \"./resources/contacts\";\nimport { Deals } from \"./resources/deals\";\nimport { Emails } from \"./resources/emails\";\nimport { Gdpr } from \"./resources/gdpr\";\nimport { Helpdesk } from \"./resources/helpdesk\";\nimport { Posts } from \"./resources/posts\";\nimport { Webhooks } from \"./resources/webhooks\";\nimport { Workspaces } from \"./resources/workspaces\";\n\n/** Options for configuring the {@link Medal} client. */\nexport interface MedalOptions {\n /** Override the base URL (defaults to https://io.medalsocial.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 30000). */\n timeout?: number;\n /**\n * Workspace ID — required for OAuth access tokens, ignored for API keys.\n * API keys are scoped to a single workspace, so the workspace is inferred.\n * OAuth tokens can access multiple workspaces, so you must specify which one.\n */\n workspaceId?: string;\n}\n\n/**\n * Medal Social SDK client.\n *\n * Supports both API key and OAuth access token authentication:\n *\n * @example API Key (recommended for server-side)\n * ```ts\n * import { Medal } from '@medalsocial/sdk';\n *\n * // API keys start with medal_ and are scoped to one workspace\n * const medal = new Medal('medal_xxx');\n * ```\n *\n * @example OAuth Access Token\n * ```ts\n * // OAuth tokens require a workspaceId\n * const medal = new Medal('oauth_access_token', {\n * workspaceId: 'workspace_id_here',\n * });\n * ```\n *\n * @example Full usage\n * ```ts\n * const medal = new Medal('medal_xxx');\n *\n * // Posts — create, schedule, publish\n * const { data: post } = await medal.posts.create({\n * content: 'Hello world!',\n * channel_ids: ['ch_1'],\n * });\n * await medal.posts.schedule(post.id, { scheduled_at: '2026-03-15T10:00:00Z' });\n *\n * // Emails — send transactional emails\n * await medal.emails.send({\n * template_slug: 'welcome',\n * to: 'user@example.com',\n * variables: { name: 'John' },\n * });\n *\n * // Contacts, Deals, GDPR, Workspaces\n * const contacts = await medal.contacts.list({ status: 'lead' });\n * const { data: deal } = await medal.deals.create({ title: 'Acme', value: 50000 });\n * await medal.gdpr.recordConsent({ email: 'u@x.com', consent_type: 'marketing_email', granted: true });\n * const { data: workspaces } = await medal.workspaces.list();\n * ```\n */\nexport class Medal {\n readonly emails: Emails;\n readonly contacts: Contacts;\n readonly deals: Deals;\n readonly gdpr: Gdpr;\n readonly helpdesk: Helpdesk;\n readonly posts: Posts;\n readonly webhooks: Webhooks;\n readonly workspaces: Workspaces;\n\n constructor(token: string, options?: MedalOptions) {\n if (!token) {\n throw new Error(\n \"Authentication token is required. Pass your medal_xxx API key or OAuth access token as the first argument.\",\n );\n }\n\n const client = new BaseClient({\n baseUrl: (options?.baseUrl ?? \"https://io.medalsocial.com\").replace(/\\/$/, \"\"),\n token,\n workspaceId: options?.workspaceId,\n timeout: options?.timeout ?? 30000,\n userAgent: \"medalsocial-sdk/1.0.0 (+https://github.com/Medal-Social/MedalSocial)\",\n });\n\n this.emails = new Emails(client);\n this.contacts = new Contacts(client);\n this.deals = new Deals(client);\n this.gdpr = new Gdpr(client);\n this.helpdesk = new Helpdesk(client);\n this.posts = new Posts(client);\n this.webhooks = new Webhooks(client);\n this.workspaces = new Workspaces(client);\n }\n}\n\n// Re-export all types\nexport { MedalApiError } from \"./types/common\";\nexport type { ApiResponse, PaginatedResponse, PaginationOptions } from \"./types/common\";\nexport type {\n SendEmailInput,\n EmailSendResult,\n EmailSend,\n BatchSendInput,\n BatchSendSummary,\n BatchSendResult,\n EmailTemplate,\n EmailTemplateDetail,\n GetTemplateOptions,\n} from \"./types/emails\";\nexport type {\n Contact,\n ContactCreateResult,\n ContactUpdateResult,\n ContactRemoveResult,\n ContactNoteResult,\n ContactStatus,\n EmailStatus,\n CreateContactInput,\n UpdateContactInput,\n ListContactsOptions,\n ImportContactInput,\n ImportContactsResult,\n Activity,\n AddNoteInput,\n} from \"./types/contacts\";\nexport type {\n Deal,\n DealCreateResult,\n DealUpdateResult,\n DealRemoveResult,\n DealStatus,\n CreateDealInput,\n UpdateDealInput,\n ListDealsOptions,\n} from \"./types/deals\";\nexport type {\n GdprExport,\n ConsentType,\n RecordConsentInput,\n ConsentRecord,\n ConsentResult,\n ContactConsents,\n CookieConsentInput,\n CookieCategoryConsent,\n} from \"./types/gdpr\";\nexport type {\n Post,\n PostType,\n PostVariant,\n PostDetail,\n Channel,\n CreatePostInput,\n UpdatePostInput,\n SchedulePostInput,\n ListPostsOptions,\n ScheduleResult,\n PublishResult,\n} from \"./types/posts\";\nexport type { Workspace } from \"./types/workspaces\";\nexport type {\n Conversation,\n ConversationMessage,\n ConversationStatus,\n ConversationUpdateResult,\n CreateReplyInput,\n HelpdeskMessageType,\n ListConversationsOptions,\n MessageAuthorType,\n ReplyCreateResult,\n UpdateConversationInput,\n} from \"./types/helpdesk\";\nexport type {\n CreateWebhookInput,\n ListDeliveriesOptions,\n UpdateWebhookInput,\n WebhookDeleteResult,\n WebhookDelivery,\n WebhookEndpoint,\n WebhookTestResult,\n} from \"./types/webhooks\";\n\n// Webhook event verification + typed events\nexport {\n DEFAULT_WEBHOOK_TOLERANCE_MS,\n verifyWebhookSignature,\n WebhookVerificationError,\n} from \"./webhook-events\";\nexport type {\n ConversationAssignedEvent,\n ConversationCreatedEvent,\n ConversationStatusChangedEvent,\n MessageDeliveryUpdatedEvent,\n MessageReceivedEvent,\n MessageSentEvent,\n TestPingEvent,\n VerifyWebhookSignatureInput,\n WebhookConversationSnapshot,\n WebhookEvent,\n WebhookMessageSnapshot,\n WebhookVerificationErrorCode,\n} from \"./webhook-events\";\nexport type {\n paths as OpenApiPaths,\n components as OpenApiComponents,\n operations as OpenApiOperations,\n} from \"./openapi.generated\";\n\n// Resource class re-exports (for advanced usage)\nexport { Emails } from \"./resources/emails\";\nexport { Contacts } from \"./resources/contacts\";\nexport { Deals } from \"./resources/deals\";\nexport { Gdpr } from \"./resources/gdpr\";\nexport { Helpdesk } from \"./resources/helpdesk\";\nexport { Posts } from \"./resources/posts\";\nexport { Webhooks } from \"./resources/webhooks\";\nexport { Workspaces } from \"./resources/workspaces\";\nexport { BaseClient } from \"./client\";\nexport type { RequestOptions } from \"./client\";\n\n/** Convenience factory — equivalent to `new Medal(apiKey, options)`. */\nexport function createMedalClient(apiKey: string, options?: MedalOptions): Medal {\n return new Medal(apiKey, options);\n}\n\nexport default Medal;\n","/** Successful API response wrapper */\nexport interface ApiResponse<T> {\n data: T;\n}\n\n/** Paginated API response */\nexport interface PaginatedResponse<T> {\n data: T[];\n pagination: {\n has_more: boolean;\n next_cursor: string | null;\n };\n}\n\n/** API error thrown by the client */\nexport class MedalApiError extends Error {\n readonly status: number;\n readonly code: string;\n readonly details?: unknown;\n\n constructor(status: number, code: string, message: string, details?: unknown) {\n super(message);\n this.name = \"MedalApiError\";\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n\n/** Pagination options for list endpoints */\nexport interface PaginationOptions {\n limit?: number;\n cursor?: string;\n}\n","import { MedalApiError } from \"./types/common\";\n\n/** Configuration for the low-level HTTP client. */\nexport interface ClientConfig {\n baseUrl: string;\n token: string;\n workspaceId?: string;\n timeout: number;\n userAgent: string;\n}\n\n/** Per-request options for write operations. */\nexport interface RequestOptions {\n /**\n * Idempotency key sent as the `Idempotency-Key` header. Retries with the\n * same key return the original result instead of repeating the operation.\n * Required by some endpoints for capability-scoped tokens (e.g. helpdesk\n * replies, webhook creation).\n */\n idempotencyKey?: string;\n /**\n * Capability confirmation token sent as the `X-Capability-Confirmation`\n * header. Required alongside `idempotencyKey` when a token granted a\n * capability-style scope directly (e.g. `helpdesk.webhook.manage`) executes\n * a confirmable write route. Obtain one from\n * `POST /api/v1/capability-confirmations`. API keys with legacy scopes do\n * not need it.\n */\n capabilityConfirmation?: string;\n}\n\n/**\n * Low-level HTTP client used by all resource classes.\n * Handles authentication, retries, timeout, and error parsing.\n */\nexport class BaseClient {\n /** Resolved client configuration. */\n readonly config: ClientConfig;\n\n constructor(config: ClientConfig) {\n this.config = config;\n }\n\n /** Execute an authenticated GET request and return the parsed JSON body. */\n async get<T>(path: string, params?: Record<string, string | undefined>): Promise<T> {\n const url = this.buildUrl(path, params);\n return this.request<T>(url, { method: \"GET\" });\n }\n\n /** Execute an authenticated POST request with a JSON body. */\n async post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>(this.buildUrl(path), {\n method: \"POST\",\n headers: this.writeHeaders(options),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n }\n\n /** Execute an authenticated PATCH request with a JSON body. */\n async patch<T>(path: string, body: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>(this.buildUrl(path), {\n method: \"PATCH\",\n headers: this.writeHeaders(options),\n body: JSON.stringify(body),\n });\n }\n\n /** Execute an authenticated DELETE request. */\n async delete<T>(path: string, options?: RequestOptions): Promise<T> {\n return this.request<T>(this.buildUrl(path), {\n method: \"DELETE\",\n headers: this.writeHeaders(options),\n });\n }\n\n private writeHeaders(options?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n if (options?.idempotencyKey) {\n headers[\"idempotency-key\"] = options.idempotencyKey;\n }\n if (options?.capabilityConfirmation) {\n headers[\"x-capability-confirmation\"] = options.capabilityConfirmation;\n }\n return headers;\n }\n\n private buildUrl(path: string, params?: Record<string, string | undefined>): string {\n const url = new URL(`${this.config.baseUrl}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, value);\n }\n }\n }\n return url.toString();\n }\n\n private async request<T>(url: string, init: RequestInit): Promise<T> {\n const maxAttempts = 3;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const headers = new Headers(init.headers);\n headers.set(\"authorization\", `Bearer ${this.config.token}`);\n if (this.config.workspaceId) {\n headers.set(\"x-workspace-id\", this.config.workspaceId);\n }\n try {\n headers.set(\"user-agent\", this.config.userAgent);\n } catch {\n // Browsers disallow setting user-agent\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeout);\n\n let res: Response;\n try {\n res = await fetch(url, { ...init, headers, signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n\n // Retry on 429 / 5xx (but not on the final attempt)\n if (\n (res.status === 429 || (res.status >= 500 && res.status <= 599)) &&\n attempt < maxAttempts\n ) {\n const retryAfter = res.headers.get(\"retry-after\");\n let delayMs = 0;\n if (retryAfter) {\n const seconds = Number(retryAfter);\n delayMs = Number.isFinite(seconds) ? seconds * 1000 : 0;\n }\n if (delayMs <= 0) {\n delayMs = 250 * attempt;\n }\n await new Promise((r) => setTimeout(r, delayMs));\n continue;\n }\n\n // Parse response\n const text = await res.text();\n let parsed: unknown;\n try {\n parsed = text ? JSON.parse(text) : undefined;\n } catch {\n parsed = text;\n }\n\n if (!res.ok) {\n const body = parsed as\n | { error?: { code?: string; message?: string; details?: unknown } }\n | undefined;\n throw new MedalApiError(\n res.status,\n body?.error?.code ?? \"UNKNOWN_ERROR\",\n body?.error?.message ?? `HTTP ${res.status}: ${res.statusText}`,\n body?.error?.details,\n );\n }\n\n return parsed as T;\n }\n\n /* v8 ignore next -- unreachable: loop always returns or throws */\n throw new Error(\"Request failed after retries\");\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse, PaginatedResponse, PaginationOptions } from \"../types/common\";\nimport type {\n Activity,\n AddNoteInput,\n Contact,\n ContactCreateResult,\n ContactNoteResult,\n ContactRemoveResult,\n ContactUpdateResult,\n CreateContactInput,\n ImportContactInput,\n ImportContactsResult,\n ListContactsOptions,\n UpdateContactInput,\n} from \"../types/contacts\";\n\n/** Manage contacts in the workspace CRM. */\nexport class Contacts {\n constructor(private client: BaseClient) {}\n\n /** List contacts with cursor-based pagination and optional filters. */\n async list(options?: ListContactsOptions): Promise<PaginatedResponse<Contact>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.email_status) params.email_status = options.email_status;\n if (options?.label_ids) params.label_ids = options.label_ids.join(\",\");\n if (options?.search) params.search = options.search;\n return this.client.get(\"/api/v1/contacts\", params);\n }\n\n /** Create a new contact. Email must be unique in the workspace. */\n async create(input: CreateContactInput): Promise<ApiResponse<ContactCreateResult>> {\n return this.client.post(\"/api/v1/contacts\", input);\n }\n\n /** Get a contact by ID. */\n async get(id: string): Promise<ApiResponse<Contact>> {\n return this.client.get(`/api/v1/contacts/${encodeURIComponent(id)}`);\n }\n\n /** Update one or more fields on a contact. */\n async update(id: string, input: UpdateContactInput): Promise<ApiResponse<ContactUpdateResult>> {\n return this.client.patch(`/api/v1/contacts/${encodeURIComponent(id)}`, input);\n }\n\n /** Permanently delete a contact. */\n async remove(id: string): Promise<ApiResponse<ContactRemoveResult>> {\n return this.client.delete(`/api/v1/contacts/${encodeURIComponent(id)}`);\n }\n\n /** Get the activity timeline for a contact. */\n async activities(id: string, options?: PaginationOptions): Promise<PaginatedResponse<Activity>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n return this.client.get(`/api/v1/contacts/${encodeURIComponent(id)}/activities`, params);\n }\n\n /** Add a note to a contact's timeline. */\n async addNote(id: string, input: AddNoteInput): Promise<ApiResponse<ContactNoteResult>> {\n return this.client.post(`/api/v1/contacts/${encodeURIComponent(id)}/notes`, input);\n }\n\n /** Bulk import contacts (max 500). Duplicates are skipped. */\n async import(contacts: ImportContactInput[]): Promise<ApiResponse<ImportContactsResult>> {\n return this.client.post(\"/api/v1/contacts/import\", { contacts });\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse, PaginatedResponse } from \"../types/common\";\nimport type {\n CreateDealInput,\n Deal,\n DealCreateResult,\n DealRemoveResult,\n DealUpdateResult,\n ListDealsOptions,\n UpdateDealInput,\n} from \"../types/deals\";\n\n/** Manage sponsorship deals in the workspace. */\nexport class Deals {\n constructor(private client: BaseClient) {}\n\n /** List deals with cursor-based pagination and optional filters. */\n async list(options?: ListDealsOptions): Promise<PaginatedResponse<Deal>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.search) params.search = options.search;\n return this.client.get(\"/api/v1/deals\", params);\n }\n\n /** Create a new deal. */\n async create(input: CreateDealInput): Promise<ApiResponse<DealCreateResult>> {\n return this.client.post(\"/api/v1/deals\", input);\n }\n\n /** Get a deal by ID. */\n async get(id: string): Promise<ApiResponse<Deal>> {\n return this.client.get(`/api/v1/deals/${encodeURIComponent(id)}`);\n }\n\n /** Update one or more fields on a deal. Set contact_id to null to unlink. */\n async update(id: string, input: UpdateDealInput): Promise<ApiResponse<DealUpdateResult>> {\n return this.client.patch(`/api/v1/deals/${encodeURIComponent(id)}`, input);\n }\n\n /** Permanently delete a deal. */\n async remove(id: string): Promise<ApiResponse<DealRemoveResult>> {\n return this.client.delete(`/api/v1/deals/${encodeURIComponent(id)}`);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type {\n BatchSendInput,\n BatchSendSummary,\n EmailSend,\n EmailSendResult,\n EmailTemplate,\n EmailTemplateDetail,\n GetTemplateOptions,\n SendEmailInput,\n} from \"../types/emails\";\n\n/** Manage email templates stored in the workspace. */\nclass EmailTemplates {\n constructor(private client: BaseClient) {}\n\n /** List all active email templates in the workspace. */\n async list(): Promise<ApiResponse<EmailTemplate[]>> {\n return this.client.get(\"/api/v1/emails/templates\");\n }\n\n /** Get a specific email template by slug, optionally with locale resolution. */\n async get(slug: string, options?: GetTemplateOptions): Promise<ApiResponse<EmailTemplateDetail>> {\n const params: Record<string, string | undefined> = {};\n if (options?.locale) params.locale = options.locale;\n if (options?.fallback_locale) params.fallback_locale = options.fallback_locale;\n return this.client.get(`/api/v1/emails/templates/${encodeURIComponent(slug)}`, params);\n }\n}\n\n/** Send transactional emails and manage templates. */\nexport class Emails {\n readonly templates: EmailTemplates;\n\n constructor(private client: BaseClient) {\n this.templates = new EmailTemplates(client);\n }\n\n /** Send a transactional email using a template. Returns a queued job ID (HTTP 202). */\n async send(input: SendEmailInput): Promise<ApiResponse<EmailSendResult>> {\n return this.client.post(\"/api/v1/emails\", input);\n }\n\n /** Get the delivery status of a sent email. */\n async get(id: string): Promise<ApiResponse<EmailSend>> {\n return this.client.get(`/api/v1/emails/${encodeURIComponent(id)}`);\n }\n\n /** Send the same template to multiple recipients (max 100). Returns HTTP 202. */\n async batch(input: BatchSendInput): Promise<ApiResponse<BatchSendSummary>> {\n return this.client.post(\"/api/v1/emails/batch\", input);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type {\n ConsentRecord,\n ConsentResult,\n CookieConsentInput,\n GdprExport,\n RecordConsentInput,\n} from \"../types/gdpr\";\n\n/** Manage GDPR compliance — data exports, consent records, and cookie consent. */\nexport class Gdpr {\n constructor(private client: BaseClient) {}\n\n /** Request a workspace data export. Runs asynchronously. */\n async requestExport(): Promise<ApiResponse<{ request_id: string; status: string }>> {\n return this.client.post(\"/api/v1/gdpr/export\");\n }\n\n /** List all workspace export requests. */\n async listExports(): Promise<ApiResponse<GdprExport[]>> {\n return this.client.get(\"/api/v1/gdpr/exports\");\n }\n\n /** Get the status of a specific export. */\n async getExport(id: string): Promise<ApiResponse<GdprExport>> {\n return this.client.get(`/api/v1/gdpr/exports/${encodeURIComponent(id)}`);\n }\n\n /** Record a GDPR consent decision for a contact by email. */\n async recordConsent(input: RecordConsentInput): Promise<ApiResponse<ConsentResult>> {\n return this.client.post(\"/api/v1/gdpr/consent\", input);\n }\n\n /** Get all consent records for a contact by email. */\n async getConsent(email: string): Promise<ApiResponse<ConsentRecord[]>> {\n return this.client.get(`/api/v1/gdpr/consent/${encodeURIComponent(email)}`);\n }\n\n /** Record cookie consent from an external site (legacy endpoint). */\n async cookieConsent(input: CookieConsentInput): Promise<{ success: boolean; logId?: string }> {\n return this.client.post(\"/api/cookie-consent\", input);\n }\n}\n","import type { BaseClient, RequestOptions } from \"../client\";\nimport type { ApiResponse, PaginatedResponse, PaginationOptions } from \"../types/common\";\nimport type {\n Conversation,\n ConversationMessage,\n ConversationUpdateResult,\n CreateReplyInput,\n ListConversationsOptions,\n ReplyCreateResult,\n UpdateConversationInput,\n} from \"../types/helpdesk\";\n\n/** Browse and manage helpdesk conversations. */\nclass HelpdeskConversations {\n constructor(private client: BaseClient) {}\n\n /** List/search conversations with cursor-based pagination and optional filters. */\n async list(options?: ListConversationsOptions): Promise<PaginatedResponse<Conversation>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.assignee_user_id) params.assignee_user_id = options.assignee_user_id;\n if (options?.requester) params.requester = options.requester;\n if (options?.query) params.query = options.query;\n if (options?.channels) params.channels = options.channels.join(\",\");\n return this.client.get(\"/api/v1/helpdesk/conversations\", params);\n }\n\n /** Get a conversation by ID. */\n async get(id: string): Promise<ApiResponse<Conversation>> {\n return this.client.get(`/api/v1/helpdesk/conversations/${encodeURIComponent(id)}`);\n }\n\n /** Update a conversation's status and/or assignee (pass `assignee_user_id: null` to unassign). */\n async update(\n id: string,\n input: UpdateConversationInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ConversationUpdateResult>> {\n return this.client.patch(\n `/api/v1/helpdesk/conversations/${encodeURIComponent(id)}`,\n input,\n options,\n );\n }\n\n /** Read a conversation's messages with cursor-based pagination. */\n async messages(\n id: string,\n options?: PaginationOptions,\n ): Promise<PaginatedResponse<ConversationMessage>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n return this.client.get(\n `/api/v1/helpdesk/conversations/${encodeURIComponent(id)}/messages`,\n params,\n );\n }\n}\n\n/** Send operator replies (or internal notes) into conversations. */\nclass HelpdeskReplies {\n constructor(private client: BaseClient) {}\n\n /**\n * Send an operator reply or internal note. Returns HTTP 201.\n *\n * Pass an `idempotencyKey` so retried requests do not create duplicate\n * messages — it is REQUIRED for capability-scoped tokens.\n */\n async create(\n input: CreateReplyInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ReplyCreateResult>> {\n return this.client.post(\"/api/v1/helpdesk/replies\", input, options);\n }\n}\n\n/** Helpdesk bridge — read conversations, reply, and manage assignment/status. */\nexport class Helpdesk {\n readonly conversations: HelpdeskConversations;\n readonly replies: HelpdeskReplies;\n\n constructor(client: BaseClient) {\n this.conversations = new HelpdeskConversations(client);\n this.replies = new HelpdeskReplies(client);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse, PaginatedResponse } from \"../types/common\";\nimport type {\n Channel,\n CreatePostInput,\n ListPostsOptions,\n Post,\n PostDetail,\n PublishResult,\n SchedulePostInput,\n ScheduleResult,\n UpdatePostInput,\n} from \"../types/posts\";\n\n/** Create and publish posts across connected channels. */\nexport class Posts {\n constructor(private client: BaseClient) {}\n\n /** List posts with cursor-based pagination and optional filters. */\n async list(options?: ListPostsOptions): Promise<PaginatedResponse<Post>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.type) params.type = options.type;\n return this.client.get(\"/api/v1/posts\", params);\n }\n\n /** Create a new post with content and target channels. */\n async create(input: CreatePostInput): Promise<ApiResponse<{ id: string }>> {\n return this.client.post(\"/api/v1/posts\", input);\n }\n\n /** Get a post by ID, including its per-channel variants. */\n async get(id: string): Promise<ApiResponse<PostDetail>> {\n return this.client.get(`/api/v1/posts/${encodeURIComponent(id)}`);\n }\n\n /** Update a draft post's title or content. */\n async update(id: string, input: UpdatePostInput): Promise<ApiResponse<{ success: boolean }>> {\n return this.client.patch(`/api/v1/posts/${encodeURIComponent(id)}`, input);\n }\n\n /** Delete a post. */\n async remove(id: string): Promise<ApiResponse<{ success: boolean }>> {\n return this.client.delete(`/api/v1/posts/${encodeURIComponent(id)}`);\n }\n\n /** Schedule a post for future publication. */\n async schedule(id: string, input: SchedulePostInput): Promise<ApiResponse<ScheduleResult>> {\n return this.client.post(`/api/v1/posts/${encodeURIComponent(id)}/schedule`, input);\n }\n\n /** Publish a post immediately to all target channels. */\n async publish(id: string): Promise<ApiResponse<PublishResult>> {\n return this.client.post(`/api/v1/posts/${encodeURIComponent(id)}/publish`);\n }\n\n /** List connected publishing channels for this workspace. */\n async channels(): Promise<ApiResponse<Channel[]>> {\n return this.client.get(\"/api/v1/posts/channels\");\n }\n}\n","import type { BaseClient, RequestOptions } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type {\n CreateWebhookInput,\n ListDeliveriesOptions,\n UpdateWebhookInput,\n WebhookDeleteResult,\n WebhookDelivery,\n WebhookEndpoint,\n WebhookTestResult,\n} from \"../types/webhooks\";\n\n/** Manage webhook endpoints and inspect their deliveries. */\nexport class Webhooks {\n constructor(private client: BaseClient) {}\n\n /** List all webhook endpoints in the workspace. */\n async list(): Promise<ApiResponse<WebhookEndpoint[]>> {\n return this.client.get(\"/api/v1/webhooks\");\n }\n\n /**\n * Create a webhook endpoint. Returns HTTP 201.\n *\n * **The response's `data.secret` contains the signing secret EXACTLY ONCE.**\n * It can never be retrieved again — store it securely immediately. You need\n * it to verify the `X-Medal-Signature` header on incoming deliveries (see\n * `verifyWebhookSignature`).\n *\n * `secret` is typed optional because an idempotent replay (retrying with the\n * same `Idempotency-Key`, `X-Idempotent-Replayed: true`) returns the existing\n * endpoint WITHOUT the secret — handle that case (rotate if you lost it).\n */\n async create(\n input: CreateWebhookInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<WebhookEndpoint>> {\n return this.client.post(\"/api/v1/webhooks\", input, options);\n }\n\n /** Get a webhook endpoint by ID. */\n async get(id: string): Promise<ApiResponse<WebhookEndpoint>> {\n return this.client.get(`/api/v1/webhooks/${encodeURIComponent(id)}`);\n }\n\n /** Update a webhook endpoint (name, url, event types, filters, enabled). */\n async update(\n id: string,\n input: UpdateWebhookInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<WebhookEndpoint>> {\n return this.client.patch(`/api/v1/webhooks/${encodeURIComponent(id)}`, input, options);\n }\n\n /**\n * Permanently delete a webhook endpoint (stops all outbound deliveries).\n * Capability-scoped tokens must pass `idempotencyKey` — the API requires\n * `Idempotency-Key` + `X-Capability-Confirmation` for direct capability\n * grants on this route. API keys with legacy scopes may omit it.\n */\n async delete(id: string, options?: RequestOptions): Promise<ApiResponse<WebhookDeleteResult>> {\n return this.client.delete(`/api/v1/webhooks/${encodeURIComponent(id)}`, options);\n }\n\n /** List recent deliveries for an endpoint (most recent first). */\n async deliveries(\n id: string,\n options?: ListDeliveriesOptions,\n ): Promise<ApiResponse<WebhookDelivery[]>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n return this.client.get(`/api/v1/webhooks/${encodeURIComponent(id)}/deliveries`, params);\n }\n\n /** Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202. */\n async test(id: string): Promise<ApiResponse<WebhookTestResult>> {\n return this.client.post(`/api/v1/webhooks/${encodeURIComponent(id)}/test`);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type { Workspace } from \"../types/workspaces\";\n\n/** Access workspaces for the authenticated credential. */\nexport class Workspaces {\n constructor(private client: BaseClient) {}\n\n /** List workspaces accessible to the current API key or OAuth token. */\n async list(): Promise<ApiResponse<Workspace[]>> {\n return this.client.get(\"/api/v1/me/workspaces\");\n }\n}\n","/**\n * Webhook event types and signature verification for the Medal Social\n * outbound webhook bridge.\n *\n * Every delivery is an HTTP POST with headers:\n * - `X-Medal-Timestamp` — Unix milliseconds when the request was signed\n * - `X-Medal-Signature` — `sha256=<base64(HMAC-SHA256(\"{timestamp}.{rawBody}\", secret))>`\n * - `X-Medal-Event` — the event type\n * - `X-Medal-Delivery-Id` / `Idempotency-Key` — unique delivery ID (deduplicate on this)\n *\n * Use {@link verifyWebhookSignature} to authenticate a delivery and get the\n * parsed, typed event back. Uses Web Crypto (`crypto.subtle`) so it works in\n * Node.js 18+, Deno, Bun, Cloudflare Workers, and browsers.\n */\n\n/** Snapshot of a conversation included in every helpdesk webhook event. */\nexport interface WebhookConversationSnapshot {\n id: string;\n channel: string;\n channelConnectionId: string | null;\n status: string;\n subject: string | null;\n assigneeUserId: string | null;\n contactId: string | null;\n visitorName: string | null;\n visitorEmail: string | null;\n externalConversationId: string | null;\n channelAccountId: string | null;\n messageCount: number;\n /** Unix timestamp in milliseconds. */\n lastMessageAt: number;\n /** Unix timestamp in milliseconds. */\n createdAt: number;\n}\n\n/** Snapshot of a message included in helpdesk message events. */\nexport interface WebhookMessageSnapshot {\n id: string;\n authorType: \"visitor\" | \"operator\" | \"ai\" | \"system\";\n messageType: \"chat\" | \"email\" | \"note\";\n body: string;\n authorUserId: string | null;\n authorName: string | null;\n externalMessageId: string | null;\n deliveryStatus: string | null;\n deliveryError: string | null;\n /** Unix timestamp in milliseconds. */\n createdAt: number;\n}\n\n/** Fields present in the `data` of every helpdesk event. */\ninterface HelpdeskEventData {\n /** Channel type at the top level, for quick filtering. */\n channel: string;\n channelConnectionId: string | null;\n conversation: WebhookConversationSnapshot;\n}\n\n/** Envelope fields shared by all webhook events. */\ninterface WebhookEventBase {\n /** Unique delivery/event ID — use for deduplication. */\n id: string;\n /** Unix timestamp in milliseconds when the event was created. */\n created_at: number;\n workspace_id: string;\n}\n\n/** A new conversation was created. */\nexport interface ConversationCreatedEvent extends WebhookEventBase {\n type: \"helpdesk.conversation_created\";\n data: HelpdeskEventData;\n}\n\n/** A conversation was assigned or unassigned. */\nexport interface ConversationAssignedEvent extends WebhookEventBase {\n type: \"helpdesk.conversation_assigned\";\n data: HelpdeskEventData & {\n assigneeUserId: string | null;\n previousAssigneeUserId: string | null;\n };\n}\n\n/** A conversation's status changed (open / snoozed / closed). */\nexport interface ConversationStatusChangedEvent extends WebhookEventBase {\n type: \"helpdesk.conversation_status_changed\";\n data: HelpdeskEventData & {\n status: string;\n previousStatus: string;\n };\n}\n\n/** A message arrived from the visitor/customer. */\nexport interface MessageReceivedEvent extends WebhookEventBase {\n type: \"helpdesk.message_received\";\n data: HelpdeskEventData & { message: WebhookMessageSnapshot };\n}\n\n/** A message was sent by an operator, AI, or the system. */\nexport interface MessageSentEvent extends WebhookEventBase {\n type: \"helpdesk.message_sent\";\n data: HelpdeskEventData & { message: WebhookMessageSnapshot };\n}\n\n/** The delivery status of an outbound message changed (sent / delivered / failed …). */\nexport interface MessageDeliveryUpdatedEvent extends WebhookEventBase {\n type: \"helpdesk.message_delivery_updated\";\n data: HelpdeskEventData & { message: WebhookMessageSnapshot };\n}\n\n/** A `test.ping` delivery queued via `medal.webhooks.test(id)`. Carries sample data. */\nexport interface TestPingEvent extends WebhookEventBase {\n type: \"test.ping\";\n data: Record<string, unknown>;\n}\n\n/**\n * Discriminated union of all webhook events, keyed on `type`.\n *\n * @example\n * ```ts\n * switch (event.type) {\n * case 'helpdesk.message_received':\n * console.log(event.data.message.body);\n * break;\n * case 'helpdesk.conversation_status_changed':\n * console.log(event.data.previousStatus, '→', event.data.status);\n * break;\n * }\n * ```\n */\nexport type WebhookEvent =\n | ConversationCreatedEvent\n | ConversationAssignedEvent\n | ConversationStatusChangedEvent\n | MessageReceivedEvent\n | MessageSentEvent\n | MessageDeliveryUpdatedEvent\n | TestPingEvent;\n\n/** Machine-readable reason a webhook verification failed. */\nexport type WebhookVerificationErrorCode =\n | \"malformed_header\"\n | \"timestamp_out_of_tolerance\"\n | \"invalid_signature\"\n | \"invalid_payload\";\n\n/** Thrown by {@link verifyWebhookSignature} when a delivery cannot be authenticated. */\nexport class WebhookVerificationError extends Error {\n readonly code: WebhookVerificationErrorCode;\n\n constructor(code: WebhookVerificationErrorCode, message: string) {\n super(message);\n this.name = \"WebhookVerificationError\";\n this.code = code;\n }\n}\n\n/** Input for {@link verifyWebhookSignature}. */\nexport interface VerifyWebhookSignatureInput {\n /** The RAW request body string, exactly as received (do not re-serialize parsed JSON). */\n payload: string;\n /** Value of the `X-Medal-Timestamp` header (Unix milliseconds). */\n timestamp: string;\n /** Value of the `X-Medal-Signature` header (`sha256=<base64>`). */\n signature: string;\n /** The endpoint signing secret (`whsec_…`) returned once at creation time. */\n secret: string;\n /** Max allowed clock skew between now and the signed timestamp. Default 5 minutes. */\n toleranceMs?: number;\n}\n\n/** Default allowed clock skew for webhook verification (5 minutes). */\nexport const DEFAULT_WEBHOOK_TOLERANCE_MS = 5 * 60 * 1000;\n\nfunction base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Verify a webhook delivery's signature and timestamp, then return the parsed\n * typed event.\n *\n * Recomputes `HMAC-SHA256(\"{timestamp}.{payload}\", secret)` with Web Crypto\n * and compares it against the signature in constant time. Deliveries whose\n * timestamp deviates from the current time by more than `toleranceMs`\n * (default 5 minutes) are rejected to prevent replay attacks.\n *\n * @throws {WebhookVerificationError} if the headers are malformed, the\n * timestamp is outside the tolerance window, the signature does not match,\n * or the payload is not valid JSON.\n *\n * @example\n * ```ts\n * const event = await verifyWebhookSignature({\n * payload: rawBody,\n * timestamp: req.headers['x-medal-timestamp'],\n * signature: req.headers['x-medal-signature'],\n * secret: process.env.MEDAL_WEBHOOK_SECRET,\n * });\n * ```\n */\nexport async function verifyWebhookSignature(\n input: VerifyWebhookSignatureInput,\n): Promise<WebhookEvent> {\n const { payload, timestamp, signature, secret } = input;\n const toleranceMs = input.toleranceMs ?? DEFAULT_WEBHOOK_TOLERANCE_MS;\n\n if (typeof signature !== \"string\" || !signature.startsWith(\"sha256=\")) {\n throw new WebhookVerificationError(\n \"malformed_header\",\n \"Signature header must be in the form 'sha256=<base64>'\",\n );\n }\n\n const timestampMs = Number(timestamp);\n if (typeof timestamp !== \"string\" || timestamp === \"\" || !Number.isFinite(timestampMs)) {\n throw new WebhookVerificationError(\n \"malformed_header\",\n \"Timestamp header must be a Unix-milliseconds number string\",\n );\n }\n if (Math.abs(Date.now() - timestampMs) > toleranceMs) {\n throw new WebhookVerificationError(\n \"timestamp_out_of_tolerance\",\n `Timestamp is outside the allowed tolerance of ${toleranceMs}ms`,\n );\n }\n\n let signatureBytes: Uint8Array<ArrayBuffer>;\n try {\n signatureBytes = base64ToBytes(signature.slice(\"sha256=\".length));\n } catch {\n throw new WebhookVerificationError(\"invalid_signature\", \"Signature is not valid base64\");\n }\n\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"verify\"],\n );\n // crypto.subtle.verify performs a constant-time comparison internally.\n const valid = await crypto.subtle.verify(\n \"HMAC\",\n key,\n signatureBytes,\n encoder.encode(`${timestamp}.${payload}`),\n );\n if (!valid) {\n throw new WebhookVerificationError(\"invalid_signature\", \"Signature does not match the payload\");\n }\n\n try {\n return JSON.parse(payload) as WebhookEvent;\n } catch {\n throw new WebhookVerificationError(\"invalid_payload\", \"Payload is not valid JSON\");\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/types/common.ts","../../src/client.ts","../../src/resources/contacts.ts","../../src/resources/deals.ts","../../src/resources/emails.ts","../../src/resources/gdpr.ts","../../src/resources/helpdesk.ts","../../src/resources/posts.ts","../../src/resources/webhooks.ts","../../src/resources/workspaces.ts","../../src/webhook-events.ts"],"sourcesContent":["/**\n * Official TypeScript SDK for the Medal Social API.\n *\n * Provides typed access to posts, emails, contacts, deals, GDPR compliance,\n * helpdesk conversations, webhooks, and workspace management. Works in\n * Node.js, Deno, Bun, Cloudflare Workers, and modern browsers.\n *\n * @example\n * ```ts\n * import { Medal } from \"@medalsocial/sdk\";\n *\n * const medal = new Medal(\"medal_xxx\");\n * const { data: post } = await medal.posts.create({\n * content: \"Hello world!\",\n * channel_ids: [\"ch_1\"],\n * });\n * ```\n *\n * @module\n */\nimport { BaseClient } from \"./client\";\nimport { Contacts } from \"./resources/contacts\";\nimport { Deals } from \"./resources/deals\";\nimport { Emails } from \"./resources/emails\";\nimport { Gdpr } from \"./resources/gdpr\";\nimport { Helpdesk } from \"./resources/helpdesk\";\nimport { Posts } from \"./resources/posts\";\nimport { Webhooks } from \"./resources/webhooks\";\nimport { Workspaces } from \"./resources/workspaces\";\n\n/** Options for configuring the {@link Medal} client. */\nexport interface MedalOptions {\n /** Override the base URL (defaults to https://io.medalsocial.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 30000). */\n timeout?: number;\n /**\n * Workspace ID — required for OAuth access tokens, ignored for API keys.\n * API keys are scoped to a single workspace, so the workspace is inferred.\n * OAuth tokens can access multiple workspaces, so you must specify which one.\n */\n workspaceId?: string;\n}\n\n/**\n * Medal Social SDK client.\n *\n * Supports both API key and OAuth access token authentication:\n *\n * @example API Key (recommended for server-side)\n * ```ts\n * import { Medal } from '@medalsocial/sdk';\n *\n * // API keys start with medal_ and are scoped to one workspace\n * const medal = new Medal('medal_xxx');\n * ```\n *\n * @example OAuth Access Token\n * ```ts\n * // OAuth tokens require a workspaceId\n * const medal = new Medal('oauth_access_token', {\n * workspaceId: 'workspace_id_here',\n * });\n * ```\n *\n * @example Full usage\n * ```ts\n * const medal = new Medal('medal_xxx');\n *\n * // Posts — create, schedule, publish\n * const { data: post } = await medal.posts.create({\n * content: 'Hello world!',\n * channel_ids: ['ch_1'],\n * });\n * await medal.posts.schedule(post.id, { scheduled_at: '2026-03-15T10:00:00Z' });\n *\n * // Emails — send transactional emails\n * await medal.emails.send({\n * template_slug: 'welcome',\n * to: 'user@example.com',\n * variables: { name: 'John' },\n * });\n *\n * // Contacts, Deals, GDPR, Workspaces\n * const contacts = await medal.contacts.list({ status: 'lead' });\n * const { data: deal } = await medal.deals.create({ title: 'Acme', value: 50000 });\n * await medal.gdpr.recordConsent({ email: 'u@x.com', consent_type: 'marketing_email', granted: true });\n * const { data: workspaces } = await medal.workspaces.list();\n * ```\n */\nexport class Medal {\n readonly emails: Emails;\n readonly contacts: Contacts;\n readonly deals: Deals;\n readonly gdpr: Gdpr;\n readonly helpdesk: Helpdesk;\n readonly posts: Posts;\n readonly webhooks: Webhooks;\n readonly workspaces: Workspaces;\n\n constructor(token: string, options?: MedalOptions) {\n if (!token) {\n throw new Error(\n \"Authentication token is required. Pass your medal_xxx API key or OAuth access token as the first argument.\",\n );\n }\n\n const client = new BaseClient({\n baseUrl: (options?.baseUrl ?? \"https://io.medalsocial.com\").replace(/\\/$/, \"\"),\n token,\n workspaceId: options?.workspaceId,\n timeout: options?.timeout ?? 30000,\n userAgent: \"medalsocial-sdk/1.0.0 (+https://github.com/Medal-Social/MedalSocial)\",\n });\n\n this.emails = new Emails(client);\n this.contacts = new Contacts(client);\n this.deals = new Deals(client);\n this.gdpr = new Gdpr(client);\n this.helpdesk = new Helpdesk(client);\n this.posts = new Posts(client);\n this.webhooks = new Webhooks(client);\n this.workspaces = new Workspaces(client);\n }\n}\n\nexport type { RequestOptions } from \"./client\";\nexport { BaseClient } from \"./client\";\nexport type {\n components as OpenApiComponents,\n operations as OpenApiOperations,\n paths as OpenApiPaths,\n} from \"./openapi.generated\";\n// Resource class re-exports (for advanced usage)\nexport { Contacts } from \"./resources/contacts\";\nexport { Deals } from \"./resources/deals\";\nexport { Emails } from \"./resources/emails\";\nexport { Gdpr } from \"./resources/gdpr\";\nexport { Helpdesk } from \"./resources/helpdesk\";\nexport { Posts } from \"./resources/posts\";\nexport { Webhooks } from \"./resources/webhooks\";\nexport { Workspaces } from \"./resources/workspaces\";\nexport type { ApiResponse, PaginatedResponse, PaginationOptions } from \"./types/common\";\n// Re-export all types\nexport { MedalApiError } from \"./types/common\";\nexport type {\n Activity,\n AddNoteInput,\n Contact,\n ContactCreateResult,\n ContactNoteResult,\n ContactRemoveResult,\n ContactStatus,\n ContactUpdateResult,\n CreateContactInput,\n EmailStatus,\n ImportContactInput,\n ImportContactsResult,\n ListContactsOptions,\n UpdateContactInput,\n} from \"./types/contacts\";\nexport type {\n CreateDealInput,\n Deal,\n DealCreateResult,\n DealRemoveResult,\n DealStatus,\n DealUpdateResult,\n ListDealsOptions,\n UpdateDealInput,\n} from \"./types/deals\";\nexport type {\n BatchSendInput,\n BatchSendResult,\n BatchSendSummary,\n EmailSend,\n EmailSendResult,\n EmailTemplate,\n EmailTemplateDetail,\n GetTemplateOptions,\n SendEmailInput,\n} from \"./types/emails\";\nexport type {\n ConsentRecord,\n ConsentResult,\n ConsentType,\n ContactConsents,\n CookieCategoryConsent,\n CookieConsentInput,\n GdprExport,\n RecordConsentInput,\n} from \"./types/gdpr\";\nexport type {\n Conversation,\n ConversationMessage,\n ConversationStatus,\n ConversationUpdateResult,\n CreateReplyInput,\n HelpdeskMessageType,\n ListConversationsOptions,\n MessageAuthorType,\n ReplyCreateResult,\n UpdateConversationInput,\n} from \"./types/helpdesk\";\nexport type {\n Channel,\n CreatePostInput,\n ListPostsOptions,\n Post,\n PostDetail,\n PostType,\n PostVariant,\n PublishResult,\n SchedulePostInput,\n ScheduleResult,\n UpdatePostInput,\n} from \"./types/posts\";\nexport type {\n CreateWebhookInput,\n ListDeliveriesOptions,\n UpdateWebhookInput,\n WebhookDeleteResult,\n WebhookDelivery,\n WebhookEndpoint,\n WebhookTestResult,\n} from \"./types/webhooks\";\nexport type { Workspace } from \"./types/workspaces\";\nexport type {\n ConversationAssignedEvent,\n ConversationCreatedEvent,\n ConversationStatusChangedEvent,\n MessageDeliveryUpdatedEvent,\n MessageReceivedEvent,\n MessageSentEvent,\n TestPingEvent,\n VerifyWebhookSignatureInput,\n WebhookConversationSnapshot,\n WebhookEvent,\n WebhookMessageSnapshot,\n WebhookVerificationErrorCode,\n} from \"./webhook-events\";\n// Webhook event verification + typed events\nexport {\n DEFAULT_WEBHOOK_TOLERANCE_MS,\n verifyWebhookSignature,\n WebhookVerificationError,\n} from \"./webhook-events\";\n\n/** Convenience factory — equivalent to `new Medal(apiKey, options)`. */\nexport function createMedalClient(apiKey: string, options?: MedalOptions): Medal {\n return new Medal(apiKey, options);\n}\n\nexport default Medal;\n","/** Successful API response wrapper */\nexport interface ApiResponse<T> {\n data: T;\n}\n\n/** Paginated API response */\nexport interface PaginatedResponse<T> {\n data: T[];\n pagination: {\n has_more: boolean;\n next_cursor: string | null;\n };\n}\n\n/** API error thrown by the client */\nexport class MedalApiError extends Error {\n readonly status: number;\n readonly code: string;\n readonly details?: unknown;\n\n constructor(status: number, code: string, message: string, details?: unknown) {\n super(message);\n this.name = \"MedalApiError\";\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n\n/** Pagination options for list endpoints */\nexport interface PaginationOptions {\n limit?: number;\n cursor?: string;\n}\n","import { MedalApiError } from \"./types/common\";\n\n/** Configuration for the low-level HTTP client. */\nexport interface ClientConfig {\n baseUrl: string;\n token: string;\n workspaceId?: string;\n timeout: number;\n userAgent: string;\n}\n\n/** Per-request options for write operations. */\nexport interface RequestOptions {\n /**\n * Idempotency key sent as the `Idempotency-Key` header. Retries with the\n * same key return the original result instead of repeating the operation.\n * Required by some endpoints for capability-scoped tokens (e.g. helpdesk\n * replies, webhook creation).\n */\n idempotencyKey?: string;\n /**\n * Capability confirmation token sent as the `X-Capability-Confirmation`\n * header. Required alongside `idempotencyKey` when a token granted a\n * capability-style scope directly (e.g. `helpdesk.webhook.manage`) executes\n * a confirmable write route. Obtain one from\n * `POST /api/v1/capability-confirmations`. API keys with legacy scopes do\n * not need it.\n */\n capabilityConfirmation?: string;\n}\n\n/**\n * Low-level HTTP client used by all resource classes.\n * Handles authentication, retries, timeout, and error parsing.\n */\nexport class BaseClient {\n /** Resolved client configuration. */\n readonly config: ClientConfig;\n\n constructor(config: ClientConfig) {\n this.config = config;\n }\n\n /** Execute an authenticated GET request and return the parsed JSON body. */\n async get<T>(path: string, params?: Record<string, string | undefined>): Promise<T> {\n const url = this.buildUrl(path, params);\n return this.request<T>(url, { method: \"GET\" });\n }\n\n /** Execute an authenticated POST request with a JSON body. */\n async post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>(this.buildUrl(path), {\n method: \"POST\",\n headers: this.writeHeaders(options),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n }\n\n /** Execute an authenticated PATCH request with a JSON body. */\n async patch<T>(path: string, body: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>(this.buildUrl(path), {\n method: \"PATCH\",\n headers: this.writeHeaders(options),\n body: JSON.stringify(body),\n });\n }\n\n /** Execute an authenticated DELETE request. */\n async delete<T>(path: string, options?: RequestOptions): Promise<T> {\n return this.request<T>(this.buildUrl(path), {\n method: \"DELETE\",\n headers: this.writeHeaders(options),\n });\n }\n\n private writeHeaders(options?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n if (options?.idempotencyKey) {\n headers[\"idempotency-key\"] = options.idempotencyKey;\n }\n if (options?.capabilityConfirmation) {\n headers[\"x-capability-confirmation\"] = options.capabilityConfirmation;\n }\n return headers;\n }\n\n private buildUrl(path: string, params?: Record<string, string | undefined>): string {\n const url = new URL(`${this.config.baseUrl}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, value);\n }\n }\n }\n return url.toString();\n }\n\n private async request<T>(url: string, init: RequestInit): Promise<T> {\n const maxAttempts = 3;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const headers = new Headers(init.headers);\n headers.set(\"authorization\", `Bearer ${this.config.token}`);\n if (this.config.workspaceId) {\n headers.set(\"x-workspace-id\", this.config.workspaceId);\n }\n try {\n headers.set(\"user-agent\", this.config.userAgent);\n } catch {\n // Browsers disallow setting user-agent\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeout);\n\n let res: Response;\n try {\n res = await fetch(url, { ...init, headers, signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n\n // Retry on 429 / 5xx (but not on the final attempt)\n if (\n (res.status === 429 || (res.status >= 500 && res.status <= 599)) &&\n attempt < maxAttempts\n ) {\n const retryAfter = res.headers.get(\"retry-after\");\n let delayMs = 0;\n if (retryAfter) {\n const seconds = Number(retryAfter);\n delayMs = Number.isFinite(seconds) ? seconds * 1000 : 0;\n }\n if (delayMs <= 0) {\n delayMs = 250 * attempt;\n }\n await new Promise((r) => setTimeout(r, delayMs));\n continue;\n }\n\n // Parse response\n const text = await res.text();\n let parsed: unknown;\n try {\n parsed = text ? JSON.parse(text) : undefined;\n } catch {\n parsed = text;\n }\n\n if (!res.ok) {\n const body = parsed as\n | { error?: { code?: string; message?: string; details?: unknown } }\n | undefined;\n throw new MedalApiError(\n res.status,\n body?.error?.code ?? \"UNKNOWN_ERROR\",\n body?.error?.message ?? `HTTP ${res.status}: ${res.statusText}`,\n body?.error?.details,\n );\n }\n\n return parsed as T;\n }\n\n /* v8 ignore next -- unreachable: loop always returns or throws */\n throw new Error(\"Request failed after retries\");\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse, PaginatedResponse, PaginationOptions } from \"../types/common\";\nimport type {\n Activity,\n AddNoteInput,\n Contact,\n ContactCreateResult,\n ContactNoteResult,\n ContactRemoveResult,\n ContactUpdateResult,\n CreateContactInput,\n ImportContactInput,\n ImportContactsResult,\n ListContactsOptions,\n UpdateContactInput,\n} from \"../types/contacts\";\n\n/** Manage contacts in the workspace CRM. */\nexport class Contacts {\n constructor(private client: BaseClient) {}\n\n /** List contacts with cursor-based pagination and optional filters. */\n async list(options?: ListContactsOptions): Promise<PaginatedResponse<Contact>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.email_status) params.email_status = options.email_status;\n if (options?.label_ids) params.label_ids = options.label_ids.join(\",\");\n if (options?.search) params.search = options.search;\n return this.client.get(\"/api/v1/contacts\", params);\n }\n\n /** Create a new contact. Email must be unique in the workspace. */\n async create(input: CreateContactInput): Promise<ApiResponse<ContactCreateResult>> {\n return this.client.post(\"/api/v1/contacts\", input);\n }\n\n /** Get a contact by ID. */\n async get(id: string): Promise<ApiResponse<Contact>> {\n return this.client.get(`/api/v1/contacts/${encodeURIComponent(id)}`);\n }\n\n /** Update one or more fields on a contact. */\n async update(id: string, input: UpdateContactInput): Promise<ApiResponse<ContactUpdateResult>> {\n return this.client.patch(`/api/v1/contacts/${encodeURIComponent(id)}`, input);\n }\n\n /** Permanently delete a contact. */\n async remove(id: string): Promise<ApiResponse<ContactRemoveResult>> {\n return this.client.delete(`/api/v1/contacts/${encodeURIComponent(id)}`);\n }\n\n /** Get the activity timeline for a contact. */\n async activities(id: string, options?: PaginationOptions): Promise<PaginatedResponse<Activity>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n return this.client.get(`/api/v1/contacts/${encodeURIComponent(id)}/activities`, params);\n }\n\n /** Add a note to a contact's timeline. */\n async addNote(id: string, input: AddNoteInput): Promise<ApiResponse<ContactNoteResult>> {\n return this.client.post(`/api/v1/contacts/${encodeURIComponent(id)}/notes`, input);\n }\n\n /** Bulk import contacts (max 500). Duplicates are skipped. */\n async import(contacts: ImportContactInput[]): Promise<ApiResponse<ImportContactsResult>> {\n return this.client.post(\"/api/v1/contacts/import\", { contacts });\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse, PaginatedResponse } from \"../types/common\";\nimport type {\n CreateDealInput,\n Deal,\n DealCreateResult,\n DealRemoveResult,\n DealUpdateResult,\n ListDealsOptions,\n UpdateDealInput,\n} from \"../types/deals\";\n\n/** Manage sponsorship deals in the workspace. */\nexport class Deals {\n constructor(private client: BaseClient) {}\n\n /** List deals with cursor-based pagination and optional filters. */\n async list(options?: ListDealsOptions): Promise<PaginatedResponse<Deal>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.search) params.search = options.search;\n return this.client.get(\"/api/v1/deals\", params);\n }\n\n /** Create a new deal. */\n async create(input: CreateDealInput): Promise<ApiResponse<DealCreateResult>> {\n return this.client.post(\"/api/v1/deals\", input);\n }\n\n /** Get a deal by ID. */\n async get(id: string): Promise<ApiResponse<Deal>> {\n return this.client.get(`/api/v1/deals/${encodeURIComponent(id)}`);\n }\n\n /** Update one or more fields on a deal. Set contact_id to null to unlink. */\n async update(id: string, input: UpdateDealInput): Promise<ApiResponse<DealUpdateResult>> {\n return this.client.patch(`/api/v1/deals/${encodeURIComponent(id)}`, input);\n }\n\n /** Permanently delete a deal. */\n async remove(id: string): Promise<ApiResponse<DealRemoveResult>> {\n return this.client.delete(`/api/v1/deals/${encodeURIComponent(id)}`);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type {\n BatchSendInput,\n BatchSendSummary,\n EmailSend,\n EmailSendResult,\n EmailTemplate,\n EmailTemplateDetail,\n GetTemplateOptions,\n SendEmailInput,\n} from \"../types/emails\";\n\n/** Manage email templates stored in the workspace. */\nclass EmailTemplates {\n constructor(private client: BaseClient) {}\n\n /** List all active email templates in the workspace. */\n async list(): Promise<ApiResponse<EmailTemplate[]>> {\n return this.client.get(\"/api/v1/emails/templates\");\n }\n\n /** Get a specific email template by slug, optionally with locale resolution. */\n async get(slug: string, options?: GetTemplateOptions): Promise<ApiResponse<EmailTemplateDetail>> {\n const params: Record<string, string | undefined> = {};\n if (options?.locale) params.locale = options.locale;\n if (options?.fallback_locale) params.fallback_locale = options.fallback_locale;\n return this.client.get(`/api/v1/emails/templates/${encodeURIComponent(slug)}`, params);\n }\n}\n\n/** Send transactional emails and manage templates. */\nexport class Emails {\n readonly templates: EmailTemplates;\n\n constructor(private client: BaseClient) {\n this.templates = new EmailTemplates(client);\n }\n\n /**\n * Send a transactional email using a template (HTTP 202). The returned `id`\n * is an email send id — poll `emails.get(id)` with it to track delivery.\n */\n async send(input: SendEmailInput): Promise<ApiResponse<EmailSendResult>> {\n return this.client.post(\"/api/v1/emails\", input);\n }\n\n /** Get the delivery status of a sent email. */\n async get(id: string): Promise<ApiResponse<EmailSend>> {\n return this.client.get(`/api/v1/emails/${encodeURIComponent(id)}`);\n }\n\n /**\n * Send the same template to multiple recipients (max 100, HTTP 202). Each\n * queued recipient gets its own send id in `results` for `emails.get(id)`.\n */\n async batch(input: BatchSendInput): Promise<ApiResponse<BatchSendSummary>> {\n return this.client.post(\"/api/v1/emails/batch\", input);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type {\n ConsentRecord,\n ConsentResult,\n CookieConsentInput,\n GdprExport,\n RecordConsentInput,\n} from \"../types/gdpr\";\n\n/** Manage GDPR compliance — data exports, consent records, and cookie consent. */\nexport class Gdpr {\n constructor(private client: BaseClient) {}\n\n /** Request a workspace data export. Runs asynchronously. */\n async requestExport(): Promise<ApiResponse<{ request_id: string; status: string }>> {\n return this.client.post(\"/api/v1/gdpr/export\");\n }\n\n /** List all workspace export requests. */\n async listExports(): Promise<ApiResponse<GdprExport[]>> {\n return this.client.get(\"/api/v1/gdpr/exports\");\n }\n\n /** Get the status of a specific export. */\n async getExport(id: string): Promise<ApiResponse<GdprExport>> {\n return this.client.get(`/api/v1/gdpr/exports/${encodeURIComponent(id)}`);\n }\n\n /** Record a GDPR consent decision for a contact by email. */\n async recordConsent(input: RecordConsentInput): Promise<ApiResponse<ConsentResult>> {\n return this.client.post(\"/api/v1/gdpr/consent\", input);\n }\n\n /** Get all consent records for a contact by email. */\n async getConsent(email: string): Promise<ApiResponse<ConsentRecord[]>> {\n return this.client.get(`/api/v1/gdpr/consent/${encodeURIComponent(email)}`);\n }\n\n /** Record cookie consent from an external site (legacy endpoint). */\n async cookieConsent(input: CookieConsentInput): Promise<{ success: boolean; logId?: string }> {\n return this.client.post(\"/api/cookie-consent\", input);\n }\n}\n","import type { BaseClient, RequestOptions } from \"../client\";\nimport type { ApiResponse, PaginatedResponse, PaginationOptions } from \"../types/common\";\nimport type {\n Conversation,\n ConversationMessage,\n ConversationUpdateResult,\n CreateReplyInput,\n ListConversationsOptions,\n ReplyCreateResult,\n UpdateConversationInput,\n} from \"../types/helpdesk\";\n\n/** Browse and manage helpdesk conversations. */\nclass HelpdeskConversations {\n constructor(private client: BaseClient) {}\n\n /** List/search conversations with cursor-based pagination and optional filters. */\n async list(options?: ListConversationsOptions): Promise<PaginatedResponse<Conversation>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.assignee_user_id) params.assignee_user_id = options.assignee_user_id;\n if (options?.requester) params.requester = options.requester;\n if (options?.query) params.query = options.query;\n if (options?.channels) params.channels = options.channels.join(\",\");\n return this.client.get(\"/api/v1/helpdesk/conversations\", params);\n }\n\n /** Get a conversation by ID. */\n async get(id: string): Promise<ApiResponse<Conversation>> {\n return this.client.get(`/api/v1/helpdesk/conversations/${encodeURIComponent(id)}`);\n }\n\n /** Update a conversation's status and/or assignee (pass `assignee_user_id: null` to unassign). */\n async update(\n id: string,\n input: UpdateConversationInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ConversationUpdateResult>> {\n return this.client.patch(\n `/api/v1/helpdesk/conversations/${encodeURIComponent(id)}`,\n input,\n options,\n );\n }\n\n /** Read a conversation's messages with cursor-based pagination. */\n async messages(\n id: string,\n options?: PaginationOptions,\n ): Promise<PaginatedResponse<ConversationMessage>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n return this.client.get(\n `/api/v1/helpdesk/conversations/${encodeURIComponent(id)}/messages`,\n params,\n );\n }\n}\n\n/** Send operator replies (or internal notes) into conversations. */\nclass HelpdeskReplies {\n constructor(private client: BaseClient) {}\n\n /**\n * Send an operator reply or internal note. Returns HTTP 201.\n *\n * Pass an `idempotencyKey` so retried requests do not create duplicate\n * messages — it is REQUIRED for capability-scoped tokens.\n */\n async create(\n input: CreateReplyInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ReplyCreateResult>> {\n return this.client.post(\"/api/v1/helpdesk/replies\", input, options);\n }\n}\n\n/** Helpdesk bridge — read conversations, reply, and manage assignment/status. */\nexport class Helpdesk {\n readonly conversations: HelpdeskConversations;\n readonly replies: HelpdeskReplies;\n\n constructor(client: BaseClient) {\n this.conversations = new HelpdeskConversations(client);\n this.replies = new HelpdeskReplies(client);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse, PaginatedResponse } from \"../types/common\";\nimport type {\n Channel,\n CreatePostInput,\n ListPostsOptions,\n Post,\n PostDetail,\n PublishResult,\n SchedulePostInput,\n ScheduleResult,\n UpdatePostInput,\n} from \"../types/posts\";\n\n/** Create and publish posts across connected channels. */\nexport class Posts {\n constructor(private client: BaseClient) {}\n\n /** List posts with cursor-based pagination and optional filters. */\n async list(options?: ListPostsOptions): Promise<PaginatedResponse<Post>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n if (options?.cursor) params.cursor = options.cursor;\n if (options?.status) params.status = options.status;\n if (options?.type) params.type = options.type;\n return this.client.get(\"/api/v1/posts\", params);\n }\n\n /** Create a new post with content and target channels. */\n async create(input: CreatePostInput): Promise<ApiResponse<{ id: string }>> {\n return this.client.post(\"/api/v1/posts\", input);\n }\n\n /** Get a post by ID, including its per-channel variants. */\n async get(id: string): Promise<ApiResponse<PostDetail>> {\n return this.client.get(`/api/v1/posts/${encodeURIComponent(id)}`);\n }\n\n /** Update a draft post's title or content. */\n async update(id: string, input: UpdatePostInput): Promise<ApiResponse<{ success: boolean }>> {\n return this.client.patch(`/api/v1/posts/${encodeURIComponent(id)}`, input);\n }\n\n /** Delete a post. */\n async remove(id: string): Promise<ApiResponse<{ success: boolean }>> {\n return this.client.delete(`/api/v1/posts/${encodeURIComponent(id)}`);\n }\n\n /** Schedule a post for future publication. */\n async schedule(id: string, input: SchedulePostInput): Promise<ApiResponse<ScheduleResult>> {\n return this.client.post(`/api/v1/posts/${encodeURIComponent(id)}/schedule`, input);\n }\n\n /** Publish a post immediately to all target channels. */\n async publish(id: string): Promise<ApiResponse<PublishResult>> {\n return this.client.post(`/api/v1/posts/${encodeURIComponent(id)}/publish`);\n }\n\n /** List connected publishing channels for this workspace. */\n async channels(): Promise<ApiResponse<Channel[]>> {\n return this.client.get(\"/api/v1/posts/channels\");\n }\n}\n","import type { BaseClient, RequestOptions } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type {\n CreateWebhookInput,\n ListDeliveriesOptions,\n UpdateWebhookInput,\n WebhookDeleteResult,\n WebhookDelivery,\n WebhookEndpoint,\n WebhookTestResult,\n} from \"../types/webhooks\";\n\n/** Manage webhook endpoints and inspect their deliveries. */\nexport class Webhooks {\n constructor(private client: BaseClient) {}\n\n /** List all webhook endpoints in the workspace. */\n async list(): Promise<ApiResponse<WebhookEndpoint[]>> {\n return this.client.get(\"/api/v1/webhooks\");\n }\n\n /**\n * Create a webhook endpoint. Returns HTTP 201.\n *\n * **The response's `data.secret` contains the signing secret EXACTLY ONCE.**\n * It can never be retrieved again — store it securely immediately. You need\n * it to verify the `X-Medal-Signature` header on incoming deliveries (see\n * `verifyWebhookSignature`).\n *\n * `secret` is typed optional because an idempotent replay (retrying with the\n * same `Idempotency-Key`, `X-Idempotent-Replayed: true`) returns the existing\n * endpoint WITHOUT the secret — handle that case (rotate if you lost it).\n */\n async create(\n input: CreateWebhookInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<WebhookEndpoint>> {\n return this.client.post(\"/api/v1/webhooks\", input, options);\n }\n\n /** Get a webhook endpoint by ID. */\n async get(id: string): Promise<ApiResponse<WebhookEndpoint>> {\n return this.client.get(`/api/v1/webhooks/${encodeURIComponent(id)}`);\n }\n\n /** Update a webhook endpoint (name, url, event types, filters, enabled). */\n async update(\n id: string,\n input: UpdateWebhookInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<WebhookEndpoint>> {\n return this.client.patch(`/api/v1/webhooks/${encodeURIComponent(id)}`, input, options);\n }\n\n /**\n * Permanently delete a webhook endpoint (stops all outbound deliveries).\n * Capability-scoped tokens must pass `idempotencyKey` — the API requires\n * `Idempotency-Key` + `X-Capability-Confirmation` for direct capability\n * grants on this route. API keys with legacy scopes may omit it.\n */\n async delete(id: string, options?: RequestOptions): Promise<ApiResponse<WebhookDeleteResult>> {\n return this.client.delete(`/api/v1/webhooks/${encodeURIComponent(id)}`, options);\n }\n\n /** List recent deliveries for an endpoint (most recent first). */\n async deliveries(\n id: string,\n options?: ListDeliveriesOptions,\n ): Promise<ApiResponse<WebhookDelivery[]>> {\n const params: Record<string, string | undefined> = {};\n if (options?.limit !== undefined) params.limit = String(options.limit);\n return this.client.get(`/api/v1/webhooks/${encodeURIComponent(id)}/deliveries`, params);\n }\n\n /** Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202. */\n async test(id: string): Promise<ApiResponse<WebhookTestResult>> {\n return this.client.post(`/api/v1/webhooks/${encodeURIComponent(id)}/test`);\n }\n}\n","import type { BaseClient } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type { Workspace } from \"../types/workspaces\";\n\n/** Access workspaces for the authenticated credential. */\nexport class Workspaces {\n constructor(private client: BaseClient) {}\n\n /** List workspaces accessible to the current API key or OAuth token. */\n async list(): Promise<ApiResponse<Workspace[]>> {\n return this.client.get(\"/api/v1/me/workspaces\");\n }\n}\n","/**\n * Webhook event types and signature verification for the Medal Social\n * outbound webhook bridge.\n *\n * Every delivery is an HTTP POST with headers:\n * - `X-Medal-Timestamp` — Unix milliseconds when the request was signed\n * - `X-Medal-Signature` — `sha256=<base64(HMAC-SHA256(\"{timestamp}.{rawBody}\", secret))>`\n * - `X-Medal-Event` — the event type\n * - `X-Medal-Delivery-Id` / `Idempotency-Key` — unique delivery ID (deduplicate on this)\n *\n * Use {@link verifyWebhookSignature} to authenticate a delivery and get the\n * parsed, typed event back. Uses Web Crypto (`crypto.subtle`) so it works in\n * Node.js 18+, Deno, Bun, Cloudflare Workers, and browsers.\n */\n\n/** Snapshot of a conversation included in every helpdesk webhook event. */\nexport interface WebhookConversationSnapshot {\n id: string;\n channel: string;\n channelConnectionId: string | null;\n status: string;\n subject: string | null;\n assigneeUserId: string | null;\n contactId: string | null;\n visitorName: string | null;\n visitorEmail: string | null;\n externalConversationId: string | null;\n channelAccountId: string | null;\n messageCount: number;\n /** Unix timestamp in milliseconds. */\n lastMessageAt: number;\n /** Unix timestamp in milliseconds. */\n createdAt: number;\n}\n\n/** Snapshot of a message included in helpdesk message events. */\nexport interface WebhookMessageSnapshot {\n id: string;\n authorType: \"visitor\" | \"operator\" | \"ai\" | \"system\";\n messageType: \"chat\" | \"email\" | \"note\";\n body: string;\n authorUserId: string | null;\n authorName: string | null;\n externalMessageId: string | null;\n deliveryStatus: string | null;\n deliveryError: string | null;\n /** Unix timestamp in milliseconds. */\n createdAt: number;\n}\n\n/** Fields present in the `data` of every helpdesk event. */\ninterface HelpdeskEventData {\n /** Channel type at the top level, for quick filtering. */\n channel: string;\n channelConnectionId: string | null;\n conversation: WebhookConversationSnapshot;\n}\n\n/** Envelope fields shared by all webhook events. */\ninterface WebhookEventBase {\n /** Unique delivery/event ID — use for deduplication. */\n id: string;\n /** Unix timestamp in milliseconds when the event was created. */\n created_at: number;\n workspace_id: string;\n}\n\n/** A new conversation was created. */\nexport interface ConversationCreatedEvent extends WebhookEventBase {\n type: \"helpdesk.conversation_created\";\n data: HelpdeskEventData;\n}\n\n/** A conversation was assigned or unassigned. */\nexport interface ConversationAssignedEvent extends WebhookEventBase {\n type: \"helpdesk.conversation_assigned\";\n data: HelpdeskEventData & {\n assigneeUserId: string | null;\n previousAssigneeUserId: string | null;\n };\n}\n\n/** A conversation's status changed (open / snoozed / closed). */\nexport interface ConversationStatusChangedEvent extends WebhookEventBase {\n type: \"helpdesk.conversation_status_changed\";\n data: HelpdeskEventData & {\n status: string;\n previousStatus: string;\n };\n}\n\n/** A message arrived from the visitor/customer. */\nexport interface MessageReceivedEvent extends WebhookEventBase {\n type: \"helpdesk.message_received\";\n data: HelpdeskEventData & { message: WebhookMessageSnapshot };\n}\n\n/** A message was sent by an operator, AI, or the system. */\nexport interface MessageSentEvent extends WebhookEventBase {\n type: \"helpdesk.message_sent\";\n data: HelpdeskEventData & { message: WebhookMessageSnapshot };\n}\n\n/** The delivery status of an outbound message changed (sent / delivered / failed …). */\nexport interface MessageDeliveryUpdatedEvent extends WebhookEventBase {\n type: \"helpdesk.message_delivery_updated\";\n data: HelpdeskEventData & { message: WebhookMessageSnapshot };\n}\n\n/** A `test.ping` delivery queued via `medal.webhooks.test(id)`. Carries sample data. */\nexport interface TestPingEvent extends WebhookEventBase {\n type: \"test.ping\";\n data: Record<string, unknown>;\n}\n\n/**\n * Discriminated union of all webhook events, keyed on `type`.\n *\n * @example\n * ```ts\n * switch (event.type) {\n * case 'helpdesk.message_received':\n * console.log(event.data.message.body);\n * break;\n * case 'helpdesk.conversation_status_changed':\n * console.log(event.data.previousStatus, '→', event.data.status);\n * break;\n * }\n * ```\n */\nexport type WebhookEvent =\n | ConversationCreatedEvent\n | ConversationAssignedEvent\n | ConversationStatusChangedEvent\n | MessageReceivedEvent\n | MessageSentEvent\n | MessageDeliveryUpdatedEvent\n | TestPingEvent;\n\n/** Machine-readable reason a webhook verification failed. */\nexport type WebhookVerificationErrorCode =\n | \"malformed_header\"\n | \"timestamp_out_of_tolerance\"\n | \"invalid_signature\"\n | \"invalid_payload\";\n\n/** Thrown by {@link verifyWebhookSignature} when a delivery cannot be authenticated. */\nexport class WebhookVerificationError extends Error {\n readonly code: WebhookVerificationErrorCode;\n\n constructor(code: WebhookVerificationErrorCode, message: string) {\n super(message);\n this.name = \"WebhookVerificationError\";\n this.code = code;\n }\n}\n\n/** Input for {@link verifyWebhookSignature}. */\nexport interface VerifyWebhookSignatureInput {\n /** The RAW request body string, exactly as received (do not re-serialize parsed JSON). */\n payload: string;\n /** Value of the `X-Medal-Timestamp` header (Unix milliseconds). */\n timestamp: string;\n /** Value of the `X-Medal-Signature` header (`sha256=<base64>`). */\n signature: string;\n /** The endpoint signing secret (`whsec_…`) returned once at creation time. */\n secret: string;\n /** Max allowed clock skew between now and the signed timestamp. Default 5 minutes. */\n toleranceMs?: number;\n}\n\n/** Default allowed clock skew for webhook verification (5 minutes). */\nexport const DEFAULT_WEBHOOK_TOLERANCE_MS = 5 * 60 * 1000;\n\nfunction base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Verify a webhook delivery's signature and timestamp, then return the parsed\n * typed event.\n *\n * Recomputes `HMAC-SHA256(\"{timestamp}.{payload}\", secret)` with Web Crypto\n * and compares it against the signature in constant time. Deliveries whose\n * timestamp deviates from the current time by more than `toleranceMs`\n * (default 5 minutes) are rejected to prevent replay attacks.\n *\n * @throws {WebhookVerificationError} if the headers are malformed, the\n * timestamp is outside the tolerance window, the signature does not match,\n * or the payload is not valid JSON.\n *\n * @example\n * ```ts\n * const event = await verifyWebhookSignature({\n * payload: rawBody,\n * timestamp: req.headers['x-medal-timestamp'],\n * signature: req.headers['x-medal-signature'],\n * secret: process.env.MEDAL_WEBHOOK_SECRET,\n * });\n * ```\n */\nexport async function verifyWebhookSignature(\n input: VerifyWebhookSignatureInput,\n): Promise<WebhookEvent> {\n const { payload, timestamp, signature, secret } = input;\n const toleranceMs = input.toleranceMs ?? DEFAULT_WEBHOOK_TOLERANCE_MS;\n\n if (typeof signature !== \"string\" || !signature.startsWith(\"sha256=\")) {\n throw new WebhookVerificationError(\n \"malformed_header\",\n \"Signature header must be in the form 'sha256=<base64>'\",\n );\n }\n\n const timestampMs = Number(timestamp);\n if (typeof timestamp !== \"string\" || timestamp === \"\" || !Number.isFinite(timestampMs)) {\n throw new WebhookVerificationError(\n \"malformed_header\",\n \"Timestamp header must be a Unix-milliseconds number string\",\n );\n }\n if (Math.abs(Date.now() - timestampMs) > toleranceMs) {\n throw new WebhookVerificationError(\n \"timestamp_out_of_tolerance\",\n `Timestamp is outside the allowed tolerance of ${toleranceMs}ms`,\n );\n }\n\n let signatureBytes: Uint8Array<ArrayBuffer>;\n try {\n signatureBytes = base64ToBytes(signature.slice(\"sha256=\".length));\n } catch {\n throw new WebhookVerificationError(\"invalid_signature\", \"Signature is not valid base64\");\n }\n\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"verify\"],\n );\n // crypto.subtle.verify performs a constant-time comparison internally.\n const valid = await crypto.subtle.verify(\n \"HMAC\",\n key,\n signatureBytes,\n encoder.encode(`${timestamp}.${payload}`),\n );\n if (!valid) {\n throw new WebhookVerificationError(\"invalid_signature\", \"Signature does not match the payload\");\n }\n\n try {\n return JSON.parse(payload) as WebhookEvent;\n } catch {\n throw new WebhookVerificationError(\"invalid_payload\", \"Payload is not valid JSON\");\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/src/index.mjs
CHANGED
|
@@ -231,7 +231,10 @@ var Emails = class {
|
|
|
231
231
|
}
|
|
232
232
|
client;
|
|
233
233
|
templates;
|
|
234
|
-
/**
|
|
234
|
+
/**
|
|
235
|
+
* Send a transactional email using a template (HTTP 202). The returned `id`
|
|
236
|
+
* is an email send id — poll `emails.get(id)` with it to track delivery.
|
|
237
|
+
*/
|
|
235
238
|
async send(input) {
|
|
236
239
|
return this.client.post("/api/v1/emails", input);
|
|
237
240
|
}
|
|
@@ -239,7 +242,10 @@ var Emails = class {
|
|
|
239
242
|
async get(id) {
|
|
240
243
|
return this.client.get(`/api/v1/emails/${encodeURIComponent(id)}`);
|
|
241
244
|
}
|
|
242
|
-
/**
|
|
245
|
+
/**
|
|
246
|
+
* Send the same template to multiple recipients (max 100, HTTP 202). Each
|
|
247
|
+
* queued recipient gets its own send id in `results` for `emails.get(id)`.
|
|
248
|
+
*/
|
|
243
249
|
async batch(input) {
|
|
244
250
|
return this.client.post("/api/v1/emails/batch", input);
|
|
245
251
|
}
|