Documentação / Planejamento — SDK NPM do Pilot Status

Planejamento — SDK NPM do Pilot Status

Entrar

Planejamento — SDK NPM do Pilot Status

Objetivo

Disponibilizar um SDK oficial (TypeScript-first) para consumir a API pública do Pilot Status via API key (x-api-key), com tipagem estável, tratamento de erros consistente e utilitários para webhooks.

Princípios

  • Contrato refletindo apenas o que existe hoje em /v1 (sem “inventar” endpoints).
  • Tipos e erros explícitos, com mensagens úteis para debug sem expor segredos.
  • Isomórfico (Node 18+ e runtimes com fetch), porém com posicionamento claro: API key deve ser usada em backend.
  • Dependências mínimas.

Escopo (MVP)

Cobrir os endpoints públicos existentes:

  • POST /v1/messages/send
  • GET /v1/messages/:id
  • GET /v1/analytics/dashboard?tz=...

E utilitários:

  • Tipos de payload de webhook do cliente (message.sent, message.delivered, message.failed, message.read, message.reply)
  • Função de parsing/validação do payload recebido (sem assinatura/HMAC, pois a plataforma não implementa assinatura hoje)

Fora de escopo (MVP)

  • Criação/gestão de API keys, templates, projetos (não há endpoints públicos para isso).
  • Mecanismo de verificação criptográfica de webhook (não há assinatura hoje).
  • Retries “automáticos” para operações não-idempotentes (envio de mensagem).

Estado atual da API (fonte da verdade)

Implementação em apps/web/src/app/v1/...:

Documentação de webhooks:

Design do SDK

Nome do pacote e estrutura

  • Workspace: packages/sdk
  • Nome npm sugerido: @pilot-status/sdk
    • Alternativa (se preferir público sem scope): pilot-status-sdk

Estrutura sugerida:

  • src/client.ts (client principal)
  • src/resources/messages.ts
  • src/resources/analytics.ts
  • src/webhooks/types.ts
  • src/webhooks/parse.ts
  • src/errors.ts
  • src/http.ts (wrapper de fetch, timeout, headers, parsing)
  • src/index.ts (exports)

Runtime suportado

  • Node >=18 (alinhado ao repo)
  • Runtimes com fetch global (ex.: Cloudflare Workers, Vercel Edge), desde que o consumidor aceite usar API key fora do browser.

Configuração do client

Factory recomendada:

  • new PilotStatusClient({ apiKey, baseUrl, timeoutMs, userAgent, fetch })

Regras:

  • apiKey sempre enviado como x-api-key
  • baseUrl default: sem default hardcoded; exigir explicitar ou usar process.env.PILOT_STATUS_BASE_URL no app do consumidor (o SDK apenas aceita baseUrl).
  • timeoutMs default: 30s
  • fetch injetável para testes e runtimes sem fetch global

API surface (proposta)

Messages

  • messages.send(input): Promise<SendMessageAccepted>
    • Input: { templateId, destinationNumber, variables?, deliverAt? }
    • Output (202): { id, correlationId, status, createdAt, origin }
  • messages.get(id): Promise<MessageDetails>
    • Output (200): { id, status, correlationId, destinationNumber, template, createdAt, sentAt, readAt, errorMessage }
  • messages.waitForStatus(id, options?): Promise<MessageDetails>
    • Conveniência opcional: polling com backoff até status terminal (SENT|READ|FAILED) ou timeout.
    • Default seguro: desativado no MVP, ou incluído mas sem ligar retries do send.

Analytics

  • analytics.getDashboardStats({ tz }): Promise<DashboardStats>
    • Observação: statusDistribution é percentual (0–100), não contagem.

Webhooks

  • parseCustomerWebhook(payload: unknown): CustomerWebhookEvent
    • Validação de shape com Zod e retorno tipado por event.
    • Não faz verificação de assinatura (inexistente hoje).
  • isCustomerWebhookEvent(payload: unknown): payload is CustomerWebhookEvent

Tipos (modelo público do SDK)

O SDK define seus próprios tipos públicos, alinhados ao comportamento real das rotas (mesmo que packages/shared tenha divergências hoje).

Tipos principais:

  • SendMessageInput
  • SendMessageAccepted
  • MessageDetails
  • DashboardStats
  • MessageStatus (enum string alinhado ao backend)

Webhooks:

  • CustomerWebhookEvent (discriminated union por event)
  • MessageSentEvent, MessageFailedEvent, MessageReadEvent, MessageReplyEvent

Erros (contrato do SDK)

Padrão: lançar exceções tipadas, preservando status HTTP e corpo parseado.

Base:

  • PilotStatusError extends Error
  • PilotStatusHttpError extends PilotStatusError
    • Campos: status, method, url, body, rawBody, headers

Subclasses utilitárias:

  • AuthenticationError (401)
  • ValidationError (400 com details)
  • ForbiddenError (403; inclui casos como LIVE não aprovado, opt-in, restrições do TEST)
  • NotFoundError (404)
  • ConflictError (409)
  • RateLimitError (429 com reason)
  • ServerError (>=500)

Regras de parsing:

  • Se resposta for JSON e tiver { error: string }, preencher message com esse error.
  • Preservar campos extras quando existirem (details, reason, code, state).

Retries e idempotência

  • Por default, não aplicar retry automático em messages.send (não-idempotente).
  • Permitir retry em GET (ex.: messages.get, analytics.getDashboardStats) quando houver falhas de rede/5xx, com limite curto e backoff.
  • Expor configuração: { retries: { get: number } } ou um retryPolicy simples.

Observabilidade

  • Permitir userAgent custom para identificação do integrador.
  • Expor hook opcional onResponse/onRequest para logs/metrics do consumidor.
  • Não logar apiKey nem headers sensíveis.

Segurança e boas práticas para integradores

Incluir no README do SDK:

  • Nunca usar API key em frontend.
  • Em TEST, o envio pode ser bloqueado se o destino não for o telefone cadastrado do usuário no tenant.
  • Em LIVE, o projeto precisa estar aprovado (productionApproved) ou o backend retorna 403.
  • Se houver opt-in de utilidade habilitado, envios podem falhar com code: "DESTINATION_NOT_AUTHORIZED".

DX (Developer Experience)

README do pacote (conteúdo sugerido)

  • Instalação
  • Quickstart (send + get)
  • Lista de métodos e tipos
  • Tabela de erros por endpoint (status → classe do SDK)
  • Seção “Webhooks” com exemplo de handler

Exemplos (pasta)

Opcional:

  • examples/node-express-webhook
  • examples/nextjs-route-handler

Build, exports e publicação

TypeScript e output

Proposta:

  • Source em TS
  • Build via tsc gerando dist/
  • exports no package.json com:
    • import (ESM)
    • require (CJS) se necessário, ou apenas ESM no v1 para simplicidade
    • types

Decisão a tomar:

  • ESM-only (mais simples) vs Dual ESM/CJS (mais compatível).

Versionamento

  • SemVer
  • 0.x enquanto o contrato ainda estiver mudando rápido; 1.0 quando os endpoints e shapes estiverem estabilizados.

Política de compatibilidade

  • Patch: correções internas e docs
  • Minor: novos métodos/tipos compatíveis
  • Major: mudanças de assinatura, tipos ou erros

Testes

Plano de testes:

  • Unit tests de parsing (sucesso/erro) com respostas fake.
  • Testes de webhooks (parse de cada evento).
  • Testes de retry policy para GET. Ferramentas:
  • Reusar vitest (já presente no monorepo).
  • Mock de fetch via injeção (fetch no client), sem dependência extra.

Roadmap pós-MVP

  • messages.sendAndWait(...) com polling integrado.
  • Suporte opcional a “correlationId” do cliente se o backend passar a aceitar (hoje é gerado no servidor).
  • Assinatura de webhook (quando a plataforma suportar), com verifyWebhookSignature.
  • Endpoints públicos adicionais (templates, listagens, webhooks) se/quando forem expostos.

Decisões pendentes (para você escolher)

  1. Nome do pacote: @pilot-status/sdk vs pilot-status-sdk R: pilot-status-sdk
  2. Módulos: ESM-only vs Dual ESM/CJS R: O que for melhor
  3. Incluir messages.waitForStatus já no MVP ou deixar para pós-MVP R: Nao