@geekapps/post-fastify 0.1.11 → 0.1.13

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.ts CHANGED
@@ -28,7 +28,7 @@ type DomainStatus = "pending" | "verified" | "failed";
28
28
  type PlanTier = "free" | "starter" | "pro" | "enterprise";
29
29
  type WhatsappTemplateCategory = "MARKETING" | "UTILITY" | "AUTHENTICATION";
30
30
  type WhatsappTemplateStatus = "APPROVED" | "PENDING" | "REJECTED" | "DISABLED" | "PAUSED";
31
- type WebhookEvent = "message_sent" | "message_delivered" | "message_failed" | "message_bounced" | "message_opened" | "message_clicked";
31
+ type WebhookEvent = "message_sent" | "message_delivered" | "message_failed" | "message_bounced" | "message_opened" | "message_clicked" | "message_received" | "call_connect" | "call_terminate" | "call_status" | "call_permission_reply";
32
32
  type WabaStatus = "active" | "token_expired" | "disconnected";
33
33
  type WhatsappNumberStatus = "pending_verification" | "active" | "inactive" | "failed";
34
34
  type ManagedNumberStatus = "provisioning" | "active" | "waiting_whatsapp_verification" | "whatsapp_active" | "suspended" | "canceled" | "failed";
@@ -370,6 +370,125 @@ interface WabaHealthResult {
370
370
  interface PhoneNumberStatusResult {
371
371
  status: unknown;
372
372
  }
373
+ type CallingStatus = "ENABLED" | "DISABLED";
374
+ interface CallingSettings {
375
+ status: CallingStatus;
376
+ callIconVisibility?: "DEFAULT" | "DISABLE_ALL" | null;
377
+ callbackPermissionStatus?: CallingStatus | null;
378
+ }
379
+ interface UpdateCallingSettingsInput {
380
+ status: CallingStatus;
381
+ callIconVisibility?: "DEFAULT" | "DISABLE_ALL";
382
+ callbackPermissionStatus?: CallingStatus;
383
+ }
384
+ /** Meta phone_number_id (as received in webhooks). Defaults to the org's first active number. */
385
+ interface CallTargetInput {
386
+ phoneNumberId?: string;
387
+ }
388
+ interface CallAnswerInput extends CallTargetInput {
389
+ /** SDP answer to the offer received in `call_connect`. */
390
+ sdp: string;
391
+ bizOpaqueCallbackData?: string;
392
+ }
393
+ interface CallActionResult {
394
+ ok: true;
395
+ callId: string;
396
+ action: "pre_accept" | "accept" | "reject" | "terminate";
397
+ }
398
+ interface ConnectCallInput extends CallTargetInput {
399
+ to: string;
400
+ /** SDP offer. The answer arrives in the `call_connect` webhook. */
401
+ sdp: string;
402
+ bizOpaqueCallbackData?: string;
403
+ }
404
+ interface ConnectCallResult {
405
+ callId: string;
406
+ phoneNumberId: string;
407
+ }
408
+ interface RequestCallPermissionInput extends CallTargetInput {
409
+ to: string;
410
+ body?: string;
411
+ }
412
+ interface RequestCallPermissionResult {
413
+ messageId: string;
414
+ phoneNumberId: string;
415
+ }
416
+ /** Raw Meta response from GET /{phone-number-id}/call_permissions. */
417
+ type CallPermissionStatus = Record<string, unknown>;
418
+ interface WebhookSdpSession {
419
+ sdp_type: "offer" | "answer";
420
+ sdp: string;
421
+ }
422
+ interface CallWebhookBase {
423
+ phoneNumberId: string;
424
+ displayPhoneNumber: string | null;
425
+ callId: string;
426
+ timestamp: string;
427
+ bizOpaqueCallbackData: string | null;
428
+ }
429
+ interface CallConnectWebhookData extends CallWebhookBase {
430
+ from: string;
431
+ to: string;
432
+ contactName: string | null;
433
+ direction: "USER_INITIATED" | "BUSINESS_INITIATED" | null;
434
+ session: WebhookSdpSession | null;
435
+ }
436
+ interface CallTerminateWebhookData extends CallWebhookBase {
437
+ from: string;
438
+ to: string;
439
+ direction: "USER_INITIATED" | "BUSINESS_INITIATED" | null;
440
+ status: string | null;
441
+ startTime: string | null;
442
+ endTime: string | null;
443
+ duration: number | null;
444
+ }
445
+ interface CallStatusWebhookData extends CallWebhookBase {
446
+ status: string;
447
+ recipientId: string | null;
448
+ }
449
+ interface MessageReceivedWebhookData {
450
+ phoneNumberId: string;
451
+ displayPhoneNumber: string | null;
452
+ channel: "whatsapp";
453
+ messageId: string;
454
+ from: string;
455
+ contactName: string | null;
456
+ type: string;
457
+ timestamp: string;
458
+ /** Raw Meta message object. */
459
+ message: Record<string, unknown>;
460
+ }
461
+ type PostWebhookPayload = {
462
+ event: "call_connect";
463
+ data: CallConnectWebhookData;
464
+ timestamp: number;
465
+ } | {
466
+ event: "call_terminate";
467
+ data: CallTerminateWebhookData;
468
+ timestamp: number;
469
+ } | {
470
+ event: "call_status";
471
+ data: CallStatusWebhookData;
472
+ timestamp: number;
473
+ } | {
474
+ event: "message_received" | "call_permission_reply";
475
+ data: MessageReceivedWebhookData;
476
+ timestamp: number;
477
+ } | {
478
+ event: Exclude<WebhookEvent, "call_connect" | "call_terminate" | "call_status" | "message_received" | "call_permission_reply">;
479
+ data: Record<string, unknown>;
480
+ timestamp: number;
481
+ };
482
+ interface MessagePresenceInput {
483
+ /** `messageId` da mensagem recebida (webhook `message_received`). */
484
+ messageId: string;
485
+ /** Meta phone_number_id. Padrão: primeiro número ativo da org. */
486
+ phoneNumberId?: string;
487
+ }
488
+ interface MessagePresenceResult {
489
+ success: boolean;
490
+ phoneNumberId: string;
491
+ }
373
492
 
374
493
  declare class DomainsResource {
375
494
  private readonly http;
@@ -420,6 +539,10 @@ declare class MessagesResource {
420
539
  private readonly http;
421
540
  constructor(http: HttpClient);
422
541
  send(input: SendMessageInput): Promise<SendMessageResult>;
542
+ /** Marca uma mensagem recebida como lida (tique azul). */
543
+ markAsRead(input: MessagePresenceInput): Promise<MessagePresenceResult>;
544
+ /** Marca como lida e mostra "digitando…" até a resposta ser enviada (máx. ~25s). */
545
+ sendTyping(input: MessagePresenceInput): Promise<MessagePresenceResult>;
423
546
  }
424
547
 
425
548
  declare class PlansResource {
@@ -474,6 +597,29 @@ declare class WebhooksResource {
474
597
  rotateSecret(id: string): Promise<RotateSecretResult>;
475
598
  }
476
599
 
600
+ declare class WhatsappCallsResource {
601
+ private readonly http;
602
+ constructor(http: HttpClient);
603
+ /** Lê a configuração de chamadas de um número (id do WhatsappNumber no Post). */
604
+ getSettings(numberId: string): Promise<CallingSettings>;
605
+ /** Ativa/desativa chamadas no número. */
606
+ updateSettings(numberId: string, input: UpdateCallingSettingsInput): Promise<CallingSettings>;
607
+ /** Pré-aceita uma chamada recebida (opcional, acelera a conexão de mídia). */
608
+ preAccept(callId: string, input: CallAnswerInput): Promise<CallActionResult>;
609
+ /** Aceita uma chamada recebida enviando o SDP de resposta. */
610
+ accept(callId: string, input: CallAnswerInput): Promise<CallActionResult>;
611
+ /** Rejeita uma chamada recebida. */
612
+ reject(callId: string, input?: CallTargetInput): Promise<CallActionResult>;
613
+ /** Encerra uma chamada em andamento. */
614
+ terminate(callId: string, input?: CallTargetInput): Promise<CallActionResult>;
615
+ /** Inicia uma chamada (business-initiated). Exige permissão prévia do usuário. */
616
+ connect(input: ConnectCallInput): Promise<ConnectCallResult>;
617
+ /** Envia ao usuário o pedido de permissão para receber chamadas. */
618
+ requestPermission(input: RequestCallPermissionInput): Promise<RequestCallPermissionResult>;
619
+ /** Consulta o status de permissão de chamada de um usuário. */
620
+ getPermission(to: string, phoneNumberId?: string): Promise<CallPermissionStatus>;
621
+ }
622
+
477
623
  declare class WhatsappNumbersResource {
478
624
  private readonly http;
479
625
  constructor(http: HttpClient);
@@ -511,6 +657,7 @@ declare class PostClient {
511
657
  readonly templates: TemplatesResource;
512
658
  readonly whatsappTemplates: WhatsappTemplatesResource;
513
659
  readonly whatsappNumbers: WhatsappNumbersResource;
660
+ readonly calls: WhatsappCallsResource;
514
661
  readonly domains: DomainsResource;
515
662
  readonly webhooks: WebhooksResource;
516
663
  readonly smtp: SmtpResource;
@@ -552,4 +699,12 @@ declare module "fastify" {
552
699
  }
553
700
  declare const _default: FastifyPluginAsync<PostPluginOptions>;
554
701
 
555
- export { type AreaCode, type AvailablePlan, type ConnectWabaInput, type ConnectWhatsappNumberInput, type CreateDomainInput, type CreateDomainResult, type CreateTemplateInput, type CreateWebhookInput, type CreateWhatsappTemplateInput, type DnsRecord, type DomainStatus, type DomainType, DomainsResource, HistoryResource, type ListHistoryParams, type ManagedNumber, type ManagedNumberStatus, ManagedNumbersResource, type MessageLog, type MessageLogDetail, type MessageStatus, type MessageTemplate, type MessageType, MessagesResource, type OrgDomain, type OrgPlan, type OrgUsage, type OrgWaba, type OrgWebhook, type OrgWebhookWithSecret, type PaginatedResult, type PhoneNumberStatusResult, type PlanTier, PlansResource, type PlansResult, PostApiError, PostClient, type PostPluginOptions, type RegisterWhatsappNumberInput, type RotateSecretResult, type SaveDraftInput, type SendMessageInput, type SendMessageResult, type SmtpConfig, SmtpResource, type StatisticsParams, StatisticsResource, type StatisticsResult, type SyncWhatsappTemplatesResult, type TemplateVariable, type TemplateVariables, TemplatesResource, type UpdatePlanInput, type UpdateTemplateInput, type UpdateWebhookInput, type UpdateWhatsappProfileInput, type UpdateWhatsappTemplateInput, type UpsertSmtpConfigInput, type WabaStatus, type WebhookDelivery, type WebhookEvent, WebhooksResource, type WhatsappNumber, type WhatsappNumberProfile, type WhatsappNumberStatus, WhatsappNumbersResource, type WhatsappTemplate, type WhatsappTemplateCategory, type WhatsappTemplateStatus, WhatsappTemplatesResource, _default as postPlugin };
702
+ /**
703
+ * Verifies the `X-Post-Signature` header (`sha256=<hex>`) of a Post webhook.
704
+ * `rawBody` must be the exact request body string, before JSON parsing.
705
+ */
706
+ declare function verifyWebhookSignature(rawBody: string, signatureHeader: string | null | undefined, secret: string): boolean;
707
+ /** Verifies the signature and parses the payload; throws if invalid. */
708
+ declare function constructWebhookEvent(rawBody: string, signatureHeader: string | null | undefined, secret: string): PostWebhookPayload;
709
+
710
+ export { type AreaCode, type AvailablePlan, type CallActionResult, type CallAnswerInput, type CallConnectWebhookData, type CallPermissionStatus, type CallStatusWebhookData, type CallTargetInput, type CallTerminateWebhookData, type CallingSettings, type CallingStatus, type ConnectCallInput, type ConnectCallResult, type ConnectWabaInput, type ConnectWhatsappNumberInput, type CreateDomainInput, type CreateDomainResult, type CreateTemplateInput, type CreateWebhookInput, type CreateWhatsappTemplateInput, type DnsRecord, type DomainStatus, type DomainType, DomainsResource, HistoryResource, type ListHistoryParams, type ManagedNumber, type ManagedNumberStatus, ManagedNumbersResource, type MessageLog, type MessageLogDetail, type MessagePresenceInput, type MessagePresenceResult, type MessageReceivedWebhookData, type MessageStatus, type MessageTemplate, type MessageType, MessagesResource, type OrgDomain, type OrgPlan, type OrgUsage, type OrgWaba, type OrgWebhook, type OrgWebhookWithSecret, type PaginatedResult, type PhoneNumberStatusResult, type PlanTier, PlansResource, type PlansResult, PostApiError, PostClient, type PostPluginOptions, type PostWebhookPayload, type RegisterWhatsappNumberInput, type RequestCallPermissionInput, type RequestCallPermissionResult, type RotateSecretResult, type SaveDraftInput, type SendMessageInput, type SendMessageResult, type SmtpConfig, SmtpResource, type StatisticsParams, StatisticsResource, type StatisticsResult, type SyncWhatsappTemplatesResult, type TemplateVariable, type TemplateVariables, TemplatesResource, type UpdateCallingSettingsInput, type UpdatePlanInput, type UpdateTemplateInput, type UpdateWebhookInput, type UpdateWhatsappProfileInput, type UpdateWhatsappTemplateInput, type UpsertSmtpConfigInput, type WabaStatus, type WebhookDelivery, type WebhookEvent, type WebhookSdpSession, WebhooksResource, WhatsappCallsResource, type WhatsappNumber, type WhatsappNumberProfile, type WhatsappNumberStatus, WhatsappNumbersResource, type WhatsappTemplate, type WhatsappTemplateCategory, type WhatsappTemplateStatus, WhatsappTemplatesResource, constructWebhookEvent, _default as postPlugin, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -155,6 +155,14 @@ var MessagesResource = class {
155
155
  send(input) {
156
156
  return this.http.post("/messages", input);
157
157
  }
158
+ /** Marca uma mensagem recebida como lida (tique azul). */
159
+ markAsRead(input) {
160
+ return this.http.post("/whatsapp/messages/read", input);
161
+ }
162
+ /** Marca como lida e mostra "digitando…" até a resposta ser enviada (máx. ~25s). */
163
+ sendTyping(input) {
164
+ return this.http.post("/whatsapp/messages/typing", input);
165
+ }
158
166
  };
159
167
 
160
168
  // src/resources/plans.ts
@@ -264,6 +272,49 @@ var WebhooksResource = class {
264
272
  }
265
273
  };
266
274
 
275
+ // src/resources/whatsapp-calls.ts
276
+ var WhatsappCallsResource = class {
277
+ constructor(http) {
278
+ this.http = http;
279
+ }
280
+ /** Lê a configuração de chamadas de um número (id do WhatsappNumber no Post). */
281
+ getSettings(numberId) {
282
+ return this.http.get(`/whatsapp/numbers/${numberId}/calling`);
283
+ }
284
+ /** Ativa/desativa chamadas no número. */
285
+ updateSettings(numberId, input) {
286
+ return this.http.put(`/whatsapp/numbers/${numberId}/calling`, input);
287
+ }
288
+ /** Pré-aceita uma chamada recebida (opcional, acelera a conexão de mídia). */
289
+ preAccept(callId, input) {
290
+ return this.http.post(`/whatsapp/calls/${callId}/pre-accept`, input);
291
+ }
292
+ /** Aceita uma chamada recebida enviando o SDP de resposta. */
293
+ accept(callId, input) {
294
+ return this.http.post(`/whatsapp/calls/${callId}/accept`, input);
295
+ }
296
+ /** Rejeita uma chamada recebida. */
297
+ reject(callId, input = {}) {
298
+ return this.http.post(`/whatsapp/calls/${callId}/reject`, input);
299
+ }
300
+ /** Encerra uma chamada em andamento. */
301
+ terminate(callId, input = {}) {
302
+ return this.http.post(`/whatsapp/calls/${callId}/terminate`, input);
303
+ }
304
+ /** Inicia uma chamada (business-initiated). Exige permissão prévia do usuário. */
305
+ connect(input) {
306
+ return this.http.post("/whatsapp/calls", input);
307
+ }
308
+ /** Envia ao usuário o pedido de permissão para receber chamadas. */
309
+ requestPermission(input) {
310
+ return this.http.post("/whatsapp/calls/permissions", input);
311
+ }
312
+ /** Consulta o status de permissão de chamada de um usuário. */
313
+ getPermission(to, phoneNumberId) {
314
+ return this.http.get(`/whatsapp/calls/permissions?${new URLSearchParams({ to, ...phoneNumberId ? { phoneNumberId } : {} })}`);
315
+ }
316
+ };
317
+
267
318
  // src/resources/whatsapp-numbers.ts
268
319
  var WhatsappNumbersResource = class {
269
320
  constructor(http) {
@@ -351,6 +402,7 @@ var PostClient = class {
351
402
  this.templates = new TemplatesResource(this.http);
352
403
  this.whatsappTemplates = new WhatsappTemplatesResource(this.http);
353
404
  this.whatsappNumbers = new WhatsappNumbersResource(this.http);
405
+ this.calls = new WhatsappCallsResource(this.http);
354
406
  this.domains = new DomainsResource(this.http);
355
407
  this.webhooks = new WebhooksResource(this.http);
356
408
  this.smtp = new SmtpResource(this.http);
@@ -497,6 +549,21 @@ var plugin_default = fp(postPlugin, {
497
549
  fastify: ">=4.0.0",
498
550
  name: "@geekapps/post-fastify"
499
551
  });
552
+
553
+ // src/webhook.ts
554
+ import { createHmac, timingSafeEqual } from "crypto";
555
+ function verifyWebhookSignature(rawBody, signatureHeader, secret) {
556
+ if (!signatureHeader?.startsWith("sha256=")) return false;
557
+ const expected = Buffer.from(createHmac("sha256", secret).update(rawBody).digest("hex"));
558
+ const received = Buffer.from(signatureHeader.slice("sha256=".length));
559
+ return expected.length === received.length && timingSafeEqual(expected, received);
560
+ }
561
+ function constructWebhookEvent(rawBody, signatureHeader, secret) {
562
+ if (!verifyWebhookSignature(rawBody, signatureHeader, secret)) {
563
+ throw new Error("Invalid Post webhook signature");
564
+ }
565
+ return JSON.parse(rawBody);
566
+ }
500
567
  export {
501
568
  DomainsResource,
502
569
  HistoryResource,
@@ -509,8 +576,11 @@ export {
509
576
  StatisticsResource,
510
577
  TemplatesResource,
511
578
  WebhooksResource,
579
+ WhatsappCallsResource,
512
580
  WhatsappNumbersResource,
513
581
  WhatsappTemplatesResource,
514
- plugin_default as postPlugin
582
+ constructWebhookEvent,
583
+ plugin_default as postPlugin,
584
+ verifyWebhookSignature
515
585
  };
516
586
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/domains.ts","../src/resources/history.ts","../src/resources/managed-numbers.ts","../src/resources/messages.ts","../src/resources/plans.ts","../src/resources/smtp.ts","../src/resources/statistics.ts","../src/resources/templates.ts","../src/resources/webhooks.ts","../src/resources/whatsapp-numbers.ts","../src/resources/whatsapp-templates.ts","../src/client.ts","../src/plugin.ts"],"sourcesContent":["export class PostApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n public readonly detail?: string,\n ) {\n super(detail ? `${message}: ${detail}` : message);\n this.name = \"PostApiError\";\n }\n\n get isNotFound() {\n return this.status === 404;\n }\n\n get isUnauthorized() {\n return this.status === 401;\n }\n\n get isForbidden() {\n return this.status === 403;\n }\n\n get isRateLimited() {\n return this.status === 429;\n }\n\n get isValidationError() {\n return this.status === 400;\n }\n}\n","import { PostApiError } from \"./errors.js\";\r\n\r\nexport type TokenResolver = string | (() => string | Promise<string>);\r\n\r\nexport interface HttpClientOptions {\r\n baseUrl: string;\r\n token: TokenResolver;\r\n organizationId?: string | (() => string | Promise<string>);\r\n}\r\n\r\nexport class HttpClient {\r\n private readonly baseUrl: string;\r\n private readonly token: TokenResolver;\r\n private readonly organizationId?: string | (() => string | Promise<string>);\r\n\r\n constructor(options: HttpClientOptions) {\r\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\r\n this.token = options.token;\r\n this.organizationId = options.organizationId;\r\n }\r\n\r\n private async resolveToken(): Promise<string> {\r\n return typeof this.token === \"function\" ? await this.token() : this.token;\r\n }\r\n\r\n private async resolveOrgId(): Promise<string | undefined> {\r\n if (!this.organizationId) return undefined;\r\n return typeof this.organizationId === \"function\"\r\n ? await this.organizationId()\r\n : this.organizationId;\r\n }\r\n\r\n async request<T = unknown>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n ): Promise<T> {\r\n const token = await this.resolveToken();\r\n const orgId = await this.resolveOrgId();\r\n\r\n const res = await fetch(`${this.baseUrl}/v1${path}`, {\r\n method,\r\n headers: {\r\n Authorization: `Bearer ${token}`,\r\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\r\n ...(orgId ? { \"X-Organization-Id\": orgId } : {}),\r\n },\r\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n cache: \"no-store\" as any,\r\n });\r\n\r\n if (!res.ok) {\r\n const err = await res.json().catch(() => ({})) as {\r\n error?: string;\r\n detail?: string;\r\n };\r\n throw new PostApiError(\r\n res.status,\r\n err.error ?? `HTTP ${res.status}`,\r\n err.detail,\r\n );\r\n }\r\n\r\n if (res.status === 204) return undefined as T;\r\n return res.json() as Promise<T>;\r\n }\r\n\r\n get<T>(path: string) {\r\n return this.request<T>(\"GET\", path);\r\n }\r\n\r\n post<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"POST\", path, body);\r\n }\r\n\r\n put<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"PUT\", path, body);\r\n }\r\n\r\n patch<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"PATCH\", path, body);\r\n }\r\n\r\n delete<T = void>(path: string) {\r\n return this.request<T>(\"DELETE\", path);\r\n }\r\n}\r\n","import type { HttpClient } from \"../http.js\";\nimport type { CreateDomainInput, CreateDomainResult, OrgDomain } from \"../types.js\";\n\nexport class DomainsResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgDomain[]> {\n return this.http.get<OrgDomain[]>(\"/domains\");\n }\n\n get(id: string): Promise<OrgDomain> {\n return this.http.get<OrgDomain>(`/domains/${id}`);\n }\n\n create(input: CreateDomainInput): Promise<CreateDomainResult> {\n return this.http.post<CreateDomainResult>(\"/domains\", input);\n }\n\n verify(id: string): Promise<{ status: \"verified\" | \"failed\" }> {\n return this.http.post<{ status: \"verified\" | \"failed\" }>(`/domains/${id}/verify`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/domains/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { ListHistoryParams, MessageLog, MessageLogDetail, PaginatedResult } from \"../types.js\";\n\nexport class HistoryResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: ListHistoryParams): Promise<PaginatedResult<MessageLog>> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.status) qs.set(\"status\", params.status);\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.page !== undefined) qs.set(\"page\", String(params.page));\n if (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n const query = qs.toString();\n return this.http.get<PaginatedResult<MessageLog>>(`/history${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageLogDetail> {\n return this.http.get<MessageLogDetail>(`/history/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AreaCode, ManagedNumber } from \"../types.js\";\n\nexport interface ProvisionManagedNumberInput {\n areaCode: number;\n name?: string;\n displayName: string;\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n pin?: string;\n orgWabaId?: string;\n wabaId?: string;\n accessToken?: string;\n}\n\nexport interface RetryVerificationInput {\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n}\n\nexport class ManagedNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n listAreaCodes(): Promise<AreaCode[]> {\n return this.http.get<AreaCode[]>(\"/managed-numbers/area-codes\");\n }\n\n list(): Promise<ManagedNumber[]> {\n return this.http.get<ManagedNumber[]>(\"/managed-numbers\");\n }\n\n get(id: string): Promise<ManagedNumber> {\n return this.http.get<ManagedNumber>(`/managed-numbers/${id}`);\n }\n\n provision(input: ProvisionManagedNumberInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(\"/managed-numbers\", input);\n }\n\n retryVerification(id: string, input?: RetryVerificationInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/retry-verification`, input);\n }\n\n cancel(id: string): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/cancel`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SendMessageInput, SendMessageResult } from \"../types.js\";\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n send(input: SendMessageInput): Promise<SendMessageResult> {\n return this.http.post<SendMessageResult>(\"/messages\", input);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AvailablePlan, OrgPlan, PlansResult, UpdatePlanInput } from \"../types.js\";\n\nexport class PlansResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<PlansResult> {\n return this.http.get<PlansResult>(\"/plans\");\n }\n\n update(input: UpdatePlanInput): Promise<OrgPlan> {\n return this.http.put<OrgPlan>(\"/plans\", input);\n }\n\n listAvailable(): Promise<AvailablePlan[]> {\n return this.http.get<AvailablePlan[]>(\"/plans/available\");\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SmtpConfig, UpsertSmtpConfigInput } from \"../types.js\";\n\nexport class SmtpResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<SmtpConfig> {\n return this.http.get<SmtpConfig>(\"/smtp-config\");\n }\n\n upsert(input: UpsertSmtpConfigInput): Promise<SmtpConfig> {\n return this.http.put<SmtpConfig>(\"/smtp-config\", input);\n }\n\n delete(): Promise<void> {\n return this.http.delete(\"/smtp-config\");\n }\n\n test(to: string): Promise<{ success: boolean; error?: string }> {\n return this.http.post<{ success: boolean; error?: string }>(\"/smtp-config/test\", { to });\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { StatisticsParams, StatisticsResult } from \"../types.js\";\n\nexport class StatisticsResource {\n constructor(private readonly http: HttpClient) {}\n\n get(params?: StatisticsParams): Promise<StatisticsResult> {\n const qs = new URLSearchParams();\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.groupBy) qs.set(\"groupBy\", params.groupBy);\n const query = qs.toString();\n return this.http.get<StatisticsResult>(`/statistics${query ? `?${query}` : \"\"}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateTemplateInput,\n MessageTemplate,\n MessageType,\n SaveDraftInput,\n UpdateTemplateInput,\n} from \"../types.js\";\n\nexport class TemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: { type?: MessageType; isActive?: boolean }): Promise<MessageTemplate[]> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.isActive !== undefined) qs.set(\"isActive\", String(params.isActive));\n const query = qs.toString();\n return this.http.get<MessageTemplate[]>(`/templates${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageTemplate> {\n return this.http.get<MessageTemplate>(`/templates/${id}`);\n }\n\n create(input: CreateTemplateInput): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(\"/templates\", input);\n }\n\n update(id: string, input: UpdateTemplateInput): Promise<MessageTemplate> {\n return this.http.put<MessageTemplate>(`/templates/${id}`, input);\n }\n\n saveDraft(id: string, input: SaveDraftInput): Promise<MessageTemplate> {\n return this.http.patch<MessageTemplate>(`/templates/${id}/draft`, input);\n }\n\n publish(id: string): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(`/templates/${id}/publish`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/templates/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWebhookInput,\n OrgWebhook,\n OrgWebhookWithSecret,\n RotateSecretResult,\n UpdateWebhookInput,\n WebhookDelivery,\n} from \"../types.js\";\n\nexport class WebhooksResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgWebhook[]> {\n return this.http.get<OrgWebhook[]>(\"/webhooks\");\n }\n\n create(input: CreateWebhookInput): Promise<OrgWebhookWithSecret> {\n return this.http.post<OrgWebhookWithSecret>(\"/webhooks\", input);\n }\n\n update(id: string, input: UpdateWebhookInput): Promise<OrgWebhook> {\n return this.http.put<OrgWebhook>(`/webhooks/${id}`, input);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/webhooks/${id}`);\n }\n\n listDeliveries(id: string): Promise<WebhookDelivery[]> {\n return this.http.get<WebhookDelivery[]>(`/webhooks/${id}/deliveries`);\n }\n\n rotateSecret(id: string): Promise<RotateSecretResult> {\n return this.http.post<RotateSecretResult>(`/webhooks/${id}/rotate-secret`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n ConnectWabaInput,\n ConnectWhatsappNumberInput,\n OrgWaba,\n PhoneNumberStatusResult,\n RegisterWhatsappNumberInput,\n UpdateWhatsappProfileInput,\n WabaHealthResult,\n WhatsappNumber,\n WhatsappNumberProfile,\n} from \"../types.js\";\n\nexport class WhatsappNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n // ── WABAs ────────────────────────────────────────────────────────────────\n\n listWabas(): Promise<OrgWaba[]> {\n return this.http.get<OrgWaba[]>(\"/whatsapp/wabas\");\n }\n\n connectWaba(input: ConnectWabaInput): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(\"/whatsapp/wabas/connect\", input);\n }\n\n reconnectWaba(id: string): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(`/whatsapp/wabas/${id}/reconnect`);\n }\n\n disconnectWaba(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/wabas/${id}`);\n }\n\n getWabaHealth(id: string): Promise<WabaHealthResult> {\n return this.http.get<WabaHealthResult>(`/whatsapp/wabas/${id}/health`);\n }\n\n // ── Numbers ──────────────────────────────────────────────────────────────\n\n listNumbers(): Promise<WhatsappNumber[]> {\n return this.http.get<WhatsappNumber[]>(\"/whatsapp/numbers\");\n }\n\n connectNumber(input: ConnectWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers/connect\", input);\n }\n\n registerNumber(input: RegisterWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers\", input);\n }\n\n verifyCode(id: string, code: string): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(`/whatsapp/numbers/${id}/verify-code`, { code });\n }\n\n resendCode(id: string): Promise<void> {\n return this.http.post(`/whatsapp/numbers/${id}/resend-code`);\n }\n\n getProfile(id: string): Promise<WhatsappNumberProfile> {\n return this.http.get<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`);\n }\n\n updateProfile(id: string, input: UpdateWhatsappProfileInput): Promise<WhatsappNumberProfile> {\n return this.http.patch<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`, input);\n }\n\n updateDisplayName(id: string, displayName: string): Promise<WhatsappNumber> {\n return this.http.patch<WhatsappNumber>(`/whatsapp/numbers/${id}`, { displayName });\n }\n\n getStatus(id: string): Promise<PhoneNumberStatusResult> {\n return this.http.get<PhoneNumberStatusResult>(`/whatsapp/numbers/${id}/status`);\n }\n\n refreshNumbers(): Promise<WhatsappNumber[]> {\n return this.http.post<WhatsappNumber[]>(\"/whatsapp/numbers/refresh\");\n }\n\n deleteNumber(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/numbers/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWhatsappTemplateInput,\n SyncWhatsappTemplatesResult,\n UpdateWhatsappTemplateInput,\n WhatsappTemplate,\n} from \"../types.js\";\n\nexport class WhatsappTemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<WhatsappTemplate[]> {\n return this.http.get<WhatsappTemplate[]>(\"/whatsapp/templates\");\n }\n\n create(input: CreateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.post<WhatsappTemplate>(\"/whatsapp/templates\", input);\n }\n\n update(id: string, input: UpdateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.put<WhatsappTemplate>(`/whatsapp/templates/${id}`, input);\n }\n\n sync(): Promise<SyncWhatsappTemplatesResult> {\n return this.http.post<SyncWhatsappTemplatesResult>(\"/whatsapp/templates/sync\");\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/templates/${id}`);\n }\n}\n","import { HttpClient, type HttpClientOptions } from \"./http.js\";\nimport { DomainsResource } from \"./resources/domains.js\";\nimport { HistoryResource } from \"./resources/history.js\";\nimport { ManagedNumbersResource } from \"./resources/managed-numbers.js\";\nimport { MessagesResource } from \"./resources/messages.js\";\nimport { PlansResource } from \"./resources/plans.js\";\nimport { SmtpResource } from \"./resources/smtp.js\";\nimport { StatisticsResource } from \"./resources/statistics.js\";\nimport { TemplatesResource } from \"./resources/templates.js\";\nimport { WebhooksResource } from \"./resources/webhooks.js\";\nimport { WhatsappNumbersResource } from \"./resources/whatsapp-numbers.js\";\nimport { WhatsappTemplatesResource } from \"./resources/whatsapp-templates.js\";\n\nexport interface PostClientOptions\n extends Omit<HttpClientOptions, \"baseUrl\"> {\n baseUrl?: string;\n}\n\nexport class PostClient {\n private readonly http: HttpClient;\n\n readonly messages: MessagesResource;\n readonly templates: TemplatesResource;\n readonly whatsappTemplates: WhatsappTemplatesResource;\n readonly whatsappNumbers: WhatsappNumbersResource;\n readonly domains: DomainsResource;\n readonly webhooks: WebhooksResource;\n readonly smtp: SmtpResource;\n readonly plans: PlansResource;\n readonly history: HistoryResource;\n readonly statistics: StatisticsResource;\n readonly managedNumbers: ManagedNumbersResource;\n\n constructor(optionsOrHttp: HttpClientOptions | HttpClient) {\n this.http = optionsOrHttp instanceof HttpClient ? optionsOrHttp : new HttpClient(optionsOrHttp);\n\n this.messages = new MessagesResource(this.http);\n this.templates = new TemplatesResource(this.http);\n this.whatsappTemplates = new WhatsappTemplatesResource(this.http);\n this.whatsappNumbers = new WhatsappNumbersResource(this.http);\n this.domains = new DomainsResource(this.http);\n this.webhooks = new WebhooksResource(this.http);\n this.smtp = new SmtpResource(this.http);\n this.plans = new PlansResource(this.http);\n this.history = new HistoryResource(this.http);\n this.statistics = new StatisticsResource(this.http);\n this.managedNumbers = new ManagedNumbersResource(this.http);\n }\n}\n","import type { FastifyBaseLogger, FastifyInstance, FastifyPluginAsync, FastifyRequest } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport { PostClient } from \"./client.js\";\nimport { HttpClient } from \"./http.js\";\n\nexport interface ServiceAccountOptions {\n issuerUrl: string;\n clientId: string;\n clientSecret: string;\n}\n\nexport interface PostPluginOptions {\n serviceAccount: ServiceAccountOptions;\n baseUrl?: string;\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n}\n\ninterface CachedToken {\n value: string;\n expiresAt: number;\n organizationId: string | undefined;\n}\n\ntype Logger = Pick<FastifyBaseLogger, \"debug\" | \"info\" | \"warn\" | \"error\">;\n\nfunction log(logger: Logger, msg: string, data?: Record<string, unknown>) {\n if (data) {\n process.stdout.write(`[geekapps-post] ${msg} ${JSON.stringify(data, null, 2)}\\n`);\n } else {\n process.stdout.write(`[geekapps-post] ${msg}\\n`);\n }\n}\n\nfunction decodeJwtPayload(token: string): Record<string, unknown> {\n try {\n const [, payload] = token.split(\".\");\n if (!payload) return {};\n const json = Buffer.from(payload, \"base64url\").toString(\"utf-8\");\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nclass TokenProvider {\n private readonly issuerUrl: string;\n private readonly clientId: string;\n private readonly clientSecret: string;\n private readonly logger: Logger;\n private cached: CachedToken | null = null;\n private inflight: Promise<CachedToken> | null = null;\n\n constructor(options: PostPluginOptions, logger: Logger) {\n this.issuerUrl = options.serviceAccount.issuerUrl.replace(/\\/$/, \"\");\n this.clientId = options.serviceAccount.clientId;\n this.clientSecret = options.serviceAccount.clientSecret;\n this.logger = logger;\n }\n\n private async ensureFresh(): Promise<CachedToken> {\n const now = Date.now();\n if (this.cached && this.cached.expiresAt - now > 30_000) {\n log(this.logger, \"service account token cache hit\", {\n expiresAt: new Date(this.cached.expiresAt).toISOString(),\n });\n return this.cached;\n }\n log(this.logger, \"service account token cache miss — fetching new token\");\n if (this.inflight) return this.inflight;\n this.inflight = this.fetchToken().finally(() => { this.inflight = null; });\n return this.inflight;\n }\n\n getToken(): Promise<string> {\n return this.ensureFresh().then((c) => c.value);\n }\n\n getOrganizationId(): Promise<string | undefined> {\n return this.ensureFresh().then((c) => c.organizationId);\n }\n\n private async fetchToken(): Promise<CachedToken> {\n log(this.logger, \"fetching service account token\", {\n url: `${this.issuerUrl}/auth/token`,\n clientId: this.clientId,\n });\n\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString(\"base64\");\n const res = await fetch(`${this.issuerUrl}/auth/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${credentials}`,\n },\n body: JSON.stringify({ grant_type: \"service_account\" }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as { error?: string };\n const msg = `auth failed (${res.status}): ${err.error ?? \"unknown error\"}`;\n log(this.logger, msg);\n throw new Error(`[geekapps-post] ${msg}`);\n }\n\n const data = await res.json() as { access_token: string; expires_in?: number };\n const claims = decodeJwtPayload(data.access_token);\n const organizationId =\n typeof claims.organizationId === \"string\" ? claims.organizationId :\n typeof claims.organization_id === \"string\" ? claims.organization_id :\n undefined;\n\n this.cached = {\n value: data.access_token,\n expiresAt: Date.now() + (data.expires_in ?? 420) * 1000,\n organizationId,\n };\n\n log(this.logger, \"service account token acquired\", {\n claims: { organizationId },\n expiresIn: `${data.expires_in ?? 420}s`,\n });\n\n return this.cached;\n }\n}\n\nexport class LoggingHttpClient extends HttpClient {\n private readonly logger: Logger;\n private readonly baseUrlLog: string;\n\n constructor(options: ConstructorParameters<typeof HttpClient>[0], logger: Logger) {\n super(options);\n this.logger = logger;\n this.baseUrlLog = options.baseUrl.replace(/\\/$/, \"\");\n }\n\n override async request<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrlLog}/v1${path}`;\n log(this.logger, \"api request\", { method, url });\n if (body !== undefined) {\n log(this.logger, \"api request body\", { body });\n }\n try {\n const result = await super.request<T>(method, path, body);\n log(this.logger, \"api response\", { method, url, status: 200 });\n return result;\n } catch (err) {\n const status = (err as { status?: number }).status;\n log(this.logger, \"api response error\", { method, url, status });\n throw err;\n }\n }\n}\n\ndeclare module \"fastify\" {\n interface FastifyInstance {\n postClient: PostClient;\n }\n interface FastifyRequest {\n postClient: PostClient;\n }\n}\n\nconst postPlugin: FastifyPluginAsync<PostPluginOptions> = async (\n fastify: FastifyInstance,\n options: PostPluginOptions,\n) => {\n const logger = options.logLevel\n ? fastify.log.child({ level: options.logLevel })\n : fastify.log;\n\n const baseUrl = options.baseUrl ?? \"https://post-api.geekapps.com.br\";\n const provider = new TokenProvider(options, logger);\n\n log(logger, \"geekapps post initialized\", {\n issuerUrl: options.serviceAccount.issuerUrl,\n baseUrl,\n serviceAccount: true,\n });\n\n const http = new LoggingHttpClient(\n {\n baseUrl,\n token: () => provider.getToken(),\n organizationId: () => provider.getOrganizationId() as Promise<string>,\n },\n logger,\n );\n\n const client = new PostClient(http);\n\n fastify.decorate(\"postClient\", client);\n\n fastify.addHook(\"onRequest\", async (request: FastifyRequest) => {\n request.postClient = client;\n });\n};\n\nexport default fp(postPlugin, {\n fastify: \">=4.0.0\",\n name: \"@geekapps/post-fastify\",\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/domains.ts","../src/resources/history.ts","../src/resources/managed-numbers.ts","../src/resources/messages.ts","../src/resources/plans.ts","../src/resources/smtp.ts","../src/resources/statistics.ts","../src/resources/templates.ts","../src/resources/webhooks.ts","../src/resources/whatsapp-calls.ts","../src/resources/whatsapp-numbers.ts","../src/resources/whatsapp-templates.ts","../src/client.ts","../src/plugin.ts","../src/webhook.ts"],"sourcesContent":["export class PostApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n public readonly detail?: string,\n ) {\n super(detail ? `${message}: ${detail}` : message);\n this.name = \"PostApiError\";\n }\n\n get isNotFound() {\n return this.status === 404;\n }\n\n get isUnauthorized() {\n return this.status === 401;\n }\n\n get isForbidden() {\n return this.status === 403;\n }\n\n get isRateLimited() {\n return this.status === 429;\n }\n\n get isValidationError() {\n return this.status === 400;\n }\n}\n","import { PostApiError } from \"./errors.js\";\n\nexport type TokenResolver = string | (() => string | Promise<string>);\n\nexport interface HttpClientOptions {\n baseUrl: string;\n token: TokenResolver;\n organizationId?: string | (() => string | Promise<string>);\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly token: TokenResolver;\n private readonly organizationId?: string | (() => string | Promise<string>);\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.token = options.token;\n this.organizationId = options.organizationId;\n }\n\n private async resolveToken(): Promise<string> {\n return typeof this.token === \"function\" ? await this.token() : this.token;\n }\n\n private async resolveOrgId(): Promise<string | undefined> {\n if (!this.organizationId) return undefined;\n return typeof this.organizationId === \"function\"\n ? await this.organizationId()\n : this.organizationId;\n }\n\n async request<T = unknown>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const token = await this.resolveToken();\n const orgId = await this.resolveOrgId();\n\n const res = await fetch(`${this.baseUrl}/v1${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n ...(orgId ? { \"X-Organization-Id\": orgId } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n cache: \"no-store\" as any,\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as {\n error?: string;\n detail?: string;\n };\n throw new PostApiError(\n res.status,\n err.error ?? `HTTP ${res.status}`,\n err.detail,\n );\n }\n\n if (res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n\n get<T>(path: string) {\n return this.request<T>(\"GET\", path);\n }\n\n post<T>(path: string, body?: unknown) {\n return this.request<T>(\"POST\", path, body);\n }\n\n put<T>(path: string, body?: unknown) {\n return this.request<T>(\"PUT\", path, body);\n }\n\n patch<T>(path: string, body?: unknown) {\n return this.request<T>(\"PATCH\", path, body);\n }\n\n delete<T = void>(path: string) {\n return this.request<T>(\"DELETE\", path);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { CreateDomainInput, CreateDomainResult, OrgDomain } from \"../types.js\";\n\nexport class DomainsResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgDomain[]> {\n return this.http.get<OrgDomain[]>(\"/domains\");\n }\n\n get(id: string): Promise<OrgDomain> {\n return this.http.get<OrgDomain>(`/domains/${id}`);\n }\n\n create(input: CreateDomainInput): Promise<CreateDomainResult> {\n return this.http.post<CreateDomainResult>(\"/domains\", input);\n }\n\n verify(id: string): Promise<{ status: \"verified\" | \"failed\" }> {\n return this.http.post<{ status: \"verified\" | \"failed\" }>(`/domains/${id}/verify`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/domains/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { ListHistoryParams, MessageLog, MessageLogDetail, PaginatedResult } from \"../types.js\";\n\nexport class HistoryResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: ListHistoryParams): Promise<PaginatedResult<MessageLog>> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.status) qs.set(\"status\", params.status);\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.page !== undefined) qs.set(\"page\", String(params.page));\n if (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n const query = qs.toString();\n return this.http.get<PaginatedResult<MessageLog>>(`/history${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageLogDetail> {\n return this.http.get<MessageLogDetail>(`/history/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AreaCode, ManagedNumber } from \"../types.js\";\n\nexport interface ProvisionManagedNumberInput {\n areaCode: number;\n name?: string;\n displayName: string;\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n pin?: string;\n orgWabaId?: string;\n wabaId?: string;\n accessToken?: string;\n}\n\nexport interface RetryVerificationInput {\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n}\n\nexport class ManagedNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n listAreaCodes(): Promise<AreaCode[]> {\n return this.http.get<AreaCode[]>(\"/managed-numbers/area-codes\");\n }\n\n list(): Promise<ManagedNumber[]> {\n return this.http.get<ManagedNumber[]>(\"/managed-numbers\");\n }\n\n get(id: string): Promise<ManagedNumber> {\n return this.http.get<ManagedNumber>(`/managed-numbers/${id}`);\n }\n\n provision(input: ProvisionManagedNumberInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(\"/managed-numbers\", input);\n }\n\n retryVerification(id: string, input?: RetryVerificationInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/retry-verification`, input);\n }\n\n cancel(id: string): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/cancel`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { MessagePresenceInput, MessagePresenceResult, SendMessageInput, SendMessageResult } from \"../types.js\";\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n send(input: SendMessageInput): Promise<SendMessageResult> {\n return this.http.post<SendMessageResult>(\"/messages\", input);\n }\n\n /** Marca uma mensagem recebida como lida (tique azul). */\n markAsRead(input: MessagePresenceInput): Promise<MessagePresenceResult> {\n return this.http.post<MessagePresenceResult>(\"/whatsapp/messages/read\", input);\n }\n\n /** Marca como lida e mostra \"digitando…\" até a resposta ser enviada (máx. ~25s). */\n sendTyping(input: MessagePresenceInput): Promise<MessagePresenceResult> {\n return this.http.post<MessagePresenceResult>(\"/whatsapp/messages/typing\", input);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AvailablePlan, OrgPlan, PlansResult, UpdatePlanInput } from \"../types.js\";\n\nexport class PlansResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<PlansResult> {\n return this.http.get<PlansResult>(\"/plans\");\n }\n\n update(input: UpdatePlanInput): Promise<OrgPlan> {\n return this.http.put<OrgPlan>(\"/plans\", input);\n }\n\n listAvailable(): Promise<AvailablePlan[]> {\n return this.http.get<AvailablePlan[]>(\"/plans/available\");\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SmtpConfig, UpsertSmtpConfigInput } from \"../types.js\";\n\nexport class SmtpResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<SmtpConfig> {\n return this.http.get<SmtpConfig>(\"/smtp-config\");\n }\n\n upsert(input: UpsertSmtpConfigInput): Promise<SmtpConfig> {\n return this.http.put<SmtpConfig>(\"/smtp-config\", input);\n }\n\n delete(): Promise<void> {\n return this.http.delete(\"/smtp-config\");\n }\n\n test(to: string): Promise<{ success: boolean; error?: string }> {\n return this.http.post<{ success: boolean; error?: string }>(\"/smtp-config/test\", { to });\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { StatisticsParams, StatisticsResult } from \"../types.js\";\n\nexport class StatisticsResource {\n constructor(private readonly http: HttpClient) {}\n\n get(params?: StatisticsParams): Promise<StatisticsResult> {\n const qs = new URLSearchParams();\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.groupBy) qs.set(\"groupBy\", params.groupBy);\n const query = qs.toString();\n return this.http.get<StatisticsResult>(`/statistics${query ? `?${query}` : \"\"}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateTemplateInput,\n MessageTemplate,\n MessageType,\n SaveDraftInput,\n UpdateTemplateInput,\n} from \"../types.js\";\n\nexport class TemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: { type?: MessageType; isActive?: boolean }): Promise<MessageTemplate[]> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.isActive !== undefined) qs.set(\"isActive\", String(params.isActive));\n const query = qs.toString();\n return this.http.get<MessageTemplate[]>(`/templates${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageTemplate> {\n return this.http.get<MessageTemplate>(`/templates/${id}`);\n }\n\n create(input: CreateTemplateInput): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(\"/templates\", input);\n }\n\n update(id: string, input: UpdateTemplateInput): Promise<MessageTemplate> {\n return this.http.put<MessageTemplate>(`/templates/${id}`, input);\n }\n\n saveDraft(id: string, input: SaveDraftInput): Promise<MessageTemplate> {\n return this.http.patch<MessageTemplate>(`/templates/${id}/draft`, input);\n }\n\n publish(id: string): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(`/templates/${id}/publish`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/templates/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWebhookInput,\n OrgWebhook,\n OrgWebhookWithSecret,\n RotateSecretResult,\n UpdateWebhookInput,\n WebhookDelivery,\n} from \"../types.js\";\n\nexport class WebhooksResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgWebhook[]> {\n return this.http.get<OrgWebhook[]>(\"/webhooks\");\n }\n\n create(input: CreateWebhookInput): Promise<OrgWebhookWithSecret> {\n return this.http.post<OrgWebhookWithSecret>(\"/webhooks\", input);\n }\n\n update(id: string, input: UpdateWebhookInput): Promise<OrgWebhook> {\n return this.http.put<OrgWebhook>(`/webhooks/${id}`, input);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/webhooks/${id}`);\n }\n\n listDeliveries(id: string): Promise<WebhookDelivery[]> {\n return this.http.get<WebhookDelivery[]>(`/webhooks/${id}/deliveries`);\n }\n\n rotateSecret(id: string): Promise<RotateSecretResult> {\n return this.http.post<RotateSecretResult>(`/webhooks/${id}/rotate-secret`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CallActionResult,\n CallAnswerInput,\n CallPermissionStatus,\n CallTargetInput,\n CallingSettings,\n ConnectCallInput,\n ConnectCallResult,\n RequestCallPermissionInput,\n RequestCallPermissionResult,\n UpdateCallingSettingsInput,\n} from \"../types.js\";\n\n// WhatsApp Cloud API Calling. Signaling only: media flows over WebRTC between\n// Meta and your server. Incoming calls arrive via the `call_connect` webhook.\nexport class WhatsappCallsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Lê a configuração de chamadas de um número (id do WhatsappNumber no Post). */\n getSettings(numberId: string): Promise<CallingSettings> {\n return this.http.get<CallingSettings>(`/whatsapp/numbers/${numberId}/calling`);\n }\n\n /** Ativa/desativa chamadas no número. */\n updateSettings(numberId: string, input: UpdateCallingSettingsInput): Promise<CallingSettings> {\n return this.http.put<CallingSettings>(`/whatsapp/numbers/${numberId}/calling`, input);\n }\n\n /** Pré-aceita uma chamada recebida (opcional, acelera a conexão de mídia). */\n preAccept(callId: string, input: CallAnswerInput): Promise<CallActionResult> {\n return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/pre-accept`, input);\n }\n\n /** Aceita uma chamada recebida enviando o SDP de resposta. */\n accept(callId: string, input: CallAnswerInput): Promise<CallActionResult> {\n return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/accept`, input);\n }\n\n /** Rejeita uma chamada recebida. */\n reject(callId: string, input: CallTargetInput = {}): Promise<CallActionResult> {\n return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/reject`, input);\n }\n\n /** Encerra uma chamada em andamento. */\n terminate(callId: string, input: CallTargetInput = {}): Promise<CallActionResult> {\n return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/terminate`, input);\n }\n\n /** Inicia uma chamada (business-initiated). Exige permissão prévia do usuário. */\n connect(input: ConnectCallInput): Promise<ConnectCallResult> {\n return this.http.post<ConnectCallResult>(\"/whatsapp/calls\", input);\n }\n\n /** Envia ao usuário o pedido de permissão para receber chamadas. */\n requestPermission(input: RequestCallPermissionInput): Promise<RequestCallPermissionResult> {\n return this.http.post<RequestCallPermissionResult>(\"/whatsapp/calls/permissions\", input);\n }\n\n /** Consulta o status de permissão de chamada de um usuário. */\n getPermission(to: string, phoneNumberId?: string): Promise<CallPermissionStatus> {\n return this.http.get<CallPermissionStatus>(`/whatsapp/calls/permissions?${new URLSearchParams({ to, ...(phoneNumberId ? { phoneNumberId } : {}) })}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n ConnectWabaInput,\n ConnectWhatsappNumberInput,\n OrgWaba,\n PhoneNumberStatusResult,\n RegisterWhatsappNumberInput,\n UpdateWhatsappProfileInput,\n WabaHealthResult,\n WhatsappNumber,\n WhatsappNumberProfile,\n} from \"../types.js\";\n\nexport class WhatsappNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n // ── WABAs ────────────────────────────────────────────────────────────────\n\n listWabas(): Promise<OrgWaba[]> {\n return this.http.get<OrgWaba[]>(\"/whatsapp/wabas\");\n }\n\n connectWaba(input: ConnectWabaInput): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(\"/whatsapp/wabas/connect\", input);\n }\n\n reconnectWaba(id: string): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(`/whatsapp/wabas/${id}/reconnect`);\n }\n\n disconnectWaba(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/wabas/${id}`);\n }\n\n getWabaHealth(id: string): Promise<WabaHealthResult> {\n return this.http.get<WabaHealthResult>(`/whatsapp/wabas/${id}/health`);\n }\n\n // ── Numbers ──────────────────────────────────────────────────────────────\n\n listNumbers(): Promise<WhatsappNumber[]> {\n return this.http.get<WhatsappNumber[]>(\"/whatsapp/numbers\");\n }\n\n connectNumber(input: ConnectWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers/connect\", input);\n }\n\n registerNumber(input: RegisterWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers\", input);\n }\n\n verifyCode(id: string, code: string): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(`/whatsapp/numbers/${id}/verify-code`, { code });\n }\n\n resendCode(id: string): Promise<void> {\n return this.http.post(`/whatsapp/numbers/${id}/resend-code`);\n }\n\n getProfile(id: string): Promise<WhatsappNumberProfile> {\n return this.http.get<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`);\n }\n\n updateProfile(id: string, input: UpdateWhatsappProfileInput): Promise<WhatsappNumberProfile> {\n return this.http.patch<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`, input);\n }\n\n updateDisplayName(id: string, displayName: string): Promise<WhatsappNumber> {\n return this.http.patch<WhatsappNumber>(`/whatsapp/numbers/${id}`, { displayName });\n }\n\n getStatus(id: string): Promise<PhoneNumberStatusResult> {\n return this.http.get<PhoneNumberStatusResult>(`/whatsapp/numbers/${id}/status`);\n }\n\n refreshNumbers(): Promise<WhatsappNumber[]> {\n return this.http.post<WhatsappNumber[]>(\"/whatsapp/numbers/refresh\");\n }\n\n deleteNumber(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/numbers/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWhatsappTemplateInput,\n SyncWhatsappTemplatesResult,\n UpdateWhatsappTemplateInput,\n WhatsappTemplate,\n} from \"../types.js\";\n\nexport class WhatsappTemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<WhatsappTemplate[]> {\n return this.http.get<WhatsappTemplate[]>(\"/whatsapp/templates\");\n }\n\n create(input: CreateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.post<WhatsappTemplate>(\"/whatsapp/templates\", input);\n }\n\n update(id: string, input: UpdateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.put<WhatsappTemplate>(`/whatsapp/templates/${id}`, input);\n }\n\n sync(): Promise<SyncWhatsappTemplatesResult> {\n return this.http.post<SyncWhatsappTemplatesResult>(\"/whatsapp/templates/sync\");\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/templates/${id}`);\n }\n}\n","import { HttpClient, type HttpClientOptions } from \"./http.js\";\nimport { DomainsResource } from \"./resources/domains.js\";\nimport { HistoryResource } from \"./resources/history.js\";\nimport { ManagedNumbersResource } from \"./resources/managed-numbers.js\";\nimport { MessagesResource } from \"./resources/messages.js\";\nimport { PlansResource } from \"./resources/plans.js\";\nimport { SmtpResource } from \"./resources/smtp.js\";\nimport { StatisticsResource } from \"./resources/statistics.js\";\nimport { TemplatesResource } from \"./resources/templates.js\";\nimport { WebhooksResource } from \"./resources/webhooks.js\";\nimport { WhatsappCallsResource } from \"./resources/whatsapp-calls.js\";\nimport { WhatsappNumbersResource } from \"./resources/whatsapp-numbers.js\";\nimport { WhatsappTemplatesResource } from \"./resources/whatsapp-templates.js\";\n\nexport interface PostClientOptions\n extends Omit<HttpClientOptions, \"baseUrl\"> {\n baseUrl?: string;\n}\n\nexport class PostClient {\n private readonly http: HttpClient;\n\n readonly messages: MessagesResource;\n readonly templates: TemplatesResource;\n readonly whatsappTemplates: WhatsappTemplatesResource;\n readonly whatsappNumbers: WhatsappNumbersResource;\n readonly calls: WhatsappCallsResource;\n readonly domains: DomainsResource;\n readonly webhooks: WebhooksResource;\n readonly smtp: SmtpResource;\n readonly plans: PlansResource;\n readonly history: HistoryResource;\n readonly statistics: StatisticsResource;\n readonly managedNumbers: ManagedNumbersResource;\n\n constructor(optionsOrHttp: HttpClientOptions | HttpClient) {\n this.http = optionsOrHttp instanceof HttpClient ? optionsOrHttp : new HttpClient(optionsOrHttp);\n\n this.messages = new MessagesResource(this.http);\n this.templates = new TemplatesResource(this.http);\n this.whatsappTemplates = new WhatsappTemplatesResource(this.http);\n this.whatsappNumbers = new WhatsappNumbersResource(this.http);\n this.calls = new WhatsappCallsResource(this.http);\n this.domains = new DomainsResource(this.http);\n this.webhooks = new WebhooksResource(this.http);\n this.smtp = new SmtpResource(this.http);\n this.plans = new PlansResource(this.http);\n this.history = new HistoryResource(this.http);\n this.statistics = new StatisticsResource(this.http);\n this.managedNumbers = new ManagedNumbersResource(this.http);\n }\n}\n","import type { FastifyBaseLogger, FastifyInstance, FastifyPluginAsync, FastifyRequest } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport { PostClient } from \"./client.js\";\nimport { HttpClient } from \"./http.js\";\n\nexport interface ServiceAccountOptions {\n issuerUrl: string;\n clientId: string;\n clientSecret: string;\n}\n\nexport interface PostPluginOptions {\n serviceAccount: ServiceAccountOptions;\n baseUrl?: string;\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n}\n\ninterface CachedToken {\n value: string;\n expiresAt: number;\n organizationId: string | undefined;\n}\n\ntype Logger = Pick<FastifyBaseLogger, \"debug\" | \"info\" | \"warn\" | \"error\">;\n\nfunction log(logger: Logger, msg: string, data?: Record<string, unknown>) {\n if (data) {\n process.stdout.write(`[geekapps-post] ${msg} ${JSON.stringify(data, null, 2)}\\n`);\n } else {\n process.stdout.write(`[geekapps-post] ${msg}\\n`);\n }\n}\n\nfunction decodeJwtPayload(token: string): Record<string, unknown> {\n try {\n const [, payload] = token.split(\".\");\n if (!payload) return {};\n const json = Buffer.from(payload, \"base64url\").toString(\"utf-8\");\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nclass TokenProvider {\n private readonly issuerUrl: string;\n private readonly clientId: string;\n private readonly clientSecret: string;\n private readonly logger: Logger;\n private cached: CachedToken | null = null;\n private inflight: Promise<CachedToken> | null = null;\n\n constructor(options: PostPluginOptions, logger: Logger) {\n this.issuerUrl = options.serviceAccount.issuerUrl.replace(/\\/$/, \"\");\n this.clientId = options.serviceAccount.clientId;\n this.clientSecret = options.serviceAccount.clientSecret;\n this.logger = logger;\n }\n\n private async ensureFresh(): Promise<CachedToken> {\n const now = Date.now();\n if (this.cached && this.cached.expiresAt - now > 30_000) {\n log(this.logger, \"service account token cache hit\", {\n expiresAt: new Date(this.cached.expiresAt).toISOString(),\n });\n return this.cached;\n }\n log(this.logger, \"service account token cache miss — fetching new token\");\n if (this.inflight) return this.inflight;\n this.inflight = this.fetchToken().finally(() => { this.inflight = null; });\n return this.inflight;\n }\n\n getToken(): Promise<string> {\n return this.ensureFresh().then((c) => c.value);\n }\n\n getOrganizationId(): Promise<string | undefined> {\n return this.ensureFresh().then((c) => c.organizationId);\n }\n\n private async fetchToken(): Promise<CachedToken> {\n log(this.logger, \"fetching service account token\", {\n url: `${this.issuerUrl}/auth/token`,\n clientId: this.clientId,\n });\n\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString(\"base64\");\n const res = await fetch(`${this.issuerUrl}/auth/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${credentials}`,\n },\n body: JSON.stringify({ grant_type: \"service_account\" }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as { error?: string };\n const msg = `auth failed (${res.status}): ${err.error ?? \"unknown error\"}`;\n log(this.logger, msg);\n throw new Error(`[geekapps-post] ${msg}`);\n }\n\n const data = await res.json() as { access_token: string; expires_in?: number };\n const claims = decodeJwtPayload(data.access_token);\n const organizationId =\n typeof claims.organizationId === \"string\" ? claims.organizationId :\n typeof claims.organization_id === \"string\" ? claims.organization_id :\n undefined;\n\n this.cached = {\n value: data.access_token,\n expiresAt: Date.now() + (data.expires_in ?? 420) * 1000,\n organizationId,\n };\n\n log(this.logger, \"service account token acquired\", {\n claims: { organizationId },\n expiresIn: `${data.expires_in ?? 420}s`,\n });\n\n return this.cached;\n }\n}\n\nexport class LoggingHttpClient extends HttpClient {\n private readonly logger: Logger;\n private readonly baseUrlLog: string;\n\n constructor(options: ConstructorParameters<typeof HttpClient>[0], logger: Logger) {\n super(options);\n this.logger = logger;\n this.baseUrlLog = options.baseUrl.replace(/\\/$/, \"\");\n }\n\n override async request<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrlLog}/v1${path}`;\n log(this.logger, \"api request\", { method, url });\n if (body !== undefined) {\n log(this.logger, \"api request body\", { body });\n }\n try {\n const result = await super.request<T>(method, path, body);\n log(this.logger, \"api response\", { method, url, status: 200 });\n return result;\n } catch (err) {\n const status = (err as { status?: number }).status;\n log(this.logger, \"api response error\", { method, url, status });\n throw err;\n }\n }\n}\n\ndeclare module \"fastify\" {\n interface FastifyInstance {\n postClient: PostClient;\n }\n interface FastifyRequest {\n postClient: PostClient;\n }\n}\n\nconst postPlugin: FastifyPluginAsync<PostPluginOptions> = async (\n fastify: FastifyInstance,\n options: PostPluginOptions,\n) => {\n const logger = options.logLevel\n ? fastify.log.child({ level: options.logLevel })\n : fastify.log;\n\n const baseUrl = options.baseUrl ?? \"https://post-api.geekapps.com.br\";\n const provider = new TokenProvider(options, logger);\n\n log(logger, \"geekapps post initialized\", {\n issuerUrl: options.serviceAccount.issuerUrl,\n baseUrl,\n serviceAccount: true,\n });\n\n const http = new LoggingHttpClient(\n {\n baseUrl,\n token: () => provider.getToken(),\n organizationId: () => provider.getOrganizationId() as Promise<string>,\n },\n logger,\n );\n\n const client = new PostClient(http);\n\n fastify.decorate(\"postClient\", client);\n\n fastify.addHook(\"onRequest\", async (request: FastifyRequest) => {\n request.postClient = client;\n });\n};\n\nexport default fp(postPlugin, {\n fastify: \">=4.0.0\",\n name: \"@geekapps/post-fastify\",\n});\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { PostWebhookPayload } from \"./types.js\";\n\n/**\n * Verifies the `X-Post-Signature` header (`sha256=<hex>`) of a Post webhook.\n * `rawBody` must be the exact request body string, before JSON parsing.\n */\nexport function verifyWebhookSignature(\n rawBody: string,\n signatureHeader: string | null | undefined,\n secret: string,\n): boolean {\n if (!signatureHeader?.startsWith(\"sha256=\")) return false;\n const expected = Buffer.from(createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\"));\n const received = Buffer.from(signatureHeader.slice(\"sha256=\".length));\n return expected.length === received.length && timingSafeEqual(expected, received);\n}\n\n/** Verifies the signature and parses the payload; throws if invalid. */\nexport function constructWebhookEvent(\n rawBody: string,\n signatureHeader: string | null | undefined,\n secret: string,\n): PostWebhookPayload {\n if (!verifyWebhookSignature(rawBody, signatureHeader, secret)) {\n throw new Error(\"Invalid Post webhook signature\");\n }\n return JSON.parse(rawBody) as PostWebhookPayload;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekapps/post-fastify",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Geekapps Post SDK for Fastify",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/client.ts CHANGED
@@ -8,6 +8,7 @@ import { SmtpResource } from "./resources/smtp.js";
8
8
  import { StatisticsResource } from "./resources/statistics.js";
9
9
  import { TemplatesResource } from "./resources/templates.js";
10
10
  import { WebhooksResource } from "./resources/webhooks.js";
11
+ import { WhatsappCallsResource } from "./resources/whatsapp-calls.js";
11
12
  import { WhatsappNumbersResource } from "./resources/whatsapp-numbers.js";
12
13
  import { WhatsappTemplatesResource } from "./resources/whatsapp-templates.js";
13
14
 
@@ -23,6 +24,7 @@ export class PostClient {
23
24
  readonly templates: TemplatesResource;
24
25
  readonly whatsappTemplates: WhatsappTemplatesResource;
25
26
  readonly whatsappNumbers: WhatsappNumbersResource;
27
+ readonly calls: WhatsappCallsResource;
26
28
  readonly domains: DomainsResource;
27
29
  readonly webhooks: WebhooksResource;
28
30
  readonly smtp: SmtpResource;
@@ -38,6 +40,7 @@ export class PostClient {
38
40
  this.templates = new TemplatesResource(this.http);
39
41
  this.whatsappTemplates = new WhatsappTemplatesResource(this.http);
40
42
  this.whatsappNumbers = new WhatsappNumbersResource(this.http);
43
+ this.calls = new WhatsappCallsResource(this.http);
41
44
  this.domains = new DomainsResource(this.http);
42
45
  this.webhooks = new WebhooksResource(this.http);
43
46
  this.smtp = new SmtpResource(this.http);
package/src/http.ts CHANGED
@@ -1,88 +1,88 @@
1
- import { PostApiError } from "./errors.js";
2
-
3
- export type TokenResolver = string | (() => string | Promise<string>);
4
-
5
- export interface HttpClientOptions {
6
- baseUrl: string;
7
- token: TokenResolver;
8
- organizationId?: string | (() => string | Promise<string>);
9
- }
10
-
11
- export class HttpClient {
12
- private readonly baseUrl: string;
13
- private readonly token: TokenResolver;
14
- private readonly organizationId?: string | (() => string | Promise<string>);
15
-
16
- constructor(options: HttpClientOptions) {
17
- this.baseUrl = options.baseUrl.replace(/\/$/, "");
18
- this.token = options.token;
19
- this.organizationId = options.organizationId;
20
- }
21
-
22
- private async resolveToken(): Promise<string> {
23
- return typeof this.token === "function" ? await this.token() : this.token;
24
- }
25
-
26
- private async resolveOrgId(): Promise<string | undefined> {
27
- if (!this.organizationId) return undefined;
28
- return typeof this.organizationId === "function"
29
- ? await this.organizationId()
30
- : this.organizationId;
31
- }
32
-
33
- async request<T = unknown>(
34
- method: string,
35
- path: string,
36
- body?: unknown,
37
- ): Promise<T> {
38
- const token = await this.resolveToken();
39
- const orgId = await this.resolveOrgId();
40
-
41
- const res = await fetch(`${this.baseUrl}/v1${path}`, {
42
- method,
43
- headers: {
44
- Authorization: `Bearer ${token}`,
45
- ...(body !== undefined ? { "Content-Type": "application/json" } : {}),
46
- ...(orgId ? { "X-Organization-Id": orgId } : {}),
47
- },
48
- ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
49
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
- cache: "no-store" as any,
51
- });
52
-
53
- if (!res.ok) {
54
- const err = await res.json().catch(() => ({})) as {
55
- error?: string;
56
- detail?: string;
57
- };
58
- throw new PostApiError(
59
- res.status,
60
- err.error ?? `HTTP ${res.status}`,
61
- err.detail,
62
- );
63
- }
64
-
65
- if (res.status === 204) return undefined as T;
66
- return res.json() as Promise<T>;
67
- }
68
-
69
- get<T>(path: string) {
70
- return this.request<T>("GET", path);
71
- }
72
-
73
- post<T>(path: string, body?: unknown) {
74
- return this.request<T>("POST", path, body);
75
- }
76
-
77
- put<T>(path: string, body?: unknown) {
78
- return this.request<T>("PUT", path, body);
79
- }
80
-
81
- patch<T>(path: string, body?: unknown) {
82
- return this.request<T>("PATCH", path, body);
83
- }
84
-
85
- delete<T = void>(path: string) {
86
- return this.request<T>("DELETE", path);
87
- }
88
- }
1
+ import { PostApiError } from "./errors.js";
2
+
3
+ export type TokenResolver = string | (() => string | Promise<string>);
4
+
5
+ export interface HttpClientOptions {
6
+ baseUrl: string;
7
+ token: TokenResolver;
8
+ organizationId?: string | (() => string | Promise<string>);
9
+ }
10
+
11
+ export class HttpClient {
12
+ private readonly baseUrl: string;
13
+ private readonly token: TokenResolver;
14
+ private readonly organizationId?: string | (() => string | Promise<string>);
15
+
16
+ constructor(options: HttpClientOptions) {
17
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
18
+ this.token = options.token;
19
+ this.organizationId = options.organizationId;
20
+ }
21
+
22
+ private async resolveToken(): Promise<string> {
23
+ return typeof this.token === "function" ? await this.token() : this.token;
24
+ }
25
+
26
+ private async resolveOrgId(): Promise<string | undefined> {
27
+ if (!this.organizationId) return undefined;
28
+ return typeof this.organizationId === "function"
29
+ ? await this.organizationId()
30
+ : this.organizationId;
31
+ }
32
+
33
+ async request<T = unknown>(
34
+ method: string,
35
+ path: string,
36
+ body?: unknown,
37
+ ): Promise<T> {
38
+ const token = await this.resolveToken();
39
+ const orgId = await this.resolveOrgId();
40
+
41
+ const res = await fetch(`${this.baseUrl}/v1${path}`, {
42
+ method,
43
+ headers: {
44
+ Authorization: `Bearer ${token}`,
45
+ ...(body !== undefined ? { "Content-Type": "application/json" } : {}),
46
+ ...(orgId ? { "X-Organization-Id": orgId } : {}),
47
+ },
48
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ cache: "no-store" as any,
51
+ });
52
+
53
+ if (!res.ok) {
54
+ const err = await res.json().catch(() => ({})) as {
55
+ error?: string;
56
+ detail?: string;
57
+ };
58
+ throw new PostApiError(
59
+ res.status,
60
+ err.error ?? `HTTP ${res.status}`,
61
+ err.detail,
62
+ );
63
+ }
64
+
65
+ if (res.status === 204) return undefined as T;
66
+ return res.json() as Promise<T>;
67
+ }
68
+
69
+ get<T>(path: string) {
70
+ return this.request<T>("GET", path);
71
+ }
72
+
73
+ post<T>(path: string, body?: unknown) {
74
+ return this.request<T>("POST", path, body);
75
+ }
76
+
77
+ put<T>(path: string, body?: unknown) {
78
+ return this.request<T>("PUT", path, body);
79
+ }
80
+
81
+ patch<T>(path: string, body?: unknown) {
82
+ return this.request<T>("PATCH", path, body);
83
+ }
84
+
85
+ delete<T = void>(path: string) {
86
+ return this.request<T>("DELETE", path);
87
+ }
88
+ }
package/src/index.ts CHANGED
@@ -12,10 +12,29 @@ export { SmtpResource } from "./resources/smtp.js";
12
12
  export { StatisticsResource } from "./resources/statistics.js";
13
13
  export { TemplatesResource } from "./resources/templates.js";
14
14
  export { WebhooksResource } from "./resources/webhooks.js";
15
+ export { WhatsappCallsResource } from "./resources/whatsapp-calls.js";
15
16
  export { WhatsappNumbersResource } from "./resources/whatsapp-numbers.js";
17
+ export { constructWebhookEvent, verifyWebhookSignature } from "./webhook.js";
16
18
  export { WhatsappTemplatesResource } from "./resources/whatsapp-templates.js";
17
19
 
18
20
  export type {
21
+ CallActionResult,
22
+ CallAnswerInput,
23
+ CallConnectWebhookData,
24
+ CallPermissionStatus,
25
+ CallStatusWebhookData,
26
+ CallTargetInput,
27
+ CallTerminateWebhookData,
28
+ CallingSettings,
29
+ CallingStatus,
30
+ ConnectCallInput,
31
+ ConnectCallResult,
32
+ MessageReceivedWebhookData,
33
+ PostWebhookPayload,
34
+ RequestCallPermissionInput,
35
+ RequestCallPermissionResult,
36
+ UpdateCallingSettingsInput,
37
+ WebhookSdpSession,
19
38
  AreaCode,
20
39
  AvailablePlan,
21
40
  ConnectWabaInput,
@@ -51,6 +70,8 @@ export type {
51
70
  RotateSecretResult,
52
71
  SaveDraftInput,
53
72
  SendMessageInput,
73
+ MessagePresenceInput,
74
+ MessagePresenceResult,
54
75
  SendMessageResult,
55
76
  TemplateVariable,
56
77
  TemplateVariables,
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from "../http.js";
2
- import type { SendMessageInput, SendMessageResult } from "../types.js";
2
+ import type { MessagePresenceInput, MessagePresenceResult, SendMessageInput, SendMessageResult } from "../types.js";
3
3
 
4
4
  export class MessagesResource {
5
5
  constructor(private readonly http: HttpClient) {}
@@ -7,4 +7,14 @@ export class MessagesResource {
7
7
  send(input: SendMessageInput): Promise<SendMessageResult> {
8
8
  return this.http.post<SendMessageResult>("/messages", input);
9
9
  }
10
+
11
+ /** Marca uma mensagem recebida como lida (tique azul). */
12
+ markAsRead(input: MessagePresenceInput): Promise<MessagePresenceResult> {
13
+ return this.http.post<MessagePresenceResult>("/whatsapp/messages/read", input);
14
+ }
15
+
16
+ /** Marca como lida e mostra "digitando…" até a resposta ser enviada (máx. ~25s). */
17
+ sendTyping(input: MessagePresenceInput): Promise<MessagePresenceResult> {
18
+ return this.http.post<MessagePresenceResult>("/whatsapp/messages/typing", input);
19
+ }
10
20
  }
@@ -0,0 +1,64 @@
1
+ import type { HttpClient } from "../http.js";
2
+ import type {
3
+ CallActionResult,
4
+ CallAnswerInput,
5
+ CallPermissionStatus,
6
+ CallTargetInput,
7
+ CallingSettings,
8
+ ConnectCallInput,
9
+ ConnectCallResult,
10
+ RequestCallPermissionInput,
11
+ RequestCallPermissionResult,
12
+ UpdateCallingSettingsInput,
13
+ } from "../types.js";
14
+
15
+ // WhatsApp Cloud API Calling. Signaling only: media flows over WebRTC between
16
+ // Meta and your server. Incoming calls arrive via the `call_connect` webhook.
17
+ export class WhatsappCallsResource {
18
+ constructor(private readonly http: HttpClient) {}
19
+
20
+ /** Lê a configuração de chamadas de um número (id do WhatsappNumber no Post). */
21
+ getSettings(numberId: string): Promise<CallingSettings> {
22
+ return this.http.get<CallingSettings>(`/whatsapp/numbers/${numberId}/calling`);
23
+ }
24
+
25
+ /** Ativa/desativa chamadas no número. */
26
+ updateSettings(numberId: string, input: UpdateCallingSettingsInput): Promise<CallingSettings> {
27
+ return this.http.put<CallingSettings>(`/whatsapp/numbers/${numberId}/calling`, input);
28
+ }
29
+
30
+ /** Pré-aceita uma chamada recebida (opcional, acelera a conexão de mídia). */
31
+ preAccept(callId: string, input: CallAnswerInput): Promise<CallActionResult> {
32
+ return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/pre-accept`, input);
33
+ }
34
+
35
+ /** Aceita uma chamada recebida enviando o SDP de resposta. */
36
+ accept(callId: string, input: CallAnswerInput): Promise<CallActionResult> {
37
+ return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/accept`, input);
38
+ }
39
+
40
+ /** Rejeita uma chamada recebida. */
41
+ reject(callId: string, input: CallTargetInput = {}): Promise<CallActionResult> {
42
+ return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/reject`, input);
43
+ }
44
+
45
+ /** Encerra uma chamada em andamento. */
46
+ terminate(callId: string, input: CallTargetInput = {}): Promise<CallActionResult> {
47
+ return this.http.post<CallActionResult>(`/whatsapp/calls/${callId}/terminate`, input);
48
+ }
49
+
50
+ /** Inicia uma chamada (business-initiated). Exige permissão prévia do usuário. */
51
+ connect(input: ConnectCallInput): Promise<ConnectCallResult> {
52
+ return this.http.post<ConnectCallResult>("/whatsapp/calls", input);
53
+ }
54
+
55
+ /** Envia ao usuário o pedido de permissão para receber chamadas. */
56
+ requestPermission(input: RequestCallPermissionInput): Promise<RequestCallPermissionResult> {
57
+ return this.http.post<RequestCallPermissionResult>("/whatsapp/calls/permissions", input);
58
+ }
59
+
60
+ /** Consulta o status de permissão de chamada de um usuário. */
61
+ getPermission(to: string, phoneNumberId?: string): Promise<CallPermissionStatus> {
62
+ return this.http.get<CallPermissionStatus>(`/whatsapp/calls/permissions?${new URLSearchParams({ to, ...(phoneNumberId ? { phoneNumberId } : {}) })}`);
63
+ }
64
+ }
package/src/types.ts CHANGED
@@ -32,7 +32,12 @@ export type WebhookEvent =
32
32
  | "message_failed"
33
33
  | "message_bounced"
34
34
  | "message_opened"
35
- | "message_clicked";
35
+ | "message_clicked"
36
+ | "message_received"
37
+ | "call_connect"
38
+ | "call_terminate"
39
+ | "call_status"
40
+ | "call_permission_reply";
36
41
 
37
42
  export type WabaStatus = "active" | "token_expired" | "disconnected";
38
43
 
@@ -428,3 +433,129 @@ export interface WabaHealthResult {
428
433
  export interface PhoneNumberStatusResult {
429
434
  status: unknown;
430
435
  }
436
+
437
+ // ─── WhatsApp Calling ─────────────────────────────────────────────────────────
438
+
439
+ export type CallingStatus = "ENABLED" | "DISABLED";
440
+
441
+ export interface CallingSettings {
442
+ status: CallingStatus;
443
+ callIconVisibility?: "DEFAULT" | "DISABLE_ALL" | null;
444
+ callbackPermissionStatus?: CallingStatus | null;
445
+ }
446
+
447
+ export interface UpdateCallingSettingsInput {
448
+ status: CallingStatus;
449
+ callIconVisibility?: "DEFAULT" | "DISABLE_ALL";
450
+ callbackPermissionStatus?: CallingStatus;
451
+ }
452
+
453
+ /** Meta phone_number_id (as received in webhooks). Defaults to the org's first active number. */
454
+ export interface CallTargetInput {
455
+ phoneNumberId?: string;
456
+ }
457
+
458
+ export interface CallAnswerInput extends CallTargetInput {
459
+ /** SDP answer to the offer received in `call_connect`. */
460
+ sdp: string;
461
+ bizOpaqueCallbackData?: string;
462
+ }
463
+
464
+ export interface CallActionResult {
465
+ ok: true;
466
+ callId: string;
467
+ action: "pre_accept" | "accept" | "reject" | "terminate";
468
+ }
469
+
470
+ export interface ConnectCallInput extends CallTargetInput {
471
+ to: string;
472
+ /** SDP offer. The answer arrives in the `call_connect` webhook. */
473
+ sdp: string;
474
+ bizOpaqueCallbackData?: string;
475
+ }
476
+
477
+ export interface ConnectCallResult {
478
+ callId: string;
479
+ phoneNumberId: string;
480
+ }
481
+
482
+ export interface RequestCallPermissionInput extends CallTargetInput {
483
+ to: string;
484
+ body?: string;
485
+ }
486
+
487
+ export interface RequestCallPermissionResult {
488
+ messageId: string;
489
+ phoneNumberId: string;
490
+ }
491
+
492
+ /** Raw Meta response from GET /{phone-number-id}/call_permissions. */
493
+ export type CallPermissionStatus = Record<string, unknown>;
494
+
495
+ export interface WebhookSdpSession {
496
+ sdp_type: "offer" | "answer";
497
+ sdp: string;
498
+ }
499
+
500
+ interface CallWebhookBase {
501
+ phoneNumberId: string;
502
+ displayPhoneNumber: string | null;
503
+ callId: string;
504
+ timestamp: string;
505
+ bizOpaqueCallbackData: string | null;
506
+ }
507
+
508
+ export interface CallConnectWebhookData extends CallWebhookBase {
509
+ from: string;
510
+ to: string;
511
+ contactName: string | null;
512
+ direction: "USER_INITIATED" | "BUSINESS_INITIATED" | null;
513
+ session: WebhookSdpSession | null;
514
+ }
515
+
516
+ export interface CallTerminateWebhookData extends CallWebhookBase {
517
+ from: string;
518
+ to: string;
519
+ direction: "USER_INITIATED" | "BUSINESS_INITIATED" | null;
520
+ status: string | null;
521
+ startTime: string | null;
522
+ endTime: string | null;
523
+ duration: number | null;
524
+ }
525
+
526
+ export interface CallStatusWebhookData extends CallWebhookBase {
527
+ status: string;
528
+ recipientId: string | null;
529
+ }
530
+
531
+ export interface MessageReceivedWebhookData {
532
+ phoneNumberId: string;
533
+ displayPhoneNumber: string | null;
534
+ channel: "whatsapp";
535
+ messageId: string;
536
+ from: string;
537
+ contactName: string | null;
538
+ type: string;
539
+ timestamp: string;
540
+ /** Raw Meta message object. */
541
+ message: Record<string, unknown>;
542
+ }
543
+
544
+ export type PostWebhookPayload =
545
+ | { event: "call_connect"; data: CallConnectWebhookData; timestamp: number }
546
+ | { event: "call_terminate"; data: CallTerminateWebhookData; timestamp: number }
547
+ | { event: "call_status"; data: CallStatusWebhookData; timestamp: number }
548
+ | { event: "message_received" | "call_permission_reply"; data: MessageReceivedWebhookData; timestamp: number }
549
+ | { event: Exclude<WebhookEvent, "call_connect" | "call_terminate" | "call_status" | "message_received" | "call_permission_reply">; data: Record<string, unknown>; timestamp: number };
550
+
551
+ export interface MessagePresenceInput {
552
+ /** `messageId` da mensagem recebida (webhook `message_received`). */
553
+ messageId: string;
554
+ /** Meta phone_number_id. Padrão: primeiro número ativo da org. */
555
+ phoneNumberId?: string;
556
+ }
557
+
558
+ export interface MessagePresenceResult {
559
+ success: boolean;
560
+ phoneNumberId: string;
561
+ }
package/src/webhook.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+ import type { PostWebhookPayload } from "./types.js";
3
+
4
+ /**
5
+ * Verifies the `X-Post-Signature` header (`sha256=<hex>`) of a Post webhook.
6
+ * `rawBody` must be the exact request body string, before JSON parsing.
7
+ */
8
+ export function verifyWebhookSignature(
9
+ rawBody: string,
10
+ signatureHeader: string | null | undefined,
11
+ secret: string,
12
+ ): boolean {
13
+ if (!signatureHeader?.startsWith("sha256=")) return false;
14
+ const expected = Buffer.from(createHmac("sha256", secret).update(rawBody).digest("hex"));
15
+ const received = Buffer.from(signatureHeader.slice("sha256=".length));
16
+ return expected.length === received.length && timingSafeEqual(expected, received);
17
+ }
18
+
19
+ /** Verifies the signature and parses the payload; throws if invalid. */
20
+ export function constructWebhookEvent(
21
+ rawBody: string,
22
+ signatureHeader: string | null | undefined,
23
+ secret: string,
24
+ ): PostWebhookPayload {
25
+ if (!verifyWebhookSignature(rawBody, signatureHeader, secret)) {
26
+ throw new Error("Invalid Post webhook signature");
27
+ }
28
+ return JSON.parse(rawBody) as PostWebhookPayload;
29
+ }