@medalsocial/sdk 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/dist/openapi/medal-social.openapi.json +686 -0
- package/dist/pilot/index.d.mts +2 -2
- package/dist/pilot/index.d.ts +2 -2
- package/dist/src/index.d.mts +230 -3
- package/dist/src/index.d.ts +230 -3
- package/dist/src/index.js +144 -19
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.mjs +143 -19
- package/dist/src/index.mjs.map +1 -1
- package/dist/src/openapi.generated.d.mts +473 -0
- package/dist/src/openapi.generated.d.ts +473 -0
- package/dist/src/openapi.generated.js.map +1 -1
- package/openapi/medal-social.openapi.yaml +446 -0
- package/package.json +1 -1
- package/skills/client/SKILL.md +2 -0
- package/skills/resources/SKILL.md +50 -2
package/dist/src/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types/common.ts","../../src/client.ts","../../src/types/capabilities.ts","../../src/capability-confirmer.ts","../../src/resources/bookings.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":["/** 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 * A fresh idempotency key for one logical write.\n *\n * `crypto.randomUUID` is gated to secure contexts in browsers, so a page\n * served over http:// has `crypto` but not `randomUUID`. `getRandomValues` is\n * available in every context, so fall back to assembling a v4 UUID by hand\n * rather than letting a write go out unkeyed — an unkeyed write is exactly the\n * one a retry can duplicate.\n */\nfunction randomIdempotencyKey(): string {\n const webCrypto = globalThis.crypto;\n if (typeof webCrypto.randomUUID === \"function\") {\n return webCrypto.randomUUID();\n }\n\n const bytes = new Uint8Array(16);\n webCrypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * The `Idempotency-Key` one logical write goes out under: the caller's if they\n * supplied a usable one, otherwise a fresh key.\n *\n * A blank key counts as NO key. `??` alone would treat `\"\"` as supplied,\n * {@link BaseClient} would then drop the falsy value, and the write would go\n * out with no header at all — silently unprotected, which is the one failure\n * this exists to rule out. Whitespace-only is the same hazard by a different\n * route: header values are stripped in transit, so `\" \"` reaches the server\n * as `\"\"` and is ignored there too. Both are reachable from an ordinary\n * `idempotencyKey: someVar` where the variable happens to be blank.\n *\n * Every part of the SDK that decides which key a write carries resolves it\n * here, so those parts cannot disagree. A capability confirmation is bound to\n * its idempotency key: bind one value, send another, and the server rejects a\n * write both sides believed they had authorized.\n */\nexport function resolveIdempotencyKey(supplied?: string): string {\n return (supplied ?? \"\").trim() || randomIdempotencyKey();\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 /**\n * Execute a POST that must never execute twice, guaranteeing an\n * `Idempotency-Key`.\n *\n * {@link BaseClient.post} retries 429 and 5xx automatically, so a write\n * whose transaction committed before the gateway failed would otherwise be\n * submitted a second time — booking the same slot twice. A key turns that\n * retry into a replay: the server keys on the key, the workspace, and the\n * method+path, and answers a repeat with the stored response, or 409 while\n * the first attempt is still in flight. Either way the write happens once.\n *\n * The key is minted ONCE here, outside the retry loop in `request`, so every\n * attempt of the same logical call carries the same value — a key minted per\n * attempt would deduplicate nothing. A caller-supplied key always wins, so\n * callers keeping their own records stay in control. See\n * {@link resolveIdempotencyKey} for what counts as supplied.\n */\n async postOnce<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.post(path, body, {\n ...options,\n idempotencyKey: resolveIdempotencyKey(options?.idempotencyKey),\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 // Armed across the body read, not just the fetch. `fetch` settles as soon\n // as the response HEADERS arrive, so a timer cleared there bounded only\n // time-to-headers: a server that sent headers and then stalled mid-body\n // left the reads below waiting forever, with no deadline of any kind.\n // Holding the signal until the body is in hand makes `timeout` mean what\n // it says — a budget for the whole exchange, per attempt.\n const timeout = setTimeout(() => controller.abort(), this.config.timeout);\n\n let res: Response;\n let text = \"\";\n let retrying = false;\n try {\n res = await fetch(url, { ...init, headers, signal: controller.signal });\n\n // Retry on 429 / 5xx (but not on the final attempt)\n retrying =\n (res.status === 429 || (res.status >= 500 && res.status <= 599)) && attempt < maxAttempts;\n\n if (retrying) {\n // Release the response we are about to abandon. Until a body is\n // consumed, undici holds its socket out of the connection pool, so a\n // retry storm burns a fresh connection per attempt — exactly when the\n // server can least afford it.\n //\n // Consuming returns the socket to the pool. `res.body?.cancel()` frees\n // it too, but by destroying the connection rather than reusing it,\n // which is the churn this exists to avoid. Pipe to a sink rather than\n // `res.text()`: an error body can be arbitrarily large, and buffering\n // one into a string only to discard it costs several times its size in\n // memory on every attempt of every in-flight request.\n //\n // Fall back to `text()` where there is no stream to pipe: a bodyless\n // response, or a runtime that exposes `text()` but not `body`.\n const drained = res.body ? res.body.pipeTo(new WritableStream()) : res.text();\n\n // A read that fails — including one the deadline above aborts — has\n // already released the socket, so a failure here is not worth\n // propagating over the status we are retrying on.\n await drained.catch(() => {});\n } else {\n text = await res.text();\n }\n } finally {\n clearTimeout(timeout);\n }\n\n if (retrying) {\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 // Outside the deadline above: the backoff is time we choose to wait,\n // not time we are waiting on the server.\n await new Promise((r) => setTimeout(r, delayMs));\n continue;\n }\n\n // Parse response\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 { 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 { resolveIdempotencyKey } 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 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\n // Resolve the key the SAME way the write itself will, so the value bound\n // into the token is the value that reaches the server. A blank key resolves\n // to a fresh one here exactly as it would at POST time — binding the blank\n // and sending the replacement would produce a token the server refuses.\n const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);\n const callerKeyIsUsable = idempotencyKey === options?.idempotencyKey;\n\n // Nothing to mint — the caller already brought both halves. A blank key is\n // not a half: the token paired with it was bound to a value the server can\n // never receive, so re-mint rather than send a doomed pair.\n if (callerKeyIsUsable && options?.capabilityConfirmation) return options;\n\n const route = CAPABILITY_ROUTES[request.capabilityId];\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","import type { BaseClient, RequestOptions } from \"../client\";\nimport type {\n Booking,\n BookingActionResult,\n BookingAvailabilityOptions,\n BookingCreateResult,\n BookingRescheduleResult,\n BookingResource,\n BookingScheduleDay,\n BookingScheduleOptions,\n BookingService,\n BookingSlot,\n BookingsPage,\n CancelBookingInput,\n CreateBookingInput,\n ListBookingServicesOptions,\n ListBookingsOptions,\n ManageSummary,\n RescheduleBookingInput,\n UpdateBookingInput,\n} from \"../types/bookings\";\nimport type { ApiResponse } from \"../types/common\";\n\n/**\n * Customer-side booking management, addressed by the show-once manage token\n * from `bookings.create(...)` rather than by booking id.\n *\n * These are NOT the staff routes with a different lookup key: possession of\n * the token is the customer's own authorization, so the workspace's cancel and\n * reschedule windows are ENFORCED here (they are bypassed on\n * `bookings.cancel` / `bookings.reschedule`), and a cancel is attributed to\n * the customer rather than to staff. Relay a customer's click on their\n * confirmation-email link through these; act as the business through the\n * id-addressed methods.\n */\nclass BookingsManage {\n constructor(private client: BaseClient) {}\n\n /**\n * Read what the holder of a manage token may see and do. Honour\n * `can_cancel` / `can_reschedule` — they already apply the policy windows.\n */\n async get(token: string): Promise<ApiResponse<ManageSummary>> {\n return this.client.get(`/api/v1/bookings/manage/${encodeURIComponent(token)}`);\n }\n\n /** Cancel on the customer's behalf. Rejected outside the cancel window. */\n async cancel(\n token: string,\n input?: CancelBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingActionResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/manage/${encodeURIComponent(token)}/cancel`,\n input ?? {},\n options,\n );\n }\n\n /**\n * Move the booking on the customer's behalf. Rejected outside the reschedule\n * window. Returns a NEW booking id and a new manage token — the old token\n * stops working, so relay the new one into whatever link you send next.\n */\n async reschedule(\n token: string,\n input: RescheduleBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingRescheduleResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/manage/${encodeURIComponent(token)}/reschedule`,\n input,\n options,\n );\n }\n}\n\n/**\n * Appointment bookings: the service catalogue, free slots, and the bookings\n * themselves.\n *\n * Every method here acts as the BUSINESS — policy windows are bypassed and a\n * cancel is recorded against staff. To relay a customer's own action on their\n * confirmation-email link, use {@link Bookings.manage} instead.\n *\n * Money is always integer øre (`amount_ore`, `price_ore`). Timestamps come\n * back as ISO 8601 strings; on the way in, either Unix milliseconds or an ISO\n * string is accepted.\n *\n * @example\n * ```ts\n * const { data: slots } = await medal.bookings.availability({\n * service_id: \"svc_1\",\n * from_ts: Date.now(),\n * to_ts: Date.now() + 7 * 86_400_000,\n * });\n * const { data } = await medal.bookings.create({\n * items: [{ service_id: \"svc_1\", start_ts: slots[0].start_ts! }],\n * contact: { phone: \"+4790000000\", name: \"Ida\" },\n * });\n * ```\n */\nexport class Bookings {\n /** Customer-side actions addressed by manage token. */\n readonly manage: BookingsManage;\n\n constructor(private client: BaseClient) {\n this.manage = new BookingsManage(client);\n }\n\n /** List the bookable service catalogue. Active-only unless asked otherwise. */\n async listServices(options?: ListBookingServicesOptions): Promise<ApiResponse<BookingService[]>> {\n const params: Record<string, string | undefined> = {};\n if (options?.include_inactive !== undefined) {\n params.include_inactive = String(options.include_inactive);\n }\n return this.client.get(\"/api/v1/bookings/services\", params);\n }\n\n /** List the bookable resources — staff, rooms, and equipment. */\n async listResources(): Promise<ApiResponse<BookingResource[]>> {\n return this.client.get(\"/api/v1/bookings/resources\");\n }\n\n /**\n * List free slots for a service over a window. Slots reflect opening hours,\n * time off, buffers, and existing bookings at the moment of the call — they\n * are not held, so a slot can be taken before you book it.\n */\n async availability(options: BookingAvailabilityOptions): Promise<ApiResponse<BookingSlot[]>> {\n const params: Record<string, string | undefined> = {\n service_id: options.service_id,\n from_ts: String(options.from_ts),\n to_ts: String(options.to_ts),\n };\n if (options.resource_id) params.resource_id = options.resource_id;\n return this.client.get(\"/api/v1/bookings/availability\", params);\n }\n\n /**\n * The dates a service can be booked on — the half `availability` cannot\n * answer. Availability returns free slots and nothing else, so a closed day,\n * an evening past closing and a fully booked day are all the same empty\n * array. A date absent from this list is closed; on a listed date, compare\n * `last_start_ts` against the clock to tell \"too late today\" from \"full\".\n */\n async schedule(options: BookingScheduleOptions): Promise<ApiResponse<BookingScheduleDay[]>> {\n const params: Record<string, string | undefined> = {\n service_id: options.service_id,\n from_ts: String(options.from_ts),\n to_ts: String(options.to_ts),\n };\n if (options.resource_id) params.resource_id = options.resource_id;\n return this.client.get(\"/api/v1/bookings/schedule\", params);\n }\n\n /**\n * List bookings with cursor-based pagination and optional filters.\n *\n * Check `pagination.truncated`: when true the read window was clipped and\n * matching bookings exist that no cursor reaches — narrow `from_ts`/`to_ts`.\n */\n async list(options?: ListBookingsOptions): Promise<BookingsPage> {\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?.resource_id) params.resource_id = options.resource_id;\n if (options?.from_ts !== undefined) params.from_ts = String(options.from_ts);\n if (options?.to_ts !== undefined) params.to_ts = String(options.to_ts);\n return this.client.get(\"/api/v1/bookings\", params);\n }\n\n /**\n * Book a party — every item succeeds or none do (max 50).\n *\n * Each created booking comes back with a `manage_token` exactly once; only\n * its hash is stored, so persist it if you need the customer's manage link.\n *\n * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than book the slot twice. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too — the\n * server keys on it for 24 hours, so re-sending the same key after a network\n * timeout returns the original bookings instead of a second set.\n */\n async create(\n input: CreateBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingCreateResult>> {\n return this.client.postOnce(\"/api/v1/bookings\", input, options);\n }\n\n /** Get a booking by ID. */\n async get(id: string): Promise<ApiResponse<Booking>> {\n return this.client.get(`/api/v1/bookings/${encodeURIComponent(id)}`);\n }\n\n /**\n * Annotate a booking. At least one of `notes` (customer-visible) or\n * `internal_notes` (staff-only) is required; `\"\"` clears a field.\n */\n async update(\n id: string,\n input: UpdateBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<Booking>> {\n return this.client.patch(`/api/v1/bookings/${encodeURIComponent(id)}`, input, options);\n }\n\n /** Cancel as the business — the cancel window is bypassed. */\n async cancel(\n id: string,\n input?: CancelBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingActionResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/${encodeURIComponent(id)}/cancel`,\n input ?? {},\n options,\n );\n }\n\n /**\n * Move a booking as the business — the reschedule window is bypassed.\n * Returns a NEW booking id and a new manage token; the old booking is\n * cancelled and its token stops working.\n */\n async reschedule(\n id: string,\n input: RescheduleBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingRescheduleResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/${encodeURIComponent(id)}/reschedule`,\n input,\n options,\n );\n }\n\n /** Mark a booking as a no-show. */\n async markNoShow(\n id: string,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingActionResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/${encodeURIComponent(id)}/no-show`,\n undefined,\n options,\n );\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 * Deliberately unkeyed, unlike the writes it authorizes. Minting is not the\n * state change the guarantee exists to protect: the write itself is already\n * bound to `idempotency_key`, so a retry that mints a second token cannot\n * produce a second write. Keying this call would instead park a credential\n * designed to expire in 15 minutes inside a replay cache that answers for 24\n * hours — a worse trade than the duplicate token it would avoid.\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 * Automatically idempotent: an unkeyed retry mints a SECOND live single-use\n * link for the same person, and only one of the two can ever be consumed —\n * the other stays outstanding until it is revoked or expires. The key the\n * confirmer chose is the key that goes out — a capability confirmation is\n * bound to its idempotency key, so minting a fresh one here would invalidate\n * the confirmation.\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.postOnce(\"/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, RequestOptions } 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 /**\n * Create a new contact. Email must be unique in the workspace.\n *\n * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than run the create a second time. Uniqueness\n * alone would not save you here — it turns the retry of a committed create\n * into a spurious conflict, which reads as \"the contact was not created\".\n * Supply `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async create(\n input: CreateContactInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ContactCreateResult>> {\n return this.client.postOnce(\"/api/v1/contacts\", input, options);\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 /**\n * Add a note to a contact's timeline.\n *\n * Automatically idempotent: nothing about a note is unique, so an unkeyed\n * retry appends the same text to the timeline twice. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async addNote(\n id: string,\n input: AddNoteInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ContactNoteResult>> {\n return this.client.postOnce(`/api/v1/contacts/${encodeURIComponent(id)}/notes`, input, options);\n }\n\n /**\n * Bulk import contacts (max 500). Duplicates are skipped.\n *\n * Automatically idempotent: the import is processed in chunks, so a retry\n * after a partial failure re-walks the whole batch and reports `added` /\n * `skipped` counts for a run that was not the first. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async import(\n contacts: ImportContactInput[],\n options?: RequestOptions,\n ): Promise<ApiResponse<ImportContactsResult>> {\n return this.client.postOnce(\"/api/v1/contacts/import\", { contacts }, options);\n }\n}\n","import type { BaseClient, RequestOptions } 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 /**\n * Create a new deal.\n *\n * Automatically idempotent: nothing about a deal is unique, so an unkeyed\n * retry puts a second identical deal in the pipeline. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async create(\n input: CreateDealInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<DealCreateResult>> {\n return this.client.postOnce(\"/api/v1/deals\", input, options);\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, RequestOptions } 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 * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than queue a second copy into someone's inbox —\n * a send that already committed cannot be un-sent. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n *\n * `input.idempotency_key` is the older, body-level form of the same control\n * and still takes precedence server-side, so setting it keeps working\n * unchanged.\n */\n async send(\n input: SendEmailInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<EmailSendResult>> {\n return this.client.postOnce(\"/api/v1/emails\", input, options);\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 * Automatically idempotent — and this is the call where it matters most: an\n * unkeyed retry of a batch that already committed sends up to 100 duplicate\n * emails. Supply `options.idempotencyKey` to deduplicate across your OWN\n * retries too.\n */\n async batch(\n input: BatchSendInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BatchSendSummary>> {\n return this.client.postOnce(\"/api/v1/emails/batch\", input, options);\n }\n}\n","import type { BaseClient, RequestOptions } 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 /**\n * Request a workspace data export. Runs asynchronously.\n *\n * Automatically idempotent: the request is recorded and the export is\n * scheduled in one step with no de-duplication of its own, so an unkeyed\n * retry files a second subject-access request and runs a second full export\n * of the workspace. Supply `options.idempotencyKey` to deduplicate across\n * your OWN retries too.\n */\n async requestExport(\n options?: RequestOptions,\n ): Promise<ApiResponse<{ request_id: string; status: string }>> {\n return this.client.postOnce(\"/api/v1/gdpr/export\", undefined, options);\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 /**\n * Record a GDPR consent decision for a contact by email.\n *\n * Deliberately unkeyed: a decision is stored once per\n * (workspace, email, consent type) and overwritten in place, so re-sending\n * the same body reaches the same state and returns the same record id.\n */\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 /**\n * Record cookie consent from an external site (legacy endpoint).\n *\n * Deliberately unkeyed: this legacy route predates the versioned API and\n * does not run the `Idempotency-Key` machinery, so a key here would be a\n * header that changes nothing while implying a guarantee the endpoint cannot\n * make. Treat a failed call as \"unknown\" and re-send only if a missing\n * consent log matters more to you than a duplicate one.\n */\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 * Automatically idempotent: a reply is a message to a real person, and an\n * unkeyed retry sends it to them twice. The key the confirmer chose is the\n * key that goes out — a capability confirmation is bound to its idempotency\n * key, so minting a fresh one here would invalidate the confirmation.\n *\n * Pass `options.idempotencyKey` to deduplicate across your OWN retries too.\n * It is REQUIRED for capability-scoped tokens, which need it paired with a\n * `capabilityConfirmation` — a generated key satisfies the pairing's key\n * half only; the confirmation is still yours to supply (or to let\n * `autoConfirm` mint).\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.postOnce(\"/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, RequestOptions } 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 /**\n * Create a new post with content and target channels.\n *\n * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than draft the post twice. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async create(\n input: CreatePostInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<{ id: string }>> {\n return this.client.postOnce(\"/api/v1/posts\", input, options);\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 /**\n * Schedule a post for future publication.\n *\n * Deliberately unkeyed: re-sending the same `scheduled_at` for an\n * already-scheduled post returns the original `workflow_id` rather than\n * starting a second one, so a retried schedule cannot double-publish. A\n * *different* time is rejected — unschedule first.\n */\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 /**\n * Publish a post immediately to all target channels.\n *\n * Deliberately unkeyed: publishing moves the post out of the set of statuses\n * that may be published, so the retry of a publish that already committed is\n * refused rather than posting a second time. It is refused with a 400 though\n * — treat an error here as \"check the post's status\", not as \"nothing\n * happened\".\n */\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 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 * Automatically idempotent: a scan job is queued the moment it is created,\n * so an unkeyed retry starts a second crawl of the same site and returns an\n * id for a job that duplicates one already running. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\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(\n input: ScanCreateInput,\n options?: RequestOptions,\n ): 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.postOnce(\"/api/v1/scan\", { [key]: input[key] }, options);\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 * Automatically idempotent: a duplicate endpoint is not a stray row, it is a\n * second copy of every future delivery to the same URL, forever. The key the\n * confirmer chose is the key that goes out — a capability confirmation is\n * bound to its idempotency key, so minting a fresh one here would invalidate\n * the confirmation. That the SDK now always sends a key is also what makes\n * the replay-without-secret case above reachable on a plain 5xx retry.\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.postOnce(\"/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 /**\n * Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202.\n *\n * Deliberately unkeyed: a duplicate ping is the one duplicate that costs\n * nothing. Real deliveries are retried too, so any endpoint worth pointing at\n * already tolerates receiving the same event twice — that is what this call\n * exists to prove.\n */\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, bookings, GDPR\n * compliance, helpdesk conversations, partner channel connect, webhooks, and\n * 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 { CapabilityConfirmer } from \"./capability-confirmer\";\nimport { BaseClient } from \"./client\";\nimport { Bookings } from \"./resources/bookings\";\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 * // Bookings — free slots, then book a party (money is integer øre)\n * const { data: slots } = await medal.bookings.availability({\n * service_id: 'svc_1',\n * from_ts: Date.now(),\n * to_ts: Date.now() + 7 * 86_400_000,\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 bookings: Bookings;\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.bookings = new Bookings(client);\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 { Bookings } from \"./resources/bookings\";\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 Booking,\n BookingActionResult,\n BookingAvailabilityOptions,\n BookingCancelledBy,\n BookingClaimableCreatedVia,\n BookingContactInput,\n BookingCreatedVia,\n BookingCreateResult,\n BookingPaymentStatus,\n BookingRescheduleResult,\n BookingResource,\n BookingResourceType,\n BookingScheduleDay,\n BookingScheduleOptions,\n BookingService,\n BookingSlot,\n BookingStatus,\n BookingsPage,\n BookingsPagination,\n BookingTimestampInput,\n CancelBookingInput,\n CreateBookingInput,\n CreateBookingItemInput,\n CreatedBooking,\n ListBookingServicesOptions,\n ListBookingsOptions,\n ManageSummary,\n RescheduleBookingInput,\n UpdateBookingInput,\n} from \"./types/bookings\";\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"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types/common.ts","../../src/client.ts","../../src/types/capabilities.ts","../../src/capability-confirmer.ts","../../src/resources/bookings.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/portal.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":["/** 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. `headers` applies to every verb; the named options only matter on writes. */\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 * Set to `false` to send the request exactly once — no automatic retry on\n * 429/5xx. Use it for writes whose FIRST attempt may have succeeded even\n * though the response was lost: a one-time code that is burned on use, a\n * logout or erasure that revokes the very credential a retry would present.\n * A retry there does not repeat the operation, it misreports it as failed.\n * Defaults to `true`.\n */\n retry?: boolean;\n /**\n * Extra request headers, e.g. `x-portal-session` for the customer portal.\n * Applied first: the named options (`idempotencyKey`,\n * `capabilityConfirmation`) win over a same-named entry here, so a bag can\n * never smuggle in a key the SDK did not resolve.\n */\n headers?: Record<string, string>;\n}\n\n/**\n * A fresh idempotency key for one logical write.\n *\n * `crypto.randomUUID` is gated to secure contexts in browsers, so a page\n * served over http:// has `crypto` but not `randomUUID`. `getRandomValues` is\n * available in every context, so fall back to assembling a v4 UUID by hand\n * rather than letting a write go out unkeyed — an unkeyed write is exactly the\n * one a retry can duplicate.\n */\nfunction randomIdempotencyKey(): string {\n const webCrypto = globalThis.crypto;\n if (typeof webCrypto.randomUUID === \"function\") {\n return webCrypto.randomUUID();\n }\n\n const bytes = new Uint8Array(16);\n webCrypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * The `Idempotency-Key` one logical write goes out under: the caller's if they\n * supplied a usable one, otherwise a fresh key.\n *\n * A blank key counts as NO key. `??` alone would treat `\"\"` as supplied,\n * {@link BaseClient} would then drop the falsy value, and the write would go\n * out with no header at all — silently unprotected, which is the one failure\n * this exists to rule out. Whitespace-only is the same hazard by a different\n * route: header values are stripped in transit, so `\" \"` reaches the server\n * as `\"\"` and is ignored there too. Both are reachable from an ordinary\n * `idempotencyKey: someVar` where the variable happens to be blank.\n *\n * Every part of the SDK that decides which key a write carries resolves it\n * here, so those parts cannot disagree. A capability confirmation is bound to\n * its idempotency key: bind one value, send another, and the server rejects a\n * write both sides believed they had authorized.\n */\nexport function resolveIdempotencyKey(supplied?: string): string {\n return (supplied ?? \"\").trim() || randomIdempotencyKey();\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>(\n path: string,\n params?: Record<string, string | undefined>,\n options?: Pick<RequestOptions, \"headers\">,\n ): Promise<T> {\n const url = this.buildUrl(path, params);\n return this.request<T>(url, { method: \"GET\", headers: options?.headers });\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>(\n this.buildUrl(path),\n {\n method: \"POST\",\n headers: this.writeHeaders(options),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n },\n options?.retry,\n );\n }\n\n /**\n * Execute a POST that must never execute twice, guaranteeing an\n * `Idempotency-Key`.\n *\n * {@link BaseClient.post} retries 429 and 5xx automatically, so a write\n * whose transaction committed before the gateway failed would otherwise be\n * submitted a second time — booking the same slot twice. A key turns that\n * retry into a replay: the server keys on the key, the workspace, and the\n * method+path, and answers a repeat with the stored response, or 409 while\n * the first attempt is still in flight. Either way the write happens once.\n *\n * The key is minted ONCE here, outside the retry loop in `request`, so every\n * attempt of the same logical call carries the same value — a key minted per\n * attempt would deduplicate nothing. A caller-supplied key always wins, so\n * callers keeping their own records stay in control. See\n * {@link resolveIdempotencyKey} for what counts as supplied.\n */\n async postOnce<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.post(path, body, {\n ...options,\n idempotencyKey: resolveIdempotencyKey(options?.idempotencyKey),\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>(\n this.buildUrl(path),\n {\n method: \"PATCH\",\n headers: this.writeHeaders(options),\n body: JSON.stringify(body),\n },\n options?.retry,\n );\n }\n\n /** Execute an authenticated DELETE request. */\n async delete<T>(path: string, options?: RequestOptions): Promise<T> {\n return this.request<T>(\n this.buildUrl(path),\n {\n method: \"DELETE\",\n headers: this.writeHeaders(options),\n },\n options?.retry,\n );\n }\n\n private writeHeaders(options?: RequestOptions): Record<string, string> {\n // Lower-case the bag's keys first. A plain object is case-sensitive but\n // `Headers` is not: `{ \"Content-Type\": \"text/plain\", \"content-type\":\n // \"application/json\" }` would reach the wire as BOTH values joined, so a\n // capitalised bag entry could smuggle past the protected names below.\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(options?.headers ?? {})) {\n headers[key.toLowerCase()] = value;\n }\n headers[\"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, retry = true): Promise<T> {\n const maxAttempts = retry ? 3 : 1;\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 // Armed across the body read, not just the fetch. `fetch` settles as soon\n // as the response HEADERS arrive, so a timer cleared there bounded only\n // time-to-headers: a server that sent headers and then stalled mid-body\n // left the reads below waiting forever, with no deadline of any kind.\n // Holding the signal until the body is in hand makes `timeout` mean what\n // it says — a budget for the whole exchange, per attempt.\n const timeout = setTimeout(() => controller.abort(), this.config.timeout);\n\n let res: Response;\n let text = \"\";\n let retrying = false;\n try {\n res = await fetch(url, { ...init, headers, signal: controller.signal });\n\n // Retry on 429 / 5xx (but not on the final attempt)\n retrying =\n (res.status === 429 || (res.status >= 500 && res.status <= 599)) && attempt < maxAttempts;\n\n if (retrying) {\n // Release the response we are about to abandon. Until a body is\n // consumed, undici holds its socket out of the connection pool, so a\n // retry storm burns a fresh connection per attempt — exactly when the\n // server can least afford it.\n //\n // Consuming returns the socket to the pool. `res.body?.cancel()` frees\n // it too, but by destroying the connection rather than reusing it,\n // which is the churn this exists to avoid. Pipe to a sink rather than\n // `res.text()`: an error body can be arbitrarily large, and buffering\n // one into a string only to discard it costs several times its size in\n // memory on every attempt of every in-flight request.\n //\n // Fall back to `text()` where there is no stream to pipe: a bodyless\n // response, or a runtime that exposes `text()` but not `body`.\n const drained = res.body ? res.body.pipeTo(new WritableStream()) : res.text();\n\n // A read that fails — including one the deadline above aborts — has\n // already released the socket, so a failure here is not worth\n // propagating over the status we are retrying on.\n await drained.catch(() => {});\n } else {\n text = await res.text();\n }\n } finally {\n clearTimeout(timeout);\n }\n\n if (retrying) {\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 // Outside the deadline above: the backoff is time we choose to wait,\n // not time we are waiting on the server.\n await new Promise((r) => setTimeout(r, delayMs));\n continue;\n }\n\n // Parse response\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 { 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 { resolveIdempotencyKey } 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 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\n // Resolve the key the SAME way the write itself will, so the value bound\n // into the token is the value that reaches the server. A blank key resolves\n // to a fresh one here exactly as it would at POST time — binding the blank\n // and sending the replacement would produce a token the server refuses.\n const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);\n const callerKeyIsUsable = idempotencyKey === options?.idempotencyKey;\n\n // Nothing to mint — the caller already brought both halves. A blank key is\n // not a half: the token paired with it was bound to a value the server can\n // never receive, so re-mint rather than send a doomed pair.\n if (callerKeyIsUsable && options?.capabilityConfirmation) return options;\n\n const route = CAPABILITY_ROUTES[request.capabilityId];\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","import type { BaseClient, RequestOptions } from \"../client\";\nimport type {\n Booking,\n BookingActionResult,\n BookingAvailabilityOptions,\n BookingCreateResult,\n BookingRescheduleResult,\n BookingResource,\n BookingScheduleDay,\n BookingScheduleOptions,\n BookingService,\n BookingSlot,\n BookingsPage,\n CancelBookingInput,\n CreateBookingInput,\n ListBookingServicesOptions,\n ListBookingsOptions,\n ManageSummary,\n RescheduleBookingInput,\n UpdateBookingInput,\n} from \"../types/bookings\";\nimport type { ApiResponse } from \"../types/common\";\n\n/**\n * Customer-side booking management, addressed by the show-once manage token\n * from `bookings.create(...)` rather than by booking id.\n *\n * These are NOT the staff routes with a different lookup key: possession of\n * the token is the customer's own authorization, so the workspace's cancel and\n * reschedule windows are ENFORCED here (they are bypassed on\n * `bookings.cancel` / `bookings.reschedule`), and a cancel is attributed to\n * the customer rather than to staff. Relay a customer's click on their\n * confirmation-email link through these; act as the business through the\n * id-addressed methods.\n */\nclass BookingsManage {\n constructor(private client: BaseClient) {}\n\n /**\n * Read what the holder of a manage token may see and do. Honour\n * `can_cancel` / `can_reschedule` — they already apply the policy windows.\n */\n async get(token: string): Promise<ApiResponse<ManageSummary>> {\n return this.client.get(`/api/v1/bookings/manage/${encodeURIComponent(token)}`);\n }\n\n /** Cancel on the customer's behalf. Rejected outside the cancel window. */\n async cancel(\n token: string,\n input?: CancelBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingActionResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/manage/${encodeURIComponent(token)}/cancel`,\n input ?? {},\n options,\n );\n }\n\n /**\n * Move the booking on the customer's behalf. Rejected outside the reschedule\n * window. Returns a NEW booking id and a new manage token — the old token\n * stops working, so relay the new one into whatever link you send next.\n */\n async reschedule(\n token: string,\n input: RescheduleBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingRescheduleResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/manage/${encodeURIComponent(token)}/reschedule`,\n input,\n options,\n );\n }\n}\n\n/**\n * Appointment bookings: the service catalogue, free slots, and the bookings\n * themselves.\n *\n * Every method here acts as the BUSINESS — policy windows are bypassed and a\n * cancel is recorded against staff. To relay a customer's own action on their\n * confirmation-email link, use {@link Bookings.manage} instead.\n *\n * Money is always integer øre (`amount_ore`, `price_ore`). Timestamps come\n * back as ISO 8601 strings; on the way in, either Unix milliseconds or an ISO\n * string is accepted.\n *\n * @example\n * ```ts\n * const { data: slots } = await medal.bookings.availability({\n * service_id: \"svc_1\",\n * from_ts: Date.now(),\n * to_ts: Date.now() + 7 * 86_400_000,\n * });\n * const { data } = await medal.bookings.create({\n * items: [{ service_id: \"svc_1\", start_ts: slots[0].start_ts! }],\n * contact: { phone: \"+4790000000\", name: \"Ida\" },\n * });\n * ```\n */\nexport class Bookings {\n /** Customer-side actions addressed by manage token. */\n readonly manage: BookingsManage;\n\n constructor(private client: BaseClient) {\n this.manage = new BookingsManage(client);\n }\n\n /** List the bookable service catalogue. Active-only unless asked otherwise. */\n async listServices(options?: ListBookingServicesOptions): Promise<ApiResponse<BookingService[]>> {\n const params: Record<string, string | undefined> = {};\n if (options?.include_inactive !== undefined) {\n params.include_inactive = String(options.include_inactive);\n }\n return this.client.get(\"/api/v1/bookings/services\", params);\n }\n\n /** List the bookable resources — staff, rooms, and equipment. */\n async listResources(): Promise<ApiResponse<BookingResource[]>> {\n return this.client.get(\"/api/v1/bookings/resources\");\n }\n\n /**\n * List free slots for a service over a window. Slots reflect opening hours,\n * time off, buffers, and existing bookings at the moment of the call — they\n * are not held, so a slot can be taken before you book it.\n */\n async availability(options: BookingAvailabilityOptions): Promise<ApiResponse<BookingSlot[]>> {\n const params: Record<string, string | undefined> = {\n service_id: options.service_id,\n from_ts: String(options.from_ts),\n to_ts: String(options.to_ts),\n };\n if (options.resource_id) params.resource_id = options.resource_id;\n return this.client.get(\"/api/v1/bookings/availability\", params);\n }\n\n /**\n * The dates a service can be booked on — the half `availability` cannot\n * answer. Availability returns free slots and nothing else, so a closed day,\n * an evening past closing and a fully booked day are all the same empty\n * array. A date absent from this list is closed; on a listed date, compare\n * `last_start_ts` against the clock to tell \"too late today\" from \"full\".\n */\n async schedule(options: BookingScheduleOptions): Promise<ApiResponse<BookingScheduleDay[]>> {\n const params: Record<string, string | undefined> = {\n service_id: options.service_id,\n from_ts: String(options.from_ts),\n to_ts: String(options.to_ts),\n };\n if (options.resource_id) params.resource_id = options.resource_id;\n return this.client.get(\"/api/v1/bookings/schedule\", params);\n }\n\n /**\n * List bookings with cursor-based pagination and optional filters.\n *\n * Check `pagination.truncated`: when true the read window was clipped and\n * matching bookings exist that no cursor reaches — narrow `from_ts`/`to_ts`.\n */\n async list(options?: ListBookingsOptions): Promise<BookingsPage> {\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?.resource_id) params.resource_id = options.resource_id;\n if (options?.from_ts !== undefined) params.from_ts = String(options.from_ts);\n if (options?.to_ts !== undefined) params.to_ts = String(options.to_ts);\n return this.client.get(\"/api/v1/bookings\", params);\n }\n\n /**\n * Book a party — every item succeeds or none do (max 50).\n *\n * Each created booking comes back with a `manage_token` exactly once; only\n * its hash is stored, so persist it if you need the customer's manage link.\n *\n * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than book the slot twice. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too — the\n * server keys on it for 24 hours, so re-sending the same key after a network\n * timeout returns the original bookings instead of a second set.\n */\n async create(\n input: CreateBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingCreateResult>> {\n return this.client.postOnce(\"/api/v1/bookings\", input, options);\n }\n\n /** Get a booking by ID. */\n async get(id: string): Promise<ApiResponse<Booking>> {\n return this.client.get(`/api/v1/bookings/${encodeURIComponent(id)}`);\n }\n\n /**\n * Annotate a booking. At least one of `notes` (customer-visible) or\n * `internal_notes` (staff-only) is required; `\"\"` clears a field.\n */\n async update(\n id: string,\n input: UpdateBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<Booking>> {\n return this.client.patch(`/api/v1/bookings/${encodeURIComponent(id)}`, input, options);\n }\n\n /** Cancel as the business — the cancel window is bypassed. */\n async cancel(\n id: string,\n input?: CancelBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingActionResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/${encodeURIComponent(id)}/cancel`,\n input ?? {},\n options,\n );\n }\n\n /**\n * Move a booking as the business — the reschedule window is bypassed.\n * Returns a NEW booking id and a new manage token; the old booking is\n * cancelled and its token stops working.\n */\n async reschedule(\n id: string,\n input: RescheduleBookingInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingRescheduleResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/${encodeURIComponent(id)}/reschedule`,\n input,\n options,\n );\n }\n\n /** Mark a booking as a no-show. */\n async markNoShow(\n id: string,\n options?: RequestOptions,\n ): Promise<ApiResponse<BookingActionResult>> {\n return this.client.postOnce(\n `/api/v1/bookings/${encodeURIComponent(id)}/no-show`,\n undefined,\n options,\n );\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 * Deliberately unkeyed, unlike the writes it authorizes. Minting is not the\n * state change the guarantee exists to protect: the write itself is already\n * bound to `idempotency_key`, so a retry that mints a second token cannot\n * produce a second write. Keying this call would instead park a credential\n * designed to expire in 15 minutes inside a replay cache that answers for 24\n * hours — a worse trade than the duplicate token it would avoid.\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 * Automatically idempotent: an unkeyed retry mints a SECOND live single-use\n * link for the same person, and only one of the two can ever be consumed —\n * the other stays outstanding until it is revoked or expires. The key the\n * confirmer chose is the key that goes out — a capability confirmation is\n * bound to its idempotency key, so minting a fresh one here would invalidate\n * the confirmation.\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.postOnce(\"/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, RequestOptions } 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 /**\n * Create a new contact. Email must be unique in the workspace.\n *\n * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than run the create a second time. Uniqueness\n * alone would not save you here — it turns the retry of a committed create\n * into a spurious conflict, which reads as \"the contact was not created\".\n * Supply `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async create(\n input: CreateContactInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ContactCreateResult>> {\n return this.client.postOnce(\"/api/v1/contacts\", input, options);\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 /**\n * Add a note to a contact's timeline.\n *\n * Automatically idempotent: nothing about a note is unique, so an unkeyed\n * retry appends the same text to the timeline twice. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async addNote(\n id: string,\n input: AddNoteInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<ContactNoteResult>> {\n return this.client.postOnce(`/api/v1/contacts/${encodeURIComponent(id)}/notes`, input, options);\n }\n\n /**\n * Bulk import contacts (max 500). Duplicates are skipped.\n *\n * Automatically idempotent: the import is processed in chunks, so a retry\n * after a partial failure re-walks the whole batch and reports `added` /\n * `skipped` counts for a run that was not the first. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async import(\n contacts: ImportContactInput[],\n options?: RequestOptions,\n ): Promise<ApiResponse<ImportContactsResult>> {\n return this.client.postOnce(\"/api/v1/contacts/import\", { contacts }, options);\n }\n}\n","import type { BaseClient, RequestOptions } 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 /**\n * Create a new deal.\n *\n * Automatically idempotent: nothing about a deal is unique, so an unkeyed\n * retry puts a second identical deal in the pipeline. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async create(\n input: CreateDealInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<DealCreateResult>> {\n return this.client.postOnce(\"/api/v1/deals\", input, options);\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, RequestOptions } 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 * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than queue a second copy into someone's inbox —\n * a send that already committed cannot be un-sent. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n *\n * `input.idempotency_key` is the older, body-level form of the same control\n * and still takes precedence server-side, so setting it keeps working\n * unchanged.\n */\n async send(\n input: SendEmailInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<EmailSendResult>> {\n return this.client.postOnce(\"/api/v1/emails\", input, options);\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 * Automatically idempotent — and this is the call where it matters most: an\n * unkeyed retry of a batch that already committed sends up to 100 duplicate\n * emails. Supply `options.idempotencyKey` to deduplicate across your OWN\n * retries too.\n */\n async batch(\n input: BatchSendInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<BatchSendSummary>> {\n return this.client.postOnce(\"/api/v1/emails/batch\", input, options);\n }\n}\n","import type { BaseClient, RequestOptions } 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 /**\n * Request a workspace data export. Runs asynchronously.\n *\n * Automatically idempotent: the request is recorded and the export is\n * scheduled in one step with no de-duplication of its own, so an unkeyed\n * retry files a second subject-access request and runs a second full export\n * of the workspace. Supply `options.idempotencyKey` to deduplicate across\n * your OWN retries too.\n */\n async requestExport(\n options?: RequestOptions,\n ): Promise<ApiResponse<{ request_id: string; status: string }>> {\n return this.client.postOnce(\"/api/v1/gdpr/export\", undefined, options);\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 /**\n * Record a GDPR consent decision for a contact by email.\n *\n * Deliberately unkeyed: a decision is stored once per\n * (workspace, email, consent type) and overwritten in place, so re-sending\n * the same body reaches the same state and returns the same record id.\n */\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 /**\n * Record cookie consent from an external site (legacy endpoint).\n *\n * Deliberately unkeyed: this legacy route predates the versioned API and\n * does not run the `Idempotency-Key` machinery, so a key here would be a\n * header that changes nothing while implying a guarantee the endpoint cannot\n * make. Treat a failed call as \"unknown\" and re-send only if a missing\n * consent log matters more to you than a duplicate one.\n */\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 * Automatically idempotent: a reply is a message to a real person, and an\n * unkeyed retry sends it to them twice. The key the confirmer chose is the\n * key that goes out — a capability confirmation is bound to its idempotency\n * key, so minting a fresh one here would invalidate the confirmation.\n *\n * Pass `options.idempotencyKey` to deduplicate across your OWN retries too.\n * It is REQUIRED for capability-scoped tokens, which need it paired with a\n * `capabilityConfirmation` — a generated key satisfies the pairing's key\n * half only; the confirmation is still yours to supply (or to let\n * `autoConfirm` mint).\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.postOnce(\"/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 } from \"../types/common\";\nimport type {\n PortalBookings,\n PortalExport,\n PortalLoginStartInput,\n PortalLoginStartResult,\n PortalProfile,\n PortalProfilePatch,\n PortalSession,\n PortalVerifyInput,\n} from \"../types/portal\";\n\n/** The per-request options a session-bound portal call sends. */\nfunction withSession(session: string): { headers: Record<string, string> } {\n return { headers: { \"x-portal-session\": session } };\n}\n\n/**\n * For calls whose first attempt may have SUCCEEDED while the response was\n * lost: verifying burns the code, logout and deleteMe revoke the session. An\n * automatic retry would then meet `PORTAL_CODE_INVALID` /\n * `PORTAL_SESSION_INVALID` and report a completed operation as a failure, so\n * these go to the wire exactly once. A 5xx surfaces as-is; the caller decides.\n */\nconst ONCE = { retry: false } as const;\n\n/**\n * E-mail one-time-code login.\n *\n * Both routes are plain POSTs — deliberately not idempotency-keyed. A start\n * that is retried sends at most one more code, and a verify that is retried\n * meets a code that has already been burned and answers\n * `PORTAL_CODE_INVALID` — neither can duplicate anything.\n */\nclass PortalLogin {\n constructor(private client: BaseClient) {}\n\n /**\n * E-mail a one-time code to the address.\n *\n * Always 202 `{ status: \"sent\" }` — enumeration-safe: `\"sent\"` does not\n * confirm that the address belongs to a contact. Rate-limited per address\n * and per caller (`429 RATE_LIMITED`).\n */\n async start(input: PortalLoginStartInput): Promise<ApiResponse<PortalLoginStartResult>> {\n return this.client.post(\"/api/v1/portal/login/start\", input);\n }\n\n /**\n * Exchange the e-mailed code for a session.\n *\n * `session_token` is a bearer credential for ONE contact — keep it in an\n * HttpOnly cookie on the site's server. A wrong, burned or expired code all\n * answer `401 PORTAL_CODE_INVALID`; the three are not distinguished, so the\n * response is not an oracle for which codes exist.\n */\n async verify(input: PortalVerifyInput): Promise<ApiResponse<PortalSession>> {\n return this.client.post(\"/api/v1/portal/login/verify\", input, ONCE);\n }\n}\n\n/**\n * Customer self-service portal. The session token is a bearer credential for a\n * single contact: the calling site server must keep it in an HttpOnly cookie\n * and never hand it to the browser. Session-bound methods send it as\n * `X-Portal-Session`; none of them carries an `Idempotency-Key`.\n *\n * The API key needs `read:portal` and `write:portal` (`403 FORBIDDEN`\n * otherwise). A missing header answers `401 PORTAL_SESSION_REQUIRED`; an\n * unknown, expired or revoked token answers `401 PORTAL_SESSION_INVALID` —\n * treat both as \"sign in again\".\n */\nexport class Portal {\n /** E-mail one-time-code login: `start` sends the code, `verify` exchanges it. */\n readonly login: PortalLogin;\n\n constructor(private client: BaseClient) {\n this.login = new PortalLogin(client);\n }\n\n /**\n * Revoke the session. Resolves to `undefined` (the route answers 204).\n *\n * Not keyed: revoking twice reaches the same state — the second call answers\n * `401 PORTAL_SESSION_INVALID`, which is the outcome you wanted anyway.\n */\n async logout(session: string): Promise<void> {\n await this.client.post(\"/api/v1/portal/logout\", undefined, {\n ...withSession(session),\n ...ONCE,\n });\n }\n\n /** The signed-in contact's own profile. */\n async me(session: string): Promise<ApiResponse<PortalProfile>> {\n return this.client.get(\"/api/v1/portal/me\", undefined, withSession(session));\n }\n\n /**\n * Update the signed-in contact's profile. Only the supplied fields change;\n * `phone: null` clears the number and `family` replaces the whole list.\n * `marketing_consent` records a `marketing_email` consent decision with\n * source `portal`. Returns the profile as it is after the change.\n */\n /**\n * A profile patch is not idempotency-keyed on the server and a `marketing_consent`\n * change records a dated consent event, so a retry after a committed-but-lost\n * response would repeat that event: sent exactly once, like the other writes.\n */\n async updateMe(session: string, patch: PortalProfilePatch): Promise<ApiResponse<PortalProfile>> {\n return this.client.patch(\"/api/v1/portal/me\", patch, { ...withSession(session), ...ONCE });\n }\n\n /**\n * The contact's bookings split into `upcoming` and `past`. An upcoming\n * booking that is still inside the workspace's policy windows carries\n * `manage_token` and `can_manage: true`; use the token to open the site's\n * manage page (`medal.bookings.manage.*`).\n */\n async myBookings(session: string): Promise<ApiResponse<PortalBookings>> {\n return this.client.get(\"/api/v1/portal/me/bookings\", undefined, withSession(session));\n }\n\n /**\n * Everything the workspace holds about the contact — profile, family,\n * consents and bookings — as one JSON document (GDPR Art. 15). Synchronous,\n * unlike `medal.gdpr.requestExport()`, which exports the whole workspace.\n *\n * Not keyed: a read-only snapshot, so a retried call costs nothing and\n * duplicates nothing.\n */\n async exportMyData(session: string): Promise<ApiResponse<PortalExport>> {\n return this.client.post(\"/api/v1/portal/me/export\", undefined, withSession(session));\n }\n\n /**\n * Erase the contact (GDPR Art. 17). Resolves to `undefined` (the route\n * answers 204); the session is revoked as part of the deletion.\n *\n * Not keyed: deletion is terminal, so a retry meets a revoked session and\n * answers `401 PORTAL_SESSION_INVALID` rather than deleting anything else.\n */\n async deleteMe(session: string): Promise<void> {\n await this.client.post(\"/api/v1/portal/me/delete\", undefined, {\n ...withSession(session),\n ...ONCE,\n });\n }\n}\n","import type { BaseClient, RequestOptions } 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 /**\n * Create a new post with content and target channels.\n *\n * Automatically idempotent: the SDK mints an `Idempotency-Key` so its own\n * 5xx retries replay rather than draft the post twice. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\n */\n async create(\n input: CreatePostInput,\n options?: RequestOptions,\n ): Promise<ApiResponse<{ id: string }>> {\n return this.client.postOnce(\"/api/v1/posts\", input, options);\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 /**\n * Schedule a post for future publication.\n *\n * Deliberately unkeyed: re-sending the same `scheduled_at` for an\n * already-scheduled post returns the original `workflow_id` rather than\n * starting a second one, so a retried schedule cannot double-publish. A\n * *different* time is rejected — unschedule first.\n */\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 /**\n * Publish a post immediately to all target channels.\n *\n * Deliberately unkeyed: publishing moves the post out of the set of statuses\n * that may be published, so the retry of a publish that already committed is\n * refused rather than posting a second time. It is refused with a 400 though\n * — treat an error here as \"check the post's status\", not as \"nothing\n * happened\".\n */\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 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 * Automatically idempotent: a scan job is queued the moment it is created,\n * so an unkeyed retry starts a second crawl of the same site and returns an\n * id for a job that duplicates one already running. Supply\n * `options.idempotencyKey` to deduplicate across your OWN retries too.\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(\n input: ScanCreateInput,\n options?: RequestOptions,\n ): 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.postOnce(\"/api/v1/scan\", { [key]: input[key] }, options);\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 * Automatically idempotent: a duplicate endpoint is not a stray row, it is a\n * second copy of every future delivery to the same URL, forever. The key the\n * confirmer chose is the key that goes out — a capability confirmation is\n * bound to its idempotency key, so minting a fresh one here would invalidate\n * the confirmation. That the SDK now always sends a key is also what makes\n * the replay-without-secret case above reachable on a plain 5xx retry.\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.postOnce(\"/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 /**\n * Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202.\n *\n * Deliberately unkeyed: a duplicate ping is the one duplicate that costs\n * nothing. Real deliveries are retried too, so any endpoint worth pointing at\n * already tolerates receiving the same event twice — that is what this call\n * exists to prove.\n */\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, bookings, the\n * customer portal, GDPR compliance, helpdesk conversations, partner channel\n * connect, 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 { CapabilityConfirmer } from \"./capability-confirmer\";\nimport { BaseClient } from \"./client\";\nimport { Bookings } from \"./resources/bookings\";\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 { Portal } from \"./resources/portal\";\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 * // Bookings — free slots, then book a party (money is integer øre)\n * const { data: slots } = await medal.bookings.availability({\n * service_id: 'svc_1',\n * from_ts: Date.now(),\n * to_ts: Date.now() + 7 * 86_400_000,\n * });\n *\n * // Customer portal — e-mail code login, then session-bound self-service.\n * // Keep `session_token` in an HttpOnly cookie on your server.\n * await medal.portal.login.start({ email: 'ida@example.com' });\n * const { data: session } = await medal.portal.login.verify({ email: 'ida@example.com', code: '123456' });\n * const { data: mine } = await medal.portal.myBookings(session.session_token);\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 bookings: Bookings;\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 portal: Portal;\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.bookings = new Bookings(client);\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.portal = new Portal(client);\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 { Bookings } from \"./resources/bookings\";\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 { Portal } from \"./resources/portal\";\nexport { Posts } from \"./resources/posts\";\nexport { Scan } from \"./resources/scan\";\nexport { Webhooks } from \"./resources/webhooks\";\nexport { Workspaces } from \"./resources/workspaces\";\nexport type {\n Booking,\n BookingActionResult,\n BookingAvailabilityOptions,\n BookingCancelledBy,\n BookingClaimableCreatedVia,\n BookingContactInput,\n BookingCreatedVia,\n BookingCreateResult,\n BookingPaymentStatus,\n BookingRescheduleResult,\n BookingResource,\n BookingResourceType,\n BookingScheduleDay,\n BookingScheduleOptions,\n BookingService,\n BookingSlot,\n BookingStatus,\n BookingsPage,\n BookingsPagination,\n BookingTimestampInput,\n CancelBookingInput,\n CreateBookingInput,\n CreateBookingItemInput,\n CreatedBooking,\n ListBookingServicesOptions,\n ListBookingsOptions,\n ManageSummary,\n RescheduleBookingInput,\n UpdateBookingInput,\n} from \"./types/bookings\";\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 PortalBooking,\n PortalBookingStatus,\n PortalBookings,\n PortalConsentRecord,\n PortalContactSummary,\n PortalExport,\n PortalFamilyMember,\n PortalLoginStartInput,\n PortalLoginStartResult,\n PortalProfile,\n PortalProfilePatch,\n PortalSession,\n PortalVerifyInput,\n} from \"./types/portal\";\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"]}
|