Para desenvolvimento e QA
Receba SMS num número americano de verdade, direto no seu código.
Uma linha móvel americana de verdade, não VoIP, que entrega cada SMS recebido para o seu código. Um webhook assinado sai assim que a mensagem é guardada. Uma API REST lista e consulta. A equipe continua vendo a caixa de entrada ao vivo, o e-mail e o Telegram. Uma linha, US$ 24,99 por mês, cancele quando quiser.
Por que não um número de plataforma de telecom?
Números programáveis de plataformas de telecom costumam ser classificados como VoIP. O próprio texto da Twilio sobre filtrar VoIP antes de mandar códigos mostra o Lookup dela marcando um número da Twilio como nonFixedVoip (Twilio). Se um serviço filtra VoIP, o código dele nunca chega num número de plataforma. Uma linha Cleat é uma linha móvel. É essa a razão de usar uma. Mais sobre tipos de linha.
Primeiros passos
- Pegue uma linha. Crie a conta e assine. Listar suas linhas funciona na hora; ler as mensagens de uma linha responde
403até o dono da conta verificar a identidade. - Crie uma API key em Configurações. Ela começa com
clt_e aparece uma vez só. - Adicione um endpoint de webhook em Configurações, se quiser receber pushes: uma URL HTTPS pública. O segredo de assinatura começa com
whsec_e aparece uma vez só. - Liste suas linhas e depois consulte os SMS ou espere os webhooks.
curl https://cleat.so/api/v1/lines \
-H "Authorization: Bearer $CLEAT_API_KEY"{
"data": [
{
"id": "8f14e45f-ceea-4b6b-9d3c-2a1f0e7c5b10",
"phone": "13055550100",
"label": "Staging sign-ups",
"status": "active",
"createdAt": "2026-09-11T10:00:00.000Z"
}
]
}curl "https://cleat.so/api/v1/lines/$LINE_ID/messages?after=2026-09-11T10:00:00Z&limit=50" \
-H "Authorization: Bearer $CLEAT_API_KEY"{
"data": [
{
"id": "c9a7e0d2-5b1f-4e8a-9f3c-6d2b1a0e4f77",
"line": { "id": "8f14e45f-ceea-4b6b-9d3c-2a1f0e7c5b10", "phone": "13055550100", "label": "Staging sign-ups" },
"from": "32665",
"body": "704118 is your Facebook confirmation code",
"code": "704118",
"receivedAt": "2026-09-11T10:02:41.000Z",
"service": { "id": "facebook", "name": "Facebook", "color": "#0866FF" },
"contact": null,
"label": "Facebook"
}
]
}Webhooks
Quando um SMS chega, a Cleat manda um POST para cada um dos seus endpoints com este corpo JSON, um header cleat-signature e o user agent Cleat-Webhooks/1.0:
{
"type": "message.received",
"data": {
"id": "c9a7e0d2-5b1f-4e8a-9f3c-6d2b1a0e4f77",
"line": { "id": "8f14e45f-ceea-4b6b-9d3c-2a1f0e7c5b10", "phone": "13055550100", "label": "Staging sign-ups" },
"from": "32665",
"body": "704118 is your Facebook confirmation code",
"code": "704118",
"receivedAt": "2026-09-11T10:02:41.000Z",
"service": { "id": "facebook", "name": "Facebook", "color": "#0866FF" },
"contact": null,
"label": "Facebook"
}
}O header de assinatura fica assim: t=1789120961,v1=5d41…. v1 é o HMAC-SHA256 em hexadecimal do timestamp, de um ponto e do corpo cru, com a chave do segredo de assinatura do endpoint. Confira contra os bytes crus antes de fazer o parse. Recuse timestamps antigos para bloquear reenvios.
import crypto from "node:crypto";
import express from "express";
const app = express();
const secret = process.env.CLEAT_WEBHOOK_SECRET; // whsec_…
// cleat-signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.body">
function verified(header, body) {
const parts = Object.fromEntries(String(header).split(",").map((part) => part.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${body}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && parts.v1?.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
app.post("/cleat-webhook", express.raw({ type: "application/json" }), (req, res) => {
const body = req.body.toString("utf8");
if (!verified(req.get("cleat-signature"), body)) return res.sendStatus(401);
const { type, data } = JSON.parse(body);
if (type === "message.received") {
// data.code is Cleat's best guess at a one-time code; data.body is the full text.
console.log(data.line.phone, data.from, data.code ?? data.body);
}
res.sendStatus(200);
});
app.listen(3000);Detalhes da entrega
- Os endpoints precisam ser URLs
https://públicas. A Cleat não segue redirecionamentos. - Responda com qualquer status 2xx em até 10 segundos.
- A entrega é pelo menos uma vez. Se uma falha ou estoura o tempo, ela é tentada de novo: cinco tentativas no total, a primeira na hora e depois com 30 segundos, 2 minutos, 10 minutos e uma hora, mandando os mesmos bytes com uma assinatura nova. Faça o seu handler ser idempotente no
data.id; oidde um SMS nunca muda. Configurações guarda cada tentativa e pode reenviar qualquer entrega. - Se as cinco tentativas falharem, se atualize pela API com
?after=e oreceivedAtdo último SMS que você tratou. - Enquanto a linha está suspensa ou o dono dela não verificou a identidade, nada é enviado. Esses SMS ficam guardados e podem ser lidos assim que o motivo se resolve, mas nenhum webhook é mandado por eles depois: leia com
?after=. - Até cinco endpoints por espaço de trabalho. Configurações mostra o último status de cada endpoint e quantas entregas seguidas falharam.
typeémessage.received, outestpara a entrega de teste que você dispara em Configurações: o mesmodata, com ids próprios. Responda 200 a um tipo que você não conhece; outros podem aparecer.- Se em vez da sua URL você colar um webhook de entrada do Slack ou um link de workflow do Microsoft Teams, aquele endpoint recebe o formato de mensagem do próprio app e sem
cleat-signature: com um app de chat não existe segredo compartilhado. codeé o melhor palpite da Cleat para o código de uso único, ounull. Obodycompleto está sempre lá.labeldiz de quem é o SMS em palavras: um contato que a sua equipe salvou ou, senão, o serviço que o Cleat reconheceu pelo que o SMS diz, comcontacteservicepor trás. Os três vêmnullquando não dá para ter certeza. Foram adicionados depois e não tiram nada:from,bodyecodecontinuam iguais.- Um código ditado numa ligação automática chega do mesmo jeito. A ligação é transcrita e entregue como um
message.received, com a transcrição embody, o número que ligou emfrome ocodepreenchido. Nada no payload indica que a entrega foi uma ligação em vez de um SMS.
Referência da API
| Endpoint | O que devolve |
|---|---|
GET /api/v1/lines | As linhas do espaço de trabalho da API key, da mais nova para a mais antiga: id, phone, label, status (active, grace ou released) e createdAt. |
GET /api/v1/lines/{lineId}/messages | Os SMS de uma linha. after (ISO 8601) devolve os SMS recebidos depois daquele momento, do mais antigo para o mais novo, para consultar em loop. before pagina para trás, do mais novo para o mais antigo. limit vai de 1 a 200, e são 50 por padrão. |
Authorization: Bearer clt_…. Um limit fora de 1–200 é ajustado ao intervalo, não recusado. Erros: 400 para timestamp mal formado, 401 para chave faltando, revogada ou expirada (code key_expired), 402 enquanto a linha está suspensa, 403 até o dono do espaço de trabalho verificar a identidade (code verify_first), 404 para uma linha fora do espaço de trabalho da chave *ou* fora do escopo dela — de propósito é a mesma resposta, 429 acima do limite, 503 se o escopo da chave não puder ser verificado.Limites e permissões
120 requisições por minuto por API key; passou disso, a API responde 429, sem cabeçalho Retry-After, então o backoff é seu. Só o dono do espaço de trabalho cria API keys e endpoints de webhook, e uma chave revogada para de funcionar na hora. Você também pode limitar uma chave na criação: a linhas específicas e a uma data em que ela deixa de valer, o que é o que torna seguro entregá-la a um agente, a um job de CI ou a um terceiro. Uma linha fora do escopo responde 404, e uma chave expirada responde 401 com code key_expired.
Casos que encaixam
- As contas da sua própria empresa, como consoles de nuvem, registros de domínio e gateways de pagamento, com os códigos indo para um canal que a pessoa de plantão acompanha.
- Staging e QA dos seus próprios fluxos de cadastro e login, contra um número que se comporta como o celular de um cliente.
- Projetos pessoais que precisam de um número de celular americano no cadastro.
Casos que não encaixam
- Enviar SMS. A Cleat Line só recebe: não envia mensagens, não faz chamadas e não liga para o 911.
- Teste de carga ou pilhas de números. Cada linha é uma assinatura separada, com um dono verificado. A Cleat não foi feita para ter muitos números.
- Contas que não são suas nem da sua empresa.
- Qualquer coisa que precise de voz ao vivo. A API não liga para fora. Não tem chamada para atender, áudio para transmitir nem gravação para baixar. Só a transcrição de uma ligação depois que ela acabou. As ligações da Cleat Full são atendidas e feitas no app web, não pela API.
Como se compara
| Número de plataforma de telecom | Números da MailSlurp | Cleat Line | |
|---|---|---|---|
| Tipo de número | Muitas vezes VoIP na classificação; o Lookup da Twilio marca um número da Twilio como nonFixedVoip | “dedicated, non-VoIP mobile numbers” (MailSlurp) | Linha móvel americana de verdade, não VoIP |
| Feito para | Enviar e receber em escala | QA e testes de desenvolvimento | Suas próprias contas, QA e equipes pequenas |
| Verificação do dono | KYC de empresa e registro 10DLC | Não informa | Documento oficial e selfie, ou só documento revisado por uma pessoa |
| Levar os SMS para o código | Webhooks e API | API e webhooks | Webhooks assinados e API REST, além de Telegram, e-mail e a caixa de entrada |
| Preço | Por número mais por mensagem | Não aparece na página | US$ 24,99 por mês, SMS recebidos inclusos |
Precisa dividir códigos com gente que não programa? Veja 2FA compartilhado para equipes. Por que o Google Voice e outros números de internet perdem códigos: Google Voice e os códigos de verificação. Como tratamos identidade: confiança e segurança. As regras de cobrança: preço.
Perguntas
Tem plano grátis ou sandbox?
Não. Uma linha custa US$ 24,99 por mês, cancele quando quiser, e as mensagens ficam bloqueadas até o dono verificar a identidade.
Dá para enviar SMS pela API?
Não. A Cleat Line recebe; ela nunca envia.
E se um serviço ditar o código numa ligação em vez de mandar SMS?
A ligação é transcrita e chega no seu webhook e na API como uma mensagem comum, com o código extraído e o número que ligou em from. Nenhum campo indica que foi uma ligação.
Tem limite de requisições?
Tem: 120 requisições por minuto por API key. As entregas de webhook não contam nesse limite.
Em quanto tempo os webhooks disparam?
A Cleat manda o POST para os seus endpoints assim que guardou o SMS, junto com a caixa ao vivo, o e-mail e o Telegram. Não publicamos latência medida, então não damos um número.
Dá para ter mais de um número?
Você pode adicionar linhas a um espaço de trabalho, e cada uma é uma assinatura própria de US$ 24,99. A Cleat não foi feita para ter muitos números.
Vocês guardam minhas mensagens?
Os SMS ficam guardados até você apagar o espaço de trabalho ou a conta. A Política de Privacidade, em inglês tem os detalhes.
Fontes
Os dados sobre outras empresas vêm das páginas delas, conferidas em setembro de 2026.
- Twilio, filtering VoIP numbers before sending codes https://www.twilio.com/en-us/blog/filter-voip-before-otp-verification
- MailSlurp, phone numbers https://www.mailslurp.com/product/phone-number-sms-service/