@planescrm/sdk 0.3.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/oauth.ts","../src/http.ts","../src/resources/base-resource.ts","../src/resources/opportunities.ts","../src/resources/customers.ts","../src/resources/properties.ts","../src/resources/calendar-events.ts","../src/resources/contracts.ts","../src/resources/jobs.ts","../src/resources/estimates.ts","../src/resources/invoices.ts","../src/resources/inventory.ts","../src/resources/contacts.ts","../src/resources/activities.ts","../src/resources/tags.ts","../src/webhooks.ts","../src/webhook-router.ts","../src/client.ts","../src/agent.ts"],"sourcesContent":["// Client\nexport { PlanesClient } from './client';\n\n// Config\nexport type { PlanesConfig } from './config';\nexport { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES } from './config';\n\n// Errors\nexport {\n PlanesError,\n AuthenticationError,\n AuthorizationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ServerError,\n WebhookVerificationError,\n OAuthError,\n} from './errors';\nexport type { FieldError } from './errors';\n\n// Types\nexport type { PaginatedResponse, ListParams } from './types';\n\n// Resources\nexport {\n BaseResource,\n OpportunityResource,\n CustomerResource,\n PropertyResource,\n CalendarEventResource,\n ContractResource,\n JobResource,\n EstimateResource,\n InvoiceResource,\n InventoryResource,\n ContactResource,\n ContactChannelResource,\n ActivityResource,\n TagResource,\n} from './resources';\n\n// Contact types\nexport type {\n ContactDto,\n CreateContactDto,\n UpdateContactDto,\n ContactChannelDto,\n CreateContactChannelDto,\n UpdateContactChannelDto,\n ContactMergeResult,\n} from './types/contact-types';\n\n// Activity types\nexport type { ActivityDto, CreateActivityDto } from './types/activity-types';\n\n// Tag types\nexport type {\n TagDto,\n CreateTagDto,\n UpdateTagDto,\n SetEntityTagsDto,\n EntityTagDto,\n TagEntityDto,\n} from './types/tag-types';\n\n// Agent types\nexport type {\n ActionHandler,\n ActionContext,\n ActionResult,\n ActionRequest,\n AgentHandler,\n HandlerMap,\n TriggerType,\n PermissionLevel,\n} from './types/agent-types';\n\n// Agent registration\nexport { Planes } from './agent';\nexport type { RegisterOptions } from './agent';\nexport { createAgentApp, AgentApp } from './agent';\n\n// Webhooks\nexport { verifyWebhookSignature, parseWebhookEvent } from './webhooks';\nexport type {\n WebhookDelivery,\n EntityEvent,\n EntityEventType,\n EntityType,\n} from './webhooks';\nexport { WebhookRouter } from './webhook-router';\nexport type { WebhookHandler, WebhookProcessInput } from './webhook-router';\n\n// OAuth\nexport { buildAuthorizeUrl, exchangeCode, refreshAccessToken } from './oauth';\nexport type {\n AuthorizeOptions,\n ExchangeCodeOptions,\n RefreshOptions,\n TokenResponse,\n} from './oauth';\n\n// OAuth-managed token storage (E39-F2-S4)\nexport { MemoryTokenStorage, DEFAULT_TOKEN_REFRESH_SKEW_MS } from './config';\nexport type { TokenStorage, StoredTokens } from './config';\n\n// Generated types (from OpenAPI spec)\nexport * from './generated';\n","/**\n * Tokens managed by an OAuth-credential client, persisted via {@link TokenStorage}.\n */\nexport interface StoredTokens {\n /** Current OAuth access token. */\n accessToken: string;\n /** Refresh token used to obtain a new access token. */\n refreshToken: string;\n /** Access-token expiry as epoch milliseconds. */\n expiresAt: number;\n}\n\n/**\n * Pluggable persistence for OAuth-managed tokens. Implement this to back the\n * client with your own store (encrypted disk, database, secrets manager, ...).\n * The default is {@link MemoryTokenStorage}.\n */\nexport interface TokenStorage {\n /** Load the currently stored tokens, or `null` if none are stored. */\n getTokens(): Promise<StoredTokens | null>;\n /** Persist the given tokens (called after every refresh). */\n setTokens(tokens: StoredTokens): Promise<void>;\n}\n\n/**\n * Default in-memory {@link TokenStorage}. Tokens live only for the process\n * lifetime — provide your own implementation to persist across restarts.\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null;\n\n constructor(seed?: StoredTokens | null) {\n this.tokens = seed ?? null;\n }\n\n async getTokens(): Promise<StoredTokens | null> {\n return this.tokens;\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n this.tokens = tokens;\n }\n}\n\n/**\n * Configuration for the PlanesClient.\n *\n * Exactly one authentication mode must be supplied:\n * - `accessToken` — a pre-obtained OAuth access token (not auto-refreshed)\n * - `apiKey` — a `pk_live_` API key\n * - `clientId` + `clientSecret` — OAuth-managed: the client obtains/refreshes\n * access tokens automatically using `tokenStorage`\n */\nexport interface PlanesConfig {\n /** API base URL (defaults to https://api.planescrm.com/api/v1) */\n baseUrl?: string;\n /** OAuth access token (mutually exclusive with apiKey / clientId+clientSecret) */\n accessToken?: string;\n /** API key (mutually exclusive with accessToken / clientId+clientSecret) */\n apiKey?: string;\n /** OAuth client ID — combined with `clientSecret`, enables the OAuth-managed path */\n clientId?: string;\n /** OAuth client secret — combined with `clientId`, enables the OAuth-managed path */\n clientSecret?: string;\n /**\n * Token store for the OAuth-managed path. Seed it with tokens obtained from\n * an authorization-code exchange. Defaults to {@link MemoryTokenStorage}.\n */\n tokenStorage?: TokenStorage;\n /**\n * Refresh access tokens proactively when they are within this many\n * milliseconds of expiry (OAuth-managed path only). Default 60000 (60s).\n */\n tokenRefreshSkewMs?: number;\n /** Request timeout in milliseconds (default 30000) */\n timeout?: number;\n /** Automatically retry on 429 rate limit responses (default true) */\n retryOnRateLimit?: boolean;\n /** Maximum number of retries for rate-limited requests (default 3) */\n maxRetries?: number;\n}\n\nexport const DEFAULT_BASE_URL = 'https://api.planescrm.com/api/v1';\nexport const DEFAULT_TIMEOUT = 30_000;\nexport const DEFAULT_MAX_RETRIES = 3;\nexport const DEFAULT_TOKEN_REFRESH_SKEW_MS = 60_000;\n","/**\n * Base error class for all Planes SDK errors.\n */\nexport class PlanesError extends Error {\n public readonly status: number;\n public readonly code: string;\n\n constructor(message: string, status: number, code: string) {\n super(message);\n this.name = 'PlanesError';\n this.status = status;\n this.code = code;\n }\n}\n\n/**\n * Thrown when authentication fails (401).\n */\nexport class AuthenticationError extends PlanesError {\n constructor(message = 'Authentication failed') {\n super(message, 401, 'authentication_error');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown when the user lacks permission (403).\n */\nexport class AuthorizationError extends PlanesError {\n constructor(message = 'Insufficient permissions') {\n super(message, 403, 'authorization_error');\n this.name = 'AuthorizationError';\n }\n}\n\n/**\n * Thrown when a resource is not found (404).\n */\nexport class NotFoundError extends PlanesError {\n constructor(message = 'Resource not found') {\n super(message, 404, 'not_found');\n this.name = 'NotFoundError';\n }\n}\n\n/** Field-level validation error detail. */\nexport interface FieldError {\n field: string;\n message: string;\n code?: string;\n}\n\n/**\n * Thrown for validation failures (400) with field-level error details.\n */\nexport class ValidationError extends PlanesError {\n public readonly fieldErrors: FieldError[];\n\n constructor(message = 'Validation failed', fieldErrors: FieldError[] = []) {\n super(message, 400, 'validation_error');\n this.name = 'ValidationError';\n this.fieldErrors = fieldErrors;\n }\n}\n\n/**\n * Thrown when rate limited (429). Includes retryAfter in seconds.\n */\nexport class RateLimitError extends PlanesError {\n public readonly retryAfter: number;\n\n constructor(retryAfter = 1, message = 'Rate limit exceeded') {\n super(message, 429, 'rate_limit_error');\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Thrown for server errors (500+).\n */\nexport class ServerError extends PlanesError {\n constructor(message = 'Internal server error') {\n super(message, 500, 'server_error');\n this.name = 'ServerError';\n }\n}\n\n/**\n * Thrown when webhook signature verification fails.\n */\nexport class WebhookVerificationError extends PlanesError {\n constructor(message = 'Webhook signature verification failed') {\n super(message, 400, 'webhook_verification_error');\n this.name = 'WebhookVerificationError';\n }\n}\n\n/**\n * Thrown for OAuth flow errors.\n */\nexport class OAuthError extends PlanesError {\n public readonly errorCode: string;\n\n constructor(errorCode: string, message = 'OAuth error') {\n super(message, 400, 'oauth_error');\n this.name = 'OAuthError';\n this.errorCode = errorCode;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { OAuthError } from './errors';\n\n/**\n * Options for building an OAuth authorization URL.\n */\nexport interface AuthorizeOptions {\n /** Planes API base URL (e.g. https://api.planescrm.com/api/v1). */\n baseUrl: string;\n /** OAuth client ID. */\n clientId: string;\n /** Redirect URI after authorization. */\n redirectUri: string;\n /** Requested OAuth scopes. */\n scopes: string[];\n /** CSRF state parameter (auto-generated if not provided). */\n state?: string;\n}\n\n/**\n * Options for exchanging an authorization code for tokens.\n */\nexport interface ExchangeCodeOptions {\n /** Planes API base URL. */\n baseUrl: string;\n /** OAuth client ID. */\n clientId: string;\n /** OAuth client secret. */\n clientSecret: string;\n /** Authorization code from the callback. */\n code: string;\n /** Redirect URI (must match the one used in authorization). */\n redirectUri: string;\n}\n\n/**\n * Options for refreshing an access token.\n */\nexport interface RefreshOptions {\n /** Planes API base URL. */\n baseUrl: string;\n /** OAuth client ID. */\n clientId: string;\n /** OAuth client secret. */\n clientSecret: string;\n /** Refresh token. */\n refreshToken: string;\n}\n\n/**\n * OAuth token response.\n */\nexport interface TokenResponse {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n token_type: string;\n scope: string;\n}\n\n/**\n * Build an OAuth authorization URL for the Planes platform.\n *\n * @param options - Authorization options\n * @returns The full authorization URL to redirect the user to\n */\nexport function buildAuthorizeUrl(options: AuthorizeOptions): string {\n const state = options.state ?? randomBytes(16).toString('hex');\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n\n const url = new URL(`${baseUrl}/oauth/authorize`);\n url.searchParams.set('client_id', options.clientId);\n url.searchParams.set('redirect_uri', options.redirectUri);\n url.searchParams.set('scope', options.scopes.join(' '));\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', state);\n\n return url.toString();\n}\n\n/**\n * Exchange an authorization code for access and refresh tokens.\n *\n * @param options - Exchange options\n * @returns Token response with access_token, refresh_token, etc.\n * @throws OAuthError on failure\n */\nexport async function exchangeCode(options: ExchangeCodeOptions): Promise<TokenResponse> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const url = `${baseUrl}/oauth/token`;\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: options.clientId,\n client_secret: options.clientSecret,\n code: options.code,\n redirect_uri: options.redirectUri,\n });\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({})) as Record<string, unknown>;\n const errorCode = (errorBody['error'] as string) ?? 'unknown_error';\n const errorDescription = (errorBody['error_description'] as string) ?? 'Token exchange failed';\n throw new OAuthError(errorCode, errorDescription);\n }\n\n return (await response.json()) as TokenResponse;\n}\n\n/**\n * Refresh an access token using a refresh token.\n *\n * @param options - Refresh options\n * @returns New token response\n * @throws OAuthError on failure\n */\nexport async function refreshAccessToken(options: RefreshOptions): Promise<TokenResponse> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const url = `${baseUrl}/oauth/token`;\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: options.clientId,\n client_secret: options.clientSecret,\n refresh_token: options.refreshToken,\n });\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({})) as Record<string, unknown>;\n const errorCode = (errorBody['error'] as string) ?? 'unknown_error';\n const errorDescription = (errorBody['error_description'] as string) ?? 'Token refresh failed';\n throw new OAuthError(errorCode, errorDescription);\n }\n\n return (await response.json()) as TokenResponse;\n}\n","import {\n PlanesConfig,\n DEFAULT_BASE_URL,\n DEFAULT_TIMEOUT,\n DEFAULT_MAX_RETRIES,\n DEFAULT_TOKEN_REFRESH_SKEW_MS,\n MemoryTokenStorage,\n} from './config';\nimport type { StoredTokens, TokenStorage } from './config';\nimport {\n PlanesError,\n AuthenticationError,\n AuthorizationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ServerError,\n} from './errors';\nimport type { FieldError } from './errors';\nimport { refreshAccessToken } from './oauth';\n\n/** OAuth-managed authentication context (clientId/clientSecret path). */\ninterface OAuthContext {\n clientId: string;\n clientSecret: string;\n tokenStorage: TokenStorage;\n /** Refresh proactively when within this many ms of expiry. */\n skewMs: number;\n}\n\nexport interface HttpRequestOptions {\n method: string;\n path: string;\n body?: unknown;\n params?: Record<string, unknown>;\n headers?: Record<string, string>;\n}\n\n/**\n * Internal HTTP client wrapper using native fetch.\n * Handles authentication, rate limit retries, and error mapping.\n */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly accessToken: string | undefined;\n private readonly apiKey: string | undefined;\n private readonly timeout: number;\n private readonly retryOnRateLimit: boolean;\n private readonly maxRetries: number;\n private readonly oauth: OAuthContext | undefined;\n /** Single-flight guard so concurrent requests share one refresh. */\n private refreshInFlight: Promise<StoredTokens> | undefined;\n\n constructor(config: PlanesConfig) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.accessToken = config.accessToken;\n this.apiKey = config.apiKey;\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.retryOnRateLimit = config.retryOnRateLimit !== false;\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n\n if (config.clientId && config.clientSecret) {\n this.oauth = {\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n tokenStorage: config.tokenStorage ?? new MemoryTokenStorage(),\n skewMs: config.tokenRefreshSkewMs ?? DEFAULT_TOKEN_REFRESH_SKEW_MS,\n };\n }\n }\n\n async request<T>(options: HttpRequestOptions): Promise<T> {\n const url = this.buildUrl(options.path, options.params);\n const bodyStr = options.body !== undefined ? JSON.stringify(options.body) : undefined;\n\n let lastError: Error | undefined;\n // The OAuth-managed 401 recovery is one refresh + one retry, independent of\n // the 429 rate-limit retry loop below.\n let didRefreshOn401 = false;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n // Resolve auth headers per-attempt: the OAuth path may proactively refresh\n // and the bearer token can change between attempts.\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(await this.resolveAuthHeaders()),\n ...options.headers,\n };\n const fetchOptions: RequestInit = {\n method: options.method,\n headers,\n signal: AbortSignal.timeout(this.timeout),\n };\n if (bodyStr !== undefined) {\n fetchOptions.body = bodyStr;\n }\n\n const response = await fetch(url, fetchOptions);\n\n if (response.ok) {\n // 204 No Content\n if (response.status === 204) {\n return undefined as T;\n }\n return (await response.json()) as T;\n }\n\n if (response.status === 429 && this.retryOnRateLimit && attempt < this.maxRetries) {\n const retryAfter = this.parseRetryAfter(response);\n await this.sleep(retryAfter * 1000);\n continue;\n }\n\n // OAuth-managed path: on a 401, refresh the token once and retry the\n // request. This does not consume the 429 retry budget.\n if (response.status === 401 && this.oauth && !didRefreshOn401) {\n didRefreshOn401 = true;\n await this.refreshTokens();\n attempt--;\n continue;\n }\n\n lastError = await this.mapError(response);\n\n // Only retry on 429 when enabled; all other errors fail immediately\n if (response.status !== 429 || !this.retryOnRateLimit) {\n throw lastError;\n }\n }\n\n // All retries exhausted for 429\n throw lastError ?? new RateLimitError();\n }\n\n getAuthHeaders(): Record<string, string> {\n if (this.accessToken) {\n return { Authorization: `Bearer ${this.accessToken}` };\n }\n if (this.apiKey) {\n return { 'X-API-Key': this.apiKey };\n }\n return {};\n }\n\n /**\n * Resolve the Authorization header for the current request. For the static\n * accessToken/apiKey modes this is {@link getAuthHeaders}. For the\n * OAuth-managed path it reads the stored token, proactively refreshing when\n * the token is within the skew window of expiry.\n */\n private async resolveAuthHeaders(): Promise<Record<string, string>> {\n if (!this.oauth) {\n return this.getAuthHeaders();\n }\n\n const tokens = await this.oauth.tokenStorage.getTokens();\n if (!tokens) {\n // Nothing seeded — send no auth; the server 401 will drive a refresh\n // attempt (which will fail clearly if there is no refresh token).\n return {};\n }\n\n if (tokens.expiresAt - Date.now() <= this.oauth.skewMs) {\n const refreshed = await this.refreshTokens();\n return { Authorization: `Bearer ${refreshed.accessToken}` };\n }\n\n return { Authorization: `Bearer ${tokens.accessToken}` };\n }\n\n /**\n * Refresh the OAuth access token via `refreshAccessToken` and persist the new\n * tokens through the configured {@link TokenStorage}. Concurrent callers share\n * a single in-flight refresh.\n */\n private async refreshTokens(): Promise<StoredTokens> {\n if (!this.oauth) {\n throw new AuthenticationError('Token refresh requires the OAuth clientId/clientSecret path');\n }\n if (this.refreshInFlight) {\n return this.refreshInFlight;\n }\n\n const oauth = this.oauth;\n this.refreshInFlight = (async () => {\n const current = await oauth.tokenStorage.getTokens();\n if (!current?.refreshToken) {\n throw new AuthenticationError('No refresh token available to refresh the access token');\n }\n\n const response = await refreshAccessToken({\n baseUrl: this.baseUrl,\n clientId: oauth.clientId,\n clientSecret: oauth.clientSecret,\n refreshToken: current.refreshToken,\n });\n\n const next: StoredTokens = {\n accessToken: response.access_token,\n refreshToken: response.refresh_token || current.refreshToken,\n expiresAt: Date.now() + response.expires_in * 1000,\n };\n await oauth.tokenStorage.setTokens(next);\n return next;\n })();\n\n try {\n return await this.refreshInFlight;\n } finally {\n this.refreshInFlight = undefined;\n }\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n }\n\n private parseRetryAfter(response: Response): number {\n const header = response.headers.get('Retry-After');\n if (header) {\n const seconds = Number(header);\n if (!Number.isNaN(seconds) && seconds > 0) {\n return seconds;\n }\n }\n return 1; // Default 1 second\n }\n\n private async mapError(response: Response): Promise<PlanesError> {\n let body: Record<string, unknown> = {};\n try {\n body = (await response.json()) as Record<string, unknown>;\n } catch {\n // Ignore JSON parse errors\n }\n\n const message = (body['message'] as string) ?? response.statusText ?? 'Request failed';\n\n switch (response.status) {\n case 400: {\n const fieldErrors = (body['errors'] as FieldError[] | undefined) ?? [];\n return new ValidationError(message, fieldErrors);\n }\n case 401:\n return new AuthenticationError(message);\n case 403:\n return new AuthorizationError(message);\n case 404:\n return new NotFoundError(message);\n case 429: {\n const retryAfter = this.parseRetryAfter(response);\n return new RateLimitError(retryAfter, message);\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message);\n }\n return new PlanesError(message, response.status, 'unknown_error');\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import { HttpClient } from '../http';\nimport type { PaginatedResponse, ListParams } from '../types';\n\n/**\n * Abstract base resource with standard CRUD methods.\n * Generic parameters:\n * - TEntity: the entity response type\n * - TCreate: the create request body type\n * - TUpdate: the update request body type\n */\nexport abstract class BaseResource<\n TEntity = Record<string, unknown>,\n TCreate = Record<string, unknown>,\n TUpdate = Record<string, unknown>,\n> {\n constructor(\n protected readonly http: HttpClient,\n protected readonly basePath: string,\n ) {}\n\n /**\n * List entities with optional filters and pagination.\n */\n async list(params?: ListParams): Promise<PaginatedResponse<TEntity>> {\n return this.http.request<PaginatedResponse<TEntity>>({\n method: 'GET',\n path: this.basePath,\n params: params as Record<string, unknown>,\n });\n }\n\n /**\n * Get a single entity by ID.\n */\n async get(id: string): Promise<TEntity> {\n return this.http.request<TEntity>({\n method: 'GET',\n path: `${this.basePath}/${id}`,\n });\n }\n\n /**\n * Create a new entity.\n */\n async create(data: TCreate): Promise<TEntity> {\n return this.http.request<TEntity>({\n method: 'POST',\n path: this.basePath,\n body: data,\n });\n }\n\n /**\n * Update an existing entity.\n */\n async update(id: string, data: TUpdate): Promise<TEntity> {\n return this.http.request<TEntity>({\n method: 'PATCH',\n path: `${this.basePath}/${id}`,\n body: data,\n });\n }\n\n /**\n * Delete an entity by ID.\n */\n async delete(id: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `${this.basePath}/${id}`,\n });\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Opportunity resource — CRUD operations on /opportunities. */\nexport class OpportunityResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/opportunities');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Customer resource — CRUD operations on /customers. */\nexport class CustomerResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/customers');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Property resource — CRUD operations on /properties. */\nexport class PropertyResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/properties');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Calendar event resource — CRUD operations on /calendar-events. */\nexport class CalendarEventResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/calendar-events');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Contract resource — CRUD operations on /contracts. */\nexport class ContractResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/contracts');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Job resource — CRUD operations on /jobs. */\nexport class JobResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/jobs');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Estimate resource — CRUD operations on /estimates. */\nexport class EstimateResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/estimates');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Invoice resource — CRUD operations on /invoices. */\nexport class InvoiceResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/invoices');\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Inventory resource — CRUD operations on /inventory/items. */\nexport class InventoryResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/inventory/items');\n }\n}\n","import { BaseResource } from './base-resource';\nimport type { HttpClient } from '../http';\nimport type {\n ContactDto,\n CreateContactDto,\n UpdateContactDto,\n ContactChannelDto,\n CreateContactChannelDto,\n UpdateContactChannelDto,\n AddContactChannelResult,\n ContactMergeResult,\n} from '../types/contact-types';\n\n/**\n * `/contacts/:id/channels` — POST/PATCH/DELETE only. There is no list/get\n * route: reads come embedded in the contact detail response (`GET\n * /contacts/:id` returns `{ ...contact, channels, links, mergedInto }`), so\n * this does NOT extend `BaseResource`.\n */\nexport class ContactChannelResource {\n private readonly basePath: string;\n\n constructor(\n private readonly http: HttpClient,\n contactId: string,\n ) {\n this.basePath = `/contacts/${contactId}/channels`;\n }\n\n /** Returns `{ channel, duplicateOf }` — `duplicateOf` is the dedup signal. */\n async create(data: CreateContactChannelDto): Promise<AddContactChannelResult> {\n return this.http.request<AddContactChannelResult>({\n method: 'POST',\n path: this.basePath,\n body: data,\n });\n }\n\n async update(channelId: string, data: UpdateContactChannelDto): Promise<ContactChannelDto> {\n return this.http.request<ContactChannelDto>({\n method: 'PATCH',\n path: `${this.basePath}/${channelId}`,\n body: data,\n });\n }\n\n async delete(channelId: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `${this.basePath}/${channelId}`,\n });\n }\n}\n\nexport class ContactResource extends BaseResource<\n ContactDto,\n CreateContactDto,\n UpdateContactDto\n> {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/contacts');\n }\n\n async merge(winnerId: string, loserId: string): Promise<ContactMergeResult> {\n return this.http.request<ContactMergeResult>({\n method: 'POST',\n path: `/contacts/${winnerId}/merge/${loserId}`,\n });\n }\n\n channels(contactId: string): ContactChannelResource {\n return new ContactChannelResource(this.http, contactId);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { PaginatedResponse, ListParams } from '../types';\nimport type { ActivityDto, CreateActivityDto } from '../types/activity-types';\n\nexport class ActivityResource {\n constructor(private readonly http: HttpClient) {}\n\n async create(data: CreateActivityDto): Promise<ActivityDto> {\n return this.http.request<ActivityDto>({\n method: 'POST',\n path: '/activities',\n body: data,\n });\n }\n\n async list(params?: ListParams): Promise<PaginatedResponse<ActivityDto>> {\n return this.http.request<PaginatedResponse<ActivityDto>>({\n method: 'GET',\n path: '/activities',\n params: params as Record<string, unknown>,\n });\n }\n\n async listForEntity(\n entityType: string,\n entityId: string,\n params?: ListParams,\n ): Promise<PaginatedResponse<ActivityDto>> {\n return this.http.request<PaginatedResponse<ActivityDto>>({\n method: 'GET',\n path: `/${entityType}/${entityId}/activities`,\n params: params as Record<string, unknown>,\n });\n }\n}\n","import { BaseResource } from './base-resource';\nimport type {\n TagDto,\n CreateTagDto,\n UpdateTagDto,\n SetEntityTagsDto,\n EntityTagDto,\n TagEntitiesPage,\n} from '../types/tag-types';\n\nexport class TagResource extends BaseResource<TagDto, CreateTagDto, UpdateTagDto> {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/tags');\n }\n\n /** `TagsService.setEntityTags()` returns the final tag set wrapped in `{ tags }`. */\n async setEntityTags(\n entityType: string,\n entityId: string,\n data: SetEntityTagsDto,\n ): Promise<{ tags: TagDto[] }> {\n return this.http.request<{ tags: TagDto[] }>({\n method: 'PUT',\n path: `/entities/${entityType}/${entityId}/tags`,\n body: data,\n });\n }\n\n async getEntityTags(entityType: string, entityId: string): Promise<EntityTagDto[]> {\n return this.http.request<EntityTagDto[]>({\n method: 'GET',\n path: `/entities/${entityType}/${entityId}/tags`,\n });\n }\n\n async getEntities(tagId: string, params?: Record<string, unknown>): Promise<TagEntitiesPage> {\n return this.http.request<TagEntitiesPage>({\n method: 'GET',\n path: `/tags/${tagId}/entities`,\n params: params as Record<string, unknown>,\n });\n }\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { WebhookVerificationError } from './errors';\n\n/**\n * The kinds of change a webhook event can describe.\n *\n * Mirrored (self-contained, NOT imported) from\n * `packages/shared/src/types/entity-events.ts` — the SDK must not depend on\n * `@blueprint/shared`. Keep this union byte-identical to that source of truth.\n */\nexport type EntityEventType =\n | 'created'\n | 'updated'\n | 'deleted'\n | 'status_changed'\n | 'entity.tagged'\n | 'entity.untagged'\n | 'list.member_added'\n | 'list.member_removed'\n | 'entity.assigned'\n | 'entity.unassigned'\n | 'team.member_added'\n | 'team.member_removed'\n | 'task.created'\n | 'task.updated'\n | 'task.completed'\n | 'task.cancelled'\n | 'comment.created';\n\n/**\n * The entity kinds a webhook event can target.\n *\n * Mirrored (self-contained, NOT imported) from\n * `packages/shared/src/types/entity-events.ts`. Keep byte-identical.\n */\nexport type EntityType =\n | 'customer'\n | 'opportunity'\n | 'job'\n | 'estimate'\n | 'contract'\n | 'property'\n | 'inventory_item'\n | 'agent_execution'\n | 'calendar_event'\n | 'invoice'\n | 'payment';\n\n/**\n * A single entity-change event as emitted onto the Planes event bus and\n * delivered inside a {@link WebhookDelivery}.\n *\n * Mirrored from `EntityEvent` in\n * `packages/shared/src/types/entity-events.ts`.\n */\nexport interface EntityEvent {\n /** What happened (e.g. 'created', 'status_changed'). */\n event_type: EntityEventType;\n /** The kind of record this event is about. */\n entity_type: EntityType;\n /** The affected record's ID. */\n entity_id: string;\n /** Tenant ID that owns this event. */\n tenant_id: string;\n /** Per-field before/after values for the change. */\n changes: Record<string, { old: unknown; new: unknown }>;\n /** Full record snapshot at the time of the event. */\n snapshot: Record<string, unknown>;\n /** ISO 8601 timestamp of when the event occurred. */\n timestamp: string;\n /** Correlation ID linking related events/actions. */\n correlation_id: string;\n}\n\n/**\n * The webhook delivery envelope Planes POSTs to a subscriber's URL.\n *\n * This is the outermost body: a delivery ID, the schema `version`, the wrapped\n * {@link EntityEvent}, and the subscription that matched. The entity body lives\n * at `event.snapshot` — there is no top-level `data`.\n */\nexport interface WebhookDelivery {\n /** Delivery ID (the delivery-log row id). */\n id: string;\n /**\n * Webhook payload schema version (from the `X-Planes-Webhook-Version`\n * header / envelope, emitted API-side per E39-F1-S2). v1 requires no\n * branching; surfaced here so consumers can switch on it in the future.\n */\n version: number;\n /** The entity-change event that triggered this delivery. */\n event: EntityEvent;\n /** ID of the webhook subscription that matched and produced this delivery. */\n subscription_id: string;\n}\n\n/**\n * Verify an HMAC-SHA256 webhook signature using timing-safe comparison.\n *\n * The signature header format is: `sha256=<hex-digest>`\n *\n * @param payload - The raw request body (string or Buffer)\n * @param signature - The X-Planes-Signature header value\n * @param secret - The webhook signing secret\n * @returns true if the signature is valid, false otherwise\n */\nexport function verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n): boolean {\n if (!signature || !secret || !payload) {\n return false;\n }\n\n // Expect format: sha256=<hex>\n const prefix = 'sha256=';\n if (!signature.startsWith(prefix)) {\n return false;\n }\n\n const receivedHex = signature.slice(prefix.length);\n if (!receivedHex) {\n return false;\n }\n\n const expectedHmac = createHmac('sha256', secret)\n .update(payload)\n .digest('hex');\n\n // Convert to buffers for timing-safe comparison\n const receivedBuf = Buffer.from(receivedHex, 'hex');\n const expectedBuf = Buffer.from(expectedHmac, 'hex');\n\n if (receivedBuf.length !== expectedBuf.length) {\n return false;\n }\n\n return timingSafeEqual(receivedBuf, expectedBuf);\n}\n\n/**\n * Parse and verify a webhook delivery payload.\n *\n * @param body - The raw request body as a string\n * @param signature - The X-Planes-Signature header value\n * @param secret - The webhook signing secret\n * @returns The parsed and verified {@link WebhookDelivery} envelope\n * @throws WebhookVerificationError if signature is invalid\n */\nexport function parseWebhookEvent(\n body: string,\n signature: string,\n secret: string,\n): WebhookDelivery {\n if (!verifyWebhookSignature(body, signature, secret)) {\n throw new WebhookVerificationError();\n }\n\n return JSON.parse(body) as WebhookDelivery;\n}\n","import { parseWebhookEvent, type WebhookDelivery } from './webhooks';\n\n/**\n * A handler invoked for a matching webhook delivery. May be sync or async;\n * async handlers are awaited by {@link WebhookRouter.process}.\n */\nexport type WebhookHandler = (delivery: WebhookDelivery) => void | Promise<void>;\n\n/** Input to {@link WebhookRouter.process} — the raw request pieces. */\nexport interface WebhookProcessInput {\n /** The raw request body as a string (verbatim — not re-serialized JSON). */\n body: string;\n /** The `X-Planes-Signature` header value (`sha256=<hex>`). */\n signature: string;\n /** The webhook signing secret for this subscription. */\n secret: string;\n}\n\n/**\n * Ergonomic event router over the corrected {@link WebhookDelivery} envelope.\n *\n * The platform models an event as two fields — `event.entity_type` and\n * `event.event_type` — which this router joins into a dotted composite key\n * `` `${entity_type}.${event_type}` `` (e.g. `opportunity.created`). Handlers\n * subscribe to that key, to an entity-scoped wildcard `` `${entity_type}.*` ``\n * (e.g. `opportunity.*`), or to the global wildcard `*`.\n *\n * The router is a pure dispatcher: it verifies + parses the delivery, then\n * fans it out to matching handlers. It holds no connection and no auth — the\n * signing secret is supplied per {@link process} call — so the same client's\n * `webhooks` router works across subscriptions. Wire it into your own HTTP\n * handler.\n *\n * @example\n * ```ts\n * client.webhooks\n * .on('opportunity.created', (d) => console.log(d.event.snapshot))\n * .on('opportunity.*', (d) => audit(d))\n * .on('*', (d) => metrics.count(d.event.entity_type));\n *\n * // inside your POST handler:\n * await client.webhooks.process({ body: rawBody, signature, secret });\n * ```\n *\n * ### Dotted `event_type` composite keys\n *\n * Four event types are themselves dotted — `entity.tagged`, `entity.untagged`,\n * `list.member_added`, `list.member_removed`. Joined with their entity these\n * yield keys with more than one dot, e.g. `customer.entity.tagged`. This router\n * matches on the literal `` `${entity_type}.${event_type}` `` string, so\n * subscribe with the full key (`'customer.entity.tagged'`). The entity wildcard\n * (`'customer.*'`) and global wildcard (`'*'`) still match these deliveries;\n * only mid-segment wildcards like `'customer.entity.*'` are intentionally\n * unsupported.\n */\nexport class WebhookRouter {\n /** pattern (composite key / entity wildcard / '*') → registered handlers. */\n private readonly handlers = new Map<string, WebhookHandler[]>();\n\n /**\n * Register a handler for an event pattern.\n *\n * @param pattern - `\"{entity_type}.{event_type}\"` (e.g. `opportunity.created`),\n * an entity wildcard `\"{entity_type}.*\"` (e.g. `opportunity.*`), or the\n * global wildcard `\"*\"`. Matching is exact-string against the derived key;\n * see the class docs for the dotted-`event_type` shape.\n * @param handler - Invoked with the full {@link WebhookDelivery} on a match.\n * @returns `this`, for chaining.\n */\n on(pattern: string, handler: WebhookHandler): this {\n const existing = this.handlers.get(pattern);\n if (existing) {\n existing.push(handler);\n } else {\n this.handlers.set(pattern, [handler]);\n }\n return this;\n }\n\n /**\n * Verify, parse, and dispatch a webhook delivery.\n *\n * The signature is verified first: on failure this throws\n * {@link WebhookVerificationError} and dispatches nothing. On success it\n * dispatches to every matching handler — specific key, then entity wildcard,\n * then global — awaiting async handlers before resolving.\n *\n * @param input - The raw `{ body, signature, secret }`.\n * @returns The parsed {@link WebhookDelivery}.\n * @throws WebhookVerificationError if the signature is invalid.\n */\n async process(input: WebhookProcessInput): Promise<WebhookDelivery> {\n // Verify + parse first; throws WebhookVerificationError before any dispatch.\n const delivery = parseWebhookEvent(input.body, input.signature, input.secret);\n\n const { entity_type, event_type } = delivery.event;\n // Candidate patterns, most specific first. Deriving them from the delivery\n // (rather than scanning registered patterns) keeps dispatch O(1) per key\n // and correctly handles dotted event_types like `entity.tagged`.\n const patterns = [`${entity_type}.${event_type}`, `${entity_type}.*`, '*'];\n\n for (const pattern of patterns) {\n const matched = this.handlers.get(pattern);\n if (!matched) continue;\n for (const handler of matched) {\n await handler(delivery);\n }\n }\n\n return delivery;\n }\n}\n","import type { PlanesConfig } from './config';\nimport { HttpClient } from './http';\nimport { OpportunityResource } from './resources/opportunities';\nimport { CustomerResource } from './resources/customers';\nimport { PropertyResource } from './resources/properties';\nimport { CalendarEventResource } from './resources/calendar-events';\nimport { ContractResource } from './resources/contracts';\nimport { JobResource } from './resources/jobs';\nimport { EstimateResource } from './resources/estimates';\nimport { InvoiceResource } from './resources/invoices';\nimport { InventoryResource } from './resources/inventory';\nimport { ContactResource } from './resources/contacts';\nimport { ActivityResource } from './resources/activities';\nimport { TagResource } from './resources/tags';\nimport { WebhookRouter } from './webhook-router';\n\n/**\n * PlanesClient — typed API client for the Planes CRM platform.\n *\n * @example\n * ```ts\n * const client = new PlanesClient({ accessToken: 'tok_...' });\n * const opps = await client.opportunities.list({ page: 1, limit: 20 });\n * ```\n */\nexport class PlanesClient {\n private readonly http: HttpClient;\n private _opportunities?: OpportunityResource;\n private _customers?: CustomerResource;\n private _properties?: PropertyResource;\n private _calendarEvents?: CalendarEventResource;\n private _contracts?: ContractResource;\n private _jobs?: JobResource;\n private _estimates?: EstimateResource;\n private _invoices?: InvoiceResource;\n private _inventory?: InventoryResource;\n private _contacts?: ContactResource;\n private _activities?: ActivityResource;\n private _tags?: TagResource;\n private _webhooks?: WebhookRouter;\n\n constructor(config: PlanesConfig) {\n const hasOAuthCreds = Boolean(config.clientId) || Boolean(config.clientSecret);\n const modes = [\n config.accessToken ? 'accessToken' : null,\n config.apiKey ? 'apiKey' : null,\n hasOAuthCreds ? 'clientId+clientSecret' : null,\n ].filter((m): m is string => m !== null);\n\n if (modes.length === 0) {\n throw new Error(\n 'PlanesClient requires one auth mode: accessToken, apiKey, or clientId + clientSecret',\n );\n }\n if (modes.length > 1) {\n throw new Error(\n `PlanesClient requires exactly one auth mode, not multiple (got: ${modes.join(', ')})`,\n );\n }\n if (hasOAuthCreds && (!config.clientId || !config.clientSecret)) {\n throw new Error('PlanesClient OAuth mode requires both clientId and clientSecret');\n }\n\n this.http = new HttpClient(config);\n }\n\n get opportunities(): OpportunityResource {\n if (!this._opportunities) {\n this._opportunities = new OpportunityResource(this.http);\n }\n return this._opportunities;\n }\n\n get customers(): CustomerResource {\n if (!this._customers) {\n this._customers = new CustomerResource(this.http);\n }\n return this._customers;\n }\n\n get properties(): PropertyResource {\n if (!this._properties) {\n this._properties = new PropertyResource(this.http);\n }\n return this._properties;\n }\n\n get calendarEvents(): CalendarEventResource {\n if (!this._calendarEvents) {\n this._calendarEvents = new CalendarEventResource(this.http);\n }\n return this._calendarEvents;\n }\n\n get contracts(): ContractResource {\n if (!this._contracts) {\n this._contracts = new ContractResource(this.http);\n }\n return this._contracts;\n }\n\n get jobs(): JobResource {\n if (!this._jobs) {\n this._jobs = new JobResource(this.http);\n }\n return this._jobs;\n }\n\n get estimates(): EstimateResource {\n if (!this._estimates) {\n this._estimates = new EstimateResource(this.http);\n }\n return this._estimates;\n }\n\n get invoices(): InvoiceResource {\n if (!this._invoices) {\n this._invoices = new InvoiceResource(this.http);\n }\n return this._invoices;\n }\n\n get inventory(): InventoryResource {\n if (!this._inventory) {\n this._inventory = new InventoryResource(this.http);\n }\n return this._inventory;\n }\n\n get contacts(): ContactResource {\n if (!this._contacts) {\n this._contacts = new ContactResource(this.http);\n }\n return this._contacts;\n }\n\n get activities(): ActivityResource {\n if (!this._activities) {\n this._activities = new ActivityResource(this.http);\n }\n return this._activities;\n }\n\n get tags(): TagResource {\n if (!this._tags) {\n this._tags = new TagResource(this.http);\n }\n return this._tags;\n }\n\n /**\n * Ergonomic webhook event router — `client.webhooks.on(pattern, handler)`\n * plus `client.webhooks.process({ body, signature, secret })`. Auth-independent\n * (the signing secret is supplied per `.process()` call), so it is created\n * lazily without the HTTP client. See {@link WebhookRouter}.\n */\n get webhooks(): WebhookRouter {\n if (!this._webhooks) {\n this._webhooks = new WebhookRouter();\n }\n return this._webhooks;\n }\n}\n","import * as http from 'node:http';\nimport type {\n ActionHandler,\n ActionContext,\n ActionResult,\n ActionStage,\n ActionMetadata,\n} from './types/agent-types';\n\n/**\n * The raw wire envelope the Planes platform POSTs to an agent endpoint.\n * Mirrors the `payload` built in `external-agent.handler.ts`. The SDK server\n * flattens `action_context` and lifts the callback fields onto {@link ActionContext}\n * before handing it to a handler.\n */\ninterface WireEnvelope {\n action_context?: {\n action_id: string;\n action_type: string;\n entity_type: string;\n entity_id: string;\n tenant_id: string;\n data?: Record<string, unknown>;\n stage?: ActionStage | null;\n metadata: ActionMetadata;\n };\n callback_token: string;\n callback_base_url: string;\n}\n\n/**\n * Options for Planes.register() and AgentApp.listen().\n */\nexport interface RegisterOptions {\n /** Port to listen on (default 3000). */\n port?: number;\n /** HTTP path to listen on (default '/actions'). */\n path?: string;\n /** Whether to verify incoming request signatures (default true). */\n verifySignature?: boolean;\n /** Error callback for unhandled errors. */\n onError?: (error: Error) => void;\n}\n\n/**\n * Shared server/dispatch factory used by both {@link Planes.register} and\n * {@link AgentApp.listen}. Given a resolved handler map, it creates an HTTP\n * server that parses the platform's `{ action_context, callback_token,\n * callback_base_url }` POST envelope, dispatches to the matching handler by\n * `action_context.action_type`, and serializes the {@link ActionResult} back as\n * the synchronous HTTP response body (which is exactly how the platform's\n * external-agent handler consumes the result).\n *\n * Keeping this in one place ensures the low-level `Planes.register` array\n * API and the `createAgentApp()` builder are guaranteed to behave identically\n * (routing, 400/404 handling, handler-error capture, graceful shutdown).\n */\nfunction createActionServer(\n handlerMap: Map<string, ActionHandler>,\n options?: RegisterOptions,\n): http.Server {\n const port = options?.port ?? 3000;\n const basePath = options?.path ?? '/actions';\n const onError = options?.onError;\n\n const server = http.createServer(async (req: http.IncomingMessage, res: http.ServerResponse) => {\n // Only handle POST to the configured path\n if (req.method !== 'POST' || req.url !== basePath) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not found' }));\n return;\n }\n\n try {\n const body = await readBody(req);\n\n // The platform POSTs a wrapped envelope:\n // { action_context: { action_type, ... }, callback_token, callback_base_url }\n // (see apps/api/.../external-agent.handler.ts). Parse and validate it.\n let envelope: WireEnvelope;\n try {\n envelope = JSON.parse(body) as WireEnvelope;\n } catch {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Malformed request body' }));\n return;\n }\n\n const wire = envelope?.action_context;\n const actionType = wire?.action_type;\n if (!wire || !actionType) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({ error: 'Missing action_context.action_type in request body' }),\n );\n return;\n }\n\n const handler = handlerMap.get(actionType);\n if (!handler) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n error: `No handler registered for action type: ${actionType}`,\n }),\n );\n return;\n }\n\n // Flatten the wire `action_context` and lift the envelope-level callback\n // fields + the X-Planes-Request-Id header onto a single handler context.\n const requestIdHeader = req.headers['x-planes-request-id'];\n const requestId = Array.isArray(requestIdHeader) ? requestIdHeader[0] : requestIdHeader;\n const context: ActionContext = {\n action_id: wire.action_id,\n action_type: actionType,\n entity_type: wire.entity_type,\n entity_id: wire.entity_id,\n tenant_id: wire.tenant_id,\n data: wire.data ?? {},\n stage: wire.stage ?? null,\n metadata: wire.metadata,\n callback_token: envelope.callback_token,\n callback_base_url: envelope.callback_base_url,\n // Only set request_id when the header was present (exactOptionalPropertyTypes).\n ...(requestId !== undefined ? { request_id: requestId } : {}),\n };\n\n let result: ActionResult;\n try {\n result = await handler.execute(context);\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : 'Unknown handler error';\n if (onError && err instanceof Error) {\n onError(err);\n }\n result = {\n status: 'failed',\n error: errorMessage,\n retryable: false,\n };\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result));\n } catch (err) {\n if (onError && err instanceof Error) {\n onError(err);\n }\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Internal server error' }));\n }\n });\n\n server.listen(port);\n\n // Graceful shutdown\n const shutdown = () => {\n server.close();\n };\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n\n return server;\n}\n\n/**\n * Planes agent registration namespace.\n * Provides a convenient way to register action handlers as an HTTP server.\n */\nexport const Planes = {\n /**\n * Register action handlers and start an HTTP server to receive action requests.\n *\n * @example\n * ```ts\n * Planes.register([\n * {\n * type: 'send_email',\n * async execute(ctx) {\n * // ... send email logic\n * return { status: 'completed', output: { sent: true } };\n * },\n * },\n * ], { port: 3000 });\n * ```\n */\n register(handlers: ActionHandler[], options?: RegisterOptions): http.Server {\n // Build handler map for O(1) lookup\n const handlerMap = new Map<string, ActionHandler>();\n for (const handler of handlers) {\n handlerMap.set(handler.type, handler);\n }\n return createActionServer(handlerMap, options);\n },\n};\n\n/**\n * Builder-style agent app. Register action handlers incrementally by type,\n * then start the server with {@link AgentApp.listen}.\n *\n * This is ergonomic sugar over {@link Planes.register} — both share the same\n * underlying server/dispatch implementation ({@link createActionServer}).\n *\n * @example\n * ```ts\n * createAgentApp()\n * .registerAction('enrich-opportunity', async (ctx) => {\n * // ... enrichment logic\n * return { status: 'completed', output: { enriched: true } };\n * })\n * .listen({ port: 3000 });\n * ```\n */\nexport class AgentApp {\n private readonly handlers = new Map<string, ActionHandler>();\n\n /**\n * Register a handler for an action type. Accepts a bare `execute` function\n * (the caller does not build the `{ type, execute }` object).\n *\n * @param type - The action type this handler responds to.\n * @param execute - The handler function invoked with the {@link ActionContext}.\n * @returns `this`, for chaining.\n */\n registerAction(type: string, execute: ActionHandler['execute']): this {\n this.handlers.set(type, { type, execute });\n return this;\n }\n\n /**\n * Start an HTTP server that dispatches incoming action requests to the\n * registered handlers. Delegates to the same server factory as\n * {@link Planes.register}.\n *\n * The handler set is snapshotted at call time, so registering further actions\n * after `listen()` does not mutate the running server (parity with\n * `Planes.register`, which snapshots its array).\n */\n listen(options?: RegisterOptions): http.Server {\n return createActionServer(new Map(this.handlers), options);\n }\n}\n\n/**\n * Create a new {@link AgentApp} builder.\n */\nexport function createAgentApp(): AgentApp {\n return new AgentApp();\n}\n\nfunction readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString()));\n req.on('error', reject);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4BO,IAAM,qBAAN,MAAiD;AAAA,EAGtD,YAAY,MAA4B;AACtC,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA,EAEA,MAAM,YAA0C;AAC9C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,UAAU,QAAqC;AACnD,SAAK,SAAS;AAAA,EAChB;AACF;AAwCO,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,sBAAsB;AAC5B,IAAM,gCAAgC;;;AClFtC,IAAM,cAAN,cAA0B,MAAM;AAAA,EAIrC,YAAY,SAAiB,QAAgB,MAAc;AACzD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,sBAAN,cAAkC,YAAY;AAAA,EACnD,YAAY,UAAU,yBAAyB;AAC7C,UAAM,SAAS,KAAK,sBAAsB;AAC1C,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,qBAAN,cAAiC,YAAY;AAAA,EAClD,YAAY,UAAU,4BAA4B;AAChD,UAAM,SAAS,KAAK,qBAAqB;AACzC,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAC7C,YAAY,UAAU,sBAAsB;AAC1C,UAAM,SAAS,KAAK,WAAW;AAC/B,SAAK,OAAO;AAAA,EACd;AACF;AAYO,IAAM,kBAAN,cAA8B,YAAY;AAAA,EAG/C,YAAY,UAAU,qBAAqB,cAA4B,CAAC,GAAG;AACzE,UAAM,SAAS,KAAK,kBAAkB;AACtC,SAAK,OAAO;AACZ,SAAK,cAAc;AAAA,EACrB;AACF;AAKO,IAAM,iBAAN,cAA6B,YAAY;AAAA,EAG9C,YAAY,aAAa,GAAG,UAAU,uBAAuB;AAC3D,UAAM,SAAS,KAAK,kBAAkB;AACtC,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAKO,IAAM,cAAN,cAA0B,YAAY;AAAA,EAC3C,YAAY,UAAU,yBAAyB;AAC7C,UAAM,SAAS,KAAK,cAAc;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,2BAAN,cAAuC,YAAY;AAAA,EACxD,YAAY,UAAU,yCAAyC;AAC7D,UAAM,SAAS,KAAK,4BAA4B;AAChD,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,aAAN,cAAyB,YAAY;AAAA,EAG1C,YAAY,WAAmB,UAAU,eAAe;AACtD,UAAM,SAAS,KAAK,aAAa;AACjC,SAAK,OAAO;AACZ,SAAK,YAAY;AAAA,EACnB;AACF;;;AC7GA,yBAA4B;AAkErB,SAAS,kBAAkB,SAAmC;AACnE,QAAM,QAAQ,QAAQ,aAAS,gCAAY,EAAE,EAAE,SAAS,KAAK;AAC7D,QAAM,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AAElD,QAAM,MAAM,IAAI,IAAI,GAAG,OAAO,kBAAkB;AAChD,MAAI,aAAa,IAAI,aAAa,QAAQ,QAAQ;AAClD,MAAI,aAAa,IAAI,gBAAgB,QAAQ,WAAW;AACxD,MAAI,aAAa,IAAI,SAAS,QAAQ,OAAO,KAAK,GAAG,CAAC;AACtD,MAAI,aAAa,IAAI,iBAAiB,MAAM;AAC5C,MAAI,aAAa,IAAI,SAAS,KAAK;AAEnC,SAAO,IAAI,SAAS;AACtB;AASA,eAAsB,aAAa,SAAsD;AACvF,QAAM,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AAClD,QAAM,MAAM,GAAG,OAAO;AAEtB,QAAM,OAAO,IAAI,gBAAgB;AAAA,IAC/B,YAAY;AAAA,IACZ,WAAW,QAAQ;AAAA,IACnB,eAAe,QAAQ;AAAA,IACvB,MAAM,QAAQ;AAAA,IACd,cAAc,QAAQ;AAAA,EACxB,CAAC;AAED,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,KAAK,SAAS;AAAA,EACtB,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACxD,UAAM,YAAa,UAAU,OAAO,KAAgB;AACpD,UAAM,mBAAoB,UAAU,mBAAmB,KAAgB;AACvE,UAAM,IAAI,WAAW,WAAW,gBAAgB;AAAA,EAClD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AASA,eAAsB,mBAAmB,SAAiD;AACxF,QAAM,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AAClD,QAAM,MAAM,GAAG,OAAO;AAEtB,QAAM,OAAO,IAAI,gBAAgB;AAAA,IAC/B,YAAY;AAAA,IACZ,WAAW,QAAQ;AAAA,IACnB,eAAe,QAAQ;AAAA,IACvB,eAAe,QAAQ;AAAA,EACzB,CAAC;AAED,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,KAAK,SAAS;AAAA,EACtB,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACxD,UAAM,YAAa,UAAU,OAAO,KAAgB;AACpD,UAAM,mBAAoB,UAAU,mBAAmB,KAAgB;AACvE,UAAM,IAAI,WAAW,WAAW,gBAAgB;AAAA,EAClD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;;;ACzGO,IAAM,aAAN,MAAiB;AAAA,EAWtB,YAAY,QAAsB;AAChC,SAAK,WAAW,OAAO,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACtE,SAAK,cAAc,OAAO;AAC1B,SAAK,SAAS,OAAO;AACrB,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,mBAAmB,OAAO,qBAAqB;AACpD,SAAK,aAAa,OAAO,cAAc;AAEvC,QAAI,OAAO,YAAY,OAAO,cAAc;AAC1C,WAAK,QAAQ;AAAA,QACX,UAAU,OAAO;AAAA,QACjB,cAAc,OAAO;AAAA,QACrB,cAAc,OAAO,gBAAgB,IAAI,mBAAmB;AAAA,QAC5D,QAAQ,OAAO,sBAAsB;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,QAAW,SAAyC;AACxD,UAAM,MAAM,KAAK,SAAS,QAAQ,MAAM,QAAQ,MAAM;AACtD,UAAM,UAAU,QAAQ,SAAS,SAAY,KAAK,UAAU,QAAQ,IAAI,IAAI;AAE5E,QAAI;AAGJ,QAAI,kBAAkB;AAEtB,aAAS,UAAU,GAAG,WAAW,KAAK,YAAY,WAAW;AAG3D,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,GAAI,MAAM,KAAK,mBAAmB;AAAA,QAClC,GAAG,QAAQ;AAAA,MACb;AACA,YAAM,eAA4B;AAAA,QAChC,QAAQ,QAAQ;AAAA,QAChB;AAAA,QACA,QAAQ,YAAY,QAAQ,KAAK,OAAO;AAAA,MAC1C;AACA,UAAI,YAAY,QAAW;AACzB,qBAAa,OAAO;AAAA,MACtB;AAEA,YAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAE9C,UAAI,SAAS,IAAI;AAEf,YAAI,SAAS,WAAW,KAAK;AAC3B,iBAAO;AAAA,QACT;AACA,eAAQ,MAAM,SAAS,KAAK;AAAA,MAC9B;AAEA,UAAI,SAAS,WAAW,OAAO,KAAK,oBAAoB,UAAU,KAAK,YAAY;AACjF,cAAM,aAAa,KAAK,gBAAgB,QAAQ;AAChD,cAAM,KAAK,MAAM,aAAa,GAAI;AAClC;AAAA,MACF;AAIA,UAAI,SAAS,WAAW,OAAO,KAAK,SAAS,CAAC,iBAAiB;AAC7D,0BAAkB;AAClB,cAAM,KAAK,cAAc;AACzB;AACA;AAAA,MACF;AAEA,kBAAY,MAAM,KAAK,SAAS,QAAQ;AAGxC,UAAI,SAAS,WAAW,OAAO,CAAC,KAAK,kBAAkB;AACrD,cAAM;AAAA,MACR;AAAA,IACF;AAGA,UAAM,aAAa,IAAI,eAAe;AAAA,EACxC;AAAA,EAEA,iBAAyC;AACvC,QAAI,KAAK,aAAa;AACpB,aAAO,EAAE,eAAe,UAAU,KAAK,WAAW,GAAG;AAAA,IACvD;AACA,QAAI,KAAK,QAAQ;AACf,aAAO,EAAE,aAAa,KAAK,OAAO;AAAA,IACpC;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,qBAAsD;AAClE,QAAI,CAAC,KAAK,OAAO;AACf,aAAO,KAAK,eAAe;AAAA,IAC7B;AAEA,UAAM,SAAS,MAAM,KAAK,MAAM,aAAa,UAAU;AACvD,QAAI,CAAC,QAAQ;AAGX,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,OAAO,YAAY,KAAK,IAAI,KAAK,KAAK,MAAM,QAAQ;AACtD,YAAM,YAAY,MAAM,KAAK,cAAc;AAC3C,aAAO,EAAE,eAAe,UAAU,UAAU,WAAW,GAAG;AAAA,IAC5D;AAEA,WAAO,EAAE,eAAe,UAAU,OAAO,WAAW,GAAG;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,gBAAuC;AACnD,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,oBAAoB,6DAA6D;AAAA,IAC7F;AACA,QAAI,KAAK,iBAAiB;AACxB,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAAQ,KAAK;AACnB,SAAK,mBAAmB,YAAY;AAClC,YAAM,UAAU,MAAM,MAAM,aAAa,UAAU;AACnD,UAAI,CAAC,SAAS,cAAc;AAC1B,cAAM,IAAI,oBAAoB,wDAAwD;AAAA,MACxF;AAEA,YAAM,WAAW,MAAM,mBAAmB;AAAA,QACxC,SAAS,KAAK;AAAA,QACd,UAAU,MAAM;AAAA,QAChB,cAAc,MAAM;AAAA,QACpB,cAAc,QAAQ;AAAA,MACxB,CAAC;AAED,YAAM,OAAqB;AAAA,QACzB,aAAa,SAAS;AAAA,QACtB,cAAc,SAAS,iBAAiB,QAAQ;AAAA,QAChD,WAAW,KAAK,IAAI,IAAI,SAAS,aAAa;AAAA,MAChD;AACA,YAAM,MAAM,aAAa,UAAU,IAAI;AACvC,aAAO;AAAA,IACT,GAAG;AAEH,QAAI;AACF,aAAO,MAAM,KAAK;AAAA,IACpB,UAAE;AACA,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,SAAS,MAAc,QAA0C;AACvE,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,GAAG,IAAI,EAAE;AAC5C,QAAI,QAAQ;AACV,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,YAAI,UAAU,UAAa,UAAU,MAAM;AACzC,cAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AACA,WAAO,IAAI,SAAS;AAAA,EACtB;AAAA,EAEQ,gBAAgB,UAA4B;AAClD,UAAM,SAAS,SAAS,QAAQ,IAAI,aAAa;AACjD,QAAI,QAAQ;AACV,YAAM,UAAU,OAAO,MAAM;AAC7B,UAAI,CAAC,OAAO,MAAM,OAAO,KAAK,UAAU,GAAG;AACzC,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,SAAS,UAA0C;AAC/D,QAAI,OAAgC,CAAC;AACrC,QAAI;AACF,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,QAAQ;AAAA,IAER;AAEA,UAAM,UAAW,KAAK,SAAS,KAAgB,SAAS,cAAc;AAEtE,YAAQ,SAAS,QAAQ;AAAA,MACvB,KAAK,KAAK;AACR,cAAM,cAAe,KAAK,QAAQ,KAAkC,CAAC;AACrE,eAAO,IAAI,gBAAgB,SAAS,WAAW;AAAA,MACjD;AAAA,MACA,KAAK;AACH,eAAO,IAAI,oBAAoB,OAAO;AAAA,MACxC,KAAK;AACH,eAAO,IAAI,mBAAmB,OAAO;AAAA,MACvC,KAAK;AACH,eAAO,IAAI,cAAc,OAAO;AAAA,MAClC,KAAK,KAAK;AACR,cAAM,aAAa,KAAK,gBAAgB,QAAQ;AAChD,eAAO,IAAI,eAAe,YAAY,OAAO;AAAA,MAC/C;AAAA,MACA;AACE,YAAI,SAAS,UAAU,KAAK;AAC1B,iBAAO,IAAI,YAAY,OAAO;AAAA,QAChC;AACA,eAAO,IAAI,YAAY,SAAS,SAAS,QAAQ,eAAe;AAAA,IACpE;AAAA,EACF;AAAA,EAEQ,MAAM,IAA2B;AACvC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AACF;;;ACtQO,IAAe,eAAf,MAIL;AAAA,EACA,YACqBA,OACA,UACnB;AAFmB,gBAAAA;AACA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKH,MAAM,KAAK,QAA0D;AACnE,WAAO,KAAK,KAAK,QAAoC;AAAA,MACnD,QAAQ;AAAA,MACR,MAAM,KAAK;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,IAA8B;AACtC,WAAO,KAAK,KAAK,QAAiB;AAAA,MAChC,QAAQ;AAAA,MACR,MAAM,GAAG,KAAK,QAAQ,IAAI,EAAE;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,MAAiC;AAC5C,WAAO,KAAK,KAAK,QAAiB;AAAA,MAChC,QAAQ;AAAA,MACR,MAAM,KAAK;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,IAAY,MAAiC;AACxD,WAAO,KAAK,KAAK,QAAiB;AAAA,MAChC,QAAQ;AAAA,MACR,MAAM,GAAG,KAAK,QAAQ,IAAI,EAAE;AAAA,MAC5B,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,IAA2B;AACtC,UAAM,KAAK,KAAK,QAAc;AAAA,MAC5B,QAAQ;AAAA,MACR,MAAM,GAAG,KAAK,QAAQ,IAAI,EAAE;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;;;ACrEO,IAAM,sBAAN,cAAkC,aAAa;AAAA,EACpD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,gBAAgB;AAAA,EAC9B;AACF;;;ACJO,IAAM,mBAAN,cAA+B,aAAa;AAAA,EACjD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,YAAY;AAAA,EAC1B;AACF;;;ACJO,IAAM,mBAAN,cAA+B,aAAa;AAAA,EACjD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,aAAa;AAAA,EAC3B;AACF;;;ACJO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,kBAAkB;AAAA,EAChC;AACF;;;ACJO,IAAM,mBAAN,cAA+B,aAAa;AAAA,EACjD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,YAAY;AAAA,EAC1B;AACF;;;ACJO,IAAM,cAAN,cAA0B,aAAa;AAAA,EAC5C,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,OAAO;AAAA,EACrB;AACF;;;ACJO,IAAM,mBAAN,cAA+B,aAAa;AAAA,EACjD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,YAAY;AAAA,EAC1B;AACF;;;ACJO,IAAM,kBAAN,cAA8B,aAAa;AAAA,EAChD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,WAAW;AAAA,EACzB;AACF;;;ACJO,IAAM,oBAAN,cAAgC,aAAa;AAAA,EAClD,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,kBAAkB;AAAA,EAChC;AACF;;;ACYO,IAAM,yBAAN,MAA6B;AAAA,EAGlC,YACmBC,OACjB,WACA;AAFiB,gBAAAA;AAGjB,SAAK,WAAW,aAAa,SAAS;AAAA,EACxC;AAAA;AAAA,EAGA,MAAM,OAAO,MAAiE;AAC5E,WAAO,KAAK,KAAK,QAAiC;AAAA,MAChD,QAAQ;AAAA,MACR,MAAM,KAAK;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,WAAmB,MAA2D;AACzF,WAAO,KAAK,KAAK,QAA2B;AAAA,MAC1C,QAAQ;AAAA,MACR,MAAM,GAAG,KAAK,QAAQ,IAAI,SAAS;AAAA,MACnC,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,WAAkC;AAC7C,UAAM,KAAK,KAAK,QAAc;AAAA,MAC5B,QAAQ;AAAA,MACR,MAAM,GAAG,KAAK,QAAQ,IAAI,SAAS;AAAA,IACrC,CAAC;AAAA,EACH;AACF;AAEO,IAAM,kBAAN,cAA8B,aAInC;AAAA,EACA,YAAYA,OAAqD;AAC/D,UAAMA,OAAM,WAAW;AAAA,EACzB;AAAA,EAEA,MAAM,MAAM,UAAkB,SAA8C;AAC1E,WAAO,KAAK,KAAK,QAA4B;AAAA,MAC3C,QAAQ;AAAA,MACR,MAAM,aAAa,QAAQ,UAAU,OAAO;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EAEA,SAAS,WAA2C;AAClD,WAAO,IAAI,uBAAuB,KAAK,MAAM,SAAS;AAAA,EACxD;AACF;;;ACrEO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YAA6BC,OAAkB;AAAlB,gBAAAA;AAAA,EAAmB;AAAA,EAEhD,MAAM,OAAO,MAA+C;AAC1D,WAAO,KAAK,KAAK,QAAqB;AAAA,MACpC,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,KAAK,QAA8D;AACvE,WAAO,KAAK,KAAK,QAAwC;AAAA,MACvD,QAAQ;AAAA,MACR,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cACJ,YACA,UACA,QACyC;AACzC,WAAO,KAAK,KAAK,QAAwC;AAAA,MACvD,QAAQ;AAAA,MACR,MAAM,IAAI,UAAU,IAAI,QAAQ;AAAA,MAChC;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACxBO,IAAM,cAAN,cAA0B,aAAiD;AAAA,EAChF,YAAYC,OAAqD;AAC/D,UAAMA,OAAM,OAAO;AAAA,EACrB;AAAA;AAAA,EAGA,MAAM,cACJ,YACA,UACA,MAC6B;AAC7B,WAAO,KAAK,KAAK,QAA4B;AAAA,MAC3C,QAAQ;AAAA,MACR,MAAM,aAAa,UAAU,IAAI,QAAQ;AAAA,MACzC,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cAAc,YAAoB,UAA2C;AACjF,WAAO,KAAK,KAAK,QAAwB;AAAA,MACvC,QAAQ;AAAA,MACR,MAAM,aAAa,UAAU,IAAI,QAAQ;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,OAAe,QAA4D;AAC3F,WAAO,KAAK,KAAK,QAAyB;AAAA,MACxC,QAAQ;AAAA,MACR,MAAM,SAAS,KAAK;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AC1CA,IAAAC,sBAA4C;AA0GrC,SAAS,uBACd,SACA,WACA,QACS;AACT,MAAI,CAAC,aAAa,CAAC,UAAU,CAAC,SAAS;AACrC,WAAO;AAAA,EACT;AAGA,QAAM,SAAS;AACf,MAAI,CAAC,UAAU,WAAW,MAAM,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,UAAU,MAAM,OAAO,MAAM;AACjD,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,mBAAe,gCAAW,UAAU,MAAM,EAC7C,OAAO,OAAO,EACd,OAAO,KAAK;AAGf,QAAM,cAAc,OAAO,KAAK,aAAa,KAAK;AAClD,QAAM,cAAc,OAAO,KAAK,cAAc,KAAK;AAEnD,MAAI,YAAY,WAAW,YAAY,QAAQ;AAC7C,WAAO;AAAA,EACT;AAEA,aAAO,qCAAgB,aAAa,WAAW;AACjD;AAWO,SAAS,kBACd,MACA,WACA,QACiB;AACjB,MAAI,CAAC,uBAAuB,MAAM,WAAW,MAAM,GAAG;AACpD,UAAM,IAAI,yBAAyB;AAAA,EACrC;AAEA,SAAO,KAAK,MAAM,IAAI;AACxB;;;ACzGO,IAAM,gBAAN,MAAoB;AAAA,EAApB;AAEL;AAAA,SAAiB,WAAW,oBAAI,IAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY9D,GAAG,SAAiB,SAA+B;AACjD,UAAM,WAAW,KAAK,SAAS,IAAI,OAAO;AAC1C,QAAI,UAAU;AACZ,eAAS,KAAK,OAAO;AAAA,IACvB,OAAO;AACL,WAAK,SAAS,IAAI,SAAS,CAAC,OAAO,CAAC;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,QAAQ,OAAsD;AAElE,UAAM,WAAW,kBAAkB,MAAM,MAAM,MAAM,WAAW,MAAM,MAAM;AAE5E,UAAM,EAAE,aAAa,WAAW,IAAI,SAAS;AAI7C,UAAM,WAAW,CAAC,GAAG,WAAW,IAAI,UAAU,IAAI,GAAG,WAAW,MAAM,GAAG;AAEzE,eAAW,WAAW,UAAU;AAC9B,YAAM,UAAU,KAAK,SAAS,IAAI,OAAO;AACzC,UAAI,CAAC,QAAS;AACd,iBAAW,WAAW,SAAS;AAC7B,cAAM,QAAQ,QAAQ;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACtFO,IAAM,eAAN,MAAmB;AAAA,EAgBxB,YAAY,QAAsB;AAChC,UAAM,gBAAgB,QAAQ,OAAO,QAAQ,KAAK,QAAQ,OAAO,YAAY;AAC7E,UAAM,QAAQ;AAAA,MACZ,OAAO,cAAc,gBAAgB;AAAA,MACrC,OAAO,SAAS,WAAW;AAAA,MAC3B,gBAAgB,0BAA0B;AAAA,IAC5C,EAAE,OAAO,CAAC,MAAmB,MAAM,IAAI;AAEvC,QAAI,MAAM,WAAW,GAAG;AACtB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,IAAI;AAAA,QACR,mEAAmE,MAAM,KAAK,IAAI,CAAC;AAAA,MACrF;AAAA,IACF;AACA,QAAI,kBAAkB,CAAC,OAAO,YAAY,CAAC,OAAO,eAAe;AAC/D,YAAM,IAAI,MAAM,iEAAiE;AAAA,IACnF;AAEA,SAAK,OAAO,IAAI,WAAW,MAAM;AAAA,EACnC;AAAA,EAEA,IAAI,gBAAqC;AACvC,QAAI,CAAC,KAAK,gBAAgB;AACxB,WAAK,iBAAiB,IAAI,oBAAoB,KAAK,IAAI;AAAA,IACzD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAA8B;AAChC,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,aAAa,IAAI,iBAAiB,KAAK,IAAI;AAAA,IAClD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,aAA+B;AACjC,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,cAAc,IAAI,iBAAiB,KAAK,IAAI;AAAA,IACnD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,iBAAwC;AAC1C,QAAI,CAAC,KAAK,iBAAiB;AACzB,WAAK,kBAAkB,IAAI,sBAAsB,KAAK,IAAI;AAAA,IAC5D;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAA8B;AAChC,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,aAAa,IAAI,iBAAiB,KAAK,IAAI;AAAA,IAClD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,OAAoB;AACtB,QAAI,CAAC,KAAK,OAAO;AACf,WAAK,QAAQ,IAAI,YAAY,KAAK,IAAI;AAAA,IACxC;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAA8B;AAChC,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,aAAa,IAAI,iBAAiB,KAAK,IAAI;AAAA,IAClD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,WAA4B;AAC9B,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,IAAI,gBAAgB,KAAK,IAAI;AAAA,IAChD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAA+B;AACjC,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,aAAa,IAAI,kBAAkB,KAAK,IAAI;AAAA,IACnD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,WAA4B;AAC9B,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,IAAI,gBAAgB,KAAK,IAAI;AAAA,IAChD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,aAA+B;AACjC,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,cAAc,IAAI,iBAAiB,KAAK,IAAI;AAAA,IACnD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,OAAoB;AACtB,QAAI,CAAC,KAAK,OAAO;AACf,WAAK,QAAQ,IAAI,YAAY,KAAK,IAAI;AAAA,IACxC;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,WAA0B;AAC5B,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,IAAI,cAAc;AAAA,IACrC;AACA,WAAO,KAAK;AAAA,EACd;AACF;;;AClKA,WAAsB;AAyDtB,SAAS,mBACP,YACA,SACa;AACb,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,WAAW,SAAS,QAAQ;AAClC,QAAM,UAAU,SAAS;AAEzB,QAAM,SAAc,kBAAa,OAAO,KAA2B,QAA6B;AAE9F,QAAI,IAAI,WAAW,UAAU,IAAI,QAAQ,UAAU;AACjD,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,YAAY,CAAC,CAAC;AAC9C;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,GAAG;AAK/B,UAAI;AACJ,UAAI;AACF,mBAAW,KAAK,MAAM,IAAI;AAAA,MAC5B,QAAQ;AACN,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,yBAAyB,CAAC,CAAC;AAC3D;AAAA,MACF;AAEA,YAAM,OAAO,UAAU;AACvB,YAAM,aAAa,MAAM;AACzB,UAAI,CAAC,QAAQ,CAAC,YAAY;AACxB,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI;AAAA,UACF,KAAK,UAAU,EAAE,OAAO,qDAAqD,CAAC;AAAA,QAChF;AACA;AAAA,MACF;AAEA,YAAM,UAAU,WAAW,IAAI,UAAU;AACzC,UAAI,CAAC,SAAS;AACZ,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI;AAAA,UACF,KAAK,UAAU;AAAA,YACb,OAAO,0CAA0C,UAAU;AAAA,UAC7D,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAIA,YAAM,kBAAkB,IAAI,QAAQ,qBAAqB;AACzD,YAAM,YAAY,MAAM,QAAQ,eAAe,IAAI,gBAAgB,CAAC,IAAI;AACxE,YAAM,UAAyB;AAAA,QAC7B,WAAW,KAAK;AAAA,QAChB,aAAa;AAAA,QACb,aAAa,KAAK;AAAA,QAClB,WAAW,KAAK;AAAA,QAChB,WAAW,KAAK;AAAA,QAChB,MAAM,KAAK,QAAQ,CAAC;AAAA,QACpB,OAAO,KAAK,SAAS;AAAA,QACrB,UAAU,KAAK;AAAA,QACf,gBAAgB,SAAS;AAAA,QACzB,mBAAmB,SAAS;AAAA;AAAA,QAE5B,GAAI,cAAc,SAAY,EAAE,YAAY,UAAU,IAAI,CAAC;AAAA,MAC7D;AAEA,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,QAAQ,QAAQ,OAAO;AAAA,MACxC,SAAS,KAAK;AACZ,cAAM,eACJ,eAAe,QAAQ,IAAI,UAAU;AACvC,YAAI,WAAW,eAAe,OAAO;AACnC,kBAAQ,GAAG;AAAA,QACb;AACA,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,WAAW;AAAA,QACb;AAAA,MACF;AAEA,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,IAChC,SAAS,KAAK;AACZ,UAAI,WAAW,eAAe,OAAO;AACnC,gBAAQ,GAAG;AAAA,MACb;AACA,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,wBAAwB,CAAC,CAAC;AAAA,IAC5D;AAAA,EACF,CAAC;AAED,SAAO,OAAO,IAAI;AAGlB,QAAM,WAAW,MAAM;AACrB,WAAO,MAAM;AAAA,EACf;AACA,UAAQ,GAAG,WAAW,QAAQ;AAC9B,UAAQ,GAAG,UAAU,QAAQ;AAE7B,SAAO;AACT;AAMO,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBpB,SAAS,UAA2B,SAAwC;AAE1E,UAAM,aAAa,oBAAI,IAA2B;AAClD,eAAW,WAAW,UAAU;AAC9B,iBAAW,IAAI,QAAQ,MAAM,OAAO;AAAA,IACtC;AACA,WAAO,mBAAmB,YAAY,OAAO;AAAA,EAC/C;AACF;AAmBO,IAAM,WAAN,MAAe;AAAA,EAAf;AACL,SAAiB,WAAW,oBAAI,IAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU3D,eAAe,MAAc,SAAyC;AACpE,SAAK,SAAS,IAAI,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,SAAwC;AAC7C,WAAO,mBAAmB,IAAI,IAAI,KAAK,QAAQ,GAAG,OAAO;AAAA,EAC3D;AACF;AAKO,SAAS,iBAA2B;AACzC,SAAO,IAAI,SAAS;AACtB;AAEA,SAAS,SAAS,KAA4C;AAC5D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,QAAI,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,CAAC,CAAC;AAC7D,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;","names":["http","http","http","http","http","http","http","http","http","http","http","http","http","import_node_crypto"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/oauth.ts","../src/http.ts","../src/resources/base-resource.ts","../src/resources/query-params.ts","../src/resources/opportunities.ts","../src/resources/customers.ts","../src/resources/properties.ts","../src/resources/calendar-events.ts","../src/resources/contracts.ts","../src/resources/jobs.ts","../src/resources/estimates.ts","../src/resources/invoices.ts","../src/resources/inventory.ts","../src/resources/contacts.ts","../src/resources/activities.ts","../src/resources/communications.ts","../src/resources/conversations.ts","../src/resources/gmail.ts","../src/resources/documents.ts","../src/resources/tags.ts","../src/resources/tasks.ts","../src/resources/record-views.ts","../src/resources/modules.ts","../src/resources/plan.ts","../src/resources/me.ts","../src/resources/roles.ts","../src/resources/invoice-settings.ts","../src/resources/invoice-reminders.ts","../src/resources/contact-sequences.ts","../src/resources/enrollments.ts","../src/resources/workflow-event-rules.ts","../src/resources/workflows.ts","../src/resources/service-schedules.ts","../src/resources/billing-schedules.ts","../src/resources/custom-fields.ts","../src/resources/ortto.ts","../src/webhooks.ts","../src/webhook-router.ts","../src/client.ts","../src/types/billing-schedule-types.ts","../src/types/contact-types.ts","../src/types/customer-types.ts","../src/types/opportunity-types.ts","../src/types/job-types.ts","../src/types/estimate-types.ts","../src/types/record-view-types.ts","../src/types/module-types.ts","../src/types/plan-types.ts","../src/types/me-types.ts","../src/types/workflow-event-rule-types.ts","../src/agent.ts"],"sourcesContent":["// Client\nexport { PlanesClient } from './client';\n\n// Config\nexport type { PlanesConfig } from './config';\nexport { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES } from './config';\n\n// Errors\nexport {\n PlanesError,\n AuthenticationError,\n AuthorizationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ServerError,\n WebhookVerificationError,\n OAuthError,\n} from './errors';\nexport type { FieldError } from './errors';\n\n// Types\nexport type { PaginatedResponse, ListParams } from './types';\nexport type { ListCalendarEventsParams } from './resources/calendar-events';\nexport type {\n CompleteCalendarEventDto,\n CompleteCalendarEventResponse,\n CompletionWorkflowTrigger,\n CompletionTriggerStage,\n CompletionTriggerStatus,\n CompletionTriggerReason,\n UpdateCalendarEventDto,\n UpdateCalendarEventOptions,\n RecurrenceEditMode,\n CalendarEventParticipantDto,\n CalendarEventType,\n CalendarEventLocationType,\n CalendarEventStatus,\n} from './types/calendar-event-types';\n\n// Resources\nexport {\n ApiResource,\n BaseResource,\n OpportunityResource,\n CustomerResource,\n PropertyResource,\n CalendarEventResource,\n ContractResource,\n JobResource,\n EstimateResource,\n InvoiceResource,\n InventoryResource,\n ContactResource,\n ContactChannelResource,\n ActivityResource,\n CommunicationsResource,\n ConversationsResource,\n GmailResource,\n DocumentsResource,\n TagResource,\n TaskResource,\n RecordViewsResource,\n ModulesResource,\n PlanResource,\n MeResource,\n RolesResource,\n InvoiceSettingsResource,\n InvoiceRemindersResource,\n ContactSequencesResource,\n EnrollmentsResource,\n WorkflowsResource,\n WorkflowEventRulesResource,\n OrttoResource,\n ServiceSchedulesResource,\n BillingScheduleResource,\n} from './resources';\n\n// Service schedule types (crew conflict preview)\nexport type {\n ConflictPreviewParticipant,\n ConflictPreviewWindow,\n ConflictPreviewSchedule,\n ConflictPreviewRequest,\n ConflictPreviewResponse,\n CrewOverlapConflictDto,\n WorkingHoursConflictDto,\n} from './types/service-schedule-types';\n\n// Billing schedule types (E56-F3-S4 — recurring billing)\nexport {\n BILLING_CADENCES,\n BILLING_SCHEDULE_STATUSES,\n BILL_TIMINGS,\n BILLING_DELIVERY_MODES,\n BILLING_COLLECTION_MODES,\n BILLING_BACKFILL_MODES,\n BILLING_PERIOD_STATUSES,\n BILLING_PREVIEW_PERIOD_STATUSES,\n BILLING_DELIVERY_STATUSES,\n BILLING_ADJUSTMENT_STATUSES,\n BILLING_ADJUSTMENT_TARGETS,\n AUTOPAY_UNAVAILABLE_REASONS,\n BILLING_AUTOPAY_STATUSES,\n} from './types/billing-schedule-types';\nexport type {\n BillingCadence,\n BillingScheduleStatus,\n BillTiming,\n BillingDeliveryMode,\n BillingCollectionMode,\n AutopayUnavailableReason,\n BillingAutopayStatus,\n SavedPaymentMethodSummary,\n BillingBackfillMode,\n BillingPeriodStatus,\n BillingPreviewPeriodStatus,\n BillingDeliveryStatus,\n BillingInvoiceTerms,\n BillingInvoiceStatus,\n BillingScheduleLineInput,\n CreateBillingScheduleDto,\n UpdateBillingScheduleDto,\n BillingScheduleListParams,\n BillingSchedulePreviewParams,\n BillingSchedulePeriodListParams,\n BillingNeedsReviewParams,\n SkipBillingPeriodDto,\n BillingUpcomingPeriod,\n BillingScheduleLine,\n BillingScheduleLineVersion,\n BillingScheduleLineVersionListResponse,\n BillingAdjustmentStatus,\n BillingAdjustmentTarget,\n CreateBillingAdjustmentDto,\n BillingAdjustmentListParams,\n BillingAdjustment,\n BillingAdjustmentListResponse,\n BillingPreviewAdjustment,\n BillingSchedule,\n BillingScheduleDetail,\n BillingScheduleListItem,\n BillingSchedulePreviewPeriod,\n BillingSchedulePreview,\n BillingSchedulePeriod,\n BillingNeedsReviewItem,\n BillingScheduleListResponse,\n BillingSchedulePeriodListResponse,\n BillingNeedsReviewResponse,\n BillingAutopaySummary,\n BillingAutopaySummaryResponse,\n GenerateNowResult,\n RegeneratePeriodResult,\n} from './types/billing-schedule-types';\n\n// Contact types\nexport { CONTACT_SORT_KEYS, CONTACT_LIST_INCLUDES } from './types/contact-types';\nexport type {\n ContactDto,\n ContactListItemDto,\n ContactListInclude,\n ContactLinkDto,\n ContactLinkTargetType,\n ContactListParams,\n ContactSortKey,\n CreateContactDto,\n UpdateContactDto,\n ContactChannelDto,\n CreateContactChannelDto,\n UpdateContactChannelDto,\n ContactMergeResult,\n} from './types/contact-types';\n\n// Consent state on contact-channel / customer reads (E53-F5-S1)\nexport type { ConsentStateDto, ConsentLastChangeDto } from './types/consent-types';\n\n// Customer types\nexport { CUSTOMER_SORT_KEYS } from './types/customer-types';\nexport type { CustomerMergeResult, CustomerAddressInput, UpdateCustomerDto, CustomerListParams, CustomerSortKey } from './types/customer-types';\n\n// Opportunity types\nexport { OPPORTUNITY_SORT_KEYS, OPPORTUNITY_STATES } from './types/opportunity-types';\nexport type {\n OpportunityListParams,\n OpportunitySortKey,\n OpportunityState,\n UpdateOpportunityDto,\n} from './types/opportunity-types';\n\n// Property types\nexport type { CreatePropertyDto, UpdatePropertyDto, PropertyListParams } from './types/property-types';\n\n// Inventory item types\nexport type {\n CreateInventoryItemDto,\n UpdateInventoryItemDto,\n InventoryItemListParams,\n InventoryUnitOfMeasure,\n} from './types/inventory-types';\n\n// Invoice types\nexport type {\n InvoiceListItemDto,\n InvoiceListParams,\n InvoiceStatus,\n InvoiceSourceType,\n InvoicePayLinkStatusDto,\n InvoicePayLinkDto,\n InvoiceDetailDto,\n InvoiceLineItemDto,\n InvoicePaymentDto,\n PaymentStatus,\n OverdueInvoiceDto,\n OverdueInvoiceListParams,\n InvoiceOverdueSummaryDto,\n} from './types/invoice-types';\n\n// Job types\nexport { JOB_SORT_KEYS } from './types/job-types';\nexport type {\n CreateJobDto,\n UpdateJobDto,\n JobListParams,\n JobSortKey,\n JobStatus,\n JobTradeCoverageItemDto,\n JobTradeCoverageResponse,\n} from './types/job-types';\n\n// Estimate types\nexport { ESTIMATE_STATUSES } from './types/estimate-types';\nexport type {\n CreateEstimateDto,\n UpdateEstimateDto,\n EstimateListParams,\n EstimateStatus,\n EstimateSummaryDto,\n} from './types/estimate-types';\n\n// Record view types (E41-F7 — \"recently viewed\")\nexport { RECORD_VIEW_ENTITY_TYPES } from './types/record-view-types';\nexport type {\n RecordViewEntityType,\n RecordViewInput,\n RecentRecordViewsParams,\n RecentRecordView,\n} from './types/record-view-types';\n\n// Module status types (E51-F1-S1)\nexport { MODULE_KEYS } from './types/module-types';\nexport type { ModuleKey, TenantModuleStatusDto } from './types/module-types';\n\n// Plan (E54-F1-S2)\nexport { PLAN_KEYS, BILLING_STATUSES, PLAN_SOURCES, BILLING_INTERVALS, FEATURE_KEYS } from './types/plan-types';\nexport type {\n PlanKey,\n BillingStatus,\n PlanSource,\n BillingInterval,\n FeatureKey,\n TenantPlanDto,\n} from './types/plan-types';\n\n// Caller abilities (E52-F1-S3)\nexport { AGENT_ROLES } from './types/me-types';\nexport type { AgentRole, MeAbilitiesResponse, PackedAbilityRule } from './types/me-types';\n\n// Invoice settings (E56-F5-S4)\nexport type { OfflineInstructionsDto, OverdueOffsetsDto, AttachPdfSettingDto } from './resources';\n\n// Payment reminder settings (E56-F8-S6)\nexport type {\n InvoiceReminderAppliesTo,\n InvoiceReminderRuleInputDto,\n InvoiceReminderRuleDto,\n InvoiceReminderSettingsDto,\n InvoiceReminderPreviewParams,\n InvoiceReminderPreviewDto,\n} from './resources';\n\n// Settings → Roles (E52-F2-S5)\nexport type {\n RoleKey,\n TunableRole,\n GrantEffect,\n GrantTier,\n RoleSummaryDto,\n RoleListResponse,\n RoleCellDto,\n RoleRowDto,\n RoleDetailDto,\n RoleGrantChange,\n UpdateRoleGrantsBody,\n RoleHistoryParams,\n RoleHistoryEntryDto,\n RoleHistoryPage,\n} from './types/role-types';\n\n// Enrollable sequence types (E51-F2-S6)\nexport type {\n ContactSequenceDto,\n ContactSequenceListResponse,\n} from './types/contact-sequence-types';\n\n// Sequence enrollment types (E51-F2-S8)\nexport type {\n EnrollmentStatus,\n EnrollmentTrigger,\n ContactEnrollmentDto,\n ContactEnrollmentListItemDto,\n ListEnrollmentsParams,\n} from './types/enrollment-types';\n\n// Workflow version stage types\nexport type {\n WorkflowEntityType,\n WorkflowVersionStageDto,\n WorkflowVersionStagesResponse,\n WorkflowEndOutcome,\n} from './types/workflow-stage-types';\n\n// Workflow transition authoring types (admin workflow API)\nexport type {\n CreateWorkflowTransitionInput,\n UpdateWorkflowTransitionInput,\n WorkflowTransitionDto,\n} from './types/workflow-transition-types';\n\n// Custom-field definitions (GET /custom-fields)\nexport { CustomFieldsResource } from './resources';\nexport type { CustomFieldDefinitionDto, ListCustomFieldDefinitionsParams } from './resources';\nexport type {\n CustomFieldEntityType,\n CustomFieldType,\n CustomFieldValidationRules,\n} from './types/custom-field-types';\n\n// Workflow event rules (admin workflow API, /admin/workflows/:wid/event-rules)\nexport {\n WORKFLOW_EVENT_RULE_EVENT_TYPES,\n WORKFLOW_EVENT_RULE_MATCH_ALL_EVENT_TYPES,\n} from './types/workflow-event-rule-types';\nexport type {\n WorkflowEventRuleEventType,\n WorkflowEventRuleWhen,\n WorkflowTransitionRef,\n CreateWorkflowEventRuleInput,\n UpdateWorkflowEventRuleInput,\n WorkflowEventRuleDto,\n DeleteWorkflowEventRuleResult,\n} from './types/workflow-event-rule-types';\n\n// Workflow transition summaries (GET /workflows/transitions)\nexport type {\n TransitionSummaryDto,\n TransitionSummariesResponse,\n ListTransitionSummariesParams,\n} from './types/workflow-transition-summary-types';\n\n// Activity types\nexport type { ActivityDto, CreateActivityDto } from './types/activity-types';\n\n// Communication types\nexport type {\n CommunicationAttachmentDto,\n CommunicationChannel,\n CommunicationEntityType,\n CommunicationTarget,\n CallOutcome,\n LogCommunicationDto,\n LogEmailCommunicationDto,\n LogCallCommunicationDto,\n LogCommunicationResult,\n ListCommunicationsParams,\n LinkCommunicationDto,\n CommunicationsListResponse,\n CommunicationSummaryDto,\n CommunicationDto,\n} from './types/communication-types';\n\n// Conversation types (E55 — logged meetings)\nexport type {\n ConversationKind,\n ConversationMatchStatus,\n ConversationLinkEntityType,\n ConversationAttendeeDto,\n ConversationActionItemDto,\n LogConversationDto,\n PatchConversationDto,\n LinkConversationDto,\n ResolveConversationMatchDto,\n InternalDomainsDto,\n ListConversationsParams,\n ConversationParticipant,\n ConversationLink,\n ConversationMatchCandidate,\n Conversation,\n ConversationListItem,\n ConversationListResponse,\n LogConversationResult,\n InternalDomainsResult,\n SourceConnectionStatus,\n DisconnectSourceConnectionDto,\n SourceConnectionResult,\n PullConversationsDto,\n PullConversationsResult,\n PullStatus,\n PullStatusCounts,\n PullStatusState,\n SyncNowResult,\n ConversationSyncErrorClass,\n ConversationSyncStatusMine,\n ConversationSyncRosterItem,\n ConversationSyncStatus,\n TranscriptSegment,\n ConversationTranscript,\n TranscriptAccessMode,\n TranscriptAccess,\n} from './types/conversation-types';\n\n// Gmail send types\nexport type { SendGmailDto, SendGmailResult } from './types/gmail-types';\n\n// Document types (Drive Rung 0 — external references)\nexport type {\n DocumentEntityType,\n DocumentKind,\n AttachExternalDocumentDto,\n DocumentDto,\n DocumentsListResponse,\n} from './types/document-types';\n\n// Tag types\nexport type {\n TagDto,\n CreateTagDto,\n UpdateTagDto,\n SetEntityTagsDto,\n EntityTagDto,\n TagEntityDto,\n} from './types/tag-types';\n\n// Task types\nexport type {\n TaskDto,\n TaskAssigneeDto,\n TaskAssigneeInput,\n TaskAssigneeType,\n TaskStatus,\n TaskPriority,\n TaskEntityType,\n CompletedTaskDto,\n CreateTaskDto,\n UpdateTaskDto,\n ListTasksParams,\n} from './types/task-types';\n\n// Ortto connector types (E53)\nexport type {\n OrttoMappingDirection,\n OrttoMappingDto,\n OrttoAudienceDto,\n CreateOrttoMappingDto,\n CreateOrttoPushMappingDto,\n CreateOrttoPullMappingDto,\n UpdateOrttoMappingDto,\n ListOrttoAudiencesParams,\n RequestOrttoSyncDto,\n OrttoSyncRequestResult,\n OrttoSyncStatusDto,\n OrttoSyncTotals,\n OrttoSyncLastRun,\n OrttoMappingLastResult,\n} from './types/ortto-types';\n\n// Agent types\nexport type {\n ActionHandler,\n ActionContext,\n ActionResult,\n ActionRequest,\n AgentHandler,\n HandlerMap,\n TriggerType,\n PermissionLevel,\n} from './types/agent-types';\n\n// Agent registration\nexport { Planes } from './agent';\nexport type { RegisterOptions } from './agent';\nexport { createAgentApp, AgentApp } from './agent';\n\n// Webhooks\nexport { verifyWebhookSignature, parseWebhookEvent, WEBHOOK_EVENT_TYPES } from './webhooks';\nexport type {\n WebhookDelivery,\n EntityEvent,\n EntityEventType,\n EntityType,\n InvoiceOverdueSnapshot,\n InvoiceOverdueEvent,\n IntegrationConnectionDeactivatedSnapshot,\n IntegrationConnectionDeactivatedEvent,\n CustomerPaymentMethodInvalidatedSnapshot,\n CustomerPaymentMethodInvalidatedEvent,\n InvoiceAutopaySucceededSnapshot,\n InvoiceAutopaySucceededEvent,\n InvoiceAutopayFailedSnapshot,\n InvoiceAutopayFailedEvent,\n} from './webhooks';\nexport { WebhookRouter } from './webhook-router';\nexport type { WebhookHandler, WebhookProcessInput } from './webhook-router';\n\n// OAuth\nexport {\n buildAuthorizeUrl,\n exchangeCode,\n refreshAccessToken,\n clientCredentialsGrant,\n} from './oauth';\nexport type {\n AuthorizeOptions,\n ExchangeCodeOptions,\n RefreshOptions,\n ClientCredentialsOptions,\n TokenResponse,\n} from './oauth';\n\n// OAuth-managed token storage (E39-F2-S4)\nexport { MemoryTokenStorage, DEFAULT_TOKEN_REFRESH_SKEW_MS } from './config';\nexport type { TokenStorage, StoredTokens } from './config';\n\n// Generated types (from OpenAPI spec)\nexport * from './generated';\n","/**\n * Tokens managed by an OAuth-credential client, persisted via {@link TokenStorage}.\n */\nexport interface StoredTokens {\n /** Current OAuth access token. */\n accessToken: string;\n /**\n * Refresh token used to obtain a new access token. `null` for tokens issued\n * via the client_credentials grant, which have no refresh token — the\n * client re-authenticates with clientId/clientSecret instead.\n */\n refreshToken: string | null;\n /** Access-token expiry as epoch milliseconds. */\n expiresAt: number;\n}\n\n/**\n * Pluggable persistence for OAuth-managed tokens. Implement this to back the\n * client with your own store (encrypted disk, database, secrets manager, ...).\n * The default is {@link MemoryTokenStorage}.\n */\nexport interface TokenStorage {\n /** Load the currently stored tokens, or `null` if none are stored. */\n getTokens(): Promise<StoredTokens | null>;\n /** Persist the given tokens (called after every refresh). */\n setTokens(tokens: StoredTokens): Promise<void>;\n}\n\n/**\n * Default in-memory {@link TokenStorage}. Tokens live only for the process\n * lifetime — provide your own implementation to persist across restarts.\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private tokens: StoredTokens | null;\n\n constructor(seed?: StoredTokens | null) {\n this.tokens = seed ?? null;\n }\n\n async getTokens(): Promise<StoredTokens | null> {\n return this.tokens;\n }\n\n async setTokens(tokens: StoredTokens): Promise<void> {\n this.tokens = tokens;\n }\n}\n\n/**\n * Configuration for the PlanesClient.\n *\n * Exactly one authentication mode must be supplied:\n * - `accessToken` — a pre-obtained OAuth access token (not auto-refreshed)\n * - `apiKey` — a `pk_live_` API key\n * - `clientId` + `clientSecret` — OAuth-managed: the client obtains/refreshes\n * access tokens automatically using `tokenStorage`\n */\nexport interface PlanesConfig {\n /** API base URL (defaults to https://api.planescrm.com/api/v1) */\n baseUrl?: string;\n /** OAuth access token (mutually exclusive with apiKey / clientId+clientSecret) */\n accessToken?: string;\n /** API key (mutually exclusive with accessToken / clientId+clientSecret) */\n apiKey?: string;\n /** OAuth client ID — combined with `clientSecret`, enables the OAuth-managed path */\n clientId?: string;\n /** OAuth client secret — combined with `clientId`, enables the OAuth-managed path */\n clientSecret?: string;\n /**\n * Scopes to request when the OAuth-managed path falls back to the\n * client_credentials grant (no stored refresh token).\n */\n scopes?: string[];\n /**\n * Token store for the OAuth-managed path. Seed it with tokens obtained from\n * an authorization-code exchange. Defaults to {@link MemoryTokenStorage}.\n */\n tokenStorage?: TokenStorage;\n /**\n * Refresh access tokens proactively when they are within this many\n * milliseconds of expiry (OAuth-managed path only). Default 60000 (60s).\n */\n tokenRefreshSkewMs?: number;\n /** Request timeout in milliseconds (default 30000) */\n timeout?: number;\n /** Automatically retry on 429 rate limit responses (default true) */\n retryOnRateLimit?: boolean;\n /** Maximum number of retries for rate-limited requests (default 3) */\n maxRetries?: number;\n}\n\nexport const DEFAULT_BASE_URL = 'https://api.planescrm.com/api/v1';\nexport const DEFAULT_TIMEOUT = 30_000;\nexport const DEFAULT_MAX_RETRIES = 3;\nexport const DEFAULT_TOKEN_REFRESH_SKEW_MS = 60_000;\n","/**\n * Base error class for all Planes SDK errors.\n */\nexport class PlanesError extends Error {\n public readonly status: number;\n public readonly code: string;\n\n constructor(message: string, status: number, code: string) {\n super(message);\n this.name = 'PlanesError';\n this.status = status;\n this.code = code;\n }\n}\n\n/**\n * Thrown when authentication fails (401).\n */\nexport class AuthenticationError extends PlanesError {\n constructor(message = 'Authentication failed') {\n super(message, 401, 'authentication_error');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown when the user lacks permission (403).\n */\nexport class AuthorizationError extends PlanesError {\n constructor(message = 'Insufficient permissions') {\n super(message, 403, 'authorization_error');\n this.name = 'AuthorizationError';\n }\n}\n\n/**\n * Thrown when a resource is not found (404).\n */\nexport class NotFoundError extends PlanesError {\n constructor(message = 'Resource not found') {\n super(message, 404, 'not_found');\n this.name = 'NotFoundError';\n }\n}\n\n/** Field-level validation error detail. */\nexport interface FieldError {\n field: string;\n message: string;\n code?: string;\n}\n\n/**\n * Thrown for validation failures (400) with field-level error details.\n */\nexport class ValidationError extends PlanesError {\n public readonly fieldErrors: FieldError[];\n\n constructor(message = 'Validation failed', fieldErrors: FieldError[] = []) {\n super(message, 400, 'validation_error');\n this.name = 'ValidationError';\n this.fieldErrors = fieldErrors;\n }\n}\n\n/**\n * Thrown when rate limited (429). Includes retryAfter in seconds.\n */\nexport class RateLimitError extends PlanesError {\n public readonly retryAfter: number;\n\n constructor(retryAfter = 1, message = 'Rate limit exceeded') {\n super(message, 429, 'rate_limit_error');\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Thrown for server errors (500+).\n */\nexport class ServerError extends PlanesError {\n constructor(message = 'Internal server error') {\n super(message, 500, 'server_error');\n this.name = 'ServerError';\n }\n}\n\n/**\n * Thrown when webhook signature verification fails.\n */\nexport class WebhookVerificationError extends PlanesError {\n constructor(message = 'Webhook signature verification failed') {\n super(message, 400, 'webhook_verification_error');\n this.name = 'WebhookVerificationError';\n }\n}\n\n/**\n * Thrown for OAuth flow errors.\n */\nexport class OAuthError extends PlanesError {\n public readonly errorCode: string;\n\n constructor(errorCode: string, message = 'OAuth error') {\n super(message, 400, 'oauth_error');\n this.name = 'OAuthError';\n this.errorCode = errorCode;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { OAuthError } from './errors';\n\n/**\n * Options for building an OAuth authorization URL.\n */\nexport interface AuthorizeOptions {\n /** Planes API base URL (e.g. https://api.planescrm.com/api/v1). */\n baseUrl: string;\n /** OAuth client ID. */\n clientId: string;\n /** Redirect URI after authorization. */\n redirectUri: string;\n /** Requested OAuth scopes. */\n scopes: string[];\n /** CSRF state parameter (auto-generated if not provided). */\n state?: string;\n}\n\n/**\n * Options for exchanging an authorization code for tokens.\n */\nexport interface ExchangeCodeOptions {\n /** Planes API base URL. */\n baseUrl: string;\n /** OAuth client ID. */\n clientId: string;\n /** OAuth client secret. */\n clientSecret: string;\n /** Authorization code from the callback. */\n code: string;\n /** Redirect URI (must match the one used in authorization). */\n redirectUri: string;\n}\n\n/**\n * Options for refreshing an access token.\n */\nexport interface RefreshOptions {\n /** Planes API base URL. */\n baseUrl: string;\n /** OAuth client ID. */\n clientId: string;\n /** OAuth client secret. */\n clientSecret: string;\n /** Refresh token. */\n refreshToken: string;\n}\n\n/**\n * Options for the client_credentials grant (M2M auth — no user/tenant context).\n */\nexport interface ClientCredentialsOptions {\n /** Planes API base URL. */\n baseUrl: string;\n /** OAuth client ID. */\n clientId: string;\n /** OAuth client secret. */\n clientSecret: string;\n /** Requested OAuth scopes. */\n scopes: string[];\n}\n\n/**\n * OAuth token response. `refresh_token` is absent for the client_credentials\n * grant (there is no user session to refresh on behalf of).\n */\nexport interface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n scope: string;\n}\n\n/**\n * Build an OAuth authorization URL for the Planes platform.\n *\n * @param options - Authorization options\n * @returns The full authorization URL to redirect the user to\n */\nexport function buildAuthorizeUrl(options: AuthorizeOptions): string {\n const state = options.state ?? randomBytes(16).toString('hex');\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n\n const url = new URL(`${baseUrl}/oauth/authorize`);\n url.searchParams.set('client_id', options.clientId);\n url.searchParams.set('redirect_uri', options.redirectUri);\n url.searchParams.set('scope', options.scopes.join(' '));\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', state);\n\n return url.toString();\n}\n\n/**\n * Exchange an authorization code for access and refresh tokens.\n *\n * @param options - Exchange options\n * @returns Token response with access_token, refresh_token, etc.\n * @throws OAuthError on failure\n */\nexport async function exchangeCode(options: ExchangeCodeOptions): Promise<TokenResponse> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const url = `${baseUrl}/oauth/token`;\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: options.clientId,\n client_secret: options.clientSecret,\n code: options.code,\n redirect_uri: options.redirectUri,\n });\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({})) as Record<string, unknown>;\n const errorCode = (errorBody['error'] as string) ?? 'unknown_error';\n const errorDescription = (errorBody['error_description'] as string) ?? 'Token exchange failed';\n throw new OAuthError(errorCode, errorDescription);\n }\n\n return (await response.json()) as TokenResponse;\n}\n\n/**\n * Refresh an access token using a refresh token.\n *\n * @param options - Refresh options\n * @returns New token response\n * @throws OAuthError on failure\n */\nexport async function refreshAccessToken(options: RefreshOptions): Promise<TokenResponse> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const url = `${baseUrl}/oauth/token`;\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: options.clientId,\n client_secret: options.clientSecret,\n refresh_token: options.refreshToken,\n });\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({})) as Record<string, unknown>;\n const errorCode = (errorBody['error'] as string) ?? 'unknown_error';\n const errorDescription = (errorBody['error_description'] as string) ?? 'Token refresh failed';\n throw new OAuthError(errorCode, errorDescription);\n }\n\n return (await response.json()) as TokenResponse;\n}\n\n/**\n * Obtain an access token via the client_credentials grant — the M2M auth\n * pattern for connectors and external services with no user/tenant context.\n *\n * @param options - Client credentials options\n * @returns Token response (no refresh_token)\n * @throws OAuthError on failure\n */\nexport async function clientCredentialsGrant(\n options: ClientCredentialsOptions,\n): Promise<TokenResponse> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const url = `${baseUrl}/oauth/token`;\n\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: options.clientId,\n client_secret: options.clientSecret,\n scope: options.scopes.join(' '),\n });\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorBody = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const errorCode = (errorBody['error'] as string) ?? 'unknown_error';\n const errorDescription =\n (errorBody['error_description'] as string) ?? 'Client credentials grant failed';\n throw new OAuthError(errorCode, errorDescription);\n }\n\n return (await response.json()) as TokenResponse;\n}\n","import {\n PlanesConfig,\n DEFAULT_BASE_URL,\n DEFAULT_TIMEOUT,\n DEFAULT_MAX_RETRIES,\n DEFAULT_TOKEN_REFRESH_SKEW_MS,\n MemoryTokenStorage,\n} from './config';\nimport type { StoredTokens, TokenStorage } from './config';\nimport {\n PlanesError,\n AuthenticationError,\n AuthorizationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ServerError,\n} from './errors';\nimport type { FieldError } from './errors';\nimport { clientCredentialsGrant, refreshAccessToken } from './oauth';\n\n/** OAuth-managed authentication context (clientId/clientSecret path). */\ninterface OAuthContext {\n clientId: string;\n clientSecret: string;\n tokenStorage: TokenStorage;\n /** Refresh proactively when within this many ms of expiry. */\n skewMs: number;\n /** Scopes to request via the client_credentials grant (M2M fallback). */\n scopes: string[] | undefined;\n}\n\nexport interface HttpRequestOptions {\n method: string;\n path: string;\n body?: unknown;\n params?: Record<string, unknown>;\n headers?: Record<string, string>;\n /** 'arrayBuffer' returns the raw response body (binary downloads); default 'json'. */\n responseType?: 'json' | 'arrayBuffer';\n}\n\n/**\n * Internal HTTP client wrapper using native fetch.\n * Handles authentication, rate limit retries, and error mapping.\n */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly accessToken: string | undefined;\n private readonly apiKey: string | undefined;\n private readonly timeout: number;\n private readonly retryOnRateLimit: boolean;\n private readonly maxRetries: number;\n private readonly oauth: OAuthContext | undefined;\n /** Single-flight guard so concurrent requests share one refresh. */\n private refreshInFlight: Promise<StoredTokens> | undefined;\n\n constructor(config: PlanesConfig) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.accessToken = config.accessToken;\n this.apiKey = config.apiKey;\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.retryOnRateLimit = config.retryOnRateLimit !== false;\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n\n if (config.clientId && config.clientSecret) {\n this.oauth = {\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n tokenStorage: config.tokenStorage ?? new MemoryTokenStorage(),\n skewMs: config.tokenRefreshSkewMs ?? DEFAULT_TOKEN_REFRESH_SKEW_MS,\n scopes: config.scopes,\n };\n }\n }\n\n async request<T>(options: HttpRequestOptions): Promise<T> {\n const url = this.buildUrl(options.path, options.params);\n const bodyStr = options.body !== undefined ? JSON.stringify(options.body) : undefined;\n\n let lastError: Error | undefined;\n // The OAuth-managed 401 recovery is one refresh + one retry, independent of\n // the 429 rate-limit retry loop below.\n let didRefreshOn401 = false;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n // Resolve auth headers per-attempt: the OAuth path may proactively refresh\n // and the bearer token can change between attempts.\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(await this.resolveAuthHeaders()),\n ...options.headers,\n };\n const fetchOptions: RequestInit = {\n method: options.method,\n headers,\n signal: AbortSignal.timeout(this.timeout),\n };\n if (bodyStr !== undefined) {\n fetchOptions.body = bodyStr;\n }\n\n const response = await fetch(url, fetchOptions);\n\n if (response.ok) {\n // 204 No Content\n if (response.status === 204) {\n return undefined as T;\n }\n if (options.responseType === 'arrayBuffer') {\n return (await response.arrayBuffer()) as T;\n }\n return (await response.json()) as T;\n }\n\n if (response.status === 429 && this.retryOnRateLimit && attempt < this.maxRetries) {\n const retryAfter = this.parseRetryAfter(response);\n await this.sleep(retryAfter * 1000);\n continue;\n }\n\n // OAuth-managed path: on a 401, refresh the token once and retry the\n // request. This does not consume the 429 retry budget.\n if (response.status === 401 && this.oauth && !didRefreshOn401) {\n didRefreshOn401 = true;\n await this.refreshTokens();\n attempt--;\n continue;\n }\n\n lastError = await this.mapError(response);\n\n // Only retry on 429 when enabled; all other errors fail immediately\n if (response.status !== 429 || !this.retryOnRateLimit) {\n throw lastError;\n }\n }\n\n // All retries exhausted for 429\n throw lastError ?? new RateLimitError();\n }\n\n getAuthHeaders(): Record<string, string> {\n if (this.accessToken) {\n return { Authorization: `Bearer ${this.accessToken}` };\n }\n if (this.apiKey) {\n return { 'X-API-Key': this.apiKey };\n }\n return {};\n }\n\n /**\n * Resolve the Authorization header for the current request. For the static\n * accessToken/apiKey modes this is {@link getAuthHeaders}. For the\n * OAuth-managed path it reads the stored token, proactively refreshing when\n * the token is within the skew window of expiry.\n */\n private async resolveAuthHeaders(): Promise<Record<string, string>> {\n if (!this.oauth) {\n return this.getAuthHeaders();\n }\n\n const tokens = await this.oauth.tokenStorage.getTokens();\n if (!tokens) {\n // Nothing seeded — send no auth; the server 401 will drive a refresh\n // attempt (which will fail clearly if there is no refresh token).\n return {};\n }\n\n if (tokens.expiresAt - Date.now() <= this.oauth.skewMs) {\n const refreshed = await this.refreshTokens();\n return { Authorization: `Bearer ${refreshed.accessToken}` };\n }\n\n return { Authorization: `Bearer ${tokens.accessToken}` };\n }\n\n /**\n * Refresh the OAuth access token via `refreshAccessToken` and persist the new\n * tokens through the configured {@link TokenStorage}. Concurrent callers share\n * a single in-flight refresh.\n */\n private async refreshTokens(): Promise<StoredTokens> {\n if (!this.oauth) {\n throw new AuthenticationError('Token refresh requires the OAuth clientId/clientSecret path');\n }\n if (this.refreshInFlight) {\n return this.refreshInFlight;\n }\n\n const oauth = this.oauth;\n this.refreshInFlight = (async () => {\n const current = await oauth.tokenStorage.getTokens();\n\n if (!current?.refreshToken) {\n // No refresh token on file — fall back to the client_credentials\n // grant (M2M flow); there is no user session to refresh.\n const response = await clientCredentialsGrant({\n baseUrl: this.baseUrl,\n clientId: oauth.clientId,\n clientSecret: oauth.clientSecret,\n scopes: oauth.scopes ?? [],\n });\n\n const next: StoredTokens = {\n accessToken: response.access_token,\n refreshToken: response.refresh_token ?? null,\n expiresAt: Date.now() + response.expires_in * 1000,\n };\n await oauth.tokenStorage.setTokens(next);\n return next;\n }\n\n const response = await refreshAccessToken({\n baseUrl: this.baseUrl,\n clientId: oauth.clientId,\n clientSecret: oauth.clientSecret,\n refreshToken: current.refreshToken,\n });\n\n const next: StoredTokens = {\n accessToken: response.access_token,\n refreshToken: response.refresh_token || current.refreshToken,\n expiresAt: Date.now() + response.expires_in * 1000,\n };\n await oauth.tokenStorage.setTokens(next);\n return next;\n })();\n\n try {\n return await this.refreshInFlight;\n } finally {\n this.refreshInFlight = undefined;\n }\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n }\n\n private parseRetryAfter(response: Response): number {\n const header = response.headers.get('Retry-After');\n if (header) {\n const seconds = Number(header);\n if (!Number.isNaN(seconds) && seconds > 0) {\n return seconds;\n }\n }\n return 1; // Default 1 second\n }\n\n private async mapError(response: Response): Promise<PlanesError> {\n let body: Record<string, unknown> = {};\n try {\n body = (await response.json()) as Record<string, unknown>;\n } catch {\n // Ignore JSON parse errors\n }\n\n const message = (body['message'] as string) ?? response.statusText ?? 'Request failed';\n\n switch (response.status) {\n case 400: {\n const fieldErrors = (body['errors'] as FieldError[] | undefined) ?? [];\n return new ValidationError(message, fieldErrors);\n }\n case 401:\n return new AuthenticationError(message);\n case 403:\n return new AuthorizationError(message);\n case 404:\n return new NotFoundError(message);\n case 429: {\n const retryAfter = this.parseRetryAfter(response);\n return new RateLimitError(retryAfter, message);\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message);\n }\n return new PlanesError(message, response.status, 'unknown_error');\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import { HttpClient } from '../http';\nimport type { PaginatedResponse, ListParams } from '../types';\n\n/**\n * Minimal base: the http client and the resource's base path, no methods.\n * Resources whose API routes aren't flat CRUD on `basePath` (nested under a\n * parent like `customers/:customerId/properties`, or missing an operation like\n * tasks without delete) extend this and declare only the methods the API\n * serves, so a method the API doesn't have is a compile error, not a 404.\n */\nexport abstract class ApiResource {\n constructor(\n protected readonly http: HttpClient,\n protected readonly basePath: string,\n ) {}\n}\n\n/**\n * Flat CRUD on `basePath`: `GET /`, `POST /`, `GET /:id`, `PATCH /:id`,\n * `DELETE /:id`. Extend it only when the API serves all five (override a verb\n * where it differs); otherwise extend `ApiResource`.\n * Generic parameters:\n * - TEntity: the entity response type\n * - TCreate: the create request body type\n * - TUpdate: the update request body type\n */\nexport abstract class BaseResource<\n TEntity = Record<string, unknown>,\n TCreate = Record<string, unknown>,\n TUpdate = Record<string, unknown>,\n TList = PaginatedResponse<TEntity>,\n> extends ApiResource {\n\n /**\n * List entities with optional filters and pagination.\n */\n async list(params?: ListParams): Promise<TList> {\n return this.http.request<TList>({\n method: 'GET',\n path: this.basePath,\n params: params as Record<string, unknown>,\n });\n }\n\n /**\n * Get a single entity by ID.\n */\n async get(id: string): Promise<TEntity> {\n return this.http.request<TEntity>({\n method: 'GET',\n path: `${this.basePath}/${id}`,\n });\n }\n\n /**\n * Create a new entity.\n */\n async create(data: TCreate): Promise<TEntity> {\n return this.http.request<TEntity>({\n method: 'POST',\n path: this.basePath,\n body: data,\n });\n }\n\n /**\n * Update an existing entity.\n */\n async update(id: string, data: TUpdate): Promise<TEntity> {\n return this.http.request<TEntity>({\n method: 'PATCH',\n path: `${this.basePath}/${id}`,\n body: data,\n });\n }\n\n /**\n * Delete an entity by ID.\n */\n async delete(id: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `${this.basePath}/${id}`,\n });\n }\n}\n","/**\n * Flatten typed list params into what `HttpClient.buildUrl` puts on the query\n * string. Array values (e.g. `tag_ids`) are sent as ONE comma-separated value,\n * the shape the API's query schemas parse; an empty array is omitted so it\n * never turns into `?tag_ids=` (which the server reads as \"no filter\" anyway,\n * but a client should not send noise).\n */\nexport function toQueryParams(params?: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (!params) return out;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue;\n if (Array.isArray(value)) {\n if (value.length === 0) continue;\n out[key] = value.map(String).join(',');\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n","import { BaseResource } from './base-resource';\nimport type { PaginatedResponse } from '../types';\nimport type { OpportunityListParams, UpdateOpportunityDto } from '../types/opportunity-types';\nimport { toQueryParams } from './query-params';\n\n/** Opportunity resource — CRUD operations on /opportunities (updates are PUT). */\nexport class OpportunityResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/opportunities');\n }\n\n /**\n * List opportunities. `tag_ids` is ANY-of and travels as one comma-separated\n * query value (`?tag_ids=a,b`), which is what the API's filter schema parses.\n */\n override async list(params?: OpportunityListParams): Promise<PaginatedResponse<Record<string, unknown>>> {\n return this.http.request<PaginatedResponse<Record<string, unknown>>>({\n method: 'GET',\n path: this.basePath,\n params: toQueryParams(params),\n });\n }\n\n /** PUT /opportunities/{id} — partial update; stage moves go through workflow transitions. */\n override async update(id: string, data: UpdateOpportunityDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'PUT',\n path: `${this.basePath}/${id}`,\n body: data,\n });\n }\n}\n","import { BaseResource } from './base-resource';\nimport type { PaginatedResponse } from '../types';\nimport type { CustomerMergeResult, CustomerListParams, UpdateCustomerDto } from '../types/customer-types';\nimport { toQueryParams } from './query-params';\n\n/** Customer resource — CRUD operations on /customers. */\nexport class CustomerResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/customers');\n }\n\n /**\n * Update a customer. The API route is `PUT /customers/{id}` (not PATCH like\n * the other resources), so this overrides the base verb; the body is still a\n * partial — send only the fields to change. See `UpdateCustomerDto.address`\n * for how a service address resolves to a property.\n */\n override async update(id: string, data: UpdateCustomerDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'PUT',\n path: `/customers/${id}`,\n body: data,\n });\n }\n\n /**\n * List customers. `tag_ids` is ANY-of and travels as one comma-separated\n * query value (`?tag_ids=a,b`), which is what the API's filter schema parses.\n */\n override async list(params?: CustomerListParams): Promise<PaginatedResponse<Record<string, unknown>>> {\n return this.http.request<PaginatedResponse<Record<string, unknown>>>({\n method: 'GET',\n path: this.basePath,\n params: toQueryParams(params),\n });\n }\n\n /**\n * Fold a duplicate company (`loserId`) into a survivor (`winnerId`).\n *\n * Everything hanging off the loser moves to the winner, the winner's empty\n * fields are filled from the loser, and the loser is soft-deleted with a\n * resolvable pointer — `GET /customers/{loserId}` afterwards returns the\n * winner's detail with a `mergedFrom` pointer, so a stored loser id keeps\n * working. Irreversible; requires admin scope.\n */\n async merge(winnerId: string, loserId: string): Promise<CustomerMergeResult> {\n return this.http.request<CustomerMergeResult>({\n method: 'POST',\n path: `/customers/${winnerId}/merge/${loserId}`,\n });\n }\n}\n","import { ApiResource } from './base-resource';\nimport type { PaginatedResponse } from '../types';\nimport type { CreatePropertyDto, UpdatePropertyDto, PropertyListParams } from '../types/property-types';\nimport { toQueryParams } from './query-params';\n\n/**\n * Properties. Every write and the list are nested under the owning customer\n * (`/customers/{customerId}/properties`); only the single-property read has a\n * direct route (`GET /properties/{id}`).\n */\nexport class PropertyResource extends ApiResource {\n constructor(http: ConstructorParameters<typeof ApiResource>[0]) {\n super(http, '/properties');\n }\n\n /** GET /customers/{customerId}/properties — paginated (`page`, `limit` ≤ 100). */\n async list(\n customerId: string,\n params?: PropertyListParams,\n ): Promise<PaginatedResponse<Record<string, unknown>>> {\n return this.http.request<PaginatedResponse<Record<string, unknown>>>({\n method: 'GET',\n path: `/customers/${customerId}/properties`,\n params: toQueryParams(params),\n });\n }\n\n /** GET /properties/{id} — one property, without naming its customer. */\n async get(id: string): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'GET',\n path: `${this.basePath}/${id}`,\n });\n }\n\n /** POST /customers/{customerId}/properties — the property inherits the customer's org path. */\n async create(customerId: string, data: CreatePropertyDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'POST',\n path: `/customers/${customerId}/properties`,\n body: data,\n });\n }\n\n /** PUT /customers/{customerId}/properties/{id} — partial update. */\n async update(customerId: string, id: string, data: UpdatePropertyDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'PUT',\n path: `/customers/${customerId}/properties/${id}`,\n body: data,\n });\n }\n\n /** DELETE /customers/{customerId}/properties/{id} — soft delete (204). */\n async delete(customerId: string, id: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `/customers/${customerId}/properties/${id}`,\n });\n }\n}\n","import { BaseResource } from './base-resource';\nimport type { ListParams } from '../types';\nimport type {\n CompleteCalendarEventDto,\n CompleteCalendarEventResponse,\n UpdateCalendarEventDto,\n UpdateCalendarEventOptions,\n} from '../types/calendar-event-types';\n\n/**\n * Query filters for `GET /events`. Mirrors the API's\n * `listCalendarEventsQuerySchema` (`@blueprint/shared`) — keep in sync.\n * Deliberately NOT `ListParams`: this endpoint paginates by `limit`/`offset`\n * only and has no `page`, `sort`, `order` or `search` (Zod would strip them\n * silently and the caller would loop on page 1 forever).\n */\nexport interface ListCalendarEventsParams {\n /** ISO timestamp — events starting at or after this instant. */\n starts_after?: string;\n /** ISO timestamp — events starting at or before this instant. */\n starts_before?: string;\n /** Must be paired with `entity_id`. */\n entity_type?: string;\n entity_id?: string;\n /** Agent id of the event's creator. */\n created_by?: string;\n /**\n * Events this agent is ON: a staff participant (canonical `{id}` or legacy\n * `{agent_id}` shape), or the creator only when the event has no staff\n * participants (self-booked / Google-synced) — the same rule availability\n * uses to count busy time. An event booked for someone else is not on the\n * creator's schedule.\n */\n agent_id?: string;\n status?: string;\n event_type?: string;\n /** Only recurrence masters / one-time events (no generated instances). */\n masters_only?: boolean;\n limit?: number;\n offset?: number;\n}\n\n/** What `GET /events` actually returns: offset pagination, no `meta` envelope. */\nexport interface CalendarEventListResponse<T = Record<string, unknown>> {\n data: T[];\n total: number;\n limit: number;\n offset: number;\n}\n\n/** Calendar event resource — CRUD operations on /events. */\nexport class CalendarEventResource extends BaseResource<\n Record<string, unknown>,\n Record<string, unknown>,\n UpdateCalendarEventDto,\n CalendarEventListResponse\n> {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n // The API route is `events` (global prefix /api/v1) — not `calendar-events`.\n super(http, '/events');\n }\n\n override async list(params?: ListCalendarEventsParams): Promise<CalendarEventListResponse> {\n // Narrower than ListParams on purpose (see the interface); the base only forwards it.\n return super.list(params as ListParams | undefined);\n }\n\n /**\n * PATCH /events/:id. For an occurrence of a recurring event, `editMode`\n * picks which occurrences change (`this` when omitted — the server's\n * default); it is ignored for one-time events.\n */\n override async update(\n id: string,\n data: UpdateCalendarEventDto,\n options: UpdateCalendarEventOptions = {},\n ): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'PATCH',\n path: `${this.basePath}/${id}`,\n params: { recurrence_edit_mode: options.editMode },\n body: data,\n });\n }\n\n /**\n * POST /events/:id/complete — marks the event completed and runs its \"on\n * complete\" transition, best-effort. The event is completed even when the\n * transition can't run; `workflow_trigger` says whether it fired, was\n * skipped (none set) or failed (with a reason).\n */\n async complete(id: string, body: CompleteCalendarEventDto = {}): Promise<CompleteCalendarEventResponse> {\n return this.http.request<CompleteCalendarEventResponse>({\n method: 'POST',\n path: `${this.basePath}/${id}/complete`,\n body,\n });\n }\n}\n","import { BaseResource } from './base-resource';\n\n/** Contract resource — CRUD operations on /contracts. */\nexport class ContractResource extends BaseResource {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/contracts');\n }\n}\n","import { ApiResource } from './base-resource';\nimport type { PaginatedResponse } from '../types';\nimport type { CreateJobDto, JobListParams, JobTradeCoverageResponse, UpdateJobDto } from '../types/job-types';\nimport { toQueryParams } from './query-params';\n\n/**\n * Jobs. CRUD is nested under the parent opportunity\n * (`/opportunities/{opportunityId}/jobs`); there is no flat `/jobs` list or\n * `/jobs/{id}` read. `getTradeCoverage()` is the one top-level route.\n */\nexport class JobResource extends ApiResource {\n constructor(http: ConstructorParameters<typeof ApiResource>[0]) {\n super(http, '/jobs');\n }\n\n /** The jobs under one opportunity — the same route as `listForOpportunity()`. */\n async list(\n opportunityId: string,\n params?: JobListParams,\n ): Promise<PaginatedResponse<Record<string, unknown>>> {\n return this.listForOpportunity(opportunityId, params);\n }\n\n /**\n * List the jobs under one opportunity — `GET /opportunities/{opportunityId}/jobs`,\n * the API's job list route. Default order is `created_at` desc;\n * `sort_by: 'recent'` puts the calling user's most recently opened jobs first.\n */\n async listForOpportunity(\n opportunityId: string,\n params?: JobListParams,\n ): Promise<PaginatedResponse<Record<string, unknown>>> {\n return this.http.request<PaginatedResponse<Record<string, unknown>>>({\n method: 'GET',\n path: `/opportunities/${opportunityId}/jobs`,\n params: toQueryParams(params),\n });\n }\n\n /** GET /opportunities/{opportunityId}/jobs/{id} */\n async get(opportunityId: string, id: string): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'GET',\n path: `/opportunities/${opportunityId}/jobs/${id}`,\n });\n }\n\n /**\n * POST /opportunities/{opportunityId}/jobs — 422 WORKFLOW_NOT_CONFIGURED when\n * the trade has no published job workflow (check `getTradeCoverage()` first).\n */\n async create(opportunityId: string, data: CreateJobDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'POST',\n path: `/opportunities/${opportunityId}/jobs`,\n body: data,\n });\n }\n\n /** PUT /opportunities/{opportunityId}/jobs/{id} — partial update (status, notes). */\n async update(opportunityId: string, id: string, data: UpdateJobDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'PUT',\n path: `/opportunities/${opportunityId}/jobs/${id}`,\n body: data,\n });\n }\n\n /** DELETE /opportunities/{opportunityId}/jobs/{id} — soft delete (204). */\n async delete(opportunityId: string, id: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `/opportunities/${opportunityId}/jobs/${id}`,\n });\n }\n\n /**\n * GET /jobs/trade-coverage — every active trade with whether a job can be\n * created under it (a published job workflow resolves: the trade's own or the\n * generic one). Check before creating a job to avoid a 422\n * WORKFLOW_NOT_CONFIGURED. Readable by any role that can create jobs (403\n * otherwise); carries no workflow details.\n */\n async getTradeCoverage(): Promise<JobTradeCoverageResponse> {\n return this.http.request<JobTradeCoverageResponse>({\n method: 'GET',\n path: '/jobs/trade-coverage',\n });\n }\n}\n","import { ApiResource } from './base-resource';\nimport type { PaginatedResponse } from '../types';\nimport type {\n CreateEstimateDto,\n EstimateListParams,\n EstimateSummaryDto,\n UpdateEstimateDto,\n} from '../types/estimate-types';\nimport { toQueryParams } from './query-params';\n\n/**\n * Estimates. CRUD is nested under the parent job (`/jobs/{jobId}/estimates`);\n * `listForOpportunity()` reads every estimate across an opportunity's jobs.\n * There is no flat `/estimates` list or `/estimates/{id}` read.\n */\nexport class EstimateResource extends ApiResource {\n constructor(http: ConstructorParameters<typeof ApiResource>[0]) {\n super(http, '/estimates');\n }\n\n /** GET /jobs/{jobId}/estimates — newest first, paginated (`page`, `limit` ≤ 100). */\n async list(jobId: string, params?: EstimateListParams): Promise<PaginatedResponse<EstimateSummaryDto>> {\n return this.http.request<PaginatedResponse<EstimateSummaryDto>>({\n method: 'GET',\n path: `/jobs/${jobId}/estimates`,\n params: toQueryParams(params),\n });\n }\n\n /**\n * Every estimate across all of one opportunity's jobs, in one request —\n * `GET /opportunities/{opportunityId}/estimates`. Newest first; each row\n * carries its `jobId`. Paginated with `page` / `limit` (max 100).\n */\n async listForOpportunity(\n opportunityId: string,\n params?: EstimateListParams,\n ): Promise<PaginatedResponse<EstimateSummaryDto>> {\n return this.http.request<PaginatedResponse<EstimateSummaryDto>>({\n method: 'GET',\n path: `/opportunities/${opportunityId}/estimates`,\n params: toQueryParams(params),\n });\n }\n\n /** GET /jobs/{jobId}/estimates/{id} — the estimate with its line items. */\n async get(jobId: string, id: string): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'GET',\n path: `/jobs/${jobId}/estimates/${id}`,\n });\n }\n\n /** POST /jobs/{jobId}/estimates — optionally cloned from an estimate template. */\n async create(jobId: string, data: CreateEstimateDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'POST',\n path: `/jobs/${jobId}/estimates`,\n body: data,\n });\n }\n\n /** PUT /jobs/{jobId}/estimates/{id} — partial update (measurements, notes, discount, tax). */\n async update(jobId: string, id: string, data: UpdateEstimateDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'PUT',\n path: `/jobs/${jobId}/estimates/${id}`,\n body: data,\n });\n }\n\n /** DELETE /jobs/{jobId}/estimates/{id} — soft delete (204). */\n async delete(jobId: string, id: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `/jobs/${jobId}/estimates/${id}`,\n });\n }\n}\n","import { BaseResource } from './base-resource';\nimport type { PaginatedResponse } from '../types';\nimport type {\n InvoiceDetailDto,\n InvoiceListItemDto,\n InvoiceListParams,\n InvoicePayLinkDto,\n InvoiceOverdueSummaryDto,\n OverdueInvoiceDto,\n OverdueInvoiceListParams,\n} from '../types/invoice-types';\n\n/** Invoice resource — CRUD operations on /admin/invoices (the API's invoices controller route). */\nexport class InvoiceResource extends BaseResource<\n Record<string, unknown>,\n Record<string, unknown>,\n Record<string, unknown>,\n PaginatedResponse<InvoiceListItemDto>\n> {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/admin/invoices');\n }\n\n /** List invoices, newest first. Each row carries the customer's `customerName`. */\n override async list(params?: InvoiceListParams): Promise<PaginatedResponse<InvoiceListItemDto>> {\n return this.http.request<PaginatedResponse<InvoiceListItemDto>>({\n method: 'GET',\n path: this.basePath,\n params: params as Record<string, unknown>,\n });\n }\n\n /**\n * GET /admin/invoices/:id — the invoice with its line items, payments\n * (including any `needs_review` payment) and `payLink` state.\n */\n override async get(id: string): Promise<InvoiceDetailDto> {\n return this.http.request<InvoiceDetailDto>({\n method: 'GET',\n path: `${this.basePath}/${id}`,\n });\n }\n\n /**\n * POST /admin/invoices/:id/pay-link — issue a fresh durable pay link for a\n * `sent` or `partially_paid` invoice (Send Invoice grant) and return its URL,\n * e.g. to share by text. Rotates: the previously emailed link stops working.\n * Any other status → 409 `INVOICE_NOT_PAYABLE`.\n */\n async createPayLink(id: string): Promise<InvoicePayLinkDto> {\n return this.http.request<InvoicePayLinkDto>({\n method: 'POST',\n path: `${this.basePath}/${id}/pay-link`,\n });\n }\n\n /**\n * GET /admin/invoices/:id/pdf — the invoice PDF bytes (Read Invoice grant).\n * A sent invoice returns the copy stored when it was sent; a draft renders on\n * demand with a DRAFT watermark.\n */\n async downloadPdf(id: string): Promise<ArrayBuffer> {\n return this.http.request<ArrayBuffer>({\n method: 'GET',\n path: `${this.basePath}/${id}/pdf`,\n headers: { Accept: 'application/pdf' },\n responseType: 'arrayBuffer',\n });\n }\n\n /**\n * GET /admin/invoices/overdue — sent / partially paid invoices with a balance\n * past their due date, most overdue first, each with `daysOverdue` and the\n * last overdue offset reached (E56-F8-S2). Read Invoice.\n */\n async listOverdue(params?: OverdueInvoiceListParams): Promise<PaginatedResponse<OverdueInvoiceDto>> {\n return this.http.request<PaginatedResponse<OverdueInvoiceDto>>({\n method: 'GET',\n path: `${this.basePath}/overdue`,\n params: params as Record<string, unknown>,\n });\n }\n\n /** GET /admin/invoices/overdue/summary — `{ count, total }` of every overdue invoice. Read Invoice. */\n async overdueSummary(): Promise<InvoiceOverdueSummaryDto> {\n return this.http.request<InvoiceOverdueSummaryDto>({\n method: 'GET',\n path: `${this.basePath}/overdue/summary`,\n });\n }\n}\n","import { BaseResource } from './base-resource';\nimport type { PaginatedResponse } from '../types';\nimport type {\n CreateInventoryItemDto,\n InventoryItemListParams,\n UpdateInventoryItemDto,\n} from '../types/inventory-types';\nimport { toQueryParams } from './query-params';\n\n/** Inventory items — CRUD on /admin/inventory/items (updates are PUT). */\nexport class InventoryResource extends BaseResource<\n Record<string, unknown>,\n CreateInventoryItemDto,\n UpdateInventoryItemDto\n> {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/admin/inventory/items');\n }\n\n override async list(params?: InventoryItemListParams): Promise<PaginatedResponse<Record<string, unknown>>> {\n return this.http.request<PaginatedResponse<Record<string, unknown>>>({\n method: 'GET',\n path: this.basePath,\n params: toQueryParams(params),\n });\n }\n\n /** PUT /admin/inventory/items/{id} — partial update. */\n override async update(id: string, data: UpdateInventoryItemDto): Promise<Record<string, unknown>> {\n return this.http.request<Record<string, unknown>>({\n method: 'PUT',\n path: `${this.basePath}/${id}`,\n body: data,\n });\n }\n}\n","import { BaseResource } from './base-resource';\nimport type { HttpClient } from '../http';\nimport type { PaginatedResponse } from '../types';\nimport { toQueryParams } from './query-params';\nimport type {\n ContactDto,\n ContactListItemDto,\n ContactListParams,\n CreateContactDto,\n UpdateContactDto,\n ContactChannelDto,\n CreateContactChannelDto,\n UpdateContactChannelDto,\n AddContactChannelResult,\n ContactMergeResult,\n} from '../types/contact-types';\n\n/**\n * `/contacts/:id/channels` — POST/PATCH/DELETE only. There is no list/get\n * route: reads come embedded in the contact detail response (`GET\n * /contacts/:id` returns `{ ...contact, channels, links, mergedInto }`), so\n * this does NOT extend `BaseResource`.\n */\nexport class ContactChannelResource {\n private readonly basePath: string;\n\n constructor(\n private readonly http: HttpClient,\n contactId: string,\n ) {\n this.basePath = `/contacts/${contactId}/channels`;\n }\n\n /** Returns `{ channel, duplicateOf }` — `duplicateOf` is the dedup signal. */\n async create(data: CreateContactChannelDto): Promise<AddContactChannelResult> {\n return this.http.request<AddContactChannelResult>({\n method: 'POST',\n path: this.basePath,\n body: data,\n });\n }\n\n async update(channelId: string, data: UpdateContactChannelDto): Promise<ContactChannelDto> {\n return this.http.request<ContactChannelDto>({\n method: 'PATCH',\n path: `${this.basePath}/${channelId}`,\n body: data,\n });\n }\n\n async delete(channelId: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `${this.basePath}/${channelId}`,\n });\n }\n}\n\nexport class ContactResource extends BaseResource<\n ContactDto,\n CreateContactDto,\n UpdateContactDto\n> {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/contacts');\n }\n\n /**\n * List contacts. Typed against the real filter schema (customer_id /\n * property_id / q / sort_by / sort_order / page / limit / include) — the base\n * `list` would accept any key and let a typo reach the server as a 400.\n * `include: ['channels', 'links']` attaches each row's channels and active\n * links in the same response.\n */\n override async list(params?: ContactListParams): Promise<PaginatedResponse<ContactListItemDto>> {\n return this.http.request<PaginatedResponse<ContactListItemDto>>({\n method: 'GET',\n path: this.basePath,\n params: toQueryParams(params),\n });\n }\n\n async merge(winnerId: string, loserId: string): Promise<ContactMergeResult> {\n return this.http.request<ContactMergeResult>({\n method: 'POST',\n path: `/contacts/${winnerId}/merge/${loserId}`,\n });\n }\n\n channels(contactId: string): ContactChannelResource {\n return new ContactChannelResource(this.http, contactId);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { PaginatedResponse, ListParams } from '../types';\nimport type { ActivityDto, CreateActivityDto } from '../types/activity-types';\n\nexport class ActivityResource {\n constructor(private readonly http: HttpClient) {}\n\n async create(data: CreateActivityDto): Promise<ActivityDto> {\n return this.http.request<ActivityDto>({\n method: 'POST',\n path: '/activities',\n body: data,\n });\n }\n\n async list(params?: ListParams): Promise<PaginatedResponse<ActivityDto>> {\n return this.http.request<PaginatedResponse<ActivityDto>>({\n method: 'GET',\n path: '/activities',\n params: params as Record<string, unknown>,\n });\n }\n\n async listForEntity(\n entityType: string,\n entityId: string,\n params?: ListParams,\n ): Promise<PaginatedResponse<ActivityDto>> {\n return this.http.request<PaginatedResponse<ActivityDto>>({\n method: 'GET',\n path: `/${entityType}/${entityId}/activities`,\n params: params as Record<string, unknown>,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n LogCommunicationDto,\n LogCommunicationResult,\n ListCommunicationsParams,\n LinkCommunicationDto,\n CommunicationDto,\n CommunicationsListResponse,\n} from '../types/communication-types';\n\nexport class CommunicationsResource {\n constructor(private readonly http: HttpClient) {}\n\n async log(data: LogCommunicationDto): Promise<LogCommunicationResult> {\n return this.http.request<LogCommunicationResult>({\n method: 'POST',\n path: '/communications',\n body: data,\n });\n }\n\n async list(\n params?: ListCommunicationsParams,\n ): Promise<CommunicationsListResponse> {\n return this.http.request<CommunicationsListResponse>({\n method: 'GET',\n path: '/communications',\n params: params as Record<string, unknown>,\n });\n }\n\n async get(id: string): Promise<CommunicationDto> {\n return this.http.request<CommunicationDto>({\n method: 'GET',\n path: `/communications/${id}`,\n });\n }\n\n async link(id: string, data: LinkCommunicationDto): Promise<CommunicationDto> {\n return this.http.request<CommunicationDto>({\n method: 'POST',\n path: `/communications/${id}/link`,\n body: data,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n Conversation, ConversationListResponse, InternalDomainsDto, InternalDomainsResult,\n LinkConversationDto, ListConversationsParams, LogConversationDto, LogConversationResult,\n PatchConversationDto, ResolveConversationMatchDto,\n DisconnectSourceConnectionDto, SourceConnectionResult,\n PullConversationsDto, PullConversationsResult, SyncNowResult, ConversationSyncStatus, PullStatus,\n ConversationTranscript, TranscriptAccess, TranscriptAccessMode,\n} from '../types/conversation-types';\n\n/** E55 — client conversations (meetings): log, list, link, review-queue resolve. */\nexport class ConversationsResource {\n constructor(private readonly http: HttpClient) {}\n\n async log(data: LogConversationDto): Promise<LogConversationResult> {\n return this.http.request<LogConversationResult>({ method: 'POST', path: '/conversations', body: data });\n }\n async list(params?: ListConversationsParams): Promise<ConversationListResponse> {\n return this.http.request<ConversationListResponse>({ method: 'GET', path: '/conversations', params: params as Record<string, unknown> });\n }\n /** The review queue: `list()` pinned to `match_status: 'needs_review'`. */\n async reviewQueue(params?: Omit<ListConversationsParams, 'match_status'>): Promise<ConversationListResponse> {\n return this.list({ ...params, match_status: 'needs_review' });\n }\n async get(id: string): Promise<Conversation> {\n return this.http.request<Conversation>({ method: 'GET', path: `/conversations/${encodeURIComponent(id)}` });\n }\n async update(id: string, data: PatchConversationDto): Promise<Conversation> {\n return this.http.request<Conversation>({ method: 'PATCH', path: `/conversations/${encodeURIComponent(id)}`, body: data });\n }\n async delete(id: string): Promise<void> {\n await this.http.request<void>({ method: 'DELETE', path: `/conversations/${encodeURIComponent(id)}` });\n }\n async link(id: string, data: LinkConversationDto): Promise<Conversation> {\n return this.http.request<Conversation>({ method: 'POST', path: `/conversations/${encodeURIComponent(id)}/links`, body: data });\n }\n async unlink(id: string, linkId: string): Promise<Conversation> {\n return this.http.request<Conversation>({\n method: 'DELETE',\n path: `/conversations/${encodeURIComponent(id)}/links/${encodeURIComponent(linkId)}`,\n });\n }\n async resolve(id: string, data: ResolveConversationMatchDto): Promise<Conversation> {\n return this.http.request<Conversation>({ method: 'POST', path: `/conversations/${encodeURIComponent(id)}/resolve`, body: data });\n }\n async getInternalDomains(): Promise<InternalDomainsResult> {\n return this.http.request<InternalDomainsResult>({ method: 'GET', path: '/conversations/settings/internal-domains' });\n }\n /** Requires an admin: the server checks `Update` on `Subject.Tenant` (tenant-wide matching config), not `Subject.Conversation`. */\n async setInternalDomains(data: InternalDomainsDto): Promise<InternalDomainsResult> {\n return this.http.request<InternalDomainsResult>({ method: 'PUT', path: '/conversations/settings/internal-domains', body: data });\n }\n /**\n * E57-F2-S4 — disconnect the CALLER's own Circleback connection (Update\n * Conversation). Another user's connection id is a 404. Connecting is the\n * interactive browser OAuth flow (Settings → Conversations), not an SDK call.\n */\n async disconnectCircleback(data: DisconnectSourceConnectionDto): Promise<SourceConnectionResult> {\n return this.http.request<SourceConnectionResult>({\n method: 'POST', path: '/conversations/sources/circleback/disconnect', body: data,\n });\n }\n\n /**\n * E57-F2-S8 — pull one company's meetings from every active teammate's\n * connected source (Update Conversation). 202, runs in the background.\n * 404 for a company you can't see; 409 `NO_SOURCES` when nobody has one.\n */\n async pull(data: PullConversationsDto): Promise<PullConversationsResult> {\n return this.http.request<PullConversationsResult>({ method: 'POST', path: '/conversations/pull', body: data });\n }\n /**\n * E57-F2-S9 — whether a Pull has finished: pass the `job_ids` `pull()`\n * returned (1–50). Update Conversation, like `pull()`. 404\n * `PULL_NOT_FOUND` when none of them is readable (expired after ~2h,\n * unknown) — treat that as settled.\n */\n async getPullStatus(jobIds: readonly string[]): Promise<PullStatus> {\n return this.http.request<PullStatus>({\n method: 'GET', path: '/conversations/pull/status', params: { job_ids: jobIds.join(',') },\n });\n }\n /** E57-F2-S8 — sync the caller's OWN Circleback connection now (deduplicated). 409 `NO_CONNECTION` when they have none. */\n async syncNow(): Promise<SyncNowResult> {\n return this.http.request<SyncNowResult>({ method: 'POST', path: '/conversations/sources/circleback/sync-now' });\n }\n /** E57-F2-S8 — the caller's own connection, `has_sources`, and (admins only) the roster. */\n async syncStatus(): Promise<ConversationSyncStatus> {\n return this.http.request<ConversationSyncStatus>({ method: 'GET', path: '/conversations/sync-status' });\n }\n\n /**\n * E57-F4-S1 — a synced meeting's transcript, fetched live from the meeting\n * recorder (never stored by Planes; the response is `Cache-Control:\n * no-store`). Read Conversation, plus the tenant's transcript-access rule:\n * 403 `TRANSCRIPT_OWNER_ONLY` under `owner` for a non-owner; 409\n * `TRANSCRIPT_SOURCE_UNAVAILABLE` (`owner_name` says who must reconnect) or\n * `TRANSCRIPT_NOT_AVAILABLE` (not a synced meeting); 404 when you can't see\n * the conversation. `segments: []` = no transcript yet.\n */\n async getTranscript(id: string): Promise<ConversationTranscript> {\n return this.http.request<ConversationTranscript>({\n method: 'GET', path: `/conversations/${encodeURIComponent(id)}/transcript`,\n });\n }\n /** E57-F4-S1 — who can view transcripts: `owner` (default) or `viewers`. Read Conversation. */\n async getTranscriptAccess(): Promise<TranscriptAccess> {\n return this.http.request<TranscriptAccess>({ method: 'GET', path: '/conversations/settings/transcript-access' });\n }\n /** E57-F4-S1 — requires an admin: the server checks `Update` on `Subject.Tenant`. */\n async setTranscriptAccess(value: TranscriptAccessMode): Promise<TranscriptAccess> {\n return this.http.request<TranscriptAccess>({\n method: 'PUT', path: '/conversations/settings/transcript-access', body: { transcript_access: value },\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type { SendGmailDto, SendGmailResult } from '../types/gmail-types';\n\nexport class GmailResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Send an email RIGHT NOW from the connected user's real Gmail address.\n * Hits `POST /admin/integrations/google/gmail/send` — final per Task 4\n * (`apps/api/src/modules/integrations/google/google-connection.controller.ts`).\n */\n async send(data: SendGmailDto): Promise<SendGmailResult> {\n return this.http.request<SendGmailResult>({\n method: 'POST',\n path: '/admin/integrations/google/gmail/send',\n body: data,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n AttachExternalDocumentDto,\n DocumentDto,\n DocumentEntityType,\n DocumentsListResponse,\n} from '../types/document-types';\n\nexport class DocumentsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Attach an external (link-only) document — e.g. a Google Drive link — to a record. */\n async attachExternal(data: AttachExternalDocumentDto): Promise<DocumentDto> {\n return this.http.request<DocumentDto>({\n method: 'POST',\n path: '/documents/external',\n body: data,\n });\n }\n\n /** Documents filed directly on one record (uploaded + external), newest first. */\n async listForEntity(\n entityType: DocumentEntityType,\n entityId: string,\n ): Promise<DocumentsListResponse> {\n return this.http.request<DocumentsListResponse>({\n method: 'GET',\n path: `/${entityType}/${entityId}/documents`,\n });\n }\n}\n","import { ApiResource } from './base-resource';\nimport type { ListParams, PaginatedResponse } from '../types';\nimport type {\n TagDto,\n CreateTagDto,\n UpdateTagDto,\n SetEntityTagsDto,\n EntityTagDto,\n TagEntitiesPage,\n} from '../types/tag-types';\n\n/**\n * Tags. There is no single-tag read: `list()` returns every tag (with usage\n * counts), and `getEntities()` pages through the records carrying one.\n */\nexport class TagResource extends ApiResource {\n constructor(http: ConstructorParameters<typeof ApiResource>[0]) {\n super(http, '/tags');\n }\n\n /** GET /tags */\n async list(params?: ListParams): Promise<PaginatedResponse<TagDto>> {\n return this.http.request<PaginatedResponse<TagDto>>({\n method: 'GET',\n path: this.basePath,\n params: params as Record<string, unknown>,\n });\n }\n\n /** POST /tags — 409 on a case-insensitive duplicate name. */\n async create(data: CreateTagDto): Promise<TagDto> {\n return this.http.request<TagDto>({ method: 'POST', path: this.basePath, body: data });\n }\n\n /** PATCH /tags/:id — rename / recolor; 409 on a case-insensitive duplicate name. */\n async update(id: string, data: UpdateTagDto): Promise<TagDto> {\n return this.http.request<TagDto>({ method: 'PATCH', path: `${this.basePath}/${id}`, body: data });\n }\n\n /** DELETE /tags/:id — cascades the tag off every record. */\n async delete(id: string): Promise<void> {\n await this.http.request<void>({ method: 'DELETE', path: `${this.basePath}/${id}` });\n }\n\n /** `TagsService.setEntityTags()` returns the final tag set wrapped in `{ tags }`. */\n async setEntityTags(\n entityType: string,\n entityId: string,\n data: SetEntityTagsDto,\n ): Promise<{ tags: TagDto[] }> {\n return this.http.request<{ tags: TagDto[] }>({\n method: 'PUT',\n path: `/entities/${entityType}/${entityId}/tags`,\n body: data,\n });\n }\n\n async getEntityTags(entityType: string, entityId: string): Promise<EntityTagDto[]> {\n return this.http.request<EntityTagDto[]>({\n method: 'GET',\n path: `/entities/${entityType}/${entityId}/tags`,\n });\n }\n\n async getEntities(tagId: string, params?: Record<string, unknown>): Promise<TagEntitiesPage> {\n return this.http.request<TagEntitiesPage>({\n method: 'GET',\n path: `/tags/${tagId}/entities`,\n params: params as Record<string, unknown>,\n });\n }\n}\n","import { ApiResource } from './base-resource';\nimport type {\n TaskDto,\n CompletedTaskDto,\n CreateTaskDto,\n UpdateTaskDto,\n ListTasksParams,\n} from '../types/task-types';\n\n/**\n * Tasks — the free-core to-do module (`/api/v1/tasks`). `list()` returns a\n * BARE array (the controller does not wrap in `{ data, meta }`); paginate with\n * `limit`/`offset`. Terminal status changes go through the explicit action\n * methods, never `update()`. There is no delete: a task is cancelled\n * (`cancel()`), and `reopen()` brings it back.\n */\nexport class TaskResource extends ApiResource {\n constructor(http: ConstructorParameters<typeof ApiResource>[0]) {\n super(http, '/tasks');\n }\n\n async list(params?: ListTasksParams): Promise<TaskDto[]> {\n return this.http.request<TaskDto[]>({\n method: 'GET',\n path: this.basePath,\n params: params as Record<string, unknown>,\n });\n }\n\n /** GET /tasks/:id */\n async get(id: string): Promise<TaskDto> {\n return this.http.request<TaskDto>({ method: 'GET', path: `${this.basePath}/${id}` });\n }\n\n /** POST /tasks */\n async create(data: CreateTaskDto): Promise<TaskDto> {\n return this.http.request<TaskDto>({ method: 'POST', path: this.basePath, body: data });\n }\n\n /** PATCH /tasks/:id — non-terminal fields only; terminal status changes use the action methods. */\n async update(id: string, data: UpdateTaskDto): Promise<TaskDto> {\n return this.http.request<TaskDto>({ method: 'PATCH', path: `${this.basePath}/${id}`, body: data });\n }\n\n /**\n * POST /tasks/:id/complete — fires the linked workflow transition (best-effort).\n * The task always completes; `workflow_trigger` says whether the next step ran.\n */\n async complete(id: string): Promise<CompletedTaskDto> {\n return this.http.request<CompletedTaskDto>({ method: 'POST', path: `${this.basePath}/${id}/complete` });\n }\n\n /** POST /tasks/:id/cancel */\n async cancel(id: string): Promise<TaskDto> {\n return this.http.request<TaskDto>({ method: 'POST', path: `${this.basePath}/${id}/cancel` });\n }\n\n /** POST /tasks/:id/reopen — the sanctioned resurrect of a completed/cancelled task. */\n async reopen(id: string): Promise<TaskDto> {\n return this.http.request<TaskDto>({ method: 'POST', path: `${this.basePath}/${id}/reopen` });\n }\n}\n","import type { HttpClient } from '../http';\nimport type { RecordViewInput, RecentRecordView, RecentRecordViewsParams } from '../types/record-view-types';\nimport { toQueryParams } from './query-params';\n\n/**\n * Record views (`/api/v1/record-views`, E41-F7) — \"recently viewed\". Not a\n * CRUD resource (no id, no get/update/delete), so it does NOT extend\n * `BaseResource`.\n *\n * `touch()` is the idempotent beacon a UI sends when a record opens; the server\n * keeps one row per user × record and answers 204 even for an id that no\n * longer exists. `recent()` is the caller's newest views. Views feed\n * `sort_by: 'recent'` on `customers.list` / `contacts.list` /\n * `opportunities.list` / `jobs.listForOpportunity`.\n */\nexport class RecordViewsResource {\n private readonly basePath = '/record-views';\n\n constructor(private readonly http: HttpClient) {}\n\n /** POST /record-views — record that the calling user opened this record. Resolves on 204. */\n async touch(input: RecordViewInput): Promise<void> {\n await this.http.request<void>({ method: 'POST', path: this.basePath, body: input });\n }\n\n /** GET /record-views/recent — the calling user's most recently viewed records, newest first. */\n async recent(params?: RecentRecordViewsParams): Promise<RecentRecordView[]> {\n return this.http.request<RecentRecordView[]>({\n method: 'GET',\n path: `${this.basePath}/recent`,\n params: toQueryParams(params as Record<string, unknown> | undefined),\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type { TenantModuleStatusDto } from '../types/module-types';\n\n/**\n * Module status (`/api/v1/modules`, E51-F1-S1) — which modules are active for\n * the caller's tenant. Readable by every role; read-only (activating a module\n * is an admin action on `/admin/modules`, deliberately not exposed here). Not a\n * CRUD resource, so it does NOT extend `BaseResource`.\n */\nexport class ModulesResource {\n private readonly basePath = '/modules';\n\n constructor(private readonly http: HttpClient) {}\n\n /** GET /modules — one entry per registered module key, in canonical order. */\n async list(): Promise<TenantModuleStatusDto[]> {\n const res = await this.http.request<{ data: TenantModuleStatusDto[] }>({\n method: 'GET',\n path: this.basePath,\n });\n return res.data;\n }\n}\n","import type { HttpClient } from '../http';\nimport type { TenantPlanDto } from '../types/plan-types';\n\n/**\n * The caller tenant's plan (`/api/v1/plan`, E54-F1-S2). Readable by every role;\n * read-only — plans change through Stripe-hosted billing in Settings → Billing,\n * deliberately not through the API. Not a CRUD resource, so it does NOT extend\n * `BaseResource`.\n */\nexport class PlanResource {\n private readonly basePath = '/plan';\n\n constructor(private readonly http: HttpClient) {}\n\n /** GET /plan — plan, billing status and the plan's ceiling. */\n async get(): Promise<TenantPlanDto> {\n const res = await this.http.request<{ data: TenantPlanDto }>({\n method: 'GET',\n path: this.basePath,\n });\n return res.data;\n }\n}\n","import type { HttpClient } from '../http';\nimport type { MeAbilitiesResponse } from '../types/me-types';\n\n/**\n * The authenticated caller (`/api/v1/me/*`, E52-F1-S3). Any principal may read\n * its own abilities; not a CRUD resource, so it does NOT extend `BaseResource`.\n */\nexport class MeResource {\n private readonly basePath = '/me';\n\n constructor(private readonly http: HttpClient) {}\n\n /**\n * GET /me/abilities — the caller's effective ability: for an API key, the key\n * role intersected with its acting agent's live role (`actingRole`).\n */\n async abilities(): Promise<MeAbilitiesResponse> {\n return this.http.request<MeAbilitiesResponse>({\n method: 'GET',\n path: `${this.basePath}/abilities`,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n RoleDetailDto,\n RoleHistoryPage,\n RoleHistoryParams,\n RoleKey,\n RoleListResponse,\n TunableRole,\n UpdateRoleGrantsBody,\n} from '../types/role-types';\n\n/**\n * Settings → Roles (`/api/v1/roles`, E52-F2). Admin only: reads need Read\n * RolePermission and writes Update RolePermission, which only admins hold —\n * any other caller gets a 403 `FORBIDDEN_BY_ROLE`. Not a CRUD resource, so it\n * does NOT extend `BaseResource`.\n */\nexport class RolesResource {\n private readonly basePath = '/roles';\n\n constructor(private readonly http: HttpClient) {}\n\n /** GET /roles — every role (not `system_agent`) with user and change counts. */\n async list(): Promise<RoleListResponse> {\n return this.http.request<RoleListResponse>({ method: 'GET', path: this.basePath });\n }\n\n /** GET /roles/:key — one role's permission matrix, summary and grant version. */\n async get(key: RoleKey): Promise<RoleDetailDto> {\n return this.http.request<RoleDetailDto>({\n method: 'GET',\n path: `${this.basePath}/${encodeURIComponent(key)}`,\n });\n }\n\n /**\n * PUT /roles/:key/grants — sparse per-cell changes, all validated before any\n * is written. 422 `GRANT_REQUIRED` / `GRANT_NOT_DELEGABLE` for locked cells,\n * `CONFIRMATION_REQUIRED` when a money action is granted without\n * `confirm: true` + `reason`, `ROLE_NOT_TUNABLE` for admin; 409\n * `ROLE_VERSION_CONFLICT` on a stale `baseVersion`. Returns the updated role.\n */\n async updateGrants(key: TunableRole, body: UpdateRoleGrantsBody): Promise<RoleDetailDto> {\n return this.http.request<RoleDetailDto>({\n method: 'PUT',\n path: `${this.basePath}/${encodeURIComponent(key)}/grants`,\n body,\n });\n }\n\n /** DELETE /roles/:key/grants — back to the Planes defaults. Returns the reset role. */\n async reset(key: TunableRole): Promise<RoleDetailDto> {\n return this.http.request<RoleDetailDto>({\n method: 'DELETE',\n path: `${this.basePath}/${encodeURIComponent(key)}/grants`,\n });\n }\n\n /** GET /roles/history — the change log, newest first; page with `cursor`. */\n async history(params?: RoleHistoryParams): Promise<RoleHistoryPage> {\n return this.http.request<RoleHistoryPage>({\n method: 'GET',\n path: `${this.basePath}/history`,\n ...(params ? { params: params as Record<string, unknown> } : {}),\n });\n }\n}\n","import type { HttpClient } from '../http';\n\n/** Body and response of `admin/invoice-settings/offline-instructions`. */\nexport interface OfflineInstructionsDto {\n instructions: string | null;\n}\n\n/** Body and response of `admin/invoice-settings/overdue-offsets` (E56-F8-S2). */\nexport interface OverdueOffsetsDto {\n /** Days after the due date at which `invoice.overdue` fires, ascending. */\n offsets: number[];\n}\n\n/** Body and response of `admin/invoice-settings/attach-pdf` (E56-F6-S2). */\nexport interface AttachPdfSettingDto {\n enabled: boolean;\n}\n\n/**\n * Per-tenant invoice settings (`/api/v1/admin/invoice-settings`, E56-F5-S4).\n * Reads need Read Invoice; writes need Update Tenant (admin only) — any other\n * caller gets a 403 `FORBIDDEN_BY_ROLE`. Not a CRUD resource, so it does NOT\n * extend `BaseResource`.\n */\nexport class InvoiceSettingsResource {\n private readonly basePath = '/admin/invoice-settings';\n\n constructor(private readonly http: HttpClient) {}\n\n /**\n * GET /admin/invoice-settings/offline-instructions — the plain-text\n * instructions shown on the pay page when online payment is unavailable,\n * or null when none are set.\n */\n async getOfflineInstructions(): Promise<string | null> {\n const res = await this.http.request<OfflineInstructionsDto>({\n method: 'GET',\n path: `${this.basePath}/offline-instructions`,\n });\n return res.instructions;\n }\n\n /**\n * PUT /admin/invoice-settings/offline-instructions — store plain text\n * (trimmed, at most 2,000 characters); null or blank clears it. Returns the\n * stored value.\n */\n async setOfflineInstructions(text: string | null): Promise<string | null> {\n const body: OfflineInstructionsDto = { instructions: text };\n const res = await this.http.request<OfflineInstructionsDto>({\n method: 'PUT',\n path: `${this.basePath}/offline-instructions`,\n body,\n });\n return res.instructions;\n }\n /**\n * GET /admin/invoice-settings/overdue-offsets — the days after the due date\n * at which `invoice.overdue` fires, ascending (default `[3, 7, 14]`).\n */\n async getOverdueOffsets(): Promise<number[]> {\n const res = await this.http.request<OverdueOffsetsDto>({\n method: 'GET',\n path: `${this.basePath}/overdue-offsets`,\n });\n return res.offsets;\n }\n\n /**\n * PUT /admin/invoice-settings/overdue-offsets — replace the offsets: 1 to 5\n * unique whole days, each 1–90. Returns them sorted ascending. Admin only.\n */\n async setOverdueOffsets(offsets: number[]): Promise<number[]> {\n const body: OverdueOffsetsDto = { offsets };\n const res = await this.http.request<OverdueOffsetsDto>({\n method: 'PUT',\n path: `${this.basePath}/overdue-offsets`,\n body,\n });\n return res.offsets;\n }\n\n /**\n * GET /admin/invoice-settings/attach-pdf — whether invoice emails carry the\n * invoice PDF (on unless an admin turned it off).\n */\n async getAttachPdf(): Promise<boolean> {\n const res = await this.http.request<AttachPdfSettingDto>({\n method: 'GET',\n path: `${this.basePath}/attach-pdf`,\n });\n return res.enabled;\n }\n\n /**\n * PUT /admin/invoice-settings/attach-pdf — turn the PDF attachment on\n * invoice emails on or off. Returns the stored value.\n */\n async setAttachPdf(enabled: boolean): Promise<boolean> {\n const body: AttachPdfSettingDto = { enabled };\n const res = await this.http.request<AttachPdfSettingDto>({\n method: 'PUT',\n path: `${this.basePath}/attach-pdf`,\n body,\n });\n return res.enabled;\n }\n}\n","import type { HttpClient } from '../http';\n\n/** Who a reminder rule emails about: every overdue invoice, or recurring ones only. */\nexport type InvoiceReminderAppliesTo = 'all' | 'recurring_only';\n\n/**\n * One rule as sent to `invoiceReminders.update()` (mirrors the shared\n * `invoiceReminderRuleSchema` input; the spec parses the captured body with it).\n */\nexport interface InvoiceReminderRuleInputDto {\n /** Days past due the reminder sends at, 1–90, unique across the set. */\n offsetDays: number;\n enabled: boolean;\n /** Plain text, ≤ 200 chars. Placeholders: see `update()`. */\n subject: string;\n /** Plain text, ≤ 5,000 chars. */\n body: string;\n /** Defaults to `'all'`. */\n appliesTo?: InvoiceReminderAppliesTo;\n}\n\n/** A stored rule (mirrors `invoiceReminderRuleResponseSchema`). */\nexport interface InvoiceReminderRuleDto {\n offsetDays: number;\n enabled: boolean;\n subject: string;\n body: string;\n appliesTo: InvoiceReminderAppliesTo;\n /** The offset is not in the overdue offsets, so this rule never sends. */\n willNeverFire: boolean;\n}\n\n/** GET / PUT `admin/invoice-reminders` (mirrors `invoiceReminderSettingsSchema`). */\nexport interface InvoiceReminderSettingsDto {\n rules: InvoiceReminderRuleDto[];\n /** The days past due the overdue sweep fires at (`invoiceSettings.getOverdueOffsets()`). */\n overdueOffsets: number[];\n}\n\n/** Body of `invoiceReminders.preview()` (mirrors `invoiceReminderPreviewSchema`). */\nexport interface InvoiceReminderPreviewParams {\n subject: string;\n body: string;\n /** Render against this invoice; omit for a sample invoice. */\n invoiceId?: string;\n}\n\n/** What a reminder would send (mirrors `invoiceReminderPreviewResultSchema`). */\nexport interface InvoiceReminderPreviewDto {\n subject: string;\n body: string;\n /** True when rendered against the sample invoice. */\n sample: boolean;\n}\n\n/**\n * Payment reminder settings (`/api/v1/admin/invoice-reminders`, E56-F8-S6).\n * Admin only (Update Tenant) — reads included; anyone else gets a 403\n * `FORBIDDEN_BY_ROLE`. Not a CRUD resource, so it does NOT extend `BaseResource`.\n */\nexport class InvoiceRemindersResource {\n private readonly basePath = '/admin/invoice-reminders';\n\n constructor(private readonly http: HttpClient) {}\n\n /** GET /admin/invoice-reminders — the rules, ascending by offset, and the overdue offsets. */\n async list(): Promise<InvoiceReminderSettingsDto> {\n return this.http.request<InvoiceReminderSettingsDto>({\n method: 'GET',\n path: this.basePath,\n });\n }\n\n /**\n * PUT /admin/invoice-reminders — replace the WHOLE rule set (a rule left out\n * is deleted): up to 5 rules at unique offsets. Templates may use only\n * `{{customerName}}`, `{{invoiceNumber}}`, `{{amountDue}}`, `{{total}}`,\n * `{{dueDate}}`, `{{daysOverdue}}` and `{{companyName}}`; any other is a 422\n * `REMINDER_TEMPLATE_INVALID` listing each one. Returns the stored set.\n */\n async update(rules: InvoiceReminderRuleInputDto[]): Promise<InvoiceReminderSettingsDto> {\n return this.http.request<InvoiceReminderSettingsDto>({\n method: 'PUT',\n path: this.basePath,\n body: { rules },\n });\n }\n\n /**\n * POST /admin/invoice-reminders/preview — render a subject and body exactly\n * as the reminder email would, against an invoice or a sample. Sends nothing.\n */\n async preview(params: InvoiceReminderPreviewParams): Promise<InvoiceReminderPreviewDto> {\n return this.http.request<InvoiceReminderPreviewDto>({\n method: 'POST',\n path: `${this.basePath}/preview`,\n body: params,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type { ContactSequenceListResponse } from '../types/contact-sequence-types';\n\n/**\n * Enrollable sequences (`/api/v1/contact-sequences`, E51-F2-S6) — the published\n * contact sequences in the caller's tenant, readable by any role that can enroll\n * a contact. Read-only (authoring sequences stays on the admin workflow API). Not\n * a CRUD resource, so it does NOT extend `BaseResource`.\n */\nexport class ContactSequencesResource {\n private readonly basePath = '/contact-sequences';\n\n constructor(private readonly http: HttpClient) {}\n\n /** GET /contact-sequences — `{ data, meta: { total } }`, name-sorted, max 100. */\n async list(): Promise<ContactSequenceListResponse> {\n return this.http.request<ContactSequenceListResponse>({\n method: 'GET',\n path: this.basePath,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ContactEnrollmentDto,\n ContactEnrollmentListItemDto,\n ListEnrollmentsParams,\n} from '../types/enrollment-types';\n\n/**\n * Contact sequence enrollments (E51-F2-S8) — enroll a contact in a sequence, list\n * its enrollments, and manually exit one. Pick the sequence with\n * `client.contactSequences.list()`; its `id` is the `workflowId` here. Not a CRUD\n * resource, so it does NOT extend `BaseResource`.\n */\nexport class EnrollmentsResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * POST /contacts/:id/enrollments — `{ workflow_id }`. 409 if the contact is\n * already active in the sequence; 422 if the sequence has no published version.\n */\n async enroll(contactId: string, workflowId: string): Promise<ContactEnrollmentDto> {\n return this.http.request<ContactEnrollmentDto>({\n method: 'POST',\n path: `/contacts/${contactId}/enrollments`,\n body: { workflow_id: workflowId },\n });\n }\n\n /** GET /contacts/:id/enrollments — a bare array, newest first; optional `status` filter. */\n async list(contactId: string, params?: ListEnrollmentsParams): Promise<ContactEnrollmentListItemDto[]> {\n return this.http.request<ContactEnrollmentListItemDto[]>({\n method: 'GET',\n path: `/contacts/${contactId}/enrollments`,\n ...(params ? { params: params as Record<string, unknown> } : {}),\n });\n }\n\n /**\n * POST /enrollments/:id/exit — no body; the exit reason is recorded as\n * `'manual'`. Idempotent: an already-ended enrollment is returned unchanged.\n */\n async exit(enrollmentId: string): Promise<ContactEnrollmentDto> {\n return this.http.request<ContactEnrollmentDto>({\n method: 'POST',\n path: `/enrollments/${enrollmentId}/exit`,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWorkflowEventRuleInput,\n DeleteWorkflowEventRuleResult,\n UpdateWorkflowEventRuleInput,\n WorkflowEventRuleDto,\n} from '../types/workflow-event-rule-types';\n\n/**\n * One workflow's event rules on the admin workflow API\n * (`/api/v1/admin/workflows/:workflowId/event-rules`; manage Workflow + the\n * `workflow_builder` feature). Get one with `client.workflows.eventRules(id)`;\n * `workflowId` may be any version's id — the server keys rules by the lineage\n * root, so they survive a publish. Not flat CRUD (no single-rule read), so it\n * does NOT extend `BaseResource`.\n */\nexport class WorkflowEventRulesResource {\n constructor(\n private readonly http: HttpClient,\n readonly workflowId: string,\n ) {}\n\n // Each request spells its path out in full (no computed base path) so the\n // API's SDK route-parity audit (sdk-route-parity-audit.spec.ts) can read it.\n\n /** GET — a bare array of the workflow's live rules, newest first. 404 `WORKFLOW_NOT_FOUND`. */\n async list(): Promise<WorkflowEventRuleDto[]> {\n return this.http.request<WorkflowEventRuleDto[]>({\n method: 'GET',\n path: `/admin/workflows/${encodeURIComponent(this.workflowId)}/event-rules`,\n });\n }\n\n /**\n * POST — add a rule (201). `when` defaults to `{}` and `enabled` to true; use\n * `when: {}` for the creation types in `WORKFLOW_EVENT_RULE_MATCH_ALL_EVENT_TYPES`.\n * 400 `VALIDATION_FAILED` for a bad body; 404 `WORKFLOW_NOT_FOUND`.\n */\n async create(input: CreateWorkflowEventRuleInput): Promise<WorkflowEventRuleDto> {\n return this.http.request<WorkflowEventRuleDto>({\n method: 'POST',\n path: `/admin/workflows/${encodeURIComponent(this.workflowId)}/event-rules`,\n body: input,\n });\n }\n\n /** PATCH /:id — only the fields sent change (at least one). 404 `EVENT_RULE_NOT_FOUND`. */\n async update(ruleId: string, input: UpdateWorkflowEventRuleInput): Promise<WorkflowEventRuleDto> {\n return this.http.request<WorkflowEventRuleDto>({\n method: 'PATCH',\n path: `/admin/workflows/${encodeURIComponent(this.workflowId)}/event-rules/${encodeURIComponent(ruleId)}`,\n body: input,\n });\n }\n\n /** DELETE /:id — soft delete; returns `{ id }` (200). 404 `EVENT_RULE_NOT_FOUND`. */\n async delete(ruleId: string): Promise<DeleteWorkflowEventRuleResult> {\n return this.http.request<DeleteWorkflowEventRuleResult>({\n method: 'DELETE',\n path: `/admin/workflows/${encodeURIComponent(this.workflowId)}/event-rules/${encodeURIComponent(ruleId)}`,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport { WorkflowEventRulesResource } from './workflow-event-rules';\nimport type { WorkflowVersionStagesResponse } from '../types/workflow-stage-types';\nimport type {\n CreateWorkflowTransitionInput,\n UpdateWorkflowTransitionInput,\n WorkflowTransitionDto,\n} from '../types/workflow-transition-types';\nimport type {\n ListTransitionSummariesParams,\n TransitionSummariesResponse,\n} from '../types/workflow-transition-summary-types';\n\n/**\n * Tenant-readable workflow reads (`/api/v1/workflows`), plus transition\n * authoring on the admin workflow API (`/api/v1/admin/workflows`, which needs\n * the `workflow_builder` feature and a draft workflow), and a workflow's event\n * rules via `eventRules(workflowId)`. Not a CRUD resource, so it does NOT\n * extend `BaseResource`.\n */\nexport class WorkflowsResource {\n private readonly basePath = '/workflows';\n private readonly adminPath = '/admin/workflows';\n\n constructor(private readonly http: HttpClient) {}\n\n /**\n * GET /workflows/:id/stages — the ordered stages of one workflow version. Pass\n * a record's instance `workflowId` (e.g. from `GET /opportunities/:id/workflow`)\n * to get the stages of the version that record is pinned to, even an archived\n * one. 404 for an unknown or draft id; 403 if the caller cannot read the\n * workflow's record type.\n */\n async getStages(workflowId: string): Promise<WorkflowVersionStagesResponse> {\n return this.http.request<WorkflowVersionStagesResponse>({\n method: 'GET',\n path: `${this.basePath}/${encodeURIComponent(workflowId)}/stages`,\n });\n }\n\n /**\n * GET /workflows/transitions — transition summaries of every published\n * workflow, or with `workflowId` of that one version (published or archived;\n * an unknown, draft or other-tenant id returns an empty list). Each summary\n * carries `workflow_id`, `workflow_version`, `lineage_workflow_id` and\n * `automatic_only`. Only pipelines whose record type the caller can read\n * are listed; an unreadable `workflowId` returns an empty list, not 403.\n */\n async listTransitions(params: ListTransitionSummariesParams = {}): Promise<TransitionSummariesResponse> {\n const query: Record<string, unknown> = {};\n if (params.entityTypes && params.entityTypes.length > 0) query['entity_types'] = params.entityTypes.join(',');\n if (params.workflowId) query['workflow_id'] = params.workflowId;\n return this.http.request<TransitionSummariesResponse>({\n method: 'GET',\n path: `${this.basePath}/transitions`,\n params: query,\n });\n }\n\n /**\n * POST /admin/workflows/:wid/transitions — add a transition to a draft\n * workflow. Pass `automaticOnly: true` for an edge only automation should\n * fire (e.g. an event rule advancing on \"contract sent\").\n */\n async createTransition(\n workflowId: string,\n input: CreateWorkflowTransitionInput,\n ): Promise<WorkflowTransitionDto> {\n return this.http.request<WorkflowTransitionDto>({\n method: 'POST',\n path: `${this.adminPath}/${encodeURIComponent(workflowId)}/transitions`,\n body: input,\n });\n }\n\n /**\n * The event rules of one workflow (`/admin/workflows/:wid/event-rules`) —\n * \"advance automatically when…\". Any version's id works; rules are keyed by\n * the lineage root.\n */\n eventRules(workflowId: string): WorkflowEventRulesResource {\n return new WorkflowEventRulesResource(this.http, workflowId);\n }\n\n /** PUT /admin/workflows/:wid/transitions/:tid — update a draft workflow's transition. */\n async updateTransition(\n workflowId: string,\n transitionId: string,\n input: UpdateWorkflowTransitionInput,\n ): Promise<WorkflowTransitionDto> {\n return this.http.request<WorkflowTransitionDto>({\n method: 'PUT',\n path: `${this.adminPath}/${encodeURIComponent(workflowId)}/transitions/${encodeURIComponent(transitionId)}`,\n body: input,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type { ConflictPreviewRequest, ConflictPreviewResponse } from '../types/service-schedule-types';\n\n/**\n * Recurring-service scheduling reads (`/api/v1/service-schedules`). Only the\n * crew conflict preview for now. Not a CRUD resource, so it does NOT extend\n * `BaseResource`.\n */\nexport class ServiceSchedulesResource {\n private readonly basePath = '/service-schedules';\n\n constructor(private readonly http: HttpClient) {}\n\n /**\n * POST /service-schedules/conflicts/preview — before booking people, see\n * who is already on another event (`conflicts`) and who would be outside\n * their working hours (`working_hours`). Read-only and warn-only: nothing\n * is ever blocked. Give exactly one of `windows` (single booking) or\n * `schedule` (a cadence). 400 on invalid input, INVALID_RECURRENCE_RULE,\n * INVALID_TIMEZONE or TOO_MANY_WINDOWS (> 400).\n */\n async previewConflicts(request: ConflictPreviewRequest): Promise<ConflictPreviewResponse> {\n return this.http.request<ConflictPreviewResponse>({\n method: 'POST',\n path: `${this.basePath}/conflicts/preview`,\n body: request,\n });\n }\n}\n","import { BaseResource } from './base-resource';\nimport { toQueryParams } from './query-params';\nimport type {\n BillingAdjustment,\n BillingAdjustmentListParams,\n BillingAdjustmentListResponse,\n BillingAutopaySummaryResponse,\n BillingSchedule,\n BillingScheduleDetail,\n BillingScheduleLineVersionListResponse,\n BillingScheduleListParams,\n BillingScheduleListResponse,\n BillingSchedulePreview,\n BillingSchedulePreviewParams,\n BillingSchedulePeriod,\n BillingSchedulePeriodListParams,\n BillingSchedulePeriodListResponse,\n BillingNeedsReviewParams,\n BillingNeedsReviewResponse,\n CreateBillingAdjustmentDto,\n CreateBillingScheduleDto,\n GenerateNowResult,\n RegeneratePeriodResult,\n SkipBillingPeriodDto,\n UpdateBillingScheduleDto,\n} from '../types/billing-schedule-types';\n\n/**\n * Recurring billing schedules (`/admin/billing-schedules`, E56-F3-S1/S2):\n * CRUD, pause / resume / end, preview, the period ledger, the tenant-wide\n * Needs review queue, generate-now, per-period skip / regenerate, and one-off\n * adjustments on the next invoice (E56-F8-S5). Needs the\n * invoicing module; reads need Read and writes Update on BillingSchedule.\n */\nexport class BillingScheduleResource extends BaseResource<\n BillingSchedule,\n CreateBillingScheduleDto,\n UpdateBillingScheduleDto,\n BillingScheduleListResponse\n> {\n constructor(http: ConstructorParameters<typeof BaseResource>[0]) {\n super(http, '/admin/billing-schedules');\n }\n\n /** GET /admin/billing-schedules — list rows (no lines; `line_count` and `next_amount` instead). */\n override async list(params?: BillingScheduleListParams): Promise<BillingScheduleListResponse> {\n return this.http.request<BillingScheduleListResponse>({\n method: 'GET',\n path: this.basePath,\n params: toQueryParams(params as Record<string, unknown>),\n });\n }\n\n /** GET /admin/billing-schedules/:id — the schedule with its lines and next 3 periods. */\n override async get(id: string): Promise<BillingScheduleDetail> {\n return this.http.request<BillingScheduleDetail>({\n method: 'GET',\n path: `${this.basePath}/${encodeURIComponent(id)}`,\n });\n }\n\n /**\n * POST /admin/billing-schedules. `timezone` defaults to the tenant's. A\n * backdated `start_date` needs `backfill` ('skip' | 'draft'), else 400\n * `BACKFILL_REQUIRED`; `collection: 'autopay'` without a platform-managed\n * Stripe connection and a saved default card is 422 `AUTOPAY_UNAVAILABLE`\n * (`reason` `no_connect` | `no_saved_method`).\n */\n override async create(data: CreateBillingScheduleDto): Promise<BillingSchedule> {\n return this.http.request<BillingSchedule>({\n method: 'POST',\n path: this.basePath,\n body: data,\n });\n }\n\n /**\n * PATCH /admin/billing-schedules/:id — partial. `lines` replaces every line\n * from `effective_from` (default: the next period not yet invoiced) as a new\n * line version. 409 `CADENCE_LOCKED`; 422 `EFFECTIVE_DATE_IN_GENERATED_PERIOD`\n * for a date inside an invoiced period.\n */\n override async update(id: string, data: UpdateBillingScheduleDto): Promise<BillingSchedule> {\n return this.http.request<BillingSchedule>({\n method: 'PATCH',\n path: `${this.basePath}/${encodeURIComponent(id)}`,\n body: data,\n });\n }\n\n /** DELETE /admin/billing-schedules/:id — only a schedule that never invoiced (else 409 `SCHEDULE_HAS_INVOICES`). */\n override async delete(id: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `${this.basePath}/${encodeURIComponent(id)}`,\n });\n }\n\n /** GET /admin/billing-schedules/:id/versions — line version history, newest effective date first. */\n async listVersions(id: string): Promise<BillingScheduleLineVersionListResponse> {\n return this.http.request<BillingScheduleLineVersionListResponse>({\n method: 'GET',\n path: `${this.basePath}/${encodeURIComponent(id)}/versions`,\n });\n }\n\n /**\n * POST /admin/billing-schedules/:id/adjustments — add a one-off amount\n * (signed; negative is a credit) to the schedule's next generated invoice.\n * Billed exactly once; no automatic proration. 422 `ADJUSTMENT_INVALID`\n * (zero, or the next invoice would go below zero) or `SCHEDULE_NOT_ACTIVE`.\n */\n async addAdjustment(id: string, data: CreateBillingAdjustmentDto): Promise<BillingAdjustment> {\n return this.http.request<BillingAdjustment>({\n method: 'POST',\n path: `${this.basePath}/${encodeURIComponent(id)}/adjustments`,\n body: data,\n });\n }\n\n /** GET /admin/billing-schedules/:id/adjustments — oldest first; default pending and consumed. */\n async listAdjustments(id: string, params?: BillingAdjustmentListParams): Promise<BillingAdjustmentListResponse> {\n return this.http.request<BillingAdjustmentListResponse>({\n method: 'GET',\n path: `${this.basePath}/${encodeURIComponent(id)}/adjustments`,\n params: toQueryParams(params as Record<string, unknown>),\n });\n }\n\n /**\n * DELETE /admin/billing-schedules/:id/adjustments/:adjustmentId — cancel a\n * pending adjustment (it never bills). Returns it; 409\n * `ADJUSTMENT_ALREADY_CONSUMED` once an invoice carries it.\n */\n async cancelAdjustment(id: string, adjustmentId: string): Promise<BillingAdjustment> {\n return this.http.request<BillingAdjustment>({\n method: 'DELETE',\n path: `${this.basePath}/${encodeURIComponent(id)}/adjustments/${encodeURIComponent(adjustmentId)}`,\n });\n }\n\n /** POST /admin/billing-schedules/:id/pause — 409 `INVALID_STATUS_TRANSITION` unless active. */\n async pause(id: string): Promise<BillingSchedule> {\n return this.http.request<BillingSchedule>({\n method: 'POST',\n path: `${this.basePath}/${encodeURIComponent(id)}/pause`,\n });\n }\n\n /** POST /admin/billing-schedules/:id/resume — periods that fell due while paused are skipped. */\n async resume(id: string): Promise<BillingSchedule> {\n return this.http.request<BillingSchedule>({\n method: 'POST',\n path: `${this.basePath}/${encodeURIComponent(id)}/resume`,\n });\n }\n\n /** POST /admin/billing-schedules/:id/end — no further periods generate. */\n async end(id: string): Promise<BillingSchedule> {\n return this.http.request<BillingSchedule>({\n method: 'POST',\n path: `${this.basePath}/${encodeURIComponent(id)}/end`,\n });\n }\n\n /** GET /admin/billing-schedules/:id/preview — the next `count` (1–24, default 6) periods from the first unbilled one. */\n async preview(id: string, params?: BillingSchedulePreviewParams): Promise<BillingSchedulePreview> {\n return this.http.request<BillingSchedulePreview>({\n method: 'GET',\n path: `${this.basePath}/${encodeURIComponent(id)}/preview`,\n params: toQueryParams(params as Record<string, unknown>),\n });\n }\n\n /** GET /admin/billing-schedules/:id/periods — the period ledger, newest first. */\n async periods(id: string, params?: BillingSchedulePeriodListParams): Promise<BillingSchedulePeriodListResponse> {\n return this.http.request<BillingSchedulePeriodListResponse>({\n method: 'GET',\n path: `${this.basePath}/${encodeURIComponent(id)}/periods`,\n params: toQueryParams(params as Record<string, unknown>),\n });\n }\n\n /**\n * GET /admin/billing-schedules/periods/needs-review — tenant-wide: needs_review\n * rows plus failed and stuck (pending over 2h) deliveries.\n */\n async needsReview(params?: BillingNeedsReviewParams): Promise<BillingNeedsReviewResponse> {\n return this.http.request<BillingNeedsReviewResponse>({\n method: 'GET',\n path: `${this.basePath}/periods/needs-review`,\n params: toQueryParams(params as Record<string, unknown>),\n });\n }\n\n /**\n * GET /admin/billing-schedules/autopay-summary — what disconnecting Stripe\n * would touch: live autopay schedules, the distinct customers on them, and\n * active saved cards on the current Stripe connection. On disconnect, autopay\n * schedules that can no longer charge switch to emailed pay links.\n */\n async autopaySummary(): Promise<BillingAutopaySummaryResponse> {\n return this.http.request<BillingAutopaySummaryResponse>({\n method: 'GET',\n path: `${this.basePath}/autopay-summary`,\n });\n }\n\n /**\n * POST /admin/billing-schedules/:id/generate-now — bill the current due\n * period now. Idempotent (`already_generated`); 409 `NOTHING_DUE` or\n * `SCHEDULE_NOT_ACTIVE`.\n */\n async generateNow(id: string): Promise<GenerateNowResult> {\n return this.http.request<GenerateNowResult>({\n method: 'POST',\n path: `${this.basePath}/${encodeURIComponent(id)}/generate-now`,\n });\n }\n\n /**\n * POST /admin/billing-schedules/:id/periods/:periodStart/skip — skip one\n * period (`periodStart` is `YYYY-MM-DD`). Returns the skipped ledger row;\n * 400 `NOT_A_PERIOD`, 409 `PERIOD_ALREADY_INVOICED`.\n */\n async skipPeriod(id: string, periodStart: string, data: SkipBillingPeriodDto = {}): Promise<BillingSchedulePeriod> {\n return this.http.request<BillingSchedulePeriod>({\n method: 'POST',\n path: `${this.basePath}/${encodeURIComponent(id)}/periods/${encodeURIComponent(periodStart)}/skip`,\n body: data,\n });\n }\n\n /**\n * POST /admin/billing-schedules/:id/periods/:periodStart/regenerate — re-bill\n * a period whose invoice was voided (new draft on the same ledger row).\n * 409 `INVOICE_NOT_VOID` or `CHECKOUT_IN_FLIGHT`.\n */\n async regeneratePeriod(id: string, periodStart: string): Promise<RegeneratePeriodResult> {\n return this.http.request<RegeneratePeriodResult>({\n method: 'POST',\n path: `${this.basePath}/${encodeURIComponent(id)}/periods/${encodeURIComponent(periodStart)}/regenerate`,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CustomFieldEntityType,\n CustomFieldType,\n CustomFieldValidationRules,\n} from '../types/custom-field-types';\n\n/** A custom-field definition as returned by `GET /custom-fields`. */\nexport interface CustomFieldDefinitionDto {\n id: string;\n tenantId: string;\n entityType: CustomFieldEntityType;\n fieldKey: string;\n label: string;\n fieldType: CustomFieldType;\n options: string[];\n validationRules: CustomFieldValidationRules;\n defaultValue: unknown;\n displayOrder: number;\n section: string | null;\n searchable: boolean;\n showInList: boolean;\n showInReports: boolean;\n active: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ListCustomFieldDefinitionsParams {\n entity_type?: CustomFieldEntityType;\n /** Include deactivated definitions. Their stored values remain valid on records. */\n include_inactive?: boolean;\n}\n\n/**\n * Read-only view of a tenant's custom-field definitions — the shape of its\n * records. Needed to know which keys a `custom_fields` payload may carry before\n * writing a customer, contact or opportunity.\n *\n * Read-only by design: configuring definitions is an admin action on\n * `/admin/custom-fields` and is deliberately not exposed here.\n */\nexport class CustomFieldsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** List definitions, optionally narrowed to one entity type. */\n async list(\n params?: ListCustomFieldDefinitionsParams,\n ): Promise<CustomFieldDefinitionDto[]> {\n return this.http.request<CustomFieldDefinitionDto[]>({\n method: 'GET',\n path: '/custom-fields',\n params: params as Record<string, unknown>,\n });\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateOrttoMappingDto,\n ListOrttoAudiencesParams,\n OrttoAudienceDto,\n OrttoMappingDto,\n OrttoSyncRequestResult,\n OrttoSyncStatusDto,\n RequestOrttoSyncDto,\n UpdateOrttoMappingDto,\n} from '../types/ortto-types';\n\n/**\n * Ortto connector (E53) — list mappings, the connected account's audiences,\n * manual sync and sync status under `/admin/integrations/ortto`. Admin-only\n * (Manage IntegrationConnection) and gated by the `marketplace` feature.\n * Connecting the Ortto API key is out of scope. Every route wraps its payload\n * in `{ data }`; the methods unwrap it. Not a CRUD resource (no get-by-id), so\n * it does NOT extend `BaseResource`.\n */\nexport class OrttoResource {\n private readonly basePath = '/admin/integrations/ortto';\n\n constructor(private readonly http: HttpClient) {}\n\n /** GET /admin/integrations/ortto/mappings — live mappings with their list names. */\n async listMappings(): Promise<OrttoMappingDto[]> {\n const res = await this.http.request<{ data: OrttoMappingDto[] }>({\n method: 'GET',\n path: `${this.basePath}/mappings`,\n });\n return res.data;\n }\n\n /** GET /admin/integrations/ortto/audiences — the connected account's audiences (cached 60 s). */\n async listAudiences(params?: ListOrttoAudiencesParams): Promise<OrttoAudienceDto[]> {\n const res = await this.http.request<{ data: OrttoAudienceDto[] }>({\n method: 'GET',\n path: `${this.basePath}/audiences`,\n params: params as Record<string, unknown>,\n });\n return res.data;\n }\n\n /** POST /admin/integrations/ortto/mappings — 409 when the list already has a mapping. */\n async createMapping(data: CreateOrttoMappingDto): Promise<OrttoMappingDto> {\n const res = await this.http.request<{ data: OrttoMappingDto }>({\n method: 'POST',\n path: `${this.basePath}/mappings`,\n body: data,\n });\n return res.data;\n }\n\n /** PATCH /admin/integrations/ortto/mappings/:id — `ortto_tag` and/or `create_unmatched`. */\n async updateMapping(id: string, data: UpdateOrttoMappingDto): Promise<OrttoMappingDto> {\n const res = await this.http.request<{ data: OrttoMappingDto }>({\n method: 'PATCH',\n path: `${this.basePath}/mappings/${id}`,\n body: data,\n });\n return res.data;\n }\n\n /**\n * DELETE /admin/integrations/ortto/mappings/:id (204). A pull list becomes\n * editable again and keeps its members; a push tag is NOT removed from Ortto.\n */\n async deleteMapping(id: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: `${this.basePath}/mappings/${id}`,\n });\n }\n\n /**\n * POST /admin/integrations/ortto/sync (202) — queue a sync for one mapping or\n * the whole connection. Never calls Ortto in-request.\n */\n async sync(data: RequestOrttoSyncDto = {}): Promise<OrttoSyncRequestResult> {\n const res = await this.http.request<{ data: OrttoSyncRequestResult }>({\n method: 'POST',\n path: `${this.basePath}/sync`,\n body: data,\n });\n return res.data;\n }\n\n /** GET /admin/integrations/ortto/sync-status — 404 when Ortto is not connected. */\n async syncStatus(): Promise<OrttoSyncStatusDto> {\n const res = await this.http.request<{ data: OrttoSyncStatusDto }>({\n method: 'GET',\n path: `${this.basePath}/sync-status`,\n });\n return res.data;\n }\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { WebhookVerificationError } from './errors';\n\n/**\n * Every subscribable webhook event type, in the server's canonical order.\n *\n * Mirrored (self-contained, NOT imported) from `WEBHOOK_EVENT_TYPES` in\n * `packages/shared` — the SDK must not depend on `@blueprint/shared`.\n * `webhooks.spec.ts` asserts runtime parity, so drift fails there.\n */\nexport const WEBHOOK_EVENT_TYPES = [\n 'created',\n 'updated',\n 'deleted',\n 'status_changed',\n 'entity.tagged',\n 'entity.untagged',\n 'list.member_added',\n 'list.member_removed',\n 'entity.assigned',\n 'entity.unassigned',\n 'team.member_added',\n 'team.member_removed',\n 'task.created',\n 'task.updated',\n 'task.completed',\n 'task.cancelled',\n 'comment.created',\n 'conversation.created',\n 'conversation.matched',\n 'conversation.deleted',\n 'customer.merged',\n 'workflow.instance_moved',\n 'consent.changed',\n 'billing_schedule.period_generated',\n 'billing_schedule.period_needs_review',\n 'billing_schedule.period_delivered',\n 'billing_schedule.period_delivery_failed',\n 'payment.on_void_invoice',\n 'invoice.overdue',\n 'integration_connection.deactivated',\n 'customer_payment_method.invalidated',\n 'invoice.autopay_succeeded',\n 'invoice.autopay_failed',\n] as const;\n\n/** The kinds of change a webhook event can describe. */\nexport type EntityEventType = (typeof WEBHOOK_EVENT_TYPES)[number];\n\n/**\n * The entity kinds a webhook event can target.\n *\n * Mirrored (self-contained, NOT imported) from\n * `packages/shared/src/types/entity-events.ts`. Keep byte-identical.\n */\nexport type EntityType =\n | 'customer'\n | 'opportunity'\n | 'job'\n | 'estimate'\n | 'contract'\n | 'property'\n | 'inventory_item'\n | 'agent_execution'\n | 'calendar_event'\n | 'invoice'\n | 'payment'\n | 'billing_schedule'\n | 'integration_connection';\n\n/**\n * A single entity-change event as emitted onto the Planes event bus and\n * delivered inside a {@link WebhookDelivery}.\n *\n * Mirrored from `EntityEvent` in\n * `packages/shared/src/types/entity-events.ts`.\n */\nexport interface EntityEvent {\n /** What happened (e.g. 'created', 'status_changed'). */\n event_type: EntityEventType;\n /** The kind of record this event is about. */\n entity_type: EntityType;\n /** The affected record's ID. */\n entity_id: string;\n /** Tenant ID that owns this event. */\n tenant_id: string;\n /** Per-field before/after values for the change. */\n changes: Record<string, { old: unknown; new: unknown }>;\n /** Full record snapshot at the time of the event. */\n snapshot: Record<string, unknown>;\n /** ISO 8601 timestamp of when the event occurred. */\n timestamp: string;\n /** Correlation ID linking related events/actions. */\n correlation_id: string;\n}\n\n/**\n * `event.snapshot` of an `invoice.overdue` event (E56-F8-S2). Planes' daily\n * overdue sweep emits it once per invoice at each of the company's overdue\n * offsets (default 3, 7 and 14 days after the due date) while the invoice is\n * sent and not fully paid. After downtime only the highest offset reached is\n * emitted. Mirrored from `InvoiceOverdueSnapshot` in `packages/shared`.\n */\nexport interface InvoiceOverdueSnapshot {\n invoiceId: string;\n invoiceNumber: string;\n customerId: string;\n /** The configured offset this event is for (days after the due date). */\n offsetDays: number;\n /** Whole days since the due date when the sweep ran (at least `offsetDays`). */\n daysOverdue: number;\n /** Dollars still owed, as a decimal string, e.g. `'1250.00'`. */\n balance: string;\n currency: string;\n /** ISO 8601. */\n dueAt: string;\n /** Where the invoice came from, e.g. `'billing_schedule'`. */\n sourceType: string;\n /** The billing schedule, when the invoice came from one. */\n scheduleId?: string;\n}\n\n/**\n * An `invoice.overdue` event: the standard {@link EntityEvent} envelope\n * (`entity_type` `'invoice'`, `entity_id` the invoice, `changes` `{}`) with a\n * typed snapshot. Narrow a delivery with `event.event_type === 'invoice.overdue'`.\n */\nexport interface InvoiceOverdueEvent extends Omit<EntityEvent, 'event_type' | 'entity_type' | 'snapshot'> {\n event_type: 'invoice.overdue';\n entity_type: 'invoice';\n snapshot: InvoiceOverdueSnapshot & Record<string, unknown>;\n}\n\n/**\n * `event.snapshot` of an `integration_connection.deactivated` event\n * (E56-F7-S2): one of the company's integration connections was disconnected,\n * or replaced by a new connection for the same provider. Mirrored from\n * `IntegrationConnectionDeactivatedSnapshot` in `packages/shared`.\n */\nexport interface IntegrationConnectionDeactivatedSnapshot {\n connectionId: string;\n /** The provider, e.g. `'stripe'`. */\n providerSlug: string;\n reason: 'disconnected' | 'replaced';\n /** The new connection, for `replaced`; otherwise null. */\n replacedByConnectionId: string | null;\n}\n\n/**\n * An `integration_connection.deactivated` event: the standard\n * {@link EntityEvent} envelope (`entity_type` `'integration_connection'`,\n * `entity_id` the connection, `changes` `{}`) with a typed snapshot.\n */\nexport interface IntegrationConnectionDeactivatedEvent\n extends Omit<EntityEvent, 'event_type' | 'entity_type' | 'snapshot'> {\n event_type: 'integration_connection.deactivated';\n entity_type: 'integration_connection';\n snapshot: IntegrationConnectionDeactivatedSnapshot & Record<string, unknown>;\n}\n\n/**\n * `event.snapshot` of a `customer_payment_method.invalidated` event\n * (E56-F7-S2): a customer's saved card can no longer be charged because the\n * Stripe connection it was saved on went away. One event per card. Mirrored\n * from `CustomerPaymentMethodInvalidatedSnapshot` in `packages/shared`.\n */\nexport interface CustomerPaymentMethodInvalidatedSnapshot {\n customerId: string;\n /** The saved payment method's id. */\n paymentMethodId: string;\n /** The connection the card lived on. */\n connectionId: string;\n /** Why, e.g. `'connection_changed'`. */\n reason: string;\n}\n\n/**\n * A `customer_payment_method.invalidated` event: the standard\n * {@link EntityEvent} envelope (`entity_type` `'customer'`, `entity_id` the\n * customer, `changes` `{}`) with a typed snapshot.\n */\nexport interface CustomerPaymentMethodInvalidatedEvent\n extends Omit<EntityEvent, 'event_type' | 'entity_type' | 'snapshot'> {\n event_type: 'customer_payment_method.invalidated';\n entity_type: 'customer';\n snapshot: CustomerPaymentMethodInvalidatedSnapshot & Record<string, unknown>;\n}\n\n/**\n * `event.snapshot` of an `invoice.autopay_succeeded` event (E56-F7-S4): a\n * billing schedule's autopay charge on the customer's saved card was paid.\n * Mirrored from `InvoiceAutopaySucceededSnapshot` in `packages/shared`.\n */\nexport interface InvoiceAutopaySucceededSnapshot {\n invoiceId: string;\n scheduleId: string;\n periodId: string;\n /** The charge attempt that succeeded (1, or 2 after a decline). */\n attempt: number;\n paymentIntentId: string;\n}\n\n/**\n * An `invoice.autopay_succeeded` event: the standard {@link EntityEvent}\n * envelope (`entity_type` `'invoice'`, `entity_id` the invoice, `changes` `{}`)\n * with a typed snapshot.\n */\nexport interface InvoiceAutopaySucceededEvent\n extends Omit<EntityEvent, 'event_type' | 'entity_type' | 'snapshot'> {\n event_type: 'invoice.autopay_succeeded';\n entity_type: 'invoice';\n snapshot: InvoiceAutopaySucceededSnapshot & Record<string, unknown>;\n}\n\n/**\n * `event.snapshot` of an `invoice.autopay_failed` event (E56-F7-S4): one\n * autopay attempt ended without a payment — exactly one event per failed\n * attempt. `reason` is `'authentication_required'` (the customer was emailed\n * the pay link; never retried), the card's decline code (e.g.\n * `'insufficient_funds'`), or why autopay gave up (e.g. `'no_saved_method'`).\n * Mirrored from `InvoiceAutopayFailedSnapshot` in `packages/shared`.\n */\nexport interface InvoiceAutopayFailedSnapshot {\n invoiceId: string;\n scheduleId: string;\n periodId: string;\n attempt: number;\n reason: string;\n /** True when a second attempt is scheduled (after a first decline). */\n willRetry: boolean;\n /** ISO 8601, present when `willRetry`. */\n nextAttemptAt?: string;\n}\n\n/**\n * An `invoice.autopay_failed` event: the standard {@link EntityEvent}\n * envelope (`entity_type` `'invoice'`, `entity_id` the invoice, `changes` `{}`)\n * with a typed snapshot.\n */\nexport interface InvoiceAutopayFailedEvent\n extends Omit<EntityEvent, 'event_type' | 'entity_type' | 'snapshot'> {\n event_type: 'invoice.autopay_failed';\n entity_type: 'invoice';\n snapshot: InvoiceAutopayFailedSnapshot & Record<string, unknown>;\n}\n\n/**\n * The webhook delivery envelope Planes POSTs to a subscriber's URL.\n *\n * This is the outermost body: a delivery ID, the schema `version`, the wrapped\n * {@link EntityEvent}, and the subscription that matched. The entity body lives\n * at `event.snapshot` — there is no top-level `data`.\n */\nexport interface WebhookDelivery {\n /** Delivery ID (the delivery-log row id). */\n id: string;\n /**\n * Webhook payload schema version (from the `X-Planes-Webhook-Version`\n * header / envelope, emitted API-side per E39-F1-S2). v1 requires no\n * branching; surfaced here so consumers can switch on it in the future.\n */\n version: number;\n /** The entity-change event that triggered this delivery. */\n event: EntityEvent;\n /** ID of the webhook subscription that matched and produced this delivery. */\n subscription_id: string;\n}\n\n/**\n * Verify an HMAC-SHA256 webhook signature using timing-safe comparison.\n *\n * The signature header format is: `sha256=<hex-digest>`\n *\n * @param payload - The raw request body (string or Buffer)\n * @param signature - The X-Planes-Signature header value\n * @param secret - The webhook signing secret\n * @returns true if the signature is valid, false otherwise\n */\nexport function verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n): boolean {\n if (!signature || !secret || !payload) {\n return false;\n }\n\n // Expect format: sha256=<hex>\n const prefix = 'sha256=';\n if (!signature.startsWith(prefix)) {\n return false;\n }\n\n const receivedHex = signature.slice(prefix.length);\n if (!receivedHex) {\n return false;\n }\n\n const expectedHmac = createHmac('sha256', secret)\n .update(payload)\n .digest('hex');\n\n // Convert to buffers for timing-safe comparison\n const receivedBuf = Buffer.from(receivedHex, 'hex');\n const expectedBuf = Buffer.from(expectedHmac, 'hex');\n\n if (receivedBuf.length !== expectedBuf.length) {\n return false;\n }\n\n return timingSafeEqual(receivedBuf, expectedBuf);\n}\n\n/**\n * Parse and verify a webhook delivery payload.\n *\n * @param body - The raw request body as a string\n * @param signature - The X-Planes-Signature header value\n * @param secret - The webhook signing secret\n * @returns The parsed and verified {@link WebhookDelivery} envelope\n * @throws WebhookVerificationError if signature is invalid\n */\nexport function parseWebhookEvent(\n body: string,\n signature: string,\n secret: string,\n): WebhookDelivery {\n if (!verifyWebhookSignature(body, signature, secret)) {\n throw new WebhookVerificationError();\n }\n\n return JSON.parse(body) as WebhookDelivery;\n}\n","import { parseWebhookEvent, type WebhookDelivery } from './webhooks';\n\n/**\n * A handler invoked for a matching webhook delivery. May be sync or async;\n * async handlers are awaited by {@link WebhookRouter.process}.\n */\nexport type WebhookHandler = (delivery: WebhookDelivery) => void | Promise<void>;\n\n/** Input to {@link WebhookRouter.process} — the raw request pieces. */\nexport interface WebhookProcessInput {\n /** The raw request body as a string (verbatim — not re-serialized JSON). */\n body: string;\n /** The `X-Planes-Signature` header value (`sha256=<hex>`). */\n signature: string;\n /** The webhook signing secret for this subscription. */\n secret: string;\n}\n\n/**\n * Ergonomic event router over the corrected {@link WebhookDelivery} envelope.\n *\n * The platform models an event as two fields — `event.entity_type` and\n * `event.event_type` — which this router joins into a dotted composite key\n * `` `${entity_type}.${event_type}` `` (e.g. `opportunity.created`). Handlers\n * subscribe to that key, to an entity-scoped wildcard `` `${entity_type}.*` ``\n * (e.g. `opportunity.*`), or to the global wildcard `*`.\n *\n * The router is a pure dispatcher: it verifies + parses the delivery, then\n * fans it out to matching handlers. It holds no connection and no auth — the\n * signing secret is supplied per {@link process} call — so the same client's\n * `webhooks` router works across subscriptions. Wire it into your own HTTP\n * handler.\n *\n * @example\n * ```ts\n * client.webhooks\n * .on('opportunity.created', (d) => console.log(d.event.snapshot))\n * .on('opportunity.*', (d) => audit(d))\n * .on('*', (d) => metrics.count(d.event.entity_type));\n *\n * // inside your POST handler:\n * await client.webhooks.process({ body: rawBody, signature, secret });\n * ```\n *\n * ### Dotted `event_type` composite keys\n *\n * Four event types are themselves dotted — `entity.tagged`, `entity.untagged`,\n * `list.member_added`, `list.member_removed`. Joined with their entity these\n * yield keys with more than one dot, e.g. `customer.entity.tagged`. This router\n * matches on the literal `` `${entity_type}.${event_type}` `` string, so\n * subscribe with the full key (`'customer.entity.tagged'`). The entity wildcard\n * (`'customer.*'`) and global wildcard (`'*'`) still match these deliveries;\n * only mid-segment wildcards like `'customer.entity.*'` are intentionally\n * unsupported.\n */\nexport class WebhookRouter {\n /** pattern (composite key / entity wildcard / '*') → registered handlers. */\n private readonly handlers = new Map<string, WebhookHandler[]>();\n\n /**\n * Register a handler for an event pattern.\n *\n * @param pattern - `\"{entity_type}.{event_type}\"` (e.g. `opportunity.created`),\n * an entity wildcard `\"{entity_type}.*\"` (e.g. `opportunity.*`), or the\n * global wildcard `\"*\"`. Matching is exact-string against the derived key;\n * see the class docs for the dotted-`event_type` shape.\n * @param handler - Invoked with the full {@link WebhookDelivery} on a match.\n * @returns `this`, for chaining.\n */\n on(pattern: string, handler: WebhookHandler): this {\n const existing = this.handlers.get(pattern);\n if (existing) {\n existing.push(handler);\n } else {\n this.handlers.set(pattern, [handler]);\n }\n return this;\n }\n\n /**\n * Verify, parse, and dispatch a webhook delivery.\n *\n * The signature is verified first: on failure this throws\n * {@link WebhookVerificationError} and dispatches nothing. On success it\n * dispatches to every matching handler — specific key, then entity wildcard,\n * then global — awaiting async handlers before resolving.\n *\n * @param input - The raw `{ body, signature, secret }`.\n * @returns The parsed {@link WebhookDelivery}.\n * @throws WebhookVerificationError if the signature is invalid.\n */\n async process(input: WebhookProcessInput): Promise<WebhookDelivery> {\n // Verify + parse first; throws WebhookVerificationError before any dispatch.\n const delivery = parseWebhookEvent(input.body, input.signature, input.secret);\n\n const { entity_type, event_type } = delivery.event;\n // Candidate patterns, most specific first. Deriving them from the delivery\n // (rather than scanning registered patterns) keeps dispatch O(1) per key\n // and correctly handles dotted event_types like `entity.tagged`.\n const patterns = [`${entity_type}.${event_type}`, `${entity_type}.*`, '*'];\n\n for (const pattern of patterns) {\n const matched = this.handlers.get(pattern);\n if (!matched) continue;\n for (const handler of matched) {\n await handler(delivery);\n }\n }\n\n return delivery;\n }\n}\n","import type { PlanesConfig } from './config';\nimport { HttpClient } from './http';\nimport { OpportunityResource } from './resources/opportunities';\nimport { CustomerResource } from './resources/customers';\nimport { PropertyResource } from './resources/properties';\nimport { CalendarEventResource } from './resources/calendar-events';\nimport { ContractResource } from './resources/contracts';\nimport { JobResource } from './resources/jobs';\nimport { EstimateResource } from './resources/estimates';\nimport { InvoiceResource } from './resources/invoices';\nimport { InventoryResource } from './resources/inventory';\nimport { ContactResource } from './resources/contacts';\nimport { ActivityResource } from './resources/activities';\nimport { CommunicationsResource } from './resources/communications';\nimport { ConversationsResource } from './resources/conversations';\nimport { GmailResource } from './resources/gmail';\nimport { DocumentsResource } from './resources/documents';\nimport { TagResource } from './resources/tags';\nimport { TaskResource } from './resources/tasks';\nimport { RecordViewsResource } from './resources/record-views';\nimport { ModulesResource } from './resources/modules';\nimport { PlanResource } from './resources/plan';\nimport { MeResource } from './resources/me';\nimport { RolesResource } from './resources/roles';\nimport { InvoiceSettingsResource } from './resources/invoice-settings';\nimport { InvoiceRemindersResource } from './resources/invoice-reminders';\nimport { ContactSequencesResource } from './resources/contact-sequences';\nimport { EnrollmentsResource } from './resources/enrollments';\nimport { WorkflowsResource } from './resources/workflows';\nimport { ServiceSchedulesResource } from './resources/service-schedules';\nimport { BillingScheduleResource } from './resources/billing-schedules';\nimport { CustomFieldsResource } from './resources/custom-fields';\nimport { OrttoResource } from './resources/ortto';\nimport { WebhookRouter } from './webhook-router';\n\n/**\n * PlanesClient — typed API client for the Planes CRM platform.\n *\n * @example\n * ```ts\n * const client = new PlanesClient({ accessToken: 'tok_...' });\n * const opps = await client.opportunities.list({ page: 1, limit: 20 });\n * ```\n */\nexport class PlanesClient {\n private readonly http: HttpClient;\n private _opportunities?: OpportunityResource;\n private _customers?: CustomerResource;\n private _properties?: PropertyResource;\n private _calendarEvents?: CalendarEventResource;\n private _contracts?: ContractResource;\n private _jobs?: JobResource;\n private _estimates?: EstimateResource;\n private _invoices?: InvoiceResource;\n private _inventory?: InventoryResource;\n private _contacts?: ContactResource;\n private _activities?: ActivityResource;\n private _communications?: CommunicationsResource;\n private _conversations?: ConversationsResource;\n private _gmail?: GmailResource;\n private _documents?: DocumentsResource;\n private _tags?: TagResource;\n private _tasks?: TaskResource;\n private _recordViews?: RecordViewsResource;\n private _modules?: ModulesResource;\n private _plan?: PlanResource;\n private _me?: MeResource;\n private _roles?: RolesResource;\n private _invoiceSettings?: InvoiceSettingsResource;\n private _invoiceReminders?: InvoiceRemindersResource;\n private _contactSequences?: ContactSequencesResource;\n private _enrollments?: EnrollmentsResource;\n private _workflows?: WorkflowsResource;\n private _serviceSchedules?: ServiceSchedulesResource;\n private _billingSchedules?: BillingScheduleResource;\n private _customFields?: CustomFieldsResource;\n private _ortto?: OrttoResource;\n private _webhooks?: WebhookRouter;\n\n constructor(config: PlanesConfig) {\n const hasOAuthCreds = Boolean(config.clientId) || Boolean(config.clientSecret);\n const modes = [\n config.accessToken ? 'accessToken' : null,\n config.apiKey ? 'apiKey' : null,\n hasOAuthCreds ? 'clientId+clientSecret' : null,\n ].filter((m): m is string => m !== null);\n\n if (modes.length === 0) {\n throw new Error(\n 'PlanesClient requires one auth mode: accessToken, apiKey, or clientId + clientSecret',\n );\n }\n if (modes.length > 1) {\n throw new Error(\n `PlanesClient requires exactly one auth mode, not multiple (got: ${modes.join(', ')})`,\n );\n }\n if (hasOAuthCreds && (!config.clientId || !config.clientSecret)) {\n throw new Error('PlanesClient OAuth mode requires both clientId and clientSecret');\n }\n\n this.http = new HttpClient(config);\n }\n\n get opportunities(): OpportunityResource {\n if (!this._opportunities) {\n this._opportunities = new OpportunityResource(this.http);\n }\n return this._opportunities;\n }\n\n get customers(): CustomerResource {\n if (!this._customers) {\n this._customers = new CustomerResource(this.http);\n }\n return this._customers;\n }\n\n get properties(): PropertyResource {\n if (!this._properties) {\n this._properties = new PropertyResource(this.http);\n }\n return this._properties;\n }\n\n get calendarEvents(): CalendarEventResource {\n if (!this._calendarEvents) {\n this._calendarEvents = new CalendarEventResource(this.http);\n }\n return this._calendarEvents;\n }\n\n get contracts(): ContractResource {\n if (!this._contracts) {\n this._contracts = new ContractResource(this.http);\n }\n return this._contracts;\n }\n\n get jobs(): JobResource {\n if (!this._jobs) {\n this._jobs = new JobResource(this.http);\n }\n return this._jobs;\n }\n\n get estimates(): EstimateResource {\n if (!this._estimates) {\n this._estimates = new EstimateResource(this.http);\n }\n return this._estimates;\n }\n\n get invoices(): InvoiceResource {\n if (!this._invoices) {\n this._invoices = new InvoiceResource(this.http);\n }\n return this._invoices;\n }\n\n get inventory(): InventoryResource {\n if (!this._inventory) {\n this._inventory = new InventoryResource(this.http);\n }\n return this._inventory;\n }\n\n get contacts(): ContactResource {\n if (!this._contacts) {\n this._contacts = new ContactResource(this.http);\n }\n return this._contacts;\n }\n\n get activities(): ActivityResource {\n if (!this._activities) {\n this._activities = new ActivityResource(this.http);\n }\n return this._activities;\n }\n\n get communications(): CommunicationsResource {\n if (!this._communications) {\n this._communications = new CommunicationsResource(this.http);\n }\n return this._communications;\n }\n\n /** Logged meetings — manual + provider-synced, with company matching (E55). */\n get conversations(): ConversationsResource {\n if (!this._conversations) {\n this._conversations = new ConversationsResource(this.http);\n }\n return this._conversations;\n }\n\n get gmail(): GmailResource {\n if (!this._gmail) {\n this._gmail = new GmailResource(this.http);\n }\n return this._gmail;\n }\n\n get documents(): DocumentsResource {\n if (!this._documents) {\n this._documents = new DocumentsResource(this.http);\n }\n return this._documents;\n }\n\n get tags(): TagResource {\n if (!this._tags) {\n this._tags = new TagResource(this.http);\n }\n return this._tags;\n }\n\n get tasks(): TaskResource {\n if (!this._tasks) {\n this._tasks = new TaskResource(this.http);\n }\n return this._tasks;\n }\n\n /** Record views — the \"recently viewed\" beacon + read (E41-F7). */\n get recordViews(): RecordViewsResource {\n if (!this._recordViews) {\n this._recordViews = new RecordViewsResource(this.http);\n }\n return this._recordViews;\n }\n\n /** Module status — which modules are active for the caller's tenant (E51-F1-S1). */\n get modules(): ModulesResource {\n if (!this._modules) {\n this._modules = new ModulesResource(this.http);\n }\n return this._modules;\n }\n\n /** The tenant's plan, billing status and plan ceiling — `plan.get()` (E54-F1-S2). */\n get plan(): PlanResource {\n if (!this._plan) {\n this._plan = new PlanResource(this.http);\n }\n return this._plan;\n }\n\n /** The authenticated caller — `me.abilities()` (E52-F1-S3). */\n get me(): MeResource {\n if (!this._me) {\n this._me = new MeResource(this.http);\n }\n return this._me;\n }\n\n /** Settings → Roles, admin only — `roles.list/get/updateGrants/reset/history` (E52-F2-S5). */\n get roles(): RolesResource {\n if (!this._roles) {\n this._roles = new RolesResource(this.http);\n }\n return this._roles;\n }\n\n /** Invoice settings — `invoiceSettings.getOfflineInstructions/setOfflineInstructions` (E56-F5-S4). */\n get invoiceSettings(): InvoiceSettingsResource {\n if (!this._invoiceSettings) {\n this._invoiceSettings = new InvoiceSettingsResource(this.http);\n }\n return this._invoiceSettings;\n }\n\n /** Payment reminder settings, admin only — `invoiceReminders.list/update/preview` (E56-F8-S6). */\n get invoiceReminders(): InvoiceRemindersResource {\n if (!this._invoiceReminders) {\n this._invoiceReminders = new InvoiceRemindersResource(this.http);\n }\n return this._invoiceReminders;\n }\n\n /** Published sequences a contact can be enrolled in (E51-F2-S6). */\n get contactSequences(): ContactSequencesResource {\n if (!this._contactSequences) {\n this._contactSequences = new ContactSequencesResource(this.http);\n }\n return this._contactSequences;\n }\n\n /** Enroll a contact in a sequence, list its enrollments, exit one (E51-F2-S8). */\n get enrollments(): EnrollmentsResource {\n if (!this._enrollments) {\n this._enrollments = new EnrollmentsResource(this.http);\n }\n return this._enrollments;\n }\n\n /** Tenant-readable workflow reads — a workflow version's ordered stages. */\n get workflows(): WorkflowsResource {\n if (!this._workflows) {\n this._workflows = new WorkflowsResource(this.http);\n }\n return this._workflows;\n }\n\n get serviceSchedules(): ServiceSchedulesResource {\n if (!this._serviceSchedules) {\n this._serviceSchedules = new ServiceSchedulesResource(this.http);\n }\n return this._serviceSchedules;\n }\n\n /**\n * Recurring billing schedules (E56-F3) — CRUD, pause/resume/end, preview,\n * the period ledger, Needs review, generate-now, skip and regenerate.\n */\n get billingSchedules(): BillingScheduleResource {\n if (!this._billingSchedules) {\n this._billingSchedules = new BillingScheduleResource(this.http);\n }\n return this._billingSchedules;\n }\n\n get customFields(): CustomFieldsResource {\n if (!this._customFields) {\n this._customFields = new CustomFieldsResource(this.http);\n }\n return this._customFields;\n }\n\n /** Ortto connector — list mappings, audiences, manual sync, sync status (E53). Admin-only. */\n get ortto(): OrttoResource {\n if (!this._ortto) {\n this._ortto = new OrttoResource(this.http);\n }\n return this._ortto;\n }\n\n /**\n * Ergonomic webhook event router — `client.webhooks.on(pattern, handler)`\n * plus `client.webhooks.process({ body, signature, secret })`. Auth-independent\n * (the signing secret is supplied per `.process()` call), so it is created\n * lazily without the HTTP client. See {@link WebhookRouter}.\n */\n get webhooks(): WebhookRouter {\n if (!this._webhooks) {\n this._webhooks = new WebhookRouter();\n }\n return this._webhooks;\n }\n}\n","import type { PaginatedResponse } from '../types';\n\n/**\n * Wire shapes for recurring billing schedules (`/admin/billing-schedules`,\n * E56-F3-S1/S2). snake_case on the wire, money and quantities as 2dp decimal\n * strings, dates as `YYYY-MM-DD` and timestamps as ISO strings.\n *\n * Mirrors the schemas in `packages/shared/src/validation/billing-schedule.ts`\n * (request bodies are their `z.input`, responses their `z.infer`, which are the\n * services' declared return types). Redefined here (not imported) so the\n * published package carries no dependency on `@blueprint/shared`;\n * `billing-schedule-types.parity.ts` fails `tsc` if a shape drifts, and\n * `billing-schedules.spec.ts` parses every request body the SDK sends with the\n * REAL shared schema.\n */\n\nexport const BILLING_CADENCES = ['weekly', 'every_n_weeks', 'monthly', 'quarterly', 'annually'] as const;\nexport type BillingCadence = (typeof BILLING_CADENCES)[number];\n\nexport const BILLING_SCHEDULE_STATUSES = ['active', 'paused', 'ended'] as const;\nexport type BillingScheduleStatus = (typeof BILLING_SCHEDULE_STATUSES)[number];\n\nexport const BILL_TIMINGS = ['in_advance', 'in_arrears'] as const;\nexport type BillTiming = (typeof BILL_TIMINGS)[number];\n\nexport const BILLING_DELIVERY_MODES = ['auto_send', 'draft_for_review'] as const;\nexport type BillingDeliveryMode = (typeof BILLING_DELIVERY_MODES)[number];\n\n/**\n * `autopay` charges the customer's saved default card after each invoice is\n * sent (E56-F7-S4). It needs a platform-managed Stripe connection and a saved\n * default card on it — otherwise 422 `AUTOPAY_UNAVAILABLE` with a `reason`.\n */\nexport const BILLING_COLLECTION_MODES = ['send_link', 'autopay'] as const;\nexport type BillingCollectionMode = (typeof BILLING_COLLECTION_MODES)[number];\n\n/** Why autopay cannot be turned on (`AUTOPAY_UNAVAILABLE`'s `reason`). */\nexport const AUTOPAY_UNAVAILABLE_REASONS = ['no_connect', 'no_saved_method'] as const;\nexport type AutopayUnavailableReason = (typeof AUTOPAY_UNAVAILABLE_REASONS)[number];\n\n/**\n * Autopay state of one ledger row (E56-F7-S4). `scheduled` / `retry_scheduled`\n * charge at `autopay_next_attempt_at`; `submitted` waits for Stripe's webhook;\n * `auth_required` emailed the customer the pay link and is never retried;\n * `abandoned` makes no more automatic tries. `failed` is reserved.\n */\nexport const BILLING_AUTOPAY_STATUSES = [\n 'not_applicable',\n 'scheduled',\n 'submitted',\n 'succeeded',\n 'failed',\n 'auth_required',\n 'retry_scheduled',\n 'abandoned',\n] as const;\nexport type BillingAutopayStatus = (typeof BILLING_AUTOPAY_STATUSES)[number];\n\n/** A backdated create must choose: skip the past periods, or draft an invoice for each. */\nexport const BILLING_BACKFILL_MODES = ['skip', 'draft'] as const;\nexport type BillingBackfillMode = (typeof BILLING_BACKFILL_MODES)[number];\n\nexport const BILLING_PERIOD_STATUSES = ['generated', 'skipped', 'needs_review', 'failed', 'voided'] as const;\nexport type BillingPeriodStatus = (typeof BILLING_PERIOD_STATUSES)[number];\n\n/** A preview row's state: its ledger status, or `upcoming` with no ledger row yet. */\nexport const BILLING_PREVIEW_PERIOD_STATUSES = [...BILLING_PERIOD_STATUSES, 'upcoming'] as const;\nexport type BillingPreviewPeriodStatus = (typeof BILLING_PREVIEW_PERIOD_STATUSES)[number];\n\nexport const BILLING_DELIVERY_STATUSES = ['pending', 'sent', 'failed', 'not_applicable'] as const;\nexport type BillingDeliveryStatus = (typeof BILLING_DELIVERY_STATUSES)[number];\n\n/** One-off adjustment state (E56-F8-S5): `pending` until an invoice carries it. */\nexport const BILLING_ADJUSTMENT_STATUSES = ['pending', 'consumed', 'cancelled'] as const;\nexport type BillingAdjustmentStatus = (typeof BILLING_ADJUSTMENT_STATUSES)[number];\n\n/** Where an adjustment lands: only the schedule's next generated invoice for now. */\nexport const BILLING_ADJUSTMENT_TARGETS = ['next_invoice'] as const;\nexport type BillingAdjustmentTarget = (typeof BILLING_ADJUSTMENT_TARGETS)[number];\n\nexport type BillingInvoiceTerms = 'due_on_receipt' | 'net_15' | 'net_30' | 'net_60';\nexport type BillingInvoiceStatus = 'draft' | 'sent' | 'partially_paid' | 'paid' | 'void' | 'uncollectible';\n\n// ------------------------------------------------------------\n// Requests\n// ------------------------------------------------------------\n\nexport interface BillingScheduleLineInput {\n description: string;\n /** Decimal string greater than zero, up to 2 decimals. */\n quantity: string;\n /** Decimal string, up to 2 decimals. */\n unit_price: string;\n}\n\n/**\n * `POST /admin/billing-schedules`. `timezone` defaults to the tenant's. When\n * `start_date` is before today in that zone, `backfill` is required (else 400\n * `BACKFILL_REQUIRED` with the past-period count).\n */\nexport interface CreateBillingScheduleDto {\n customer_id: string;\n property_id?: string | null | undefined;\n contract_id?: string | null | undefined;\n name: string;\n cadence: BillingCadence;\n interval_count?: number | undefined;\n /** Monthly schedules only (1–31; short months bill on their last day). */\n anchor_day?: number | null | undefined;\n timezone?: string | undefined;\n start_date: string;\n end_date?: string | null | undefined;\n end_after_count?: number | null | undefined;\n bill_timing?: BillTiming | undefined;\n lead_days?: number | undefined;\n terms?: BillingInvoiceTerms | undefined;\n tax_amount?: string | undefined;\n currency?: string | undefined;\n delivery?: BillingDeliveryMode | undefined;\n collection?: BillingCollectionMode | undefined;\n memo?: string | null | undefined;\n lines: BillingScheduleLineInput[];\n backfill?: BillingBackfillMode | undefined;\n}\n\n/**\n * `PATCH /admin/billing-schedules/:id` — partial; `customer_id` can't change,\n * null clears an optional field and `lines` replaces every line from\n * `effective_from` (a new line version). Changing the cadence after the first\n * invoice is 409 `CADENCE_LOCKED`.\n */\nexport interface UpdateBillingScheduleDto {\n property_id?: string | null | undefined;\n contract_id?: string | null | undefined;\n name?: string | undefined;\n cadence?: BillingCadence | undefined;\n interval_count?: number | undefined;\n anchor_day?: number | null | undefined;\n timezone?: string | undefined;\n start_date?: string | undefined;\n end_date?: string | null | undefined;\n end_after_count?: number | null | undefined;\n bill_timing?: BillTiming | undefined;\n lead_days?: number | undefined;\n terms?: BillingInvoiceTerms | undefined;\n tax_amount?: string | undefined;\n currency?: string | undefined;\n delivery?: BillingDeliveryMode | undefined;\n collection?: BillingCollectionMode | undefined;\n memo?: string | null | undefined;\n lines?: BillingScheduleLineInput[] | undefined;\n /**\n * `YYYY-MM-DD` the sent `lines` take effect: periods starting on or after it\n * bill them. Needs `lines`. Omitted = the next period not yet invoiced; a\n * date inside an invoiced period is 422 `EFFECTIVE_DATE_IN_GENERATED_PERIOD`.\n */\n effective_from?: string | undefined;\n}\n\n/** `GET /admin/billing-schedules` query. */\nexport interface BillingScheduleListParams {\n customer_id?: string | undefined;\n status?: BillingScheduleStatus | undefined;\n page?: number | undefined;\n limit?: number | undefined;\n}\n\n/** `GET /admin/billing-schedules/:id/preview` query: `count` 1–24, default 6. */\nexport interface BillingSchedulePreviewParams {\n count?: number | undefined;\n}\n\n/** `GET /admin/billing-schedules/:id/periods` query. */\nexport interface BillingSchedulePeriodListParams {\n status?: BillingPeriodStatus | undefined;\n page?: number | undefined;\n limit?: number | undefined;\n}\n\n/** `GET /admin/billing-schedules/periods/needs-review` query. */\nexport interface BillingNeedsReviewParams {\n page?: number | undefined;\n limit?: number | undefined;\n}\n\n/** `POST /admin/billing-schedules/:id/periods/:periodStart/skip` body. */\nexport interface SkipBillingPeriodDto {\n reason?: string | undefined;\n}\n\n/**\n * `POST /admin/billing-schedules/:id/adjustments` (E56-F8-S5). `amount` is a\n * signed 2dp decimal string: negative for a credit. Zero, or a credit larger\n * than the next invoice, is 422 `ADJUSTMENT_INVALID`.\n */\nexport interface CreateBillingAdjustmentDto {\n description: string;\n amount: string;\n /** Defaults to `next_invoice`. */\n apply_to?: BillingAdjustmentTarget | undefined;\n}\n\n/** `GET /admin/billing-schedules/:id/adjustments` query. Omitted status: pending and consumed. */\nexport interface BillingAdjustmentListParams {\n status?: BillingAdjustmentStatus | undefined;\n}\n\n// ------------------------------------------------------------\n// Responses\n// ------------------------------------------------------------\n\n/** One upcoming period: when it generates and when the invoice is due. */\nexport interface BillingUpcomingPeriod {\n index: number;\n start: string;\n end: string;\n generate_at: string;\n due_date: string;\n}\n\nexport interface BillingScheduleLine {\n id: string;\n description: string;\n quantity: string;\n unit_price: string;\n sort_order: number;\n}\n\n/**\n * One version of a schedule's lines: periods starting on or after\n * `effective_from` (until the next version's date) bill them.\n */\nexport interface BillingScheduleLineVersion {\n id: string;\n schedule_id: string;\n effective_from: string;\n note: string | null;\n created_by: string | null;\n created_at: string;\n lines: BillingScheduleLine[];\n}\n\n/** `GET /admin/billing-schedules/:id/versions`: newest effective date first. */\nexport interface BillingScheduleLineVersionListResponse {\n data: BillingScheduleLineVersion[];\n}\n\n/**\n * Returned by create, update, pause, resume and end. `lines` are what the next\n * invoice bills (the line version in effect at `next_period`).\n */\nexport interface BillingSchedule {\n id: string;\n customer_id: string;\n property_id: string | null;\n contract_id: string | null;\n name: string;\n status: BillingScheduleStatus;\n currency: string;\n cadence: BillingCadence;\n interval_count: number;\n anchor_day: number | null;\n timezone: string;\n start_date: string;\n end_date: string | null;\n end_after_count: number | null;\n bill_timing: BillTiming;\n lead_days: number;\n terms: BillingInvoiceTerms;\n tax_amount: string;\n delivery: BillingDeliveryMode;\n collection: BillingCollectionMode;\n memo: string | null;\n lines: BillingScheduleLine[];\n next_generate_at: string | null;\n /** The next unbilled period, or null once the schedule has ended. */\n next_period: BillingUpcomingPeriod | null;\n created_at: string;\n updated_at: string;\n}\n\n/** The customer's saved default card, as shown before turning autopay on. */\nexport interface SavedPaymentMethodSummary {\n brand: string;\n last4: string;\n exp_month: number;\n exp_year: number;\n}\n\n/**\n * `GET /admin/billing-schedules/:id`: the schedule plus its next 3 periods, and\n * whether autopay can be turned on (the same check as `AUTOPAY_UNAVAILABLE`).\n */\nexport interface BillingScheduleDetail extends BillingSchedule {\n upcoming_periods: BillingUpcomingPeriod[];\n /** The customer's active default card, or null. */\n saved_method: SavedPaymentMethodSummary | null;\n autopay_available: boolean;\n autopay_unavailable_reason: AutopayUnavailableReason | null;\n}\n\n/** One list row: no lines, but their count and the next invoice's total (lines + tax). */\nexport interface BillingScheduleListItem extends Omit<BillingSchedule, 'lines'> {\n next_amount: string;\n line_count: number;\n /** Display name of the schedule's customer; null if the customer row is gone. */\n customer_name: string | null;\n}\n\n/** A one-off adjustment line a preview period includes. */\nexport interface BillingPreviewAdjustment {\n id: string;\n description: string;\n amount: string;\n}\n\nexport interface BillingSchedulePreviewPeriod extends BillingUpcomingPeriod {\n subtotal: string;\n tax: string;\n total: string;\n status: BillingPreviewPeriodStatus;\n /**\n * One-off adjustments included in the amounts: pending ones on the next\n * unbilled period, consumed ones on the period that billed them. Absent when none.\n */\n adjustments?: BillingPreviewAdjustment[] | undefined;\n}\n\n/** `GET /admin/billing-schedules/:id/preview`. */\nexport interface BillingSchedulePreview {\n schedule_id: string | null;\n currency: string;\n periods: BillingSchedulePreviewPeriod[];\n}\n\n/**\n * One period-ledger row with its invoice and delivery state.\n * `schedule_snapshot` (what the period would bill) is present only on rows\n * with no invoice.\n */\nexport interface BillingSchedulePeriod {\n id: string;\n schedule_id: string;\n period_start: string;\n period_end: string;\n invoice_id: string | null;\n superseded_invoice_id: string | null;\n status: BillingPeriodStatus;\n delivery_status: BillingDeliveryStatus;\n delivery_attempts: number;\n last_error: string | null;\n reason: string | null;\n generated_at: string | null;\n /** Autopay state (E56-F7-S4); `not_applicable` on send_link schedules. */\n autopay_status: BillingAutopayStatus;\n /** The current charge attempt (1 or 2). */\n autopay_attempt: number | null;\n /** What the current attempt charges, in cents. */\n autopay_amount_cents: number | null;\n /** Unknown-outcome re-drives of the current attempt (max 5). */\n autopay_error_tries: number;\n autopay_next_attempt_at: string | null;\n autopay_last_error: string | null;\n autopay_payment_intent_id: string | null;\n created_at: string;\n updated_at: string;\n invoice_number: string | null;\n invoice_status: BillingInvoiceStatus | null;\n /** Number of the voided invoice a regenerate replaced (null when none). */\n superseded_invoice_number: string | null;\n schedule_snapshot?: Record<string, unknown> | undefined;\n}\n\n/** A tenant-wide Needs review row: the ledger row plus its schedule and customer. */\nexport interface BillingNeedsReviewItem extends BillingSchedulePeriod {\n schedule_name: string;\n customer_id: string;\n customer_name: string | null;\n}\n\nexport type BillingScheduleListResponse = PaginatedResponse<BillingScheduleListItem>;\nexport type BillingSchedulePeriodListResponse = PaginatedResponse<BillingSchedulePeriod>;\nexport type BillingNeedsReviewResponse = PaginatedResponse<BillingNeedsReviewItem>;\n\n/**\n * `GET /admin/billing-schedules/autopay-summary` — what disconnecting Stripe\n * would touch: live autopay schedules, the distinct customers on them, and\n * active saved cards on the tenant's current Stripe connection.\n */\nexport interface BillingAutopaySummary {\n autopaySchedules: number;\n customers: number;\n savedCards: number;\n}\n\nexport interface BillingAutopaySummaryResponse {\n data: BillingAutopaySummary;\n}\n\n/**\n * `POST /admin/billing-schedules/:id/generate-now`. `delivery: 'queued'` means\n * the auto_send delivery job was enqueued after commit; read its outcome from\n * the period ledger.\n */\nexport type GenerateNowResult =\n | {\n status: 'generated';\n invoice_id: string;\n period_start: string;\n delivery: 'queued' | 'not_applicable';\n }\n | {\n status: 'already_generated';\n invoice_id: string;\n period_start: string;\n };\n\n/** `POST /admin/billing-schedules/:id/periods/:periodStart/regenerate`. */\nexport interface RegeneratePeriodResult {\n invoice_id: string;\n superseded_invoice_id: string;\n delivery: 'queued' | 'not_applicable';\n}\n\n/** A one-off adjustment on a schedule's next invoice (E56-F8-S5). */\nexport interface BillingAdjustment {\n id: string;\n schedule_id: string;\n description: string;\n amount: string;\n apply_to: BillingAdjustmentTarget;\n status: BillingAdjustmentStatus;\n /** The invoice that carried it (set once consumed). */\n consumed_invoice_id: string | null;\n consumed_period_start: string | null;\n created_by: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/** `GET /admin/billing-schedules/:id/adjustments`: oldest first. */\nexport interface BillingAdjustmentListResponse {\n data: BillingAdjustment[];\n}\n","/**\n * Wire shapes for the Contacts + Contact Channels API.\n *\n * Mirrors `createContactSchema`/`updateContactSchema` and\n * `createContactChannelSchema`/`updateContactChannelSchema` in\n * `packages/shared/src/validation/contact.ts`, plus the actual Drizzle-row\n * response shapes returned by `ContactsService` / `ContactChannelsService`.\n * Types are redefined here (not imported) so the published package carries no\n * runtime or type dependency on `@blueprint/shared`. Internal-only columns\n * (tenantId, deletedAt, createdBy, …) are intentionally omitted — see\n * `CustomerDto` for the house style.\n */\n\nimport type { ConsentStateDto } from './consent-types';\n\nimport type { ListParams } from '../types';\n\nexport type ContactSource = 'manual' | 'import' | 'convert_from_customer';\n\n/**\n * Query params for `GET /contacts` — mirrors `contactFilterSchema` in\n * `@blueprint/shared`. `customer_id` / `property_id` return only contacts with\n * an ACTIVE link to that record; `q` matches a name or a channel value, word\n * by word (every word must match), so a full name works in either order.\n * `sort_by: 'name'` is case-insensitive first name then last name; the default\n * is `created_at` desc. Extends the generic `ListParams` so it stays assignable\n * to `BaseResource.list`.\n */\n/** Sortable columns on `GET /contacts` (kept in lockstep with the server enum by the SDK's wire-contract spec). */\nexport const CONTACT_SORT_KEYS = ['name', 'created_at', 'recent'] as const;\nexport type ContactSortKey = (typeof CONTACT_SORT_KEYS)[number];\n\n/**\n * Related collections `GET /contacts?include=` eager-loads onto each row\n * (kept in lockstep with the server's `CONTACT_LIST_INCLUDES` by the SDK's\n * wire-contract spec). `channels` = all channels; `links` = ACTIVE links only.\n */\nexport const CONTACT_LIST_INCLUDES = ['channels', 'links'] as const;\nexport type ContactListInclude = (typeof CONTACT_LIST_INCLUDES)[number];\n\nexport interface ContactListParams extends ListParams {\n customer_id?: string;\n property_id?: string;\n q?: string;\n /** Mirrors `CONTACT_SORT_KEYS`. `recent` = the calling user's last view, newest first; ignores `sort_order`. */\n sort_by?: ContactSortKey;\n sort_order?: 'asc' | 'desc';\n page?: number;\n limit?: number;\n /**\n * Eager-load related collections onto each row in one request — sent as one\n * comma-separated value (`include=channels,links`). Use this instead of a\n * `get()` per row.\n */\n include?: ContactListInclude[];\n}\n\nexport interface ContactDto {\n id: string;\n firstName: string;\n lastName: string | null;\n notes: string | null;\n customFields: Record<string, unknown> | null;\n source: ContactSource | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateContactDto {\n firstName: string;\n lastName?: string | null;\n notes?: string | null;\n customFields?: Record<string, unknown> | null;\n source?: ContactSource | null;\n}\n\nexport interface UpdateContactDto {\n firstName?: string;\n lastName?: string | null;\n notes?: string | null;\n customFields?: Record<string, unknown> | null;\n source?: ContactSource | null;\n}\n\nexport type ContactChannelType = 'email' | 'phone' | 'url';\nexport type ContactChannelPhoneType = 'mobile' | 'landline' | 'voip' | 'unknown';\n\nexport interface ContactChannelDto {\n id: string;\n contactId: string;\n channelType: ContactChannelType;\n label: string | null;\n value: string;\n isPrimary: boolean;\n phoneType: ContactChannelPhoneType | null;\n smsCapable: boolean | null;\n dnc: boolean | null;\n marketingOptOut: boolean | null;\n createdAt: string;\n updatedAt: string;\n /**\n * Consent for this channel's address (E53-F5-S1): own flags, the\n * by-address verdict and the newest change. Present on every channel the\n * API returns (`GET /contacts/:id`, `include=channels`, channel create/update).\n */\n consent: ConsentStateDto;\n}\n\nexport type ContactLinkTargetType = 'customer' | 'property';\n\n/** A contact's link to a customer or property (`endedAt` null = active). */\nexport interface ContactLinkDto {\n id: string;\n contactId: string;\n targetType: ContactLinkTargetType;\n targetId: string;\n role: string | null;\n isPrimary: boolean;\n endedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * A `GET /contacts` row. `channels` / `links` are present only when requested\n * via `include` — the same shapes `GET /contacts/:id` returns (links: active only).\n */\nexport interface ContactListItemDto extends ContactDto {\n channels?: ContactChannelDto[];\n links?: ContactLinkDto[];\n}\n\nexport interface CreateContactChannelDto {\n channelType: ContactChannelType;\n label?: string | null;\n value: string;\n isPrimary?: boolean;\n dnc?: boolean | null;\n marketingOptOut?: boolean | null;\n}\n\nexport interface UpdateContactChannelDto {\n label?: string | null;\n value?: string;\n isPrimary?: boolean;\n dnc?: boolean | null;\n marketingOptOut?: boolean | null;\n}\n\n/** A channel elsewhere in the tenant with the same normalized value. */\nexport interface ContactChannelDuplicateMatch {\n contactId: string;\n channelId: string;\n value: string;\n}\n\n/**\n * `POST /contacts/:id/channels` return shape — `ContactChannelsService.addChannel()`\n * always returns this wrapper, never a flat channel. `duplicateOf` is the\n * dedup signal (empty array when there's no match) — the point of this API\n * for connectors doing identity resolution (e.g. Messenger).\n */\nexport interface AddContactChannelResult {\n channel: ContactChannelDto;\n duplicateOf: ContactChannelDuplicateMatch[];\n}\n\nexport interface ContactMergeResult {\n winnerId: string;\n loserId: string;\n}\n","import type { ListParams } from '../types';\n\n/**\n * Company merge result (E38-F10-S1) — the body of\n * `POST /customers/{winnerId}/merge/{loserId}`.\n *\n * Derived from `CustomerMergeResult` on the API service rather than hand-written\n * from the docs: `counts` is keyed by table / repoint-handler name and grows as\n * features register handlers, so it is deliberately an open record rather than a\n * closed shape that would go stale the first time a module is added.\n */\nexport interface CustomerMergeResult {\n /** The survivor. */\n winnerId: string;\n /** The duplicate, now soft-deleted; its id still resolves to the winner. */\n loserId: string;\n /** Rows moved per table / handler, e.g. `{ opportunity: 2, invoice: 1 }`. */\n counts: Record<string, number>;\n}\n\n/**\n * Optional service address embeddable on customer create/update. Mirrors\n * `createAddressInputSchema` in `packages/shared/src/validation/property.ts`:\n * all-or-nothing — once present, line1/city/state/zip are required.\n */\nexport interface CustomerAddressInput {\n addressLine1: string;\n addressLine2?: string;\n city: string;\n /** Two-letter state code. */\n state: string;\n /** 5-digit or ZIP+4. */\n zip: string;\n /** Two-letter country code; defaults to `US`. */\n country?: string;\n}\n\n/**\n * `PUT /customers/{id}` body. Mirrors `updateCustomerSchema` in\n * `packages/shared/src/validation/customer.ts`. Every field is optional; the\n * kind ↔ name rules only apply when `kind` is sent.\n *\n * `address` is an upsert, never a delete: a match on an existing property is a\n * no-op, the customer's only property is updated in place, otherwise a new\n * property is added. Omit it to leave properties untouched.\n */\nexport interface UpdateCustomerDto {\n kind?: 'person' | 'company';\n firstName?: string;\n lastName?: string;\n companyName?: string | null;\n email?: string | null;\n phone?: string | null;\n phoneDnc?: boolean | null;\n marketingOptOut?: boolean | null;\n notes?: string | null;\n source?: string | null;\n address?: CustomerAddressInput;\n customFields?: Record<string, unknown> | null;\n}\n\n/**\n * Query params for `GET /customers` — mirrors `customerFilterSchema` in\n * `@blueprint/shared`. `tag_ids` is ANY-of: a customer tagged with at least one\n * of the given tags matches (max 20). It is sent as one comma-separated value.\n * Extends the generic `ListParams` so it stays assignable to `BaseResource.list`.\n */\n/** Sortable columns on `GET /customers` (kept in lockstep with the server enum by the SDK's wire-contract spec). */\nexport const CUSTOMER_SORT_KEYS = ['created_at', 'last_name', 'first_name', 'email', 'phone', 'source', 'recent'] as const;\nexport type CustomerSortKey = (typeof CUSTOMER_SORT_KEYS)[number];\n\nexport interface CustomerListParams extends ListParams {\n /** Person or company name; every word must match, so a full name works in either order. */\n search?: string;\n notes_contains?: string;\n source?: string;\n kind?: 'person' | 'company';\n tag_ids?: string[];\n /**\n * Mirrors `CUSTOMER_SORT_KEYS`. `last_name` sorts companies by company name.\n * email / phone / source are nullable and sort NULLs last in both directions.\n * `recent` orders by the calling user's last view (newest first, never-viewed\n * last) and ignores `sort_order`.\n */\n sort_by?: CustomerSortKey;\n sort_order?: 'asc' | 'desc';\n page?: number;\n limit?: number;\n}\n","import type { ListParams } from '../types';\n\n/**\n * Query params for `GET /opportunities` — mirrors `opportunityFilterSchema` in\n * `@blueprint/shared`. `tag_ids` is ANY-of: an opportunity tagged with at least\n * one of the given tags matches (max 20). It is sent as one comma-separated value.\n * Extends the generic `ListParams` so it stays assignable to `BaseResource.list`.\n */\n/** Sortable columns on `GET /opportunities` (kept in lockstep with the server enum by the SDK's wire-contract spec). */\nexport const OPPORTUNITY_SORT_KEYS = ['created_at', 'total', 'title', 'customer_name', 'stage', 'priority', 'recent'] as const;\nexport type OpportunitySortKey = (typeof OPPORTUNITY_SORT_KEYS)[number];\n\n/**\n * Lifecycle states on `GET /opportunities` (kept in lockstep with the server's\n * `OPPORTUNITY_STATES` by the SDK's wire-contract spec).\n */\nexport const OPPORTUNITY_STATES = ['open', 'closed', 'won', 'lost'] as const;\nexport type OpportunityState = (typeof OPPORTUNITY_STATES)[number];\n\nexport interface OpportunityListParams extends ListParams {\n search?: string;\n /** Stage name (matched case-insensitively against the published pipeline). */\n stage?: string;\n /**\n * Open / closed / won / lost, read from the record's current stage — works\n * across pipeline versions (stage names can differ between versions).\n * Records with no pipeline match no state. ANDs with `stage`.\n */\n state?: OpportunityState;\n agent_id?: string;\n customer_id?: string;\n priority?: 'low' | 'normal' | 'high' | 'urgent';\n source?: string;\n tag_ids?: string[];\n /**\n * Mirrors `OPPORTUNITY_SORT_KEYS`. `customer_name` orders by the customer's\n * display name, `stage` by pipeline position (stage-less rows last),\n * `priority` low → normal → high → urgent. `recent` orders by the calling\n * user's last view (newest first, never-viewed last) and ignores `sort_order`.\n */\n sort_by?: OpportunitySortKey;\n sort_order?: 'asc' | 'desc';\n page?: number;\n limit?: number;\n}\n\n/**\n * `PUT /opportunities/{id}` body — mirrors `updateOpportunitySchema` in\n * `@blueprint/shared` (every field optional; `null` clears a nullable one).\n * `source` is validated against the tenant's lead sources; `customFields`\n * against the tenant's opportunity field definitions. Stage moves go through\n * the workflow transition routes, not this body.\n */\nexport interface UpdateOpportunityDto {\n title?: string | undefined;\n customerId?: string | undefined;\n agentId?: string | null | undefined;\n source?: string | null | undefined;\n priority?: 'low' | 'normal' | 'high' | 'urgent' | undefined;\n total?: number | undefined;\n notes?: string | null | undefined;\n /** ISO 8601 datetime. */\n closedAt?: string | null | undefined;\n closedReason?: string | null | undefined;\n primaryContactId?: string | null | undefined;\n customFields?: Record<string, unknown> | null | undefined;\n}\n","import type { ListParams } from '../types';\n\n/**\n * Query params for `GET /opportunities/{opportunityId}/jobs` — mirrors\n * `jobFilterSchema` in `packages/shared/src/validation/job.ts`. Extends the\n * generic `ListParams` so it stays assignable to `BaseResource.list`.\n */\n/** Sort keys on the per-opportunity jobs list (kept in lockstep with the server enum by the SDK's wire-contract spec). */\nexport const JOB_SORT_KEYS = ['created_at', 'recent'] as const;\nexport type JobSortKey = (typeof JOB_SORT_KEYS)[number];\n\nexport type JobStatus = 'active' | 'on_hold' | 'completed' | 'cancelled';\n\nexport interface JobListParams extends ListParams {\n tradeId?: string;\n status?: JobStatus;\n /**\n * Mirrors `JOB_SORT_KEYS`. Default `created_at` desc. `recent` orders by the\n * calling user's last view (newest first, never-viewed last) and ignores\n * `sort_order`.\n */\n sort_by?: JobSortKey;\n sort_order?: 'asc' | 'desc';\n page?: number;\n limit?: number;\n}\n\n/**\n * One active trade and whether a job can be created under it\n * (`GET /api/v1/jobs/trade-coverage`). Mirrors `jobTradeCoverageItemSchema` in\n * `packages/shared` — redefined here so the published package carries no\n * dependency on `@blueprint/shared`; `jobs.spec.ts` parses the response with\n * the REAL shared schema. `hasWorkflow: false` means creating a job with this\n * trade returns 422 WORKFLOW_NOT_CONFIGURED.\n */\nexport interface JobTradeCoverageItemDto {\n tradeId: string;\n name: string;\n hasWorkflow: boolean;\n}\n\n/** Every active trade of the tenant, ordered by name, with job-workflow coverage. */\nexport interface JobTradeCoverageResponse {\n trades: JobTradeCoverageItemDto[];\n}\n\n/**\n * `POST /opportunities/{opportunityId}/jobs` body — mirrors `createJobSchema`\n * minus `opportunityId` (it comes from the path). A trade with no published\n * job workflow returns 422 WORKFLOW_NOT_CONFIGURED; see `getTradeCoverage()`.\n */\nexport interface CreateJobDto {\n tradeId: string;\n propertyId?: string | undefined;\n notes?: string | undefined;\n}\n\n/** `PUT /opportunities/{opportunityId}/jobs/{id}` body — mirrors `updateJobSchema`. */\nexport interface UpdateJobDto {\n status?: JobStatus | undefined;\n notes?: string | null | undefined;\n}\n","import type { ListParams } from '../types';\n\n/** Estimate statuses — mirrors `ESTIMATE_STATUSES` in `packages/shared/src/validation/estimate.ts` (runtime parity spec). */\nexport const ESTIMATE_STATUSES = ['draft', 'sent', 'approved', 'rejected'] as const;\nexport type EstimateStatus = (typeof ESTIMATE_STATUSES)[number];\n\n/**\n * Query params for the estimate lists — mirrors `estimateFilterSchema`\n * (`page` ≥ 1, `limit` 1–100, default 25).\n */\nexport interface EstimateListParams extends ListParams {\n status?: EstimateStatus;\n page?: number;\n limit?: number;\n}\n\n/**\n * One estimate list row (no line items) — transcribed from\n * `EstimatesService.findAllForOpportunity`, the same shape as the per-job list.\n * Money/rate columns are Postgres numerics, so they arrive as strings.\n */\nexport interface EstimateSummaryDto {\n id: string;\n tenantId: string;\n jobId: string;\n estimateTemplateId: string | null;\n status: EstimateStatus;\n notes: string | null;\n subtotal: string;\n discountType: string | null;\n discountValue: string | null;\n taxRate: string | null;\n taxAmount: string | null;\n total: string;\n createdBy: string;\n createdAt: string;\n updatedAt: string;\n lineItemCount: number;\n}\n\n/**\n * `POST /jobs/{jobId}/estimates` body — mirrors `createEstimateSchema` minus\n * `jobId` (it comes from the path).\n */\nexport interface CreateEstimateDto {\n estimateTemplateId?: string | undefined;\n /** Defaults to `{}`. */\n measurements?: Record<string, unknown> | undefined;\n notes?: string | undefined;\n}\n\n/** `PUT /jobs/{jobId}/estimates/{id}` body — mirrors `updateEstimateSchema`. `taxRate` is a fraction (0–1). */\nexport interface UpdateEstimateDto {\n measurements?: Record<string, unknown> | undefined;\n notes?: string | null | undefined;\n discountType?: 'percent' | 'flat' | null | undefined;\n discountValue?: number | null | undefined;\n taxRate?: number | null | undefined;\n}\n","/**\n * Wire shapes for record views (`/api/v1/record-views`, E41-F7) — the\n * per-user \"recently viewed\" primitive behind `sort_by: 'recent'` on the\n * customer / contact / opportunity / job lists.\n *\n * Mirrors `recordViewSchema` / `recentRecordViewsQuerySchema` in\n * `packages/shared/src/validation/record-view.ts`. Redefined here (not\n * imported) so the published package carries no dependency on\n * `@blueprint/shared`; `record-views.spec.ts` asserts every request against the\n * REAL shared schema.\n */\n\n/** Root records whose detail views are counted (kept in lockstep with the server by the wire spec). */\nexport const RECORD_VIEW_ENTITY_TYPES = ['customer', 'contact', 'opportunity', 'job'] as const;\nexport type RecordViewEntityType = (typeof RECORD_VIEW_ENTITY_TYPES)[number];\n\n/** Body of `POST /record-views`. The viewer is always the authenticated user. */\nexport interface RecordViewInput {\n entity_type: RecordViewEntityType;\n entity_id: string;\n}\n\n/** Query of `GET /record-views/recent`. `limit` defaults to 10 on the server, max 50. */\nexport interface RecentRecordViewsParams {\n entity_type?: RecordViewEntityType;\n limit?: number;\n}\n\n/** One row of `GET /record-views/recent`, newest first. */\nexport interface RecentRecordView {\n entity_type: RecordViewEntityType;\n entity_id: string;\n /** ISO timestamp of the caller's most recent view. */\n viewed_at: string;\n}\n","/**\n * Wire shapes for tenant module status (`GET /api/v1/modules`, E51-F1-S1).\n *\n * Mirrors `tenantModuleStatusSchema` / `MODULE_KEYS` in `packages/shared`.\n * Redefined here (not imported) so the published package carries no dependency\n * on `@blueprint/shared`; `modules.spec.ts` asserts key parity and parses the\n * response with the REAL shared schema.\n */\n\n/** Every registered module key, in the server's canonical order. */\nexport const MODULE_KEYS = [\n 'estimates',\n 'contracts',\n 'inventory',\n 'production',\n 'production_scheduling',\n 'calendar',\n 'service-schedules',\n 'marketplace',\n 'invoicing',\n 'knocks',\n 'accounting',\n 'contacts',\n 'tasks',\n 'comments',\n 'conversations',\n] as const;\nexport type ModuleKey = (typeof MODULE_KEYS)[number];\n\n/** One entry of `GET /modules` — whether a module is on for the caller's tenant. */\nexport interface TenantModuleStatusDto {\n moduleKey: ModuleKey;\n /** `false` both for an explicitly deactivated module and one never activated. */\n isActive: boolean;\n}\n","/**\n * Wire shapes for the tenant's plan (`GET /api/v1/plan`, E54-F1-S2).\n *\n * Mirrors `tenantPlanSchema` and the plan enums in `packages/shared`\n * (`features/plans.ts`, `features/feature-keys.ts`). Redefined here (not\n * imported) so the published package carries no dependency on\n * `@blueprint/shared`; `plan.spec.ts` asserts enum parity and parses the\n * response with the REAL shared schema.\n */\nimport type { ModuleKey } from './module-types';\n\nexport const PLAN_KEYS = ['solo', 'sales', 'business', 'operations', 'legacy', 'internal'] as const;\nexport type PlanKey = (typeof PLAN_KEYS)[number];\n\nexport const BILLING_STATUSES = [\n 'trialing',\n 'active',\n 'past_due',\n 'unpaid',\n 'paused',\n 'canceled',\n 'comped',\n] as const;\nexport type BillingStatus = (typeof BILLING_STATUSES)[number];\n\nexport const PLAN_SOURCES = ['stripe', 'manual'] as const;\nexport type PlanSource = (typeof PLAN_SOURCES)[number];\n\nexport const BILLING_INTERVALS = ['month', 'year'] as const;\nexport type BillingInterval = (typeof BILLING_INTERVALS)[number];\n\n/** Every feature-flag key a plan ceiling can include, in the server's order. */\nexport const FEATURE_KEYS = [\n 'calendar',\n 'estimates',\n 'contracts',\n 'service_dispatch',\n 'production_scheduling',\n 'sub_management',\n 'reports',\n 'open_api',\n 'workflow_builder',\n 'integrations.quickbooks',\n 'integrations.stripe',\n 'integrations.hover',\n 'integrations.ortto',\n 'communications.marketing',\n 'ai.scheduling',\n 'ai.content_assist',\n 'ai.documents',\n 'ai.search',\n 'inventory_management',\n 'workflow_execution',\n 'marketplace',\n] as const;\nexport type FeatureKey = (typeof FEATURE_KEYS)[number];\n\n/** `GET /plan` — the caller tenant's plan, billing state and plan ceiling. */\nexport interface TenantPlanDto {\n planKey: PlanKey;\n /** Display name, e.g. \"Business\". */\n label: string;\n billingStatus: BillingStatus;\n /** Who last set the plan: `stripe` (subscription webhook) or `manual` (Planes staff). */\n planSource: PlanSource;\n /** ISO-8601, or null when not trialing. */\n trialEndsAt: string | null;\n billingInterval: BillingInterval | null;\n /** ISO-8601 end of a price lock (Founding Fleet), or null. */\n priceLockedUntil: string | null;\n /** Module keys the plan may enable (a ceiling, not what is switched on). */\n modules: ModuleKey[];\n /** Feature keys the plan may enable. */\n features: FeatureKey[];\n includedFullSeats: number;\n crewSeatsAllowed: boolean;\n monthlyAiCredits: number;\n storageGb: number;\n}\n","/**\n * Wire shapes for `GET /api/v1/me/abilities` (E52-F1-S3).\n *\n * Mirrors `meAbilitiesResponseSchema` / `AGENT_ROLES` in `packages/shared`.\n * Redefined here (not imported) so the published package carries no dependency\n * on `@blueprint/shared`; `me.spec.ts` asserts role parity and parses the\n * response with the REAL shared schema.\n */\n\n/** Every built-in role, in the server's canonical order. */\nexport const AGENT_ROLES = [\n 'admin',\n 'sales',\n 'service',\n 'production',\n 'sub',\n 'readonly',\n 'system_agent',\n] as const;\nexport type AgentRole = (typeof AGENT_ROLES)[number];\n\n/**\n * One CASL packed rule (`packRules` from `@casl/ability/extra`):\n * `[action, subject, conditions|0, inverted 1|0, fields|0, reason]`, trailing\n * empty slots trimmed. Rebuild the ability with\n * `createMongoAbility(unpackRules(rules))`.\n */\nexport type PackedAbilityRule =\n | [string, string]\n | [string, string, Record<string, unknown>]\n | [string, string, Record<string, unknown> | 0, 1]\n | [string, string, Record<string, unknown> | 0, 1 | 0, string]\n | [string, string, Record<string, unknown> | 0, 1 | 0, string | 0, string];\n\n/** `GET /me/abilities` — the caller's effective ability. */\nexport interface MeAbilitiesResponse {\n /** Live role (session), key role (API key) or token role (service token). */\n role: AgentRole;\n /** API keys only: the acting agent's live role; `rules` are the intersection. */\n actingRole?: AgentRole;\n /** Same value as the `X-Planes-Abilities-Version` response header. Opaque — compare, don't parse. */\n version: string;\n /** The exact rules the server enforces for this caller, CASL-packed. */\n rules: PackedAbilityRule[];\n}\n","/**\n * Wire shapes for a workflow's event rules on the admin workflow API\n * (`/api/v1/admin/workflows/:workflowId/event-rules`) — \"advance this record\n * automatically when a related contract / invoice / calendar event changes\".\n *\n * Mirrors `workflow-event-rule.ts` in `packages/shared`. Redefined here (not\n * imported) so the published package carries no dependency on\n * `@blueprint/shared`; `workflow-event-rule-types.parity.ts` fails the build\n * when the two drift, and `workflow-event-rules.spec.ts` parses every sent body\n * with the REAL shared schemas.\n */\n\n/**\n * Event types a rule can route on. Must equal the shared\n * `WORKFLOW_EVENT_RULE_EVENT_TYPES`, in order — the parity check and the spec\n * both pin it.\n */\nexport const WORKFLOW_EVENT_RULE_EVENT_TYPES = [\n 'contract.status_changed',\n 'invoice.status_changed',\n 'calendar_event.status_changed',\n /** \"An event is booked\": one-off events and recurring series masters. */\n 'calendar_event.created',\n] as const;\n\nexport type WorkflowEventRuleEventType = (typeof WORKFLOW_EVENT_RULE_EVENT_TYPES)[number];\n\n/**\n * Event types whose event carries no `changes` (a creation), so the only\n * matcher that can match is `when: {}`.\n */\nexport const WORKFLOW_EVENT_RULE_MATCH_ALL_EVENT_TYPES: readonly WorkflowEventRuleEventType[] = [\n 'calendar_event.created',\n];\n\n/**\n * Equality matcher over the event's `changes` map:\n * `{ status: { new: 'sent' } }` matches an event whose\n * `changes.status.new === 'sent'`. Each entry must name `old`, `new` or both\n * (an entry naming neither is a 400); `{}` matches every event of the type.\n */\nexport type WorkflowEventRuleWhen = Record<string, { old?: unknown; new?: unknown }>;\n\n/**\n * The transition to fire, by stage NAME, resolved against whichever workflow\n * version the record is pinned to — so a rule survives a version publish.\n */\nexport interface WorkflowTransitionRef {\n from_stage_name: string;\n to_stage_name: string;\n}\n\n/** Body of `POST /admin/workflows/:workflowId/event-rules`. */\nexport interface CreateWorkflowEventRuleInput {\n eventType: WorkflowEventRuleEventType;\n /** Defaults to `{}` (every event of the type). */\n when?: WorkflowEventRuleWhen | undefined;\n transitionRef: WorkflowTransitionRef;\n /** Defaults to true. */\n enabled?: boolean | undefined;\n}\n\n/** Body of `PATCH /admin/workflows/:workflowId/event-rules/:id` — at least one field; only the fields sent change. */\nexport interface UpdateWorkflowEventRuleInput {\n eventType?: WorkflowEventRuleEventType | undefined;\n when?: WorkflowEventRuleWhen | undefined;\n transitionRef?: WorkflowTransitionRef | undefined;\n enabled?: boolean | undefined;\n}\n\n/**\n * An event rule as the admin API returns it (list, create, update) — exactly\n * the shared `WorkflowEventRuleDto`. 0.53.0 dropped `tenantId`, `createdBy` and\n * `deletedAt`, which the server no longer sends.\n */\nexport interface WorkflowEventRuleDto {\n id: string;\n /** The workflow's LINEAGE ROOT id — the same for every version of one pipeline. */\n workflowId: string;\n /** TEXT on the server, so a rule stored with a newer type still reads back. */\n eventType: string;\n when: WorkflowEventRuleWhen;\n transitionRef: WorkflowTransitionRef;\n enabled: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\n/** Response of `DELETE /admin/workflows/:workflowId/event-rules/:id` (200; the delete is soft). */\nexport interface DeleteWorkflowEventRuleResult {\n id: string;\n}\n","import * as http from 'node:http';\nimport type {\n ActionHandler,\n ActionContext,\n ActionResult,\n ActionStage,\n ActionMetadata,\n} from './types/agent-types';\n\n/**\n * The raw wire envelope the Planes platform POSTs to an agent endpoint.\n * Mirrors the `payload` built in `external-agent.handler.ts`. The SDK server\n * flattens `action_context` and lifts the callback fields onto {@link ActionContext}\n * before handing it to a handler.\n */\ninterface WireEnvelope {\n action_context?: {\n action_id: string;\n action_type: string;\n entity_type: string;\n entity_id: string;\n tenant_id: string;\n data?: Record<string, unknown>;\n stage?: ActionStage | null;\n metadata: ActionMetadata;\n };\n callback_token: string;\n callback_base_url: string;\n}\n\n/**\n * Options for Planes.register() and AgentApp.listen().\n */\nexport interface RegisterOptions {\n /** Port to listen on (default 3000). */\n port?: number;\n /** HTTP path to listen on (default '/actions'). */\n path?: string;\n /** Whether to verify incoming request signatures (default true). */\n verifySignature?: boolean;\n /** Error callback for unhandled errors. */\n onError?: (error: Error) => void;\n}\n\n/**\n * Shared server/dispatch factory used by both {@link Planes.register} and\n * {@link AgentApp.listen}. Given a resolved handler map, it creates an HTTP\n * server that parses the platform's `{ action_context, callback_token,\n * callback_base_url }` POST envelope, dispatches to the matching handler by\n * `action_context.action_type`, and serializes the {@link ActionResult} back as\n * the synchronous HTTP response body (which is exactly how the platform's\n * external-agent handler consumes the result).\n *\n * Keeping this in one place ensures the low-level `Planes.register` array\n * API and the `createAgentApp()` builder are guaranteed to behave identically\n * (routing, 400/404 handling, handler-error capture, graceful shutdown).\n */\nfunction createActionServer(\n handlerMap: Map<string, ActionHandler>,\n options?: RegisterOptions,\n): http.Server {\n const port = options?.port ?? 3000;\n const basePath = options?.path ?? '/actions';\n const onError = options?.onError;\n\n const server = http.createServer(async (req: http.IncomingMessage, res: http.ServerResponse) => {\n // Only handle POST to the configured path\n if (req.method !== 'POST' || req.url !== basePath) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not found' }));\n return;\n }\n\n try {\n const body = await readBody(req);\n\n // The platform POSTs a wrapped envelope:\n // { action_context: { action_type, ... }, callback_token, callback_base_url }\n // (see apps/api/.../external-agent.handler.ts). Parse and validate it.\n let envelope: WireEnvelope;\n try {\n envelope = JSON.parse(body) as WireEnvelope;\n } catch {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Malformed request body' }));\n return;\n }\n\n const wire = envelope?.action_context;\n const actionType = wire?.action_type;\n if (!wire || !actionType) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({ error: 'Missing action_context.action_type in request body' }),\n );\n return;\n }\n\n const handler = handlerMap.get(actionType);\n if (!handler) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n error: `No handler registered for action type: ${actionType}`,\n }),\n );\n return;\n }\n\n // Flatten the wire `action_context` and lift the envelope-level callback\n // fields + the X-Planes-Request-Id header onto a single handler context.\n const requestIdHeader = req.headers['x-planes-request-id'];\n const requestId = Array.isArray(requestIdHeader) ? requestIdHeader[0] : requestIdHeader;\n const context: ActionContext = {\n action_id: wire.action_id,\n action_type: actionType,\n entity_type: wire.entity_type,\n entity_id: wire.entity_id,\n tenant_id: wire.tenant_id,\n data: wire.data ?? {},\n stage: wire.stage ?? null,\n metadata: wire.metadata,\n callback_token: envelope.callback_token,\n callback_base_url: envelope.callback_base_url,\n // Only set request_id when the header was present (exactOptionalPropertyTypes).\n ...(requestId !== undefined ? { request_id: requestId } : {}),\n };\n\n let result: ActionResult;\n try {\n result = await handler.execute(context);\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : 'Unknown handler error';\n if (onError && err instanceof Error) {\n onError(err);\n }\n result = {\n status: 'failed',\n error: errorMessage,\n retryable: false,\n };\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result));\n } catch (err) {\n if (onError && err instanceof Error) {\n onError(err);\n }\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Internal server error' }));\n }\n });\n\n server.listen(port);\n\n // Graceful shutdown\n const shutdown = () => {\n server.close();\n };\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n\n return server;\n}\n\n/**\n * Planes agent registration namespace.\n * Provides a convenient way to register action handlers as an HTTP server.\n */\nexport const Planes = {\n /**\n * Register action handlers and start an HTTP server to receive action requests.\n *\n * @example\n * ```ts\n * Planes.register([\n * {\n * type: 'send_email',\n * async execute(ctx) {\n * // ... send email logic\n * return { status: 'completed', output: { sent: true } };\n * },\n * },\n * ], { port: 3000 });\n * ```\n */\n register(handlers: ActionHandler[], options?: RegisterOptions): http.Server {\n // Build handler map for O(1) lookup\n const handlerMap = new Map<string, ActionHandler>();\n for (const handler of handlers) {\n handlerMap.set(handler.type, handler);\n }\n return createActionServer(handlerMap, options);\n },\n};\n\n/**\n * Builder-style agent app. Register action handlers incrementally by type,\n * then start the server with {@link AgentApp.listen}.\n *\n * This is ergonomic sugar over {@link Planes.register} — both share the same\n * underlying server/dispatch implementation ({@link createActionServer}).\n *\n * @example\n * ```ts\n * createAgentApp()\n * .registerAction('enrich-opportunity', async (ctx) => {\n * // ... enrichment logic\n * return { status: 'completed', output: { enriched: true } };\n * })\n * .listen({ port: 3000 });\n * ```\n */\nexport class AgentApp {\n private readonly handlers = new Map<string, ActionHandler>();\n\n /**\n * Register a handler for an action type. Accepts a bare `execute` function\n * (the caller does not build the `{ type, execute }` object).\n *\n * @param type - The action type this handler responds to.\n * @param execute - The handler function invoked with the {@link ActionContext}.\n * @returns `this`, for chaining.\n */\n registerAction(type: string, execute: ActionHandler['execute']): this {\n this.handlers.set(type, { type, execute });\n return this;\n }\n\n /**\n * Start an HTTP server that dispatches incoming action requests to the\n * registered handlers. Delegates to the same server factory as\n * {@link Planes.register}.\n *\n * The handler set is snapshotted at call time, so registering further actions\n * after `listen()` does not mutate the running server (parity with\n * `Planes.register`, which snapshots its array).\n */\n listen(options?: RegisterOptions): http.Server {\n return createActionServer(new Map(this.handlers), options);\n }\n}\n\n/**\n * Create a new {@link AgentApp} builder.\n */\nexport function createAgentApp(): AgentApp {\n return new AgentApp();\n}\n\nfunction readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString()));\n req.on('error', reject);\n });\n}\n"],"mappings":"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","names":["response","next","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","http","import_node_crypto"]}