{"version":3,"sources":["../../src/types/capabilities.ts","../../src/capability-confirmer.ts","../../src/types/common.ts","../../src/client.ts","../../src/resources/capability-confirmations.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/scan.ts","../../src/resources/webhooks.ts","../../src/resources/workspaces.ts","../../src/webhook-events.ts","../../src/index.ts"],"sourcesContent":["import type { CreateConnectLinkInput } from \"./channels\";\nimport type { CreateReplyInput, UpdateConversationInput } from \"./helpdesk\";\nimport type { CreateWebhookInput, UpdateWebhookInput } from \"./webhooks\";\n\n/**\n * Capability confirmation types.\n *\n * Medal's confirmable write routes require BOTH an `Idempotency-Key` and an\n * `X-Capability-Confirmation` token whenever the calling credential holds the\n * capability scope *directly* — which is the case for every correctly-scoped\n * partner key and OAuth grant. (API keys carrying only legacy scopes are\n * exempt.) The token is minted by `POST /api/v1/capability-confirmations` and\n * is bound to the workspace, the auth subject, the HTTP method + path, the\n * capability's required scopes, and the idempotency key.\n */\n\n/**\n * Confirmable capability ids backing the write routes this SDK exposes.\n *\n * Mirrors the server-side capability registry. Each id maps to exactly one\n * method + path template — see {@link CAPABILITY_ROUTES}.\n */\nexport const CAPABILITY_IDS = [\n  \"channel.connect_link.create.execute\",\n  \"channel.connect_link.revoke.execute\",\n  \"channel.connection.disconnect.execute\",\n  \"helpdesk.conversation.reply.execute\",\n  \"helpdesk.conversation.update.execute\",\n  \"helpdesk.webhook.create.execute\",\n  \"helpdesk.webhook.update.execute\",\n  \"helpdesk.webhook.delete.execute\",\n] as const;\n\n/** A confirmable capability id backing an SDK write route. */\nexport type CapabilityId = (typeof CAPABILITY_IDS)[number];\n\n/** The API route a capability confirms, as registered server-side. */\nexport interface CapabilityRoute {\n  method: \"POST\" | \"PATCH\" | \"DELETE\";\n  /** Path template; `{id}` is filled from `path_params.id`. */\n  path_template: string;\n}\n\n/**\n * Method + path template for each confirmable capability.\n *\n * The server resolves the same mapping from its capability registry — this\n * copy exists so the SDK can build human-readable previews and supply\n * `path_params` without a round trip.\n */\nexport const CAPABILITY_ROUTES: Record<CapabilityId, CapabilityRoute> = {\n  \"channel.connect_link.create.execute\": {\n    method: \"POST\",\n    path_template: \"/api/v1/channels/connect-links\",\n  },\n  \"channel.connect_link.revoke.execute\": {\n    method: \"DELETE\",\n    path_template: \"/api/v1/channels/connect-links/{id}\",\n  },\n  \"channel.connection.disconnect.execute\": {\n    method: \"DELETE\",\n    path_template: \"/api/v1/channels/connections/{id}\",\n  },\n  \"helpdesk.conversation.reply.execute\": {\n    method: \"POST\",\n    path_template: \"/api/v1/helpdesk/replies\",\n  },\n  \"helpdesk.conversation.update.execute\": {\n    method: \"PATCH\",\n    path_template: \"/api/v1/helpdesk/conversations/{id}\",\n  },\n  \"helpdesk.webhook.create.execute\": {\n    method: \"POST\",\n    path_template: \"/api/v1/webhooks\",\n  },\n  \"helpdesk.webhook.update.execute\": {\n    method: \"PATCH\",\n    path_template: \"/api/v1/webhooks/{id}\",\n  },\n  \"helpdesk.webhook.delete.execute\": {\n    method: \"DELETE\",\n    path_template: \"/api/v1/webhooks/{id}\",\n  },\n};\n\n/** Primitive accepted as a capability path parameter value. */\nexport type CapabilityPathParamValue = string | number | boolean;\n\n/** Input for `POST /api/v1/capability-confirmations`. */\nexport interface IssueCapabilityConfirmationInput {\n  /**\n   * Capability to confirm. Unknown ids are rejected with\n   * `CAPABILITY_NOT_FOUND`; read-only or non-confirmable capabilities with\n   * `CAPABILITY_NOT_CONFIRMABLE`.\n   */\n  capability_id: CapabilityId | (string & {});\n  /**\n   * Concrete `/api/v1/...` path the token should be bound to. Optional when\n   * the capability has exactly one API target (all capabilities in\n   * {@link CAPABILITY_ROUTES} do); required when it has several. Must match a\n   * path built from the capability's own templates.\n   */\n  api_path?: string;\n  /** Values for the capability path template's parameters, e.g. `{ id: 'wh_1' }`. */\n  path_params?: Record<string, CapabilityPathParamValue>;\n  /**\n   * The exact `Idempotency-Key` you will send on the confirmed write. The\n   * token is bound to it — a mismatch is rejected. Required for every\n   * capability in {@link CAPABILITY_ROUTES}.\n   */\n  idempotency_key?: string;\n  /**\n   * Human-readable description of the action being approved (1–4000 chars).\n   * This is the text your user saw and approved, and it is retained for audit.\n   */\n  preview_summary: string;\n  /**\n   * Must be `true`.\n   *\n   * **This asserts that a human on your side approved this specific action.**\n   * Do not send it to rubber-stamp unattended writes — it is the audit record\n   * that a person, not a script, authorised the change.\n   */\n  user_approved: true;\n}\n\n/** A minted capability confirmation token. */\nexport interface CapabilityConfirmation {\n  /** Send this as the `X-Capability-Confirmation` header on the write. */\n  confirmation_token: string;\n  token_type: \"medal_capability_confirmation\";\n  capability_id: string;\n  /** HTTP method the token is bound to. */\n  method: string;\n  /** Concrete API path the token is bound to. */\n  path: string;\n  /** Capability scopes the token was minted against. */\n  required_scopes: string[];\n  /** Idempotency key the token is bound to, or `null` if it was minted unbound. */\n  idempotency_key: string | null;\n  /** Lifetime in seconds (60–900). */\n  expires_in: number;\n  /** ISO-8601 expiry timestamp. */\n  expires_at: string;\n  /** Echo of the submitted `preview_summary`. */\n  preview_summary: string;\n}\n\n/**\n * Request body type for each confirmable capability.\n *\n * `undefined` for routes that take no request body (the `DELETE` routes).\n */\nexport interface CapabilityWriteBodies {\n  \"channel.connect_link.create.execute\": CreateConnectLinkInput;\n  \"channel.connect_link.revoke.execute\": undefined;\n  \"channel.connection.disconnect.execute\": undefined;\n  \"helpdesk.conversation.reply.execute\": CreateReplyInput;\n  \"helpdesk.conversation.update.execute\": UpdateConversationInput;\n  \"helpdesk.webhook.create.execute\": CreateWebhookInput;\n  \"helpdesk.webhook.update.execute\": UpdateWebhookInput;\n  \"helpdesk.webhook.delete.execute\": undefined;\n}\n\n/**\n * A capability paired with the request body for that exact route.\n *\n * Modelled as a discriminated union rather than two independent parameters so\n * the pair cannot be decoupled: passing a `helpdesk.conversation.reply.execute`\n * id alongside a webhook payload is a compile error, even when the id's static\n * type is the full {@link CapabilityId} union.\n */\nexport type CapabilityWriteRequest = {\n  [K in CapabilityId]: {\n    /** Capability about to be confirmed. */\n    capabilityId: K;\n    /** The request body of the pending write, or `undefined` for `DELETE` routes. */\n    body: CapabilityWriteBodies[K];\n  };\n}[CapabilityId];\n\n/** Fields common to every {@link AutoConfirmContext} variant. */\ninterface AutoConfirmContextBase {\n  /** HTTP method of the write. */\n  method: string;\n  /** Resolved API path of the write (path params substituted + encoded). */\n  path: string;\n  /** Path parameters used to resolve `path`, if any. */\n  pathParams?: Record<string, CapabilityPathParamValue>;\n  /** Idempotency key that will be bound to the token and sent on the write. */\n  idempotencyKey: string;\n}\n\n/**\n * Context handed to an {@link AutoConfirmOptions.previewSummary} callback.\n *\n * A discriminated union on `capabilityId` — narrow on it to get the exact\n * `body` type for that route:\n *\n * ```ts\n * previewSummary: (ctx) => {\n *   if (ctx.capabilityId === 'helpdesk.conversation.reply.execute') {\n *     // ctx.body is CreateReplyInput here\n *     return `Reply to ${ctx.body.conversation_id}: ${ctx.body.body}`;\n *   }\n *   return `${ctx.method} ${ctx.path}`;\n * }\n * ```\n *\n * `body` is the **exact object you passed to the SDK method**, by reference\n * and unmodified — it is your own payload, so there is nothing to redact and\n * nothing crosses a tenant boundary. Treat it as read-only: mutating it from\n * the callback would change what is actually sent.\n */\nexport type AutoConfirmContext = AutoConfirmContextBase & CapabilityWriteRequest;\n\n/**\n * Opt-in auto-confirmation.\n *\n * When configured, the SDK mints an idempotency key and a confirmation token\n * for you before each confirmable write, then attaches both headers.\n *\n * **This is not a bypass.** Every minted token carries\n * `user_approved: true`, which asserts that *your own user* approved that\n * specific action — the `preview_summary` you return is the audit record of\n * what they approved. Only enable this on a code path where a human really did\n * approve the write. Never wire it into unattended automation.\n */\nexport interface AutoConfirmOptions {\n  /**\n   * Build the `preview_summary` for the pending write. Must return a\n   * non-empty string describing what the user approved; returning blank text\n   * throws instead of asserting an approval that has no description.\n   *\n   * The context includes the pending request `body`, so the summary can name\n   * the specific action rather than the route — narrow on\n   * `context.capabilityId` to get the exact payload type. Prefer a\n   * payload-aware summary: `\"Reply to conv_1: 'Refund issued'\"` is an audit\n   * record, `\"POST /api/v1/helpdesk/replies\"` is not.\n   *\n   * The server caps `preview_summary` at 4000 characters, so summarise the\n   * payload rather than serialising it wholesale.\n   */\n  previewSummary: (context: AutoConfirmContext) => string;\n}\n","import type { RequestOptions } from \"./client\";\nimport type { CapabilityConfirmations } from \"./resources/capability-confirmations\";\nimport type {\n  AutoConfirmOptions,\n  CapabilityPathParamValue,\n  CapabilityWriteRequest,\n} from \"./types/capabilities\";\nimport { CAPABILITY_ROUTES } from \"./types/capabilities\";\n\nfunction newIdempotencyKey(): string {\n  const cryptoRef = globalThis.crypto;\n  if (typeof cryptoRef?.randomUUID === \"function\") {\n    return cryptoRef.randomUUID();\n  }\n  /* v8 ignore next 2 -- fallback for runtimes without WebCrypto randomUUID */\n  return `idem_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 12)}`;\n}\n\nfunction resolvePath(\n  template: string,\n  pathParams?: Record<string, CapabilityPathParamValue>,\n): string {\n  return template.replace(/\\{([^}/]+)\\}/g, (_match, name: string) => {\n    const value = pathParams?.[name];\n    return value === undefined ? `{${name}}` : encodeURIComponent(String(value));\n  });\n}\n\n/**\n * Resolves the `Idempotency-Key` + `X-Capability-Confirmation` pair required\n * by confirmable write routes.\n *\n * Auto-confirmation is OFF unless the integrator opts in — either globally via\n * the `Medal` constructor's `autoConfirmCapabilities`, or per call via\n * `{ autoConfirm: { previewSummary } }`. When it is off this is a pass-through:\n * whatever headers the caller supplied are what gets sent.\n */\nexport class CapabilityConfirmer {\n  constructor(\n    private confirmations: CapabilityConfirmations,\n    private defaults?: AutoConfirmOptions,\n  ) {}\n\n  /**\n   * Return the request options to use for a confirmable write, minting the\n   * idempotency key and confirmation token first when auto-confirm is active.\n   *\n   * `body` is the pending request payload (`undefined` for `DELETE` routes).\n   * It is handed to the `previewSummary` callback by reference so the summary\n   * can describe the specific action, not just the route — it is the caller's\n   * own payload, so it is passed through unmodified and unredacted.\n   */\n  async prepare(\n    request: CapabilityWriteRequest,\n    pathParams?: Record<string, CapabilityPathParamValue>,\n    options?: RequestOptions,\n  ): Promise<RequestOptions | undefined> {\n    const auto =\n      options?.autoConfirm === false ? undefined : (options?.autoConfirm ?? this.defaults);\n    if (!auto) return options;\n    // Nothing to mint — the caller already brought both halves.\n    if (options?.idempotencyKey && options?.capabilityConfirmation) return options;\n\n    const route = CAPABILITY_ROUTES[request.capabilityId];\n    const idempotencyKey = options?.idempotencyKey ?? newIdempotencyKey();\n    const path = resolvePath(route.path_template, pathParams);\n\n    const previewSummary = auto.previewSummary({\n      ...request,\n      method: route.method,\n      path,\n      ...(pathParams ? { pathParams } : {}),\n      idempotencyKey,\n    });\n    if (typeof previewSummary !== \"string\" || previewSummary.trim() === \"\") {\n      throw new Error(\n        `autoConfirm.previewSummary must return a non-empty summary for ${request.capabilityId}. ` +\n          \"The summary is the audit record of what your user approved — refusing to assert \" +\n          \"user_approved: true without one.\",\n      );\n    }\n\n    const { data } = await this.confirmations.create({\n      capability_id: request.capabilityId,\n      ...(pathParams ? { path_params: pathParams } : {}),\n      idempotency_key: idempotencyKey,\n      preview_summary: previewSummary,\n      user_approved: true,\n    });\n\n    return {\n      ...options,\n      idempotencyKey,\n      capabilityConfirmation: data.confirmation_token,\n    };\n  }\n}\n","/** Successful API response wrapper */\nexport interface ApiResponse<T> {\n  data: T;\n}\n\n/** Paginated API response */\nexport interface PaginatedResponse<T> {\n  data: T[];\n  pagination: {\n    has_more: boolean;\n    next_cursor: string | null;\n  };\n}\n\n/** API error thrown by the client */\nexport class MedalApiError extends Error {\n  readonly status: number;\n  readonly code: string;\n  readonly details?: unknown;\n\n  constructor(status: number, code: string, message: string, details?: unknown) {\n    super(message);\n    this.name = \"MedalApiError\";\n    this.status = status;\n    this.code = code;\n    this.details = details;\n  }\n}\n\n/** Pagination options for list endpoints */\nexport interface PaginationOptions {\n  limit?: number;\n  cursor?: string;\n}\n","import type { AutoConfirmOptions } from \"./types/capabilities\";\nimport { 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   * Opt in to (or out of) automatic capability confirmation for this call.\n   *\n   * Supply `{ previewSummary }` to have the SDK mint the idempotency key and\n   * the `X-Capability-Confirmation` token itself; pass `false` to suppress a\n   * client-level `autoConfirmCapabilities` default. Defaults to the client\n   * setting, which itself defaults to OFF.\n   *\n   * Auto-confirmation sends `user_approved: true` on your behalf, asserting\n   * that a human on your side approved this exact action — only use it where\n   * that is true.\n   *\n   * Ignored on routes that do not require a capability confirmation.\n   */\n  autoConfirm?: AutoConfirmOptions | false;\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 {\n  CapabilityConfirmation,\n  IssueCapabilityConfirmationInput,\n} from \"../types/capabilities\";\nimport type { ApiResponse } from \"../types/common\";\n\n/**\n * Mint short-lived capability confirmation tokens.\n *\n * Medal's confirmable write routes (connect links, channel connections,\n * helpdesk replies/updates, webhook endpoint writes) require BOTH an\n * `Idempotency-Key` and an `X-Capability-Confirmation` header when the calling\n * credential holds the capability scope *directly* — which is the case for\n * every correctly-scoped partner key. This resource issues that header value.\n *\n * @example Explicit flow\n * ```ts\n * const idempotencyKey = crypto.randomUUID();\n * const { data: confirmation } = await medal.capabilityConfirmations.create({\n *   capability_id: 'channel.connect_link.create.execute',\n *   idempotency_key: idempotencyKey,\n *   preview_summary: 'Mint a Telegram connect link for Acme Support',\n *   user_approved: true, // a human on your side approved this exact action\n * });\n *\n * await medal.channels.connectLinks.create(\n *   { channel_type: 'telegram_inbox', label: 'Acme Support' },\n *   { idempotencyKey, capabilityConfirmation: confirmation.confirmation_token },\n * );\n * ```\n */\nexport class CapabilityConfirmations {\n  constructor(private client: BaseClient) {}\n\n  /**\n   * Issue a confirmation token for one pending write.\n   *\n   * The token is bound to the workspace, the auth subject, the capability's\n   * method + path, its required scopes, and `idempotency_key` — so it is\n   * usable exactly once, for exactly the write it describes, and expires\n   * within 15 minutes.\n   *\n   * Setting `user_approved: true` asserts that a human on your side approved\n   * this specific action. `preview_summary` is what they approved, and is\n   * retained for audit — write it for a human reader, not a log parser.\n   */\n  async create(\n    input: IssueCapabilityConfirmationInput,\n  ): Promise<ApiResponse<CapabilityConfirmation>> {\n    return this.client.post(\"/api/v1/capability-confirmations\", input);\n  }\n}\n","import { CapabilityConfirmer } from \"../capability-confirmer\";\nimport 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, PaginatedResponse, PaginationOptions } from \"../types/common\";\nimport { CapabilityConfirmations } from \"./capability-confirmations\";\n\n/** Mint, list, and revoke hosted connect links. */\nclass ChannelConnectLinks {\n  constructor(\n    private client: BaseClient,\n    private confirmer: CapabilityConfirmer,\n  ) {}\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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"channel.connect_link.create.execute\", body: input },\n      undefined,\n      options,\n    );\n    return this.client.post(\"/api/v1/channels/connect-links\", input, resolved);\n  }\n\n  /**\n   * List the workspace's connect links (tokens are never returned), newest\n   * first, with cursor-based pagination.\n   *\n   * `limit` defaults to 50 server-side and is capped at 100. Follow\n   * `pagination.next_cursor` while `pagination.has_more` is true.\n   *\n   * The `channel_type` / `status` filters are applied **within** each page,\n   * so a page may hold fewer than `limit` items while `has_more` is still\n   * true — drive the loop off `has_more`, never off the item count.\n   */\n  async list(options?: ListConnectLinksOptions): Promise<PaginatedResponse<ConnectLink>> {\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?.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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"channel.connect_link.revoke.execute\", body: undefined },\n      { id },\n      options,\n    );\n    return this.client.delete(`/api/v1/channels/connect-links/${encodeURIComponent(id)}`, resolved);\n  }\n}\n\n/** List and disconnect the workspace's channel connections. */\nclass ChannelConnections {\n  constructor(\n    private client: BaseClient,\n    private confirmer: CapabilityConfirmer,\n  ) {}\n\n  /**\n   * List the workspace's channel connections (generic, channel-agnostic\n   * shape), newest first, with cursor-based pagination.\n   *\n   * `limit` defaults to 50 server-side and is capped at 100. Follow\n   * `pagination.next_cursor` while `pagination.has_more` is true. Rows that\n   * are not projectable as connections are dropped within the page, so a page\n   * may hold fewer than `limit` items while `has_more` is still true — drive\n   * the loop off `has_more`, never off the item count.\n   */\n  async list(options?: PaginationOptions): Promise<PaginatedResponse<ChannelConnection>> {\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/channels/connections\", params);\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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"channel.connection.disconnect.execute\", body: undefined },\n      { id },\n      options,\n    );\n    return this.client.delete(`/api/v1/channels/connections/${encodeURIComponent(id)}`, resolved);\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, confirmer?: CapabilityConfirmer) {\n    // Direct consumers (`new Channels(client)`) get a confirmer with no\n    // client-level default: auto-confirm stays off unless a call opts in via\n    // `{ autoConfirm: { previewSummary } }`.\n    const resolved = confirmer ?? new CapabilityConfirmer(new CapabilityConfirmations(client));\n    this.connectLinks = new ChannelConnectLinks(client, resolved);\n    this.connections = new ChannelConnections(client, resolved);\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 { CapabilityConfirmer } from \"../capability-confirmer\";\nimport 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\";\nimport { CapabilityConfirmations } from \"./capability-confirmations\";\n\n/** Browse and manage helpdesk conversations. */\nclass HelpdeskConversations {\n  constructor(\n    private client: BaseClient,\n    private confirmer: CapabilityConfirmer,\n  ) {}\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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"helpdesk.conversation.update.execute\", body: input },\n      { id },\n      options,\n    );\n    return this.client.patch(\n      `/api/v1/helpdesk/conversations/${encodeURIComponent(id)}`,\n      input,\n      resolved,\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(\n    private client: BaseClient,\n    private confirmer: CapabilityConfirmer,\n  ) {}\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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"helpdesk.conversation.reply.execute\", body: input },\n      undefined,\n      options,\n    );\n    return this.client.post(\"/api/v1/helpdesk/replies\", input, resolved);\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, confirmer?: CapabilityConfirmer) {\n    // Direct consumers (`new Helpdesk(client)`) get a confirmer with no\n    // client-level default: auto-confirm stays off unless a call opts in via\n    // `{ autoConfirm: { previewSummary } }`.\n    const resolved = confirmer ?? new CapabilityConfirmer(new CapabilityConfirmations(client));\n    this.conversations = new HelpdeskConversations(client, resolved);\n    this.replies = new HelpdeskReplies(client, resolved);\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 } from \"../client\";\nimport type { ApiResponse } from \"../types/common\";\nimport type {\n  ScanCompany,\n  ScanCreateInput,\n  ScanCreateResult,\n  ScanJob,\n  WaitForScanOptions,\n} from \"../types/scan\";\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n/**\n * Company & website scans (Nettsjekk) — score a Norwegian company's web\n * presence (performance, SEO, GDPR consent, AI visibility, mail auth) from a\n * URL, an organisation number, or a company name.\n */\nexport class Scan {\n  constructor(private client: BaseClient) {}\n\n  /**\n   * Queue a scan. Provide exactly one of `url`, `orgnr`, or `name`.\n   * Runs asynchronously — poll with `get()` or use `waitForResult()`.\n   *\n   * @throws Error before any request when zero or several selectors are set —\n   * the server would reject the body anyway; failing locally is clearer.\n   */\n  async create(input: ScanCreateInput): Promise<ApiResponse<ScanCreateResult>> {\n    const entries = ([\"url\", \"orgnr\", \"name\"] as const).filter(\n      (key) => input[key] !== undefined && input[key] !== \"\",\n    );\n    if (entries.length !== 1) {\n      throw new Error(\"scan.create requires exactly one of url, orgnr, or name\");\n    }\n    // Send only the effective selector — blank strings from form state must\n    // not ride along in the payload (they would echo back in ScanJob.input).\n    const key = entries[0];\n    return this.client.post(\"/api/v1/scan\", { [key]: input[key] });\n  }\n\n  /** Get a scan job's status and, once done, its findings payload. */\n  async get(id: string): Promise<ApiResponse<ScanJob>> {\n    return this.client.get(`/api/v1/scan/${encodeURIComponent(id)}`);\n  }\n\n  /** Search the Norwegian company registry by name (typeahead, top 5 hits). */\n  async companies(q: string): Promise<ApiResponse<ScanCompany[]>> {\n    return this.client.get(\"/api/v1/scan/companies\", { q });\n  }\n\n  /**\n   * Poll a scan until it settles. Resolves with the job for both `done` and\n   * `failed` (check `job.error`); throws only when the deadline passes while\n   * the scan is still pending/running.\n   */\n  async waitForResult(id: string, options: WaitForScanOptions = {}): Promise<ScanJob> {\n    const rawInterval = options.intervalMs ?? 2500;\n    const rawTimeout = options.timeoutMs ?? 120_000;\n    // Guard against NaN — it would disable the deadline and poll forever.\n    // An explicit zero/negative timeout is preserved: one poll, then timeout\n    // (callers passing an exhausted outer budget expect immediate expiry).\n    const intervalMs = Number.isFinite(rawInterval) && rawInterval > 0 ? rawInterval : 2500;\n    const timeoutMs = Number.isFinite(rawTimeout) ? rawTimeout : 120_000;\n    const deadline = Date.now() + timeoutMs;\n    let lastStatus = \"pending\";\n    for (;;) {\n      const { data } = await this.get(id);\n      if (data.status === \"done\" || data.status === \"failed\") return data;\n      lastStatus = data.status;\n      // Sleep only up to the remaining budget, and re-check the deadline\n      // after sleeping so no extra poll is issued once time is up.\n      const remaining = deadline - Date.now();\n      if (remaining <= 0) break;\n      await sleep(Math.min(intervalMs, remaining));\n      if (Date.now() >= deadline) break;\n    }\n    throw new Error(`Scan ${id} timed out after ${timeoutMs}ms (status: ${lastStatus})`);\n  }\n}\n","import { CapabilityConfirmer } from \"../capability-confirmer\";\nimport 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\";\nimport { CapabilityConfirmations } from \"./capability-confirmations\";\n\n/** Manage webhook endpoints and inspect their deliveries. */\nexport class Webhooks {\n  private confirmer: CapabilityConfirmer;\n\n  constructor(\n    private client: BaseClient,\n    confirmer?: CapabilityConfirmer,\n  ) {\n    // Direct consumers (`new Webhooks(client)`) get a confirmer with no\n    // client-level default: auto-confirm stays off unless a call opts in via\n    // `{ autoConfirm: { previewSummary } }`.\n    this.confirmer = confirmer ?? new CapabilityConfirmer(new CapabilityConfirmations(client));\n  }\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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"helpdesk.webhook.create.execute\", body: input },\n      undefined,\n      options,\n    );\n    return this.client.post(\"/api/v1/webhooks\", input, resolved);\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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"helpdesk.webhook.update.execute\", body: input },\n      { id },\n      options,\n    );\n    return this.client.patch(`/api/v1/webhooks/${encodeURIComponent(id)}`, input, resolved);\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    const resolved = await this.confirmer.prepare(\n      { capabilityId: \"helpdesk.webhook.delete.execute\", body: undefined },\n      { id },\n      options,\n    );\n    return this.client.delete(`/api/v1/webhooks/${encodeURIComponent(id)}`, resolved);\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 { CapabilityConfirmer } from \"./capability-confirmer\";\nimport { BaseClient } from \"./client\";\nimport { CapabilityConfirmations } from \"./resources/capability-confirmations\";\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 { Scan } from \"./resources/scan\";\nimport { Webhooks } from \"./resources/webhooks\";\nimport { Workspaces } from \"./resources/workspaces\";\nimport type { AutoConfirmOptions } from \"./types/capabilities\";\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   * Opt in to automatic capability confirmation for confirmable writes.\n   * **Defaults to OFF.**\n   *\n   * Medal's confirmable write routes (connect links, channel connections,\n   * helpdesk replies/updates, webhook endpoint writes) require BOTH an\n   * `Idempotency-Key` and an `X-Capability-Confirmation` token whenever the\n   * credential holds the capability scope directly — which is the case for\n   * every correctly-scoped partner key. With this option set, the SDK mints\n   * both for you before each such write instead of making you hand-roll\n   * `POST /api/v1/capability-confirmations`.\n   *\n   * **Read before enabling:** each minted token carries `user_approved: true`,\n   * which asserts to Medal that *a human on your side approved that specific\n   * action*, and the `previewSummary` you return is retained as the audit\n   * record of what they approved. Enable it only on code paths where that is\n   * genuinely true — never to rubber-stamp unattended writes. Pass\n   * `{ autoConfirm: false }` on an individual call to opt out again, or use\n   * `medal.capabilityConfirmations.create(...)` for full manual control.\n   *\n   * @example\n   * ```ts\n   * const medal = new Medal('medal_xxx', {\n   *   autoConfirmCapabilities: {\n   *     previewSummary: (ctx) =>\n   *       `${operator.email} approved ${ctx.method} ${ctx.path}`,\n   *   },\n   * });\n   * ```\n   */\n  autoConfirmCapabilities?: AutoConfirmOptions;\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 capabilityConfirmations: CapabilityConfirmations;\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 scan: Scan;\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.capabilityConfirmations = new CapabilityConfirmations(client);\n    const confirmer = new CapabilityConfirmer(\n      this.capabilityConfirmations,\n      options?.autoConfirmCapabilities,\n    );\n\n    this.channels = new Channels(client, confirmer);\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, confirmer);\n    this.posts = new Posts(client);\n    this.scan = new Scan(client);\n    this.webhooks = new Webhooks(client, confirmer);\n    this.workspaces = new Workspaces(client);\n  }\n}\n\nexport { CapabilityConfirmer } from \"./capability-confirmer\";\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 { CapabilityConfirmations } from \"./resources/capability-confirmations\";\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 { Scan } from \"./resources/scan\";\nexport { Webhooks } from \"./resources/webhooks\";\nexport { Workspaces } from \"./resources/workspaces\";\nexport type {\n  AutoConfirmContext,\n  AutoConfirmOptions,\n  CapabilityConfirmation,\n  CapabilityId,\n  CapabilityPathParamValue,\n  CapabilityRoute,\n  CapabilityWriteBodies,\n  CapabilityWriteRequest,\n  IssueCapabilityConfirmationInput,\n} from \"./types/capabilities\";\nexport { CAPABILITY_IDS, CAPABILITY_ROUTES } from \"./types/capabilities\";\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  MessageDeliveryStatus,\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  ScanCompany,\n  ScanCreateInput,\n  ScanCreateResult,\n  ScanJob,\n  ScanResultPayload,\n  ScanStatus,\n  ScanSubScores,\n  WaitForScanOptions,\n} from \"./types/scan\";\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":["key"]}