/** Same CRM user (idExternal) opened webphone in another tab on the same inbox. */ declare interface AgentDuplicateSessionEvent { inboxId: number; idExternal: string; incoming: { clientInstanceId: string; socketId: string; name?: string; email?: string; }; existing: Array<{ clientInstanceId: string; socketId: string; name?: string; email?: string; }>; } /** Agent attribution info — lives entirely in the host system. */ declare interface AgentInfo { /** Required: the agent's ID in the host system (e.g. user-123). */ idExternal: string; /** * Unique per browser tab / embedded widget. Auto-generated when omitted. * Send the same value on socket auth and HTTP `agent` bodies. */ clientInstanceId?: string; /** Optional display name, shown in UI and persisted in call logs. */ name?: string; /** Optional email, persisted in call logs for reporting. */ email?: string; /** Optional role/label (e.g. "sales", "support", "manager"). */ role?: string; /** Arbitrary key/value metadata. Persisted once on the call log. */ metadata?: Record; } declare interface Call { id: string; type: 'official' | 'unofficial'; device_token: string; direction: CallDirection; status: CallStatus; peer: { phone: string; displayName: string | null; profilePicture: string | null; muted: boolean; }; muted: boolean; } export declare interface CallActive extends Call { connection_status: 'disconnected' | 'connected' | 'connecting'; audio_analyser: Promise; mute(): Promise<{ err: string | null; }>; unmute(): Promise<{ err: string | null; }>; end(): Promise<{ err: string | null; }>; onError(cb: (err: string) => void): void; onPeerMute(cb: () => void): void; onPeerUnmute(cb: () => void): void; onEnd(cb: () => void): void; onStats(cb: (stats: CallStats) => void): void; onConnectionStatus(cb: (status: string) => void): void; onStatus(cb: (status: CallStatus) => void): void; } export declare type CallDirection = 'INCOMING' | 'OUTGOING'; /** Payload for call:missed — delivered when a call ends without ever being accepted. */ declare interface CallMissedEvent { callId: string; from: string; /** `incoming` = peer called us, nobody picked up. `outgoing` = we called, peer didn't pick up. */ direction: 'incoming' | 'outgoing'; inboxId?: number; timestamp?: number; } export declare interface CallOffer { call_id: string; from: string; /** When false, UI may ring but Accept stays disabled until `call:offer:ready`. */ answerable?: boolean; displayName?: string; accept(): Promise; reject(): Promise<{ err: string | null; }>; onAcceptedElsewhere(cb: () => void): void; onRejectedElsewhere(cb: () => void): void; onUnanswered(cb: () => void): void; onEnd(cb: () => void): void; onStatus(cb: (status: CallStatus) => void): void; } export declare interface CallOutgoing extends Call { onPeerAccept(cb: () => void): void; onPeerReject(cb: () => void): void; onUnanswered(cb: () => void): void; onEnd(cb: () => void): void; mute(): Promise<{ err: string | null; }>; unmute(): Promise<{ err: string | null; }>; end(): Promise<{ err: string | null; }>; onStatus(cb: (status: CallStatus) => void): void; } export declare interface CallStats { rtt: { client: { min: number; max: number; avg: number; }; whatsapp: { min: number; max: number; avg: number; }; }; tx: { total: number; total_bytes: number; loss: number; }; rx: { total: number; total_bytes: number; loss: number; }; } export declare type CallStatus = 'RINGING' | 'CALLING' | 'NOT_ANSWERED' | 'ACTIVE' | 'ENDED' | 'REJECTED' | 'FAILED' | 'DISCONNECTED' | 'DEVICE_RESTARTING'; /** Payload for call:taken — delivered when another agent claims an incoming call. */ declare interface CallTakenEvent { callId: string; takenBy: { idExternal: string; clientInstanceId?: string; name?: string; email?: string; claimedAt: number; }; } export declare interface Device { token: string; status: DeviceStatus; qrcode?: string; contact?: { phone?: string; name?: string; }; onStatus(cb: (status: DeviceStatus) => void): void; onQRCode(cb: (qr: string) => void): void; onContact(cb: (contact: { phone?: string; name?: string; }) => void): void; } export declare type DeviceStatus = 'disconnected' | 'close' | 'connecting' | 'open' | 'restarting' | 'hibernating' | 'BUILDING' | 'EXTERNAL_INTEGRATION_ERROR'; export declare class GrowhatsWebphone { private static instance; /** * Render the webphone widget and connect to the server. * Returns a Promise resolving to the WebphoneAPI (Wavoip-compatible). */ static render(config: WebphoneConfig): Promise; /** * Alias for render() — Wavoip legacy compatibility. * Allows: window.wavoipWebphone.render() pattern */ static get wavoipWebphone(): { render: typeof GrowhatsWebphone.render; destroy: typeof GrowhatsWebphone.destroy; }; /** * Remove the webphone widget from the DOM and disconnect. */ static destroy(): void; } declare interface Notification_2 { id: Date; type: NotificationType; message: string; detail?: string; token?: string; isRead: boolean; isHidden: boolean; created_at: Date; } export { Notification_2 as Notification } export declare type NotificationType = 'INFO' | 'CALL_FAILED'; /** Peer agent online on the same inbox (returned by getOnlineAgents). */ declare interface OnlineAgent { idExternal: string; clientInstanceId?: string; name?: string; email?: string; role?: string; socketId: string; inboxId?: number; since: number; } export declare type PositionPreset = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center'; export declare interface RecordingAPI { /** Whether recording is enabled via config */ readonly enabled: boolean; /** Get active recording call IDs */ getActiveRecordings(): string[]; /** Check if a specific call is being recorded */ isRecording(callId?: string): boolean; /** Get the recording URL for a specific call (from call history) */ getUrl(callId: string): Promise; } export declare interface RecordingConfig { /** Enable automatic call recording (default: false) */ enabled: boolean; } export declare interface RecordingResult { /** URL to access the recording (relative API path) */ url: string; /** Call ID this recording belongs to */ callId: string; /** Duration in seconds */ durationSeconds: number; /** When the recording started (ISO string) */ startedAt: string; /** When the recording ended (ISO string) */ endedAt: string; } /** * Socket.io base URL. Default: same origin as `serverUrl` (gapiv2 proxies * `/socket.io` on PORT → SOCKET_PORT). Pass `socketUrl` to override. */ export declare function resolveSocketUrl(serverUrl: string, socketUrl?: string, viaApiProxy?: boolean): string; export declare interface WebphoneAPI { /** Call management */ call: { start(to: string, config?: { fromTokens?: string[]; displayName?: string; }): Promise; /** Alias: startCall(to, [token]) — Wavoip legacy */ startCall(to: string, fromTokens?: string[]): Promise; getCallActive(): CallActive | undefined; getCallOutgoing(): CallOutgoing | undefined; getOffers(): CallOffer[]; setInput(to: string): void; onOffer(cb: (offer: CallOffer) => void): void; }; /** Device (inbox) management */ device: { get(): Device[]; getDevices(): Device[]; add(token: string, persist?: boolean): void; addDevice(token: string, persist?: boolean): void; remove(token: string): void; removeDevice(token: string): void; enable(token: string): void; enableDevice(token: string): void; disable(token: string): void; disableDevice(token: string): void; }; /** Notification management */ notifications: { get(): Notification_2[]; getNotifications(): Notification_2[]; add(notification: Partial): void; addNotification(notification: Partial): void; remove(id: Date): void; removeNotification(id: Date): void; clear(): void; clearNotifications(): void; read(): void; readNotifications(): void; }; /** Widget visibility */ widget: { isOpen: boolean; open(): void; close(): void; toggle(): void; buttonPosition: { value: { x: number; y: number; }; set(pos: PositionPreset | { x: number; y: number; }): void; }; }; /** Theme control */ theme: { value: string; set(theme: 'light' | 'dark' | 'system'): void; setTheme(theme: 'light' | 'dark' | 'system'): void; }; /** Position control (alias for widget.buttonPosition) */ position: { value: { x: number; y: number; }; set(pos: PositionPreset | { x: number; y: number; }): void; }; /** Runtime settings */ settings: { showNotifications: boolean; setShowNotifications(v: boolean): void; showSettings: boolean; setShowSettings(v: boolean): void; showDevices: boolean; setShowDevices(v: boolean): void; showAddDevices: boolean; setShowAddDevices(v: boolean): void; showEnableDevices: boolean; setShowEnableDevices(v: boolean): void; showRemoveDevices: boolean; setShowRemoveDevices(v: boolean): void; showWidgetButton: boolean; setShowWidgetButton(v: boolean): void; }; /** Recording management (full-duplex) */ recording: RecordingAPI; /** * Agent attribution — identifies the host-system user operating this webphone. * Used to tag call logs, coordinate concurrent claims between peers, and * organize recordings. */ agent: { /** Current agent, or `null` if none was set. */ get(): AgentInfo | null; /** * Update the active agent. Emits agent:joined/left to peers and re-scopes * subsequent call claims. Pass `null` to unset. */ set(agent: AgentInfo | null): void; /** List agents currently connected on this inbox (sorted by join time). */ getOnlineAgents(): Promise; }; /** Event listeners */ on(event: K, cb: WebphoneEvents[K]): void; off(event: K, cb: WebphoneEvents[K]): void; /** Cleanup */ destroy(): void; } export declare interface WebphoneConfig { /** Base URL of the server (gapiv2) — used for all HTTP calls */ serverUrl: string; /** * URL of the Socket.io server. If omitted, uses the same origin as `serverUrl` * (gapiv2 proxies `/socket.io` on the API port → SOCKET_PORT). Override only * when socket.io is on another host (e.g. legacy `gapisocket.example.com`) or * local dev without proxy (`http://localhost:4001`). */ socketUrl?: string; /** * When false and `socketUrl` is omitted, falls back to `serverUrl` with * `:4000` → `:4001` (old local-dev convention). Default true. */ socketViaApiProxy?: boolean; /** API key for authentication (sent as X-Api-Key header) */ apiKey?: string; /** Account ID (empresa) — identifies which company owns this webphone */ accountId: number; /** * Inbox ID — identifies which WhatsApp number to use for calls. Optional: * when omitted, the SDK asks the server to pick any active inbox of the * account at call time (useful for generic "voice channel" integrations * where the CRM does not want to pin a specific number). */ inboxId?: number; /** Theme: "light" | "dark" | "system" (default: "dark") */ theme?: 'light' | 'dark' | 'system'; /** Widget position preset or custom {x, y} */ position?: PositionPreset | { x: number; y: number; }; /** Show the floating widget button (default: true) */ showWidgetButton?: boolean; /** Start with the widget open (default: false) */ startOpen?: boolean; /** Auto-reconnect SSE on disconnect (default: true) */ autoReconnect?: boolean; /** Container element to mount into (default: document.body) */ container?: HTMLElement; /** Status bar config */ statusBar?: { showNotificationsIcon?: boolean; showSettingsIcon?: boolean; }; /** Settings menu config */ settingsMenu?: { showDevices?: boolean; showAddDevices?: boolean; showEnableDevices?: boolean; showRemoveDevices?: boolean; }; /** Recording config — if enabled, all calls are recorded automatically */ recording?: RecordingConfig; /** * Per-direction permissions enforced **client-side**. Independent from the * server-side `outboundEnabled`/`inboundEnabled` flags — both gates apply, * server is still authoritative. Use this to scope a single SDK instance * to a role (e.g. "this user can only receive", "this user can only dial"). * * Defaults: both `true`. */ calls?: { /** When `false`, `startCall()` rejects and the dialer is hidden. */ canMakeCalls?: boolean; /** When `false`, incoming `call:offer` events are ignored locally. */ canReceiveCalls?: boolean; }; /** * Allow the user to drag the widget panels (dialer, incoming modal, active * call panel, minimized pill) anywhere on screen. Position is persisted to * `localStorage` per element. Default: `true`. */ draggable?: boolean; /** * Agent identity — lets the host application (e.g. a CRM) tell our system which * of *their* users is operating this webphone. Optional: if omitted, calls are * still recorded but without attribution. When present: * * • outbound calls are tagged with this agent on the server * • incoming-call claims carry it; peers see `call:taken` when another agent answers * • recordings are organized by agent in storage */ agent?: AgentInfo; } export declare interface WebphoneEvents { 'call:incoming': (offer: CallOffer) => void; /** VoIP keys arrived after `call:ringing` — enable Accept on an existing offer. */ 'call:offer:ready': (offer: CallOffer) => void; 'call:started': (call: CallActive) => void; 'call:ended': (callId: string, reason: string) => void; 'call:status': (callId: string, status: CallStatus) => void; /** Another agent on the same inbox claimed the incoming call. UI should dismiss the offer. */ 'call:taken': (evt: CallTakenEvent) => void; /** This client tried to accept but another agent had already claimed the call. */ 'call:claim:denied': (evt: CallTakenEvent) => void; /** Call ended without ever being accepted (timeout, peer reject, local cancel). */ 'call:missed': (evt: CallMissedEvent) => void; /** An agent connected to the same inbox. */ 'agent:joined': (agent: OnlineAgent) => void; /** An agent disconnected from the same inbox. */ 'agent:left': (agent: OnlineAgent) => void; /** Same idExternal connected twice with different clientInstanceId. */ 'agent:duplicate_session': (evt: AgentDuplicateSessionEvent) => void; 'connection:status': (connected: boolean) => void; 'recording:started': (callId: string) => void; 'recording:stopped': (callId: string, result: RecordingResult | null) => void; 'error': (error: Error) => void; } export { }