portal-aiatende-api 0.3.0 → 0.4.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.d.mts CHANGED
@@ -94,6 +94,14 @@ interface AiConfig {
94
94
  untaggedLeadAiState?: 'inactive' | 'active' | 'active_for_new_leads';
95
95
  /** Estado da IA após handoff. 'inactive' = trava permanente e global (nunca reativa sozinha). 'untag' = remove a tag, elegível pro bootstrap na próxima mensagem. */
96
96
  handoffAiState?: 'inactive' | 'untag';
97
+ /** Canal WhatsApp direto (aiatendeWa) — domínio separado das tags Kommo acima, usa Conversation.aiCareStatus/aiResumeMode. Se true (default), conversas novas nesse canal já nascem com aiCareStatus ACTIVE; se false, nascem INACTIVE e exigem ativação manual pelo Portal. */
98
+ aiatendeWaDefaultActive?: boolean;
99
+ /** Canal aiatendeWa: responder em áudio (TTS) quando o lead mandar uma mensagem de áudio. Default true. */
100
+ audioReplyOnInboundAudio?: boolean;
101
+ /** Canal aiatendeWa: responder em áudio em vez de texto quando a resposta gerada passar desse tamanho (caracteres). `undefined`/`null`/`0` desliga esse gatilho específico. Default 300. */
102
+ audioReplyCharThreshold?: number | null;
103
+ /** Canal aiatendeWa: id bruto da voz de TTS escolhida — seu significado depende de qual provider `model` indica no momento do envio (OpenAI e Gemini têm listas de vozes totalmente diferentes). */
104
+ ttsVoice?: string | null;
97
105
  /** Ativa a Reflection Agent (segunda passada de revisão/auditoria da resposta) pra este cliente. Ausente/undefined = usa o fallback global (env USE_REFLECTION_AGENT) no atende360-assistant — ver Orchestrator.ts. */
98
106
  reflectionAgentEnabled?: boolean;
99
107
  /** Limiar mínimo de similaridade (cosine, 0-1) pra um trecho da base de conhecimento entrar no contexto do RAG. `undefined`/`null` = o Portal usa o fallback global 0.5 até o admin calibrar via RAG eval. Aplicado internamente pelo Portal em `POST /api/knowledge/internal/context` — não precisa ser enviado pelo assistant-api. */
@@ -286,6 +294,13 @@ declare class ClientsResource extends BaseResource {
286
294
  getByKommoSubdomain(subdomain: string): Promise<Client | null>;
287
295
  /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */
288
296
  getByWazzupChannelId(channelId: string): Promise<Client | null>;
297
+ /**
298
+ * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e
299
+ * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o
300
+ * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de
301
+ * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.
302
+ */
303
+ getById(clientId: string): Promise<Client | null>;
289
304
  }
290
305
 
291
306
  /** Uma mensagem simplificada do histórico da conversa, enviada pro Portal reescrever a
@@ -321,11 +336,191 @@ declare class KnowledgeResource extends BaseResource {
321
336
  getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult>;
322
337
  }
323
338
 
339
+ type AiCareStatus = 'ACTIVE' | 'INACTIVE' | 'PAUSED';
340
+ type AiResumeMode = 'immediate' | 'next_message';
341
+ interface MessagingContact {
342
+ id: string;
343
+ waId: string;
344
+ profileName: string | null;
345
+ displayName: string | null;
346
+ displayNameSetByName: string | null;
347
+ displayNameSetAt: Date | string | null;
348
+ }
349
+ interface MessagingPhoneNumber {
350
+ id: string;
351
+ phoneNumberId: string;
352
+ displayNumber: string;
353
+ metaConnection: {
354
+ channel: string;
355
+ };
356
+ }
357
+ interface MessagingConversation {
358
+ id: string;
359
+ contactId: string;
360
+ phoneNumberId: string;
361
+ lastMessageAt: Date | string | null;
362
+ contact: MessagingContact;
363
+ phoneNumber: MessagingPhoneNumber;
364
+ _count: {
365
+ messages: number;
366
+ };
367
+ assignedToId: string | null;
368
+ assignedTo: {
369
+ id: string;
370
+ name: string;
371
+ email: string;
372
+ } | null;
373
+ lastMessage: Record<string, unknown> | null;
374
+ lastInboundAt: Date | string | null;
375
+ lastOutboundAt: Date | string | null;
376
+ temperature: 'HOT' | 'WARM' | 'COLD' | null;
377
+ aiCareStatus: AiCareStatus;
378
+ aiCareStatusSetAt: Date | string | null;
379
+ aiCareStatusSetByName: string | null;
380
+ /** Só presente na resposta do GET de detalhe (não na listagem). */
381
+ aiResumeMode?: AiResumeMode | null;
382
+ [key: string]: unknown;
383
+ }
384
+ interface MessagingConversationsListResponse {
385
+ data: MessagingConversation[];
386
+ meta: {
387
+ total: number;
388
+ page: number;
389
+ limit: number;
390
+ totalPages: number;
391
+ };
392
+ }
393
+ interface MessagingMessage {
394
+ id: string;
395
+ externalId: string;
396
+ conversationId: string;
397
+ direction: 'inbound' | 'outbound';
398
+ type: string;
399
+ status: string;
400
+ content: Record<string, unknown>;
401
+ waMessageId: string | null;
402
+ media: unknown[];
403
+ sentByType: 'platform_user' | 'api_key' | 'external_agent' | 'whatsapp_app' | null;
404
+ sentById: string | null;
405
+ sentByName: string | null;
406
+ error: string | null;
407
+ errorCode: string | null;
408
+ errorDetails: unknown;
409
+ createdAt: Date | string;
410
+ updatedAt: Date | string;
411
+ }
412
+ interface MessagingMessagesListResponse {
413
+ data: MessagingMessage[];
414
+ meta: {
415
+ total: number;
416
+ page: number;
417
+ limit: number;
418
+ totalPages: number;
419
+ };
420
+ }
421
+ interface ListConversationsFilters {
422
+ page?: number;
423
+ limit?: number;
424
+ assignedToId?: string | 'unassigned';
425
+ phoneNumberId?: string;
426
+ contactWaId?: string;
427
+ contactName?: string;
428
+ createdFrom?: string;
429
+ createdTo?: string;
430
+ lastMessageFrom?: string;
431
+ lastMessageTo?: string;
432
+ sortBy?: 'default' | 'createdAt' | 'lastMessageAt' | 'contactName';
433
+ sortDirection?: 'asc' | 'desc';
434
+ }
435
+ interface PatchConversationPayload {
436
+ assignedToId?: string | null;
437
+ contactDisplayName?: string | null;
438
+ aiCareStatus?: AiCareStatus;
439
+ /** Só tem efeito quando `aiCareStatus` é 'ACTIVE' — em qualquer outro status o Portal força `null`. */
440
+ aiResumeMode?: AiResumeMode | null;
441
+ }
442
+ /**
443
+ * `actor` só é respeitado pelo Portal quando o chamador é um papel de sistema/staff (ex.: o
444
+ * atende360-assistant autenticado via APP_API_KEY) — um usuário comum (OWNER/MEMBER) sempre
445
+ * manda como si mesmo, a API ignora um `actor` customizado nesse caso.
446
+ */
447
+ interface SendMessagePayload {
448
+ phoneNumberId: string;
449
+ to: string;
450
+ type: string;
451
+ content: Record<string, unknown> & {
452
+ id?: string;
453
+ link?: string;
454
+ _media?: {
455
+ storagePath?: string | null;
456
+ mimeType?: string;
457
+ fileSize?: number;
458
+ };
459
+ };
460
+ replyToWaMessageId?: string;
461
+ actor?: {
462
+ externalUserId: string;
463
+ externalUserName?: string;
464
+ };
465
+ }
466
+ interface SendMessageResponse {
467
+ messageId: string;
468
+ conversationId: string;
469
+ status: string;
470
+ correlationId: string;
471
+ }
472
+ interface UploadMediaPayload {
473
+ /** Bytes do arquivo (buffer do áudio/imagem/etc. gerado ou recebido). */
474
+ file: Buffer;
475
+ filename: string;
476
+ mimeType: string;
477
+ phoneNumberId: string;
478
+ /**
479
+ * true faz a plataforma transcodificar pra OGG/Opus mono e renderizar como balão de
480
+ * mensagem de voz no WhatsApp (em vez de anexo de áudio genérico) — usar sempre que for
481
+ * uma resposta de TTS da IA.
482
+ */
483
+ isVoiceNote?: boolean;
484
+ }
485
+ interface UploadMediaResponse {
486
+ id: string | null;
487
+ storagePath: string | null;
488
+ filename: string;
489
+ mimeType: string;
490
+ fileSize: number;
491
+ }
492
+
493
+ /**
494
+ * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`
495
+ * do Portal, que por sua vez falam com a plataforma de mensageria própria
496
+ * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa
497
+ * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro
498
+ * resto do SDK.
499
+ */
500
+ declare class MessagingResource extends BaseResource {
501
+ listConversations(clientId: string, filters?: ListConversationsFilters): Promise<MessagingConversationsListResponse>;
502
+ getConversation(clientId: string, conversationId: string): Promise<MessagingConversation>;
503
+ patchConversation(clientId: string, conversationId: string, payload: PatchConversationPayload): Promise<MessagingConversation>;
504
+ getMessages(clientId: string, conversationId: string, params?: {
505
+ page?: number;
506
+ limit?: number;
507
+ }): Promise<MessagingMessagesListResponse>;
508
+ sendMessage(clientId: string, conversationId: string, payload: SendMessagePayload): Promise<SendMessageResponse>;
509
+ /**
510
+ * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no
511
+ * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`
512
+ * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o
513
+ * registro de mídia local pra conexões Cloud API).
514
+ */
515
+ uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse>;
516
+ }
517
+
324
518
  declare class PortalClient {
325
519
  config: PortalClientConfig;
326
520
  httpClient: AxiosInstance;
327
521
  clients: ClientsResource;
328
522
  knowledge: KnowledgeResource;
523
+ messaging: MessagingResource;
329
524
  constructor(config: PortalClientConfig);
330
525
  }
