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/sendGET /v1/messages/:idGET /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/...:
messages/send: send/route.tsmessages/:id: messages/[id]/route.tsanalytics/dashboard: analytics/dashboard/route.ts
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
- Alternativa (se preferir público sem scope):
Estrutura sugerida:
src/client.ts(client principal)src/resources/messages.tssrc/resources/analytics.tssrc/webhooks/types.tssrc/webhooks/parse.tssrc/errors.tssrc/http.ts(wrapper de fetch, timeout, headers, parsing)src/index.ts(exports)
Runtime suportado
- Node
>=18(alinhado ao repo) - Runtimes com
fetchglobal (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:
apiKeysempre enviado comox-api-keybaseUrldefault: sem default hardcoded; exigir explicitar ou usarprocess.env.PILOT_STATUS_BASE_URLno app do consumidor (o SDK apenas aceitabaseUrl).timeoutMsdefault: 30sfetchinjetá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 }
- Input:
messages.get(id): Promise<MessageDetails>- Output (200):
{ id, status, correlationId, destinationNumber, template, createdAt, sentAt, readAt, errorMessage }
- Output (200):
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.
- Conveniência opcional: polling com backoff até status terminal (
Analytics
analytics.getDashboardStats({ tz }): Promise<DashboardStats>- Observação:
statusDistributioné percentual (0–100), não contagem.
- Observação:
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).
- Validação de shape com Zod e retorno tipado por
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:
SendMessageInputSendMessageAcceptedMessageDetailsDashboardStatsMessageStatus(enum string alinhado ao backend)
Webhooks:
CustomerWebhookEvent(discriminated union porevent)MessageSentEvent,MessageFailedEvent,MessageReadEvent,MessageReplyEvent
Erros (contrato do SDK)
Padrão: lançar exceções tipadas, preservando status HTTP e corpo parseado.
Base:
PilotStatusError extends ErrorPilotStatusHttpError extends PilotStatusError- Campos:
status,method,url,body,rawBody,headers
- Campos:
Subclasses utilitárias:
AuthenticationError(401)ValidationError(400 comdetails)ForbiddenError(403; inclui casos como LIVE não aprovado, opt-in, restrições do TEST)NotFoundError(404)ConflictError(409)RateLimitError(429 comreason)ServerError(>=500)
Regras de parsing:
- Se resposta for JSON e tiver
{ error: string }, preenchermessagecom esseerror. - 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 umretryPolicysimples.
Observabilidade
- Permitir
userAgentcustom para identificação do integrador. - Expor hook opcional
onResponse/onRequestpara logs/metrics do consumidor. - Não logar
apiKeynem 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-webhookexamples/nextjs-route-handler
Build, exports e publicação
TypeScript e output
Proposta:
- Source em TS
- Build via
tscgerandodist/ exportsnopackage.jsoncom:import(ESM)require(CJS) se necessário, ou apenas ESM no v1 para simplicidadetypes
Decisão a tomar:
- ESM-only (mais simples) vs Dual ESM/CJS (mais compatível).
Versionamento
- SemVer
0.xenquanto o contrato ainda estiver mudando rápido;1.0quando 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 (
fetchno 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)
- Nome do pacote:
@pilot-status/sdkvspilot-status-sdkR:pilot-status-sdk - Módulos: ESM-only vs Dual ESM/CJS R: O que for melhor
- Incluir
messages.waitForStatusjá no MVP ou deixar para pós-MVP R: Nao