@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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../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","../../src/index.ts"],"sourcesContent":["/** 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","/**\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"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/types/common.ts","../../src/client.ts","../../src/resources/channels.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","../../src/index.ts"],"sourcesContent":["/** 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, RequestOptions } from \"../client\";\nimport type {\n ChannelConnection,\n ChannelConnectionDisconnectResult,\n ConnectLink,\n ConnectLinkCreateResult,\n ConnectLinkRevokeResult,\n CreateConnectLinkInput,\n ListConnectLinksOptions,\n} from \"../types/channels\";\nimport type { ApiResponse } from \"../types/common\";\n\n/** Mint, list, and revoke hosted connect links. */\nclass ChannelConnectLinks {\n constructor(private client: BaseClient) {}\n\n /**\n * Mint a single-use hosted connect link. Returns HTTP 201.\n *\n * **The response's `data.url` contains the one-time link token EXACTLY\n * ONCE.** Send it to the person who should connect their account — an\n * idempotent replay (same `Idempotency-Key`) returns the link WITHOUT\n * `url`, so store it immediately (or revoke and mint a new link if lost).\n *\n * Requires the `channel.connect.manage` scope; OAuth callers additionally\n * need the workspace `admin` role.\n */\n async create(\n input: CreateConnectLinkInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ConnectLinkCreateResult>> {\n return this.client.post(\"/api/v1/channels/connect-links\", input, options);\n }\n\n /** List the workspace's connect links (tokens are never returned). */\n async list(options?: ListConnectLinksOptions): Promise<ApiResponse<ConnectLink[]>> {\n const params: Record<string, string | undefined> = {};\n if (options?.channel_type) params.channel_type = options.channel_type;\n if (options?.status) params.status = options.status;\n return this.client.get(\"/api/v1/channels/connect-links\", params);\n }\n\n /** Revoke a pending connect link so it can no longer be consumed. */\n async revoke(\n id: string,\n options?: RequestOptions,\n ): Promise<ApiResponse<ConnectLinkRevokeResult>> {\n return this.client.delete(`/api/v1/channels/connect-links/${encodeURIComponent(id)}`, options);\n }\n}\n\n/** List and disconnect the workspace's channel connections. */\nclass ChannelConnections {\n constructor(private client: BaseClient) {}\n\n /** List the workspace's channel connections (generic, channel-agnostic shape). */\n async list(): Promise<ApiResponse<ChannelConnection[]>> {\n return this.client.get(\"/api/v1/channels/connections\");\n }\n\n /**\n * Disconnect a connected channel account (best-effort platform logout, then\n * local revoke). Emits a `helpdesk.channel_disconnected` webhook event with\n * `reason: \"api_disconnect\"` if the account was previously connected.\n */\n async disconnect(\n id: string,\n options?: RequestOptions,\n ): Promise<ApiResponse<ChannelConnectionDisconnectResult>> {\n return this.client.delete(`/api/v1/channels/connections/${encodeURIComponent(id)}`, options);\n }\n}\n\n/**\n * Partner channel connect — mint hosted connect links that let an external\n * person (no Medal account required) attach a channel account (e.g.\n * `telegram_inbox`) to the workspace's helpdesk, and manage the resulting\n * connections.\n */\nexport class Channels {\n readonly connectLinks: ChannelConnectLinks;\n readonly connections: ChannelConnections;\n\n constructor(client: BaseClient) {\n this.connectLinks = new ChannelConnectLinks(client);\n this.connections = new ChannelConnections(client);\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/**\n * Fields present in the `data` of channel lifecycle events. Unlike message\n * events there is no conversation snapshot — the payload is channel-generic.\n * `channel` / `channelConnectionId` sit at the top level so endpoint channel\n * filters match exactly like message events.\n */\nexport interface WebhookChannelLifecycleData {\n /** Helpdesk channel type (e.g. `telegram`), or `null` for non-helpdesk channels. */\n channel: string | null;\n channelConnectionId: string | null;\n /** Connector channel type (e.g. `telegram_inbox`). */\n channel_type: string;\n /** Adapter-defined stable connection ref (matches `consumed_connection_ref` on the connect link). */\n connection_ref: string;\n label: string | null;\n masked_identity: string | null;\n}\n\n/** A channel account was connected to the workspace (e.g. via a partner connect link). */\nexport interface ChannelConnectedEvent extends WebhookEventBase {\n type: \"helpdesk.channel_connected\";\n data: WebhookChannelLifecycleData;\n}\n\n/** Why a channel account was disconnected. */\nexport type ChannelDisconnectReason = \"api_disconnect\" | \"user_revoked\" | \"member_disconnect\";\n\n/** A previously connected channel account was removed from the workspace. */\nexport interface ChannelDisconnectedEvent extends WebhookEventBase {\n type: \"helpdesk.channel_disconnected\";\n data: WebhookChannelLifecycleData & {\n /** Why the account went away. */\n reason?: ChannelDisconnectReason;\n };\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 | ChannelConnectedEvent\n | ChannelDisconnectedEvent\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","/**\n * Official TypeScript SDK for the Medal Social API.\n *\n * Provides typed access to posts, emails, contacts, deals, GDPR compliance,\n * helpdesk conversations, partner channel connect, webhooks, and workspace\n * 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 { Channels } from \"./resources/channels\";\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 channels: Channels;\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.channels = new Channels(client);\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 { Channels } from \"./resources/channels\";\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 {\n ChannelConnection,\n ChannelConnectionDisconnectResult,\n ChannelConnectionState,\n ConnectLink,\n ConnectLinkCreateResult,\n ConnectLinkRevokeResult,\n ConnectLinkStatus,\n CreateConnectLinkInput,\n ListConnectLinksOptions,\n} from \"./types/channels\";\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 ChannelConnectedEvent,\n ChannelDisconnectedEvent,\n ChannelDisconnectReason,\n ConversationAssignedEvent,\n ConversationCreatedEvent,\n ConversationStatusChangedEvent,\n MessageDeliveryUpdatedEvent,\n MessageReceivedEvent,\n MessageSentEvent,\n TestPingEvent,\n VerifyWebhookSignatureInput,\n WebhookChannelLifecycleData,\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"],"mappings":"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","names":[]}
@@ -610,6 +610,91 @@ interface paths {
610
610
  patch?: never;
611
611
  trace?: never;
612
612
  };
613
+ "/api/v1/channels/connect-links": {
614
+ parameters: {
615
+ query?: never;
616
+ header?: never;
617
+ path?: never;
618
+ cookie?: never;
619
+ };
620
+ /**
621
+ * List connect links
622
+ * @description Link tokens are never returned.
623
+ */
624
+ get: operations["listChannelConnectLinks"];
625
+ put?: never;
626
+ /**
627
+ * Mint a hosted connect link
628
+ * @description Mints a single-use hosted connect link that lets an external person (no Medal account required) attach a channel account (e.g. `telegram_inbox`) to the workspace's helpdesk. The response's `data.url` contains the one-time link token EXACTLY ONCE — an idempotent replay (same `Idempotency-Key`) returns the link WITHOUT `url`. Requires the `channel.connect.manage` scope; OAuth callers additionally need the workspace `admin` role.
629
+ */
630
+ post: operations["createChannelConnectLink"];
631
+ delete?: never;
632
+ options?: never;
633
+ head?: never;
634
+ patch?: never;
635
+ trace?: never;
636
+ };
637
+ "/api/v1/channels/connect-links/{id}": {
638
+ parameters: {
639
+ query?: never;
640
+ header?: never;
641
+ path: {
642
+ id: components["parameters"]["Id"];
643
+ };
644
+ cookie?: never;
645
+ };
646
+ get?: never;
647
+ put?: never;
648
+ post?: never;
649
+ /**
650
+ * Revoke a connect link
651
+ * @description Revokes a pending connect link so it can no longer be consumed. OAuth callers need the workspace `admin` role.
652
+ */
653
+ delete: operations["revokeChannelConnectLink"];
654
+ options?: never;
655
+ head?: never;
656
+ patch?: never;
657
+ trace?: never;
658
+ };
659
+ "/api/v1/channels/connections": {
660
+ parameters: {
661
+ query?: never;
662
+ header?: never;
663
+ path?: never;
664
+ cookie?: never;
665
+ };
666
+ /** List channel connections */
667
+ get: operations["listChannelConnections"];
668
+ put?: never;
669
+ post?: never;
670
+ delete?: never;
671
+ options?: never;
672
+ head?: never;
673
+ patch?: never;
674
+ trace?: never;
675
+ };
676
+ "/api/v1/channels/connections/{id}": {
677
+ parameters: {
678
+ query?: never;
679
+ header?: never;
680
+ path: {
681
+ id: components["parameters"]["Id"];
682
+ };
683
+ cookie?: never;
684
+ };
685
+ get?: never;
686
+ put?: never;
687
+ post?: never;
688
+ /**
689
+ * Disconnect a channel connection
690
+ * @description Disconnects a connected channel account (best-effort platform logout, then local revoke). Emits a `helpdesk.channel_disconnected` webhook event with `reason: "api_disconnect"` if the account was previously connected. OAuth callers need the workspace `admin` role.
691
+ */
692
+ delete: operations["disconnectChannelConnection"];
693
+ options?: never;
694
+ head?: never;
695
+ patch?: never;
696
+ trace?: never;
697
+ };
613
698
  }
614
699
  type webhooks = Record<string, never>;
615
700
  interface components {
@@ -712,9 +797,18 @@ interface components {
712
797
  [key: string]: string;
713
798
  };
714
799
  contact_id?: string;
800
+ idempotency_key?: string;
801
+ /** Format: email */
802
+ copy_to?: string;
803
+ /** Format: email */
804
+ copy_reply_to?: string;
715
805
  };
716
806
  EmailSendResult: {
717
- id: string;
807
+ /** @description Email send id — poll GET /api/v1/emails/{id} with it. */
808
+ id: string | null;
809
+ /** @description Send id of the copy_to copy, or null when no copy was requested. */
810
+ copy_id: string | null;
811
+ contact_id: string | null;
718
812
  status: string;
719
813
  };
720
814
  EmailSend: {
@@ -756,6 +850,18 @@ interface components {
756
850
  total: number;
757
851
  queued: number;
758
852
  failed: number;
853
+ /** @description Per-recipient outcome, in request order. */
854
+ results: components["schemas"]["BatchSendRecipientResult"][];
855
+ };
856
+ BatchSendRecipientResult: {
857
+ /** Format: email */
858
+ email: string;
859
+ /** @description Email send id — poll GET /api/v1/emails/{id} with it. Null when not queued. */
860
+ id: string | null;
861
+ /** @enum {string} */
862
+ status: "queued" | "failed";
863
+ /** @description Failure reason for recipients that were not queued. */
864
+ error: string | null;
759
865
  };
760
866
  EmailTemplate: {
761
867
  id: string;
@@ -1221,6 +1327,68 @@ interface components {
1221
1327
  /** @constant */
1222
1328
  status: "queued";
1223
1329
  };
1330
+ /** @enum {string} */
1331
+ ConnectLinkStatus: "pending" | "consumed" | "expired" | "revoked";
1332
+ /** @enum {string} */
1333
+ ChannelConnectionState: "connecting" | "active" | "disconnected" | "disabled";
1334
+ CreateConnectLinkInput: {
1335
+ /** @description Channel type to connect (e.g. `telegram_inbox`). */
1336
+ channel_type: string;
1337
+ /** @description Display label shown on the hosted connect page. */
1338
+ label?: string;
1339
+ /**
1340
+ * Format: uri
1341
+ * @description URL the hosted page redirects to after a successful connect — must be https.
1342
+ */
1343
+ redirect_url?: string;
1344
+ };
1345
+ ConnectLinkCreateResult: {
1346
+ id: string;
1347
+ /**
1348
+ * Format: uri
1349
+ * @description Single-use hosted connect URL containing the one-time link token — present ONLY in the live create response. An idempotent replay of the create request omits it; the token can never be retrieved again.
1350
+ */
1351
+ url?: string;
1352
+ channel_type: string;
1353
+ label: string | null;
1354
+ status: components["schemas"]["ConnectLinkStatus"];
1355
+ /** @description Unix timestamp in milliseconds. */
1356
+ expires_at: number;
1357
+ };
1358
+ ConnectLink: {
1359
+ id: string;
1360
+ channel_type: string;
1361
+ label: string | null;
1362
+ status: components["schemas"]["ConnectLinkStatus"];
1363
+ /** @description Stable ref of the connection created by consuming this link, or `null`. */
1364
+ consumed_connection_ref: string | null;
1365
+ /** @description Unix timestamp in milliseconds. */
1366
+ expires_at: number;
1367
+ /** @description Unix timestamp in milliseconds. */
1368
+ created_at: number;
1369
+ };
1370
+ ConnectLinkRevokeResult: {
1371
+ id: string;
1372
+ /** @constant */
1373
+ status: "revoked";
1374
+ };
1375
+ ChannelConnection: {
1376
+ id: string;
1377
+ channel_type: string;
1378
+ label: string | null;
1379
+ state: components["schemas"]["ChannelConnectionState"];
1380
+ /** @description Privacy-preserving identity handle (e.g. a masked phone number). */
1381
+ masked_identity: string;
1382
+ /** @description Unix timestamp in milliseconds, or `null` if never active. */
1383
+ last_activity_at: number | null;
1384
+ /** @description Linked helpdesk channel connection ID, or `null`. */
1385
+ helpdesk_connection_id: string | null;
1386
+ };
1387
+ ChannelConnectionDisconnectResult: {
1388
+ id: string;
1389
+ /** @constant */
1390
+ state: "disconnected";
1391
+ };
1224
1392
  ApiResponse_PostCreateResult: components["schemas"]["Envelope_PostCreateResult"];
1225
1393
  ApiResponse_PostDetail: components["schemas"]["Envelope_PostDetail"];
1226
1394
  ApiResponse_Success: components["schemas"]["Envelope_Success"];
@@ -1256,6 +1424,11 @@ interface components {
1256
1424
  ApiResponse_WebhookDeleteResult: components["schemas"]["Envelope_WebhookDeleteResult"];
1257
1425
  ApiResponse_WebhookDeliveryArray: components["schemas"]["Envelope_WebhookDeliveryArray"];
1258
1426
  ApiResponse_WebhookTestResult: components["schemas"]["Envelope_WebhookTestResult"];
1427
+ ApiResponse_ConnectLinkCreateResult: components["schemas"]["Envelope_ConnectLinkCreateResult"];
1428
+ ApiResponse_ConnectLinkArray: components["schemas"]["Envelope_ConnectLinkArray"];
1429
+ ApiResponse_ConnectLinkRevokeResult: components["schemas"]["Envelope_ConnectLinkRevokeResult"];
1430
+ ApiResponse_ChannelConnectionArray: components["schemas"]["Envelope_ChannelConnectionArray"];
1431
+ ApiResponse_ChannelConnectionDisconnectResult: components["schemas"]["Envelope_ChannelConnectionDisconnectResult"];
1259
1432
  PaginatedResponse_Post: {
1260
1433
  data: components["schemas"]["Post"][];
1261
1434
  pagination: components["schemas"]["Pagination"];
@@ -1385,6 +1558,21 @@ interface components {
1385
1558
  Envelope_WebhookTestResult: {
1386
1559
  data: components["schemas"]["WebhookTestResult"];
1387
1560
  };
1561
+ Envelope_ConnectLinkCreateResult: {
1562
+ data: components["schemas"]["ConnectLinkCreateResult"];
1563
+ };
1564
+ Envelope_ConnectLinkArray: {
1565
+ data: components["schemas"]["ConnectLink"][];
1566
+ };
1567
+ Envelope_ConnectLinkRevokeResult: {
1568
+ data: components["schemas"]["ConnectLinkRevokeResult"];
1569
+ };
1570
+ Envelope_ChannelConnectionArray: {
1571
+ data: components["schemas"]["ChannelConnection"][];
1572
+ };
1573
+ Envelope_ChannelConnectionDisconnectResult: {
1574
+ data: components["schemas"]["ChannelConnectionDisconnectResult"];
1575
+ };
1388
1576
  };
1389
1577
  responses: {
1390
1578
  /** @description API error. */
@@ -2524,6 +2712,122 @@ interface operations {
2524
2712
  default: components["responses"]["ApiError"];
2525
2713
  };
2526
2714
  };
2715
+ listChannelConnectLinks: {
2716
+ parameters: {
2717
+ query?: {
2718
+ channel_type?: string;
2719
+ status?: components["schemas"]["ConnectLinkStatus"];
2720
+ };
2721
+ header?: never;
2722
+ path?: never;
2723
+ cookie?: never;
2724
+ };
2725
+ requestBody?: never;
2726
+ responses: {
2727
+ /** @description The workspace's connect links. */
2728
+ 200: {
2729
+ headers: {
2730
+ [name: string]: unknown;
2731
+ };
2732
+ content: {
2733
+ "application/json": components["schemas"]["ApiResponse_ConnectLinkArray"];
2734
+ };
2735
+ };
2736
+ default: components["responses"]["ApiError"];
2737
+ };
2738
+ };
2739
+ createChannelConnectLink: {
2740
+ parameters: {
2741
+ query?: never;
2742
+ header?: never;
2743
+ path?: never;
2744
+ cookie?: never;
2745
+ };
2746
+ requestBody: {
2747
+ content: {
2748
+ "application/json": components["schemas"]["CreateConnectLinkInput"];
2749
+ };
2750
+ };
2751
+ responses: {
2752
+ /** @description Minted connect link, including the one-time `url`. */
2753
+ 201: {
2754
+ headers: {
2755
+ [name: string]: unknown;
2756
+ };
2757
+ content: {
2758
+ "application/json": components["schemas"]["ApiResponse_ConnectLinkCreateResult"];
2759
+ };
2760
+ };
2761
+ default: components["responses"]["ApiError"];
2762
+ };
2763
+ };
2764
+ revokeChannelConnectLink: {
2765
+ parameters: {
2766
+ query?: never;
2767
+ header?: never;
2768
+ path: {
2769
+ id: components["parameters"]["Id"];
2770
+ };
2771
+ cookie?: never;
2772
+ };
2773
+ requestBody?: never;
2774
+ responses: {
2775
+ /** @description Revoke result. */
2776
+ 200: {
2777
+ headers: {
2778
+ [name: string]: unknown;
2779
+ };
2780
+ content: {
2781
+ "application/json": components["schemas"]["ApiResponse_ConnectLinkRevokeResult"];
2782
+ };
2783
+ };
2784
+ default: components["responses"]["ApiError"];
2785
+ };
2786
+ };
2787
+ listChannelConnections: {
2788
+ parameters: {
2789
+ query?: never;
2790
+ header?: never;
2791
+ path?: never;
2792
+ cookie?: never;
2793
+ };
2794
+ requestBody?: never;
2795
+ responses: {
2796
+ /** @description The workspace's channel connections (generic shape). */
2797
+ 200: {
2798
+ headers: {
2799
+ [name: string]: unknown;
2800
+ };
2801
+ content: {
2802
+ "application/json": components["schemas"]["ApiResponse_ChannelConnectionArray"];
2803
+ };
2804
+ };
2805
+ default: components["responses"]["ApiError"];
2806
+ };
2807
+ };
2808
+ disconnectChannelConnection: {
2809
+ parameters: {
2810
+ query?: never;
2811
+ header?: never;
2812
+ path: {
2813
+ id: components["parameters"]["Id"];
2814
+ };
2815
+ cookie?: never;
2816
+ };
2817
+ requestBody?: never;
2818
+ responses: {
2819
+ /** @description Disconnect result. */
2820
+ 200: {
2821
+ headers: {
2822
+ [name: string]: unknown;
2823
+ };
2824
+ content: {
2825
+ "application/json": components["schemas"]["ApiResponse_ChannelConnectionDisconnectResult"];
2826
+ };
2827
+ };
2828
+ default: components["responses"]["ApiError"];
2829
+ };
2830
+ };
2527
2831
  }
2528
2832
 
2529
2833
  export type { $defs, components, operations, paths, webhooks };