331
526
 
@@ -335,4 +530,4 @@ declare class PortalApiError extends Error {
335
530
  constructor(message: string, info?: Partial<PortalApiError>);
336
531
  }
337
532
 
338
- export { type AiActivationSchedule, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type DateException, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type LossReasonMapping, type NudgeSettings, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type StageMapping, type TimeRange, type Wazzup, type WazzupChannel };
533
+ export { type AiActivationSchedule, type AiCareStatus, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type AiResumeMode, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type DateException, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type ListConversationsFilters, type LossReasonMapping, type MessagingContact, type MessagingConversation, type MessagingConversationsListResponse, type MessagingMessage, type MessagingMessagesListResponse, type MessagingPhoneNumber, MessagingResource, type NudgeSettings, type PatchConversationPayload, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type SendMessagePayload, type SendMessageResponse, type StageMapping, type TimeRange, type UploadMediaPayload, type UploadMediaResponse, type Wazzup, type WazzupChannel };
package/dist/index.d.ts CHANGED
@@ -94,6 +94,14 @@ interface AiConfig {
94
94
  untaggedLeadAiState?: 'inactive' | 'active' | 'active_for_new_leads';
95
95
  /** Estado da IA após handoff. 'inactive' = trava permanente e global (nunca reativa sozinha). 'untag' = remove a tag, elegível pro bootstrap na próxima mensagem. */
96
96
  handoffAiState?: 'inactive' | 'untag';
97
+ /** Canal WhatsApp direto (aiatendeWa) — domínio separado das tags Kommo acima, usa Conversation.aiCareStatus/aiResumeMode. Se true (default), conversas novas nesse canal já nascem com aiCareStatus ACTIVE; se false, nascem INACTIVE e exigem ativação manual pelo Portal. */
98
+ aiatendeWaDefaultActive?: boolean;
99
+ /** Canal aiatendeWa: responder em áudio (TTS) quando o lead mandar uma mensagem de áudio. Default true. */
100
+ audioReplyOnInboundAudio?: boolean;
101
+ /** Canal aiatendeWa: responder em áudio em vez de texto quando a resposta gerada passar desse tamanho (caracteres). `undefined`/`null`/`0` desliga esse gatilho específico. Default 300. */
102
+ audioReplyCharThreshold?: number | null;
103
+ /** Canal aiatendeWa: id bruto da voz de TTS escolhida — seu significado depende de qual provider `model` indica no momento do envio (OpenAI e Gemini têm listas de vozes totalmente diferentes). */
104
+ ttsVoice?: string | null;
97
105
  /** Ativa a Reflection Agent (segunda passada de revisão/auditoria da resposta) pra este cliente. Ausente/undefined = usa o fallback global (env USE_REFLECTION_AGENT) no atende360-assistant — ver Orchestrator.ts. */
98
106
  reflectionAgentEnabled?: boolean;
99
107
  /** Limiar mínimo de similaridade (cosine, 0-1) pra um trecho da base de conhecimento entrar no contexto do RAG. `undefined`/`null` = o Portal usa o fallback global 0.5 até o admin calibrar via RAG eval. Aplicado internamente pelo Portal em `POST /api/knowledge/internal/context` — não precisa ser enviado pelo assistant-api. */
@@ -286,6 +294,13 @@ declare class ClientsResource extends BaseResource {
286
294
  getByKommoSubdomain(subdomain: string): Promise<Client | null>;
287
295
  /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */
288
296
  getByWazzupChannelId(channelId: string): Promise<Client | null>;
297
+ /**
298
+ * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e
299
+ * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o
300
+ * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de
301
+ * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.
302
+ */
303
+ getById(clientId: string): Promise<Client | null>;
289
304
  }
290
305
 
291
306
  /** Uma mensagem simplificada do histórico da conversa, enviada pro Portal reescrever a
@@ -321,11 +336,191 @@ declare class KnowledgeResource extends BaseResource {
321
336
  getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult>;
322
337
  }
323
338
 
339
+ type AiCareStatus = 'ACTIVE' | 'INACTIVE' | 'PAUSED';
340
+ type AiResumeMode = 'immediate' | 'next_message';
341
+ interface MessagingContact {
342
+ id: string;
343
+ waId: string;
344
+ profileName: string | null;
345
+ displayName: string | null;
346
+ displayNameSetByName: string | null;
347
+ displayNameSetAt: Date | string | null;
348
+ }
349
+ interface MessagingPhoneNumber {
350
+ id: string;
351
+ phoneNumberId: string;
352
+ displayNumber: string;
353
+ metaConnection: {
354
+ channel: string;
355
+ };
356
+ }
357
+ interface MessagingConversation {
358
+ id: string;
359
+ contactId: string;
360
+ phoneNumberId: string;
361
+ lastMessageAt: Date | string | null;
362
+ contact: MessagingContact;
363
+ phoneNumber: MessagingPhoneNumber;
364
+ _count: {
365
+ messages: number;
366
+ };
367
+ assignedToId: string | null;
368
+ assignedTo: {
369
+ id: string;
370
+ name: string;
371
+ email: string;
372
+ } | null;
373
+ lastMessage: Record<string, unknown> | null;
374
+ lastInboundAt: Date | string | null;
375
+ lastOutboundAt: Date | string | null;
376
+ temperature: 'HOT' | 'WARM' | 'COLD' | null;
377
+ aiCareStatus: AiCareStatus;
378
+ aiCareStatusSetAt: Date | string | null;
379
+ aiCareStatusSetByName: string | null;
380
+ /** Só presente na resposta do GET de detalhe (não na listagem). */
381
+ aiResumeMode?: AiResumeMode | null;
382
+ [key: string]: unknown;
383
+ }
384
+ interface MessagingConversationsListResponse {
385
+ data: MessagingConversation[];
386
+ meta: {
387
+ total: number;
388
+ page: number;
389
+ limit: number;
390
+ totalPages: number;
391
+ };
392
+ }
393
+ interface MessagingMessage {
394
+ id: string;
395
+ externalId: string;
396
+ conversationId: string;
397
+ direction: 'inbound' | 'outbound';
398
+ type: string;
399
+ status: string;
400
+ content: Record<string, unknown>;
401
+ waMessageId: string | null;
402
+ media: unknown[];
403
+ sentByType: 'platform_user' | 'api_key' | 'external_agent' | 'whatsapp_app' | null;
404
+ sentById: string | null;
405
+ sentByName: string | null;
406
+ error: string | null;
407
+ errorCode: string | null;
408
+ errorDetails: unknown;
409
+ createdAt: Date | string;
410
+ updatedAt: Date | string;
411
+ }
412
+ interface MessagingMessagesListResponse {
413
+ data: MessagingMessage[];
414
+ meta: {
415
+ total: number;
416
+ page: number;
417
+ limit: number;
418
+ totalPages: number;
419
+ };
420
+ }
421
+ interface ListConversationsFilters {
422
+ page?: number;
423
+ limit?: number;
424
+ assignedToId?: string | 'unassigned';
425
+ phoneNumberId?: string;
426
+ contactWaId?: string;
427
+ contactName?: string;
428
+ createdFrom?: string;
429
+ createdTo?: string;
430
+ lastMessageFrom?: string;
431
+ lastMessageTo?: string;
432
+ sortBy?: 'default' | 'createdAt' | 'lastMessageAt' | 'contactName';
433
+ sortDirection?: 'asc' | 'desc';
434
+ }
435
+ interface PatchConversationPayload {
436
+ assignedToId?: string | null;
437
+ contactDisplayName?: string | null;
438
+ aiCareStatus?: AiCareStatus;
439
+ /** Só tem efeito quando `aiCareStatus` é 'ACTIVE' — em qualquer outro status o Portal força `null`. */
440
+ aiResumeMode?: AiResumeMode | null;
441
+ }
442
+ /**
443
+ * `actor` só é respeitado pelo Portal quando o chamador é um papel de sistema/staff (ex.: o
444
+ * atende360-assistant autenticado via APP_API_KEY) — um usuário comum (OWNER/MEMBER) sempre
445
+ * manda como si mesmo, a API ignora um `actor` customizado nesse caso.
446
+ */
447
+ interface SendMessagePayload {
448
+ phoneNumberId: string;
449
+ to: string;
450
+ type: string;
451
+ content: Record<string, unknown> & {
452
+ id?: string;
453
+ link?: string;
454
+ _media?: {
455
+ storagePath?: string | null;
456
+ mimeType?: string;
457
+ fileSize?: number;
458
+ };
459
+ };
460
+ replyToWaMessageId?: string;
461
+ actor?: {
462
+ externalUserId: string;
463
+ externalUserName?: string;
464
+ };
465
+ }
466
+ interface SendMessageResponse {
467
+ messageId: string;
468
+ conversationId: string;
469
+ status: string;
470
+ correlationId: string;
471
+ }
472
+ interface UploadMediaPayload {
473
+ /** Bytes do arquivo (buffer do áudio/imagem/etc. gerado ou recebido). */
474
+ file: Buffer;
475
+ filename: string;
476
+ mimeType: string;
477
+ phoneNumberId: string;
478
+ /**
479
+ * true faz a plataforma transcodificar pra OGG/Opus mono e renderizar como balão de
480
+ * mensagem de voz no WhatsApp (em vez de anexo de áudio genérico) — usar sempre que for
481
+ * uma resposta de TTS da IA.
482
+ */
483
+ isVoiceNote?: boolean;
484
+ }
485
+ interface UploadMediaResponse {
486
+ id: string | null;
487
+ storagePath: string | null;
488
+ filename: string;
489
+ mimeType: string;
490
+ fileSize: number;
491
+ }
492
+
493
+ /**
494
+ * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`
495
+ * do Portal, que por sua vez falam com a plataforma de mensageria própria
496
+ * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa
497
+ * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro
498
+ * resto do SDK.
499
+ */
500
+ declare class MessagingResource extends BaseResource {
501
+ listConversations(clientId: string, filters?: ListConversationsFilters): Promise<MessagingConversationsListResponse>;
502
+ getConversation(clientId: string, conversationId: string): Promise<MessagingConversation>;
503
+ patchConversation(clientId: string, conversationId: string, payload: PatchConversationPayload): Promise<MessagingConversation>;
504
+ getMessages(clientId: string, conversationId: string, params?: {
505
+ page?: number;
506
+ limit?: number;
507
+ }): Promise<MessagingMessagesListResponse>;
508
+ sendMessage(clientId: string, conversationId: string, payload: SendMessagePayload): Promise<SendMessageResponse>;
509
+ /**
510
+ * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no
511
+ * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`
512
+ * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o
513
+ * registro de mídia local pra conexões Cloud API).
514
+ */
515
+ uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse>;
516
+ }
517
+
324
518
  declare class PortalClient {
325
519
  config: PortalClientConfig;
326
520
  httpClient: AxiosInstance;
327
521
  clients: ClientsResource;
328
522
  knowledge: KnowledgeResource;
523
+ messaging: MessagingResource;
329
524
  constructor(config: PortalClientConfig);
330
525
  }
331
526
 
@@ -335,4 +530,4 @@ declare class PortalApiError extends Error {
335
530
  constructor(message: string, info?: Partial<PortalApiError>);
336
531
  }
337
532
 
338
- export { type AiActivationSchedule, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type DateException, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type LossReasonMapping, type NudgeSettings, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type StageMapping, type TimeRange, type Wazzup, type WazzupChannel };
533
+ export { type AiActivationSchedule, type AiCareStatus, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type AiResumeMode, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type DateException, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type ListConversationsFilters, type LossReasonMapping, type MessagingContact, type MessagingConversation, type MessagingConversationsListResponse, type MessagingMessage, type MessagingMessagesListResponse, type MessagingPhoneNumber, MessagingResource, type NudgeSettings, type PatchConversationPayload, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type SendMessagePayload, type SendMessageResponse, type StageMapping, type TimeRange, type UploadMediaPayload, type UploadMediaResponse, type Wazzup, type WazzupChannel };
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ClientsResource: () => ClientsResource,
34
34
  KnowledgeResource: () => KnowledgeResource,
35
+ MessagingResource: () => MessagingResource,
35
36
  PortalApiError: () => PortalApiError,
36
37
  PortalClient: () => PortalClient
37
38
  });
@@ -48,6 +49,20 @@ var BaseResource = class {
48
49
  }
49
50
  };
50
51
 
52
+ // src/errors.ts
53
+ var PortalApiError = class extends Error {
54
+ status;
55
+ raw;
56
+ constructor(message, info) {
57
+ super(message);
58
+ this.name = "PortalApiError";
59
+ if (info) {
60
+ this.status = info.status;
61
+ this.raw = info.raw;
62
+ }
63
+ }
64
+ };
65
+
51
66
  // src/resources/clients/index.ts
52
67
  var ClientsResource = class extends BaseResource {
53
68
  /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */
@@ -64,26 +79,25 @@ var ClientsResource = class extends BaseResource {
64
79
  });
65
80
  return response.data.data[0] ?? null;
66
81
  }
67
- };
68
-
69
- // src/resources/knowledge/index.ts
70
- var import_zod = require("zod");
71
-
72
- // src/errors.ts
73
- var PortalApiError = class extends Error {
74
- status;
75
- raw;
76
- constructor(message, info) {
77
- super(message);
78
- this.name = "PortalApiError";
79
- if (info) {
80
- this.status = info.status;
81
- this.raw = info.raw;
82
+ /**
83
+ * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e
84
+ * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o
85
+ * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de
86
+ * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.
87
+ */
88
+ async getById(clientId) {
89
+ try {
90
+ const response = await this.client.get(`/api/clients/${clientId}`);
91
+ return response.data;
92
+ } catch (error) {
93
+ if (error instanceof PortalApiError && error.status === 404) return null;
94
+ throw error;
82
95
  }
83
96
  }
84
97
  };
85
98
 
86
99
  // src/resources/knowledge/index.ts
100
+ var import_zod = require("zod");
87
101
  var internalContextChunkSchema = import_zod.z.object({
88
102
  chunkId: import_zod.z.string().optional(),
89
103
  content: import_zod.z.string(),
@@ -109,6 +123,65 @@ var KnowledgeResource = class extends BaseResource {
109
123
  }
110
124
  };
111
125
 
126
+ // src/resources/messaging/index.ts
127
+ var MessagingResource = class extends BaseResource {
128
+ async listConversations(clientId, filters) {
129
+ const response = await this.client.get(
130
+ `/api/clients/${clientId}/messaging/conversations`,
131
+ { params: filters }
132
+ );
133
+ return response.data;
134
+ }
135
+ async getConversation(clientId, conversationId) {
136
+ const response = await this.client.get(
137
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}`
138
+ );
139
+ return response.data;
140
+ }
141
+ async patchConversation(clientId, conversationId, payload) {
142
+ const response = await this.client.patch(
143
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}`,
144
+ payload
145
+ );
146
+ return response.data;
147
+ }
148
+ async getMessages(clientId, conversationId, params) {
149
+ const response = await this.client.get(
150
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,
151
+ { params }
152
+ );
153
+ return response.data;
154
+ }
155
+ async sendMessage(clientId, conversationId, payload) {
156
+ const response = await this.client.post(
157
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,
158
+ payload
159
+ );
160
+ return response.data;
161
+ }
162
+ /**
163
+ * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no
164
+ * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`
165
+ * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o
166
+ * registro de mídia local pra conexões Cloud API).
167
+ */
168
+ async uploadMedia(clientId, payload) {
169
+ const form = new FormData();
170
+ form.append("phoneNumberId", payload.phoneNumberId);
171
+ if (payload.isVoiceNote) form.append("isVoiceNote", "true");
172
+ form.append(
173
+ "file",
174
+ new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),
175
+ payload.filename
176
+ );
177
+ const response = await this.client.post(
178
+ `/api/clients/${clientId}/messaging/media`,
179
+ form
180
+ );
181
+ return response.data;
182
+ }
183
+ };
184
+
112
185
  // src/client.ts
113
186
  var DEFAULT_BASE_URL = "https://portal.aiatende.com.br";
114
187
  var PortalClient = class {
@@ -116,6 +189,7 @@ var PortalClient = class {
116
189
  httpClient;
117
190
  clients;
118
191
  knowledge;
192
+ messaging;
119
193
  constructor(config) {
120
194
  this.config = config;
121
195
  this.httpClient = import_axios.default.create({
@@ -141,12 +215,14 @@ var PortalClient = class {
141
215
  );
142
216
  this.clients = new ClientsResource(this.httpClient);
143
217
  this.knowledge = new KnowledgeResource(this.httpClient);
218
+ this.messaging = new MessagingResource(this.httpClient);
144
219
  }
145
220
  };
146
221
  // Annotate the CommonJS export names for ESM import in node:
147
222
  0 && (module.exports = {
148
223
  ClientsResource,
149
224
  KnowledgeResource,
225
+ MessagingResource,
150
226
  PortalApiError,
151
227
  PortalClient
152
228
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/base.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/errors.ts"],"sourcesContent":["export * from './client';\nexport * from './types';\nexport * from './errors';\nexport * from './resources/clients';\nexport * from './resources/clients/types';\nexport * from './resources/knowledge';\nexport * from './resources/knowledge/types';\n","import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","import { BaseResource } from '../../base';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAqC;;;ACE9B,IAAe,eAAf,MAA4B;AAAA,EACvB;AAAA,EAEV,YAAY,QAAuB;AACjC,SAAK,SAAS;AAAA,EAChB;AACF;;;ACLO,IAAM,kBAAN,cAA8B,aAAa;AAAA;AAAA,EAEhD,MAAM,oBAAoB,WAA2C;AACnE,UAAM,WAAW,MAAM,KAAK,OAAO,IAAyB,gBAAgB;AAAA,MAC1E,QAAQ,EAAE,iBAAiB,WAAW,gBAAgB,KAAK;AAAA,IAC7D,CAAC;AACD,WAAO,SAAS,KAAK,KAAK,CAAC,KAAK;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,qBAAqB,WAA2C;AACpE,UAAM,WAAW,MAAM,KAAK,OAAO,IAAyB,gBAAgB;AAAA,MAC1E,QAAQ,EAAE,WAAW,gBAAgB,KAAK;AAAA,IAC5C,CAAC;AACD,WAAO,SAAS,KAAK,KAAK,CAAC,KAAK;AAAA,EAClC;AACF;;;ACnBA,iBAAkB;;;ACAX,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,MAAgC;AAC3D,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,QAAI,MAAM;AACR,WAAK,SAAS,KAAK;AACnB,WAAK,MAAM,KAAK;AAAA,IAClB;AAAA,EACF;AACF;;;ADRA,IAAM,6BAA6B,aAAE,OAAO;AAAA,EAC1C,SAAS,aAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,SAAS,aAAE,OAAO;AAAA,EAClB,YAAY,aAAE,OAAO;AAAA,EACrB,UAAU,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,QAAQ,CAAC,EAAE,SAAS;AACvD,CAAC;AAED,IAAM,8BAA8B,aAAE,MAAM,0BAA0B;AAE/D,IAAM,oBAAN,cAAgC,aAAa;AAAA;AAAA,EAElD,MAAM,mBAAmB,QAAkE;AACzF,UAAM,WAAW,MAAM,KAAK,OAAO,KAAc,mCAAmC;AAAA,MAClF,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,qBAAqB,OAAO;AAAA,IAC9B,CAAC;AAED,UAAM,SAAS,4BAA4B,UAAU,SAAS,IAAI;AAClE,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,eAAe,2DAAwD;AAAA,QAC/E,KAAK,OAAO,MAAM,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AACA,WAAO,OAAO;AAAA,EAChB;AACF;;;AHzBA,IAAM,mBAAmB;AAElB,IAAM,eAAN,MAAmB;AAAA,EACjB;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EAEP,YAAY,QAA4B;AACtC,SAAK,SAAS;AAEd,SAAK,aAAa,aAAAA,QAAM,OAAO;AAAA,MAC7B,SAAS,OAAO,WAAW;AAAA,MAC3B,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,OAAO;AAAA,QACpB,oBAAoB,OAAO;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,SAAK,WAAW,aAAa,SAAS;AAAA,MACpC,CAAC,aAAa;AAAA,MACd,CAAC,UAAU;AACT,YAAI,MAAM,UAAU;AAClB,gBAAM,UAAU,MAAM,SAAS;AAC/B,gBAAM,IAAI,eAAe,SAAS,SAAS,oBAAoB;AAAA,YAC7D,QAAQ,MAAM,SAAS;AAAA,YACvB,KAAK;AAAA,UACP,CAAC;AAAA,QACH;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,SAAK,UAAU,IAAI,gBAAgB,KAAK,UAAU;AAClD,SAAK,YAAY,IAAI,kBAAkB,KAAK,UAAU;AAAA,EACxD;AACF;","names":["axios"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/base.ts","../src/errors.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/resources/messaging/index.ts"],"sourcesContent":["export * from './client';\nexport * from './types';\nexport * from './errors';\nexport * from './resources/clients';\nexport * from './resources/clients/types';\nexport * from './resources/knowledge';\nexport * from './resources/knowledge/types';\nexport * from './resources/messaging';\nexport * from './resources/messaging/types';\n","import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { MessagingResource } from './resources/messaging';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n public messaging: MessagingResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n this.messaging = new MessagingResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n","import { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /**\n * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e\n * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o\n * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de\n * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.\n */\n async getById(clientId: string): Promise<Client | null> {\n try {\n const response = await this.client.get<Client>(`/api/clients/${clientId}`);\n return response.data;\n } catch (error) {\n if (error instanceof PortalApiError && error.status === 404) return null;\n throw error;\n }\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","import { BaseResource } from '../../base';\nimport {\n ListConversationsFilters,\n MessagingConversation,\n MessagingConversationsListResponse,\n MessagingMessagesListResponse,\n PatchConversationPayload,\n SendMessagePayload,\n SendMessageResponse,\n UploadMediaPayload,\n UploadMediaResponse,\n} from './types';\n\n/**\n * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`\n * do Portal, que por sua vez falam com a plataforma de mensageria própria\n * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa\n * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro\n * resto do SDK.\n */\nexport class MessagingResource extends BaseResource {\n async listConversations(\n clientId: string,\n filters?: ListConversationsFilters,\n ): Promise<MessagingConversationsListResponse> {\n const response = await this.client.get<MessagingConversationsListResponse>(\n `/api/clients/${clientId}/messaging/conversations`,\n { params: filters },\n );\n return response.data;\n }\n\n async getConversation(clientId: string, conversationId: string): Promise<MessagingConversation> {\n const response = await this.client.get<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n );\n return response.data;\n }\n\n async patchConversation(\n clientId: string,\n conversationId: string,\n payload: PatchConversationPayload,\n ): Promise<MessagingConversation> {\n const response = await this.client.patch<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n payload,\n );\n return response.data;\n }\n\n async getMessages(\n clientId: string,\n conversationId: string,\n params?: { page?: number; limit?: number },\n ): Promise<MessagingMessagesListResponse> {\n const response = await this.client.get<MessagingMessagesListResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n { params },\n );\n return response.data;\n }\n\n async sendMessage(\n clientId: string,\n conversationId: string,\n payload: SendMessagePayload,\n ): Promise<SendMessageResponse> {\n const response = await this.client.post<SendMessageResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no\n * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`\n * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o\n * registro de mídia local pra conexões Cloud API).\n */\n async uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse> {\n const form = new FormData();\n // Ordem importa: phoneNumberId/isVoiceNote precisam vir ANTES de file no multipart — o\n // parser do Portal (e o da plataforma por trás dele) lê em stream.\n form.append('phoneNumberId', payload.phoneNumberId);\n if (payload.isVoiceNote) form.append('isVoiceNote', 'true');\n form.append(\n 'file',\n new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),\n payload.filename,\n );\n\n const response = await this.client.post<UploadMediaResponse>(\n `/api/clients/${clientId}/messaging/media`,\n form,\n );\n return response.data;\n }\n}\n"],"mappings":"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","names":["axios"]}
package/dist/index.mjs CHANGED
@@ -9,6 +9,20 @@ var BaseResource = class {
9
9
  }
10
10
  };
11
11
 
12
+ // src/errors.ts
13
+ var PortalApiError = class extends Error {
14
+ status;
15
+ raw;
16
+ constructor(message, info) {
17
+ super(message);
18
+ this.name = "PortalApiError";
19
+ if (info) {
20
+ this.status = info.status;
21
+ this.raw = info.raw;
22
+ }
23
+ }
24
+ };
25
+
12
26
  // src/resources/clients/index.ts
13
27
  var ClientsResource = class extends BaseResource {
14
28
  /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */
@@ -25,26 +39,25 @@ var ClientsResource = class extends BaseResource {
25
39
  });
26
40
  return response.data.data[0] ?? null;
27
41
  }
28
- };
29
-
30
- // src/resources/knowledge/index.ts
31
- import { z } from "zod";
32
-
33
- // src/errors.ts
34
- var PortalApiError = class extends Error {
35
- status;
36
- raw;
37
- constructor(message, info) {
38
- super(message);
39
- this.name = "PortalApiError";
40
- if (info) {
41
- this.status = info.status;
42
- this.raw = info.raw;
42
+ /**
43
+ * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e
44
+ * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o
45
+ * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de
46
+ * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.
47
+ */
48
+ async getById(clientId) {
49
+ try {
50
+ const response = await this.client.get(`/api/clients/${clientId}`);
51
+ return response.data;
52
+ } catch (error) {
53
+ if (error instanceof PortalApiError && error.status === 404) return null;
54
+ throw error;
43
55
  }
44
56
  }
45
57
  };
46
58
 
47
59
  // src/resources/knowledge/index.ts
60
+ import { z } from "zod";
48
61
  var internalContextChunkSchema = z.object({
49
62
  chunkId: z.string().optional(),
50
63
  content: z.string(),
@@ -70,6 +83,65 @@ var KnowledgeResource = class extends BaseResource {
70
83
  }
71
84
  };
72
85
 
86
+ // src/resources/messaging/index.ts
87
+ var MessagingResource = class extends BaseResource {
88
+ async listConversations(clientId, filters) {
89
+ const response = await this.client.get(
90
+ `/api/clients/${clientId}/messaging/conversations`,
91
+ { params: filters }
92
+ );
93
+ return response.data;
94
+ }
95
+ async getConversation(clientId, conversationId) {
96
+ const response = await this.client.get(
97
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}`
98
+ );
99
+ return response.data;
100
+ }
101
+ async patchConversation(clientId, conversationId, payload) {
102
+ const response = await this.client.patch(
103
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}`,
104
+ payload
105
+ );
106
+ return response.data;
107
+ }
108
+ async getMessages(clientId, conversationId, params) {
109
+ const response = await this.client.get(
110
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,
111
+ { params }
112
+ );
113
+ return response.data;
114
+ }
115
+ async sendMessage(clientId, conversationId, payload) {
116
+ const response = await this.client.post(
117
+ `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,
118
+ payload
119
+ );
120
+ return response.data;
121
+ }
122
+ /**
123
+ * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no
124
+ * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`
125
+ * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o
126
+ * registro de mídia local pra conexões Cloud API).
127
+ */
128
+ async uploadMedia(clientId, payload) {
129
+ const form = new FormData();
130
+ form.append("phoneNumberId", payload.phoneNumberId);
131
+ if (payload.isVoiceNote) form.append("isVoiceNote", "true");
132
+ form.append(
133
+ "file",
134
+ new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),
135
+ payload.filename
136
+ );
137
+ const response = await this.client.post(
138
+ `/api/clients/${clientId}/messaging/media`,
139
+ form
140
+ );
141
+ return response.data;
142
+ }
143
+ };
144
+
73
145
  // src/client.ts
74
146
  var DEFAULT_BASE_URL = "https://portal.aiatende.com.br";
75
147
  var PortalClient = class {
@@ -77,6 +149,7 @@ var PortalClient = class {
77
149
  httpClient;
78
150
  clients;
79
151
  knowledge;
152
+ messaging;
80
153
  constructor(config) {
81
154
  this.config = config;
82
155
  this.httpClient = axios.create({
@@ -102,11 +175,13 @@ var PortalClient = class {
102
175
  );
103
176
  this.clients = new ClientsResource(this.httpClient);
104
177
  this.knowledge = new KnowledgeResource(this.httpClient);
178
+ this.messaging = new MessagingResource(this.httpClient);
105
179
  }
106
180
  };
107
181
  export {
108
182
  ClientsResource,
109
183
  KnowledgeResource,
184
+ MessagingResource,
110
185
  PortalApiError,
111
186
  PortalClient
112
187
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/base.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/errors.ts"],"sourcesContent":["import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","import { BaseResource } from '../../base';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n"],"mappings":";AAAA,OAAO,WAA8B;;;ACE9B,IAAe,eAAf,MAA4B;AAAA,EACvB;AAAA,EAEV,YAAY,QAAuB;AACjC,SAAK,SAAS;AAAA,EAChB;AACF;;;ACLO,IAAM,kBAAN,cAA8B,aAAa;AAAA;AAAA,EAEhD,MAAM,oBAAoB,WAA2C;AACnE,UAAM,WAAW,MAAM,KAAK,OAAO,IAAyB,gBAAgB;AAAA,MAC1E,QAAQ,EAAE,iBAAiB,WAAW,gBAAgB,KAAK;AAAA,IAC7D,CAAC;AACD,WAAO,SAAS,KAAK,KAAK,CAAC,KAAK;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,qBAAqB,WAA2C;AACpE,UAAM,WAAW,MAAM,KAAK,OAAO,IAAyB,gBAAgB;AAAA,MAC1E,QAAQ,EAAE,WAAW,gBAAgB,KAAK;AAAA,IAC5C,CAAC;AACD,WAAO,SAAS,KAAK,KAAK,CAAC,KAAK;AAAA,EAClC;AACF;;;ACnBA,SAAS,SAAS;;;ACAX,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,MAAgC;AAC3D,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,QAAI,MAAM;AACR,WAAK,SAAS,KAAK;AACnB,WAAK,MAAM,KAAK;AAAA,IAClB;AAAA,EACF;AACF;;;ADRA,IAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,SAAS,EAAE,OAAO;AAAA,EAClB,YAAY,EAAE,OAAO;AAAA,EACrB,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AACvD,CAAC;AAED,IAAM,8BAA8B,EAAE,MAAM,0BAA0B;AAE/D,IAAM,oBAAN,cAAgC,aAAa;AAAA;AAAA,EAElD,MAAM,mBAAmB,QAAkE;AACzF,UAAM,WAAW,MAAM,KAAK,OAAO,KAAc,mCAAmC;AAAA,MAClF,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,qBAAqB,OAAO;AAAA,IAC9B,CAAC;AAED,UAAM,SAAS,4BAA4B,UAAU,SAAS,IAAI;AAClE,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,eAAe,2DAAwD;AAAA,QAC/E,KAAK,OAAO,MAAM,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AACA,WAAO,OAAO;AAAA,EAChB;AACF;;;AHzBA,IAAM,mBAAmB;AAElB,IAAM,eAAN,MAAmB;AAAA,EACjB;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EAEP,YAAY,QAA4B;AACtC,SAAK,SAAS;AAEd,SAAK,aAAa,MAAM,OAAO;AAAA,MAC7B,SAAS,OAAO,WAAW;AAAA,MAC3B,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,OAAO;AAAA,QACpB,oBAAoB,OAAO;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,SAAK,WAAW,aAAa,SAAS;AAAA,MACpC,CAAC,aAAa;AAAA,MACd,CAAC,UAAU;AACT,YAAI,MAAM,UAAU;AAClB,gBAAM,UAAU,MAAM,SAAS;AAC/B,gBAAM,IAAI,eAAe,SAAS,SAAS,oBAAoB;AAAA,YAC7D,QAAQ,MAAM,SAAS;AAAA,YACvB,KAAK;AAAA,UACP,CAAC;AAAA,QACH;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,SAAK,UAAU,IAAI,gBAAgB,KAAK,UAAU;AAClD,SAAK,YAAY,IAAI,kBAAkB,KAAK,UAAU;AAAA,EACxD;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/base.ts","../src/errors.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/resources/messaging/index.ts"],"sourcesContent":["import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { MessagingResource } from './resources/messaging';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n public messaging: MessagingResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n this.messaging = new MessagingResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n","import { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /**\n * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e\n * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o\n * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de\n * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.\n */\n async getById(clientId: string): Promise<Client | null> {\n try {\n const response = await this.client.get<Client>(`/api/clients/${clientId}`);\n return response.data;\n } catch (error) {\n if (error instanceof PortalApiError && error.status === 404) return null;\n throw error;\n }\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","import { BaseResource } from '../../base';\nimport {\n ListConversationsFilters,\n MessagingConversation,\n MessagingConversationsListResponse,\n MessagingMessagesListResponse,\n PatchConversationPayload,\n SendMessagePayload,\n SendMessageResponse,\n UploadMediaPayload,\n UploadMediaResponse,\n} from './types';\n\n/**\n * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`\n * do Portal, que por sua vez falam com a plataforma de mensageria própria\n * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa\n * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro\n * resto do SDK.\n */\nexport class MessagingResource extends BaseResource {\n async listConversations(\n clientId: string,\n filters?: ListConversationsFilters,\n ): Promise<MessagingConversationsListResponse> {\n const response = await this.client.get<MessagingConversationsListResponse>(\n `/api/clients/${clientId}/messaging/conversations`,\n { params: filters },\n );\n return response.data;\n }\n\n async getConversation(clientId: string, conversationId: string): Promise<MessagingConversation> {\n const response = await this.client.get<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n );\n return response.data;\n }\n\n async patchConversation(\n clientId: string,\n conversationId: string,\n payload: PatchConversationPayload,\n ): Promise<MessagingConversation> {\n const response = await this.client.patch<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n payload,\n );\n return response.data;\n }\n\n async getMessages(\n clientId: string,\n conversationId: string,\n params?: { page?: number; limit?: number },\n ): Promise<MessagingMessagesListResponse> {\n const response = await this.client.get<MessagingMessagesListResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n { params },\n );\n return response.data;\n }\n\n async sendMessage(\n clientId: string,\n conversationId: string,\n payload: SendMessagePayload,\n ): Promise<SendMessageResponse> {\n const response = await this.client.post<SendMessageResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no\n * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`\n * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o\n * registro de mídia local pra conexões Cloud API).\n */\n async uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse> {\n const form = new FormData();\n // Ordem importa: phoneNumberId/isVoiceNote precisam vir ANTES de file no multipart — o\n // parser do Portal (e o da plataforma por trás dele) lê em stream.\n form.append('phoneNumberId', payload.phoneNumberId);\n if (payload.isVoiceNote) form.append('isVoiceNote', 'true');\n form.append(\n 'file',\n new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),\n payload.filename,\n );\n\n const response = await this.client.post<UploadMediaResponse>(\n `/api/clients/${clientId}/messaging/media`,\n form,\n );\n return response.data;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portal-aiatende-api",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Cliente TypeScript tipado para a API interna do Portal AI Atende.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",