smskitdocs

SDKs

Os SDKs oficiais cobrem a API inteira. Eles trocam a chave pelo token na primeira chamada, reaproveitam o token enquanto ele vale e pedem um novo ao receber 401. Cada falha vira uma exceção tipada, com code, status, requestId e retryAfter.

Instalação

TypeScript
pnpm add smskit
Python
pip install smskit

Primeiro envio

A chave entra no construtor e nada mais precisa ser configurado. A troca pelo token, a renovação e a leitura do erro ficam com o SDK.

TypeScript
import { SMSKit, SMSKitError } from "smskit";

const sms = new SMSKit(process.env.SMSKIT_KEY!, {
  baseUrl: "https://api.smskit.dev", // opcional
});

try {
  const sent = await sms.send({
    to: ["+5511999990123", "+5521988880456"],
    template: "order-confirmed",
    variables: { empresa: "Studio", pedido: "1042", link: "https://exemplo.com/p/1042" },
    idempotencyKey: "pedido-1042",
  });
  const status = await sms.get(sent.id);
  const { balance } = await sms.account();
} catch (error) {
  if (error instanceof SMSKitError) {
    console.error(error.code, error.status, error.requestId, error.retryAfter);
  }
}

O construtor é new SMSKit(apiKey, { baseUrl?, timeoutMs?, fetch? }) em TypeScript e SMSKit(api_key, base_url=, timeout=) em Python. baseUrl muda o destino das chamadas, útil para apontar para um ambiente local; o padrão é https://api.smskit.dev.

Uma diferença entre as duas bibliotecas: no corpo da requisição o remetente é from, mas from é palavra reservada em Python, então o SDK de lá recebe from_. Em TypeScript o nome é o mesmo do JSON.

Métodos

Tudo o que as duas bibliotecas fazem. Os nomes seguem a convenção de cada linguagem e o comportamento é o mesmo.

// Enviar SMS
sms.send({ to, text | template + variables, kind?, scheduledAt?, metadata?, idempotencyKey?, acceptReview? })

// Consultar envio
sms.get(smsId)

// Listar envios
sms.list({ limit?, startingAfter? })

// Conta e saldo
sms.account()

// Modelos
sms.templates()

// Listar webhooks
sms.webhooks.list()

// Criar webhook
sms.webhooks.create({ url, events, description? })

// Remover webhook
sms.webhooks.delete(webhookId)

// Testar webhook
sms.webhooks.test(webhookId)

// Verificar assinatura
await verifySignature(secret, header, rawBody)

Erros e novas tentativas

Toda falha vira SMSKitError com o mesmo code do envelope HTTP. Dois deles pedem uma nova tentativa e o resto não: 429 rate_limited traz em retryAfter quantos segundos esperar, e 503 provider_unavailable não descarta o envio — ele segue na fila. 422 validation_error traz o campo recusado em details, e repetir sem mudar nada devolve o mesmo erro.

retry
import { SMSKit, SMSKitError } from "smskit";

const sms = new SMSKit(process.env.SMSKIT_KEY!);

async function sendWithRetry(params: Parameters<SMSKit["send"]>[0]) {
  for (let attempt = 0; ; attempt++) {
    try {
      return await sms.send(params);
    } catch (error) {
      if (!(error instanceof SMSKitError)) throw error;
      // 429 diz quanto esperar; 503 nao descarta o envio, so adia.
      const wait = error.retryAfter ?? 2 ** attempt;
      if ((error.status === 429 || error.status === 503) && attempt < 4) {
        await new Promise((r) => setTimeout(r, wait * 1000));
        continue;
      }
      // 422 traz o campo recusado em details; o resto e definitivo.
      if (error.code === "validation_error") console.error(error.details);
      throw error;
    }
  }
}

Repetir um envio com a mesma Idempotency-Key devolve o envio original em vez de criar outro, então uma nova tentativa depois de um timeout de rede é segura.

Receber eventos

A verificação da assinatura é uma chamada, e a única regra é passar o corpo cru: um JSON.parse seguido de JSON.stringify muda os bytes e invalida o HMAC. A função confere o carimbo de tempo junto com a assinatura e devolve false em qualquer dúvida.

Express
import { verifySignature } from "smskit";

// Express: mantenha o corpo BRUTO na rota do webhook.
app.post("/webhooks/smskit", express.raw({ type: "*/*" }), async (req, res) => {
  const ok = await verifySignature(
    process.env.SMSKIT_WEBHOOK_SECRET!,
    req.header("X-SMSKit-Signature"),
    req.body, // Buffer, nao o JSON ja analisado
  );
  if (!ok) return res.sendStatus(400);
  res.sendStatus(204); // responda primeiro, processe depois
  const event = JSON.parse(req.body.toString("utf8"));
  // event.type, event.data, event.id — o mesmo id numa reentrega.
});

Assíncrono e paginação

Em Python, AsyncSMSKit tem a mesma superfície com await e fecha o cliente ao sair do async with. Em TypeScript o cliente já é assíncrono. As listagens vêm por página: siga o id do último item em startingAfter até has_more virar false.

paginação
// Paginacao: siga o ultimo id ate has_more virar false.
let startingAfter: string | undefined;
do {
  const page = await sms.list({ limit: 50, startingAfter });
  for (const sent of page.data) console.log(sent.id, sent.status);
  startingAfter = page.data.at(-1)?.id;
} while (startingAfter && page.has_more);

Modo de teste

Uma chave sk_test_ percorre as mesmas rotas, com as mesmas respostas, sem entregar nem cobrar. O cliente diz em qual modo está, o que evita mandar um ensaio para produção por engano.

sk_test_
// sk_test_ percorre as mesmas rotas sem entregar nem cobrar.
const sandbox = new SMSKit(process.env.SMSKIT_TEST_KEY!);
console.log(sandbox.testMode); // true

// Numeros terminados em 0000 falham; os demais sao entregues.
await sandbox.send({ to: "+5511999990000", text: "Falha simulada" });

Detalhes do que muda em Modo de teste, e o texto que a rede aceita em Limites e preço.