API pour les développeurs

Vendez des numéros temporaires sur votre site ou application et recevez les codes de vérification par l’API.

Démarrage rapide

  1. Créez un compte et demandez l’accès dans Revendre des numéros dans le tableau de bord. L’approbation est manuelle.
  2. Après approbation, créez une clé de test ativa_test_ et conservez-la sur votre serveur.
  3. Remplacez $ATIVA_API_KEY par votre clé de test et effectuez la première requête ci-dessous.

Exemple du mode test gratuit : montants fictifs, toujours en BRL. Cette réponse utilise l’option réelle du simulateur sans acheter de numéro.

curl 'https://www.ativasms.store/api/v1/opcoes?servico=wa&pais=187' \
  -H "Authorization: Bearer $ATIVA_API_KEY"

Réponse (HTTP 200)

[
  {
    "opcao": "op_000000000001",
    "preco_centavos": 83,
    "preco_site_centavos": 120,
    "moeda": "BRL",
    "prazo_min": 20,
    "taxa_sucesso": null,
    "estoque": 100,
    "recomendada": false
  }
]

Découvrez le programme revendeur et vendez à votre propre prix →

1. Fonctionnement et obtention de l’accès

L’API permet de revendre des numéros SMS temporaires sur votre propre site ou application. Vos clients n’ont pas besoin de visiter Ativa SMS.

Créez un compte et demandez l’accès via Revendre des numéros dans le menu du profil du tableau de bord. L’acceptation des Conditions de revente est obligatoire ; les Conditions d’utilisation s’appliquent également. Après approbation, créez les clés dans Tableau de bord → API.

2. URL de base et format des données

URL de base : https://www.ativasms.store/api/v1. Envoyez du JSON UTF-8 avec Content-Type: application/json pour les corps des requêtes. Les réponses sont en JSON et incluent Cache-Control: no-store.

Les montants sont des entiers en centimes : moeda vaut BRL ou USD selon votre compte ; 83 centimes BRL correspondent à R$ 0.83. Le mode de test utilise toujours BRL. Les dates sont au format ISO 8601 UTC. Les numéros de téléphone utilisent le format international +indicatif-pays, ou null tant que la réservation est en cours.

Les exemples sont illustratifs et suivent les formats réels des réponses ; les exemples d’achat utilisent des données de test fictives. Utilisez les codes de services et les options renvoyés par l’API ; les prix live varient. Les exemples curl lisent ATIVA_API_KEY dans une variable d’environnement de votre serveur.

Spécification OpenAPI 3.1 pour les outils et agents d’IA : /openapi.json.

3. Authentification

Envoyez Authorization: Bearer <key> sur chaque endpoint. Les clés commencent par ativa_live_ en production ou ativa_test_ pour les tests. Les clés ne sont acceptées ni dans les chaînes de requête ni dans les corps.

Une clé s’affiche une seule fois, lors de sa création. Conservez-la sur votre serveur, jamais dans le navigateur ou l’application d’un client. Un compte peut avoir jusqu’à 5 clés actives, clés live et test confondues. Révoquez et remplacez les clés perdues ou exposées dans le tableau de bord. Si une restriction d’IP est configurée, les requêtes doivent également la respecter.

Les échecs d’authentification renvoient la même réponse HTTP 401 sans en révéler la raison :

{
  "erro": "nao_autorizado",
  "mensagem": "Chave de API ausente, inválida ou revogada."
}

4. Limites d’utilisation

Les limites par défaut peuvent être ajustées pour chaque revendeur. Consultez les limites actuelles de votre compte dans le tableau de bord :

Limite Valeur par défaut
Requêtes par minute 120
Achats/remplacements par minute 20
Numéros simultanément en attente de SMS 20
Tentatives sans code par compte sur 24 h 100
Tentatives sans code par cliente_ref sur 24 h 15

Les débits de requêtes et d’achats cumulent toutes les clés du compte dans le même mode. Les compteurs test et live sont distincts et utilisent les mêmes limites configurées. Les annulations, remplacements et expirations sans SMS consomment des tentatives ; chaque numéro avec un code rétablit 5 tentatives, dans la limite du plafond. Les échecs de livraison ne comptent pas. Les limites live du compte pour les numéros ouverts et les tentatives couvrent aussi les achats sur le site.

Le dépassement des débits de requêtes ou d’achats renvoie HTTP 429 muitas_requisicoes et Retry-After: 60 (secondes). Les autres limites renvoient 429 avec leur code spécifique et sans Retry-After : attendez que des numéros ouverts ou des tentatives se libèrent, selon le cas.

5. Idempotence et prix maximal

Idempotency-Key est obligatoire pour les achats et remplacements : 1–64 lettres, chiffres ou . _ : -. Utilisez une nouvelle clé pour chaque opération souhaitée et la même clé pour les nouvelles tentatives. Les clés ont pour périmètre le compte et le mode ; achats et remplacements partagent ce périmètre. Ne réutilisez pas une clé pour un autre client ou une autre commande.

Répéter une clé enregistrée renvoie HTTP 200 avec le même numéro et son état actuel, même si le corps a changé ; un nouvel achat renvoie 201. Les recherches live ne s’arrêtent pas après 24 h : la clé désigne ce numéro tant que son enregistrement existe. L’historique de test est conservé 30 jours. Réinitialiser le solde de test préserve l’historique et l’idempotence.

preco_maximo_centavos est le montant maximal que vous acceptez dans la devise de votre compte. Si le prix actuel le dépasse, l’API renvoie preco_acima_do_maximo avec preco_centavos et opcoes, sans achat. preco_centavos est déduit de votre solde ; preco_site_centavos est le prix de référence du site. Le prix est à nouveau vérifié au moment de l’achat.

6. Endpoints et exemples

GET /saldo

Sans paramètres. Renvoie le solde dans la devise du compte (BRL ou USD). Le mode de test utilise toujours des BRL et des fonds fictifs.

curl "https://www.ativasms.store/api/v1/saldo" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

{
  "saldo_centavos": 10000,
  "moeda": "BRL"
}

GET /servicos

Sans paramètres. Renvoie tous les services disponibles, sans doublons ni services bloqués. L’exemple montre une entrée ; le mode de test inclut également teste_falha (Teste sem código). Les noms de champs et les libellés de services de l’API restent en portugais, quelle que soit la langue de la documentation.

curl "https://www.ativasms.store/api/v1/servicos" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

[
  {
    "codigo": "wa",
    "nome": "WhatsApp"
  }
]

GET /paises

Sans paramètres. Renvoie tous les pays. Utilisez codigo dans les requêtes et les achats ; iso est en majuscules et ddi est l’indicatif téléphonique. L’exemple montre une entrée.

curl "https://www.ativasms.store/api/v1/paises" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

[
  {
    "codigo": 73,
    "iso": "BR",
    "nome": "Brasil",
    "ddi": "+55"
  }
]

GET /opcoes

Paramètres de requête obligatoires : servico (un code de la liste des services) et pais (un code pays numérique). Renvoie jusqu’à 6 options par ordre croissant de preco_centavos ; le résultat peut être []. opcao est un identifiant opaque : op_ suivi de 12 caractères hexadécimaux. Renvoyez la valeur reçue sans l’interpréter. preco_centavos est le montant facturé au revendeur ; preco_site_centavos est le prix de référence du site, tous deux dans la devise indiquée. prazo_min est le délai ; taxa_sucesso peut être null ; estoque indique la disponibilité et recomendada marque une option suggérée. Cet exemple utilise l’option de test fictive.

curl "https://www.ativasms.store/api/v1/opcoes?servico=wa&pais=73" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

[
  {
    "opcao": "op_000000000001",
    "preco_centavos": 83,
    "preco_site_centavos": 120,
    "moeda": "BRL",
    "prazo_min": 20,
    "taxa_sucesso": null,
    "estoque": 100,
    "recomendada": false
  }
]

POST /numeros

Champs JSON obligatoires : servico (1–20 lettres minuscules, chiffres ou _ ; un service autorisé), pais (un code pays entier valide), preco_maximo_centavos (un entier positif) et cliente_ref (un identifiant anonyme de 1–64 lettres, chiffres ou . _ : - ; jamais une adresse e-mail ou un identifiant personnel). opcao est facultatif et peut être null ; son omission sélectionne l’option la moins chère. Envoyez Idempotency-Key. Un nouvel achat renvoie 201 ; une répétition renvoie 200 avec le même id et l’état actuel, sans nouveau débit. Ceci est un exemple d’achat de test.

curl -X POST "https://www.ativasms.store/api/v1/numeros" \
  -H "Authorization: Bearer $ATIVA_API_KEY" \
  -H "Idempotency-Key: compra_abc123" \
  -H "Content-Type: application/json" \
  -d '{"servico":"wa","pais":73,"opcao":"op_000000000001","preco_maximo_centavos":83,"cliente_ref":"cliente_abc123"}'

HTTP 201

{
  "id": "5f0c0000-0000-4000-8000-000000000001",
  "servico": "wa",
  "servico_nome": "WhatsApp",
  "pais": 73,
  "telefone": "+12025550123",
  "status": "aguardando_sms",
  "motivo": null,
  "preco_centavos": 83,
  "preco_site_centavos": 120,
  "moeda": "BRL",
  "criado_em": "2026-10-08T12:00:00.000Z",
  "cancelavel_em": "2026-10-08T12:02:00.000Z",
  "expira_em": "2026-10-08T12:20:00.000Z",
  "encerrado_em": null,
  "substitui_id": null,
  "cliente_ref": "cliente_abc123",
  "sms": []
}

GET /numeros/{id}

Chemin : /numeros/{id}, où id est l’UUID du numéro. Seuls les achats par API appartenant à ce compte et à ce mode sont visibles ; le numéro d’un autre propriétaire, un achat sur le site ou un UUID invalide renvoie nao_encontrado. Les numéros ouverts sont actualisés avant la réponse, jusqu’à 60 actualisations/min par clé live ; les requêtes suivantes renvoient le dernier état disponible (200, sans 429 de synchronisation supplémentaire). Cet exemple montre un code de test reçu.

curl "https://www.ativasms.store/api/v1/numeros/5f0c0000-0000-4000-8000-000000000001" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

{
  "id": "5f0c0000-0000-4000-8000-000000000001",
  "servico": "wa",
  "servico_nome": "WhatsApp",
  "pais": 73,
  "telefone": "+12025550123",
  "status": "recebido",
  "motivo": null,
  "preco_centavos": 83,
  "preco_site_centavos": 120,
  "moeda": "BRL",
  "criado_em": "2026-10-08T12:00:00.000Z",
  "cancelavel_em": "2026-10-08T12:02:00.000Z",
  "expira_em": "2026-10-08T12:20:00.000Z",
  "encerrado_em": null,
  "substitui_id": null,
  "cliente_ref": "cliente_abc123",
  "sms": [
    {
      "codigo": "123456",
      "texto": "Seu código de teste: 123456",
      "recebido_em": "2026-10-08T12:00:20.000Z"
    }
  ]
}

GET /numeros

Paramètres de requête facultatifs : status (un état de numéro documenté), cliente_ref (même format qu’à l’achat), pagina (1–1000, par défaut 1). Renvoie jusqu’à 50 achats par API par page pour ce compte et ce mode, les plus récents en premier. tem_mais indique une page supplémentaire. Lister les numéros live ne force pas leur actualisation ; interrogez un numéro individuellement pour l’actualiser.

curl "https://www.ativasms.store/api/v1/numeros?status=aguardando_sms&cliente_ref=cliente_abc123&pagina=1" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

{
  "numeros": [
    {
      "id": "5f0c0000-0000-4000-8000-000000000001",
      "servico": "wa",
      "servico_nome": "WhatsApp",
      "pais": 73,
      "telefone": "+12025550123",
      "status": "aguardando_sms",
      "motivo": null,
      "preco_centavos": 83,
      "preco_site_centavos": 120,
      "moeda": "BRL",
      "criado_em": "2026-10-08T12:00:00.000Z",
      "cancelavel_em": "2026-10-08T12:02:00.000Z",
      "expira_em": "2026-10-08T12:20:00.000Z",
      "encerrado_em": null,
      "substitui_id": null,
      "cliente_ref": "cliente_abc123",
      "sms": []
    }
  ],
  "pagina": 1,
  "tem_mais": false
}

POST /numeros/{id}/cancelar

Chemin : /numeros/{id}/cancelar. Champ facultatif du corps : motivo (desistiu, banido, ja_tem_dono, pede_outra_coisa ou outro ; par défaut outro). Sans code et après cancelavel_em, ferme le numéro et rembourse le solde. Avant cette échéance : 409 cedo avec segundos. Un numéro déjà reçu renvoie nao_cancelavel ; codigo_recebido peut survenir si un code arrive pendant l’annulation. Renvoie 200 avec le numéro fermé. Cet exemple utilise teste_falha après deux minutes.

curl -X POST "https://www.ativasms.store/api/v1/numeros/5f0c0000-0000-4000-8000-000000000001/cancelar" \
  -H "Authorization: Bearer $ATIVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"motivo":"desistiu"}'

HTTP 200

{
  "id": "5f0c0000-0000-4000-8000-000000000001",
  "servico": "teste_falha",
  "servico_nome": "Teste sem código",
  "pais": 73,
  "telefone": "+12025550123",
  "status": "cancelado",
  "motivo": "desistiu",
  "preco_centavos": 83,
  "preco_site_centavos": 120,
  "moeda": "BRL",
  "criado_em": "2026-10-08T12:00:00.000Z",
  "cancelavel_em": "2026-10-08T12:02:00.000Z",
  "expira_em": "2026-10-08T12:20:00.000Z",
  "encerrado_em": "2026-10-08T12:02:01.000Z",
  "substitui_id": null,
  "cliente_ref": "cliente_abc123",
  "sms": []
}

POST /numeros/{id}/trocar

Chemin : /numeros/{id}/trocar. Champ obligatoire du corps : preco_maximo_centavos (entier positif) ; opcao et motivo sont facultatifs, aux formats ci-dessus. Le service, le pays et cliente_ref sont hérités du numéro d’origine. Exige Idempotency-Key et un numéro aguardando_sms avec téléphone, sans code et dont le délai d’annulation est écoulé. Le prix est vérifié avant la fermeture de l’ancien numéro. L’ancien devient trocado et est remboursé ; un nouvel achat renvoie 201 (une répétition renvoie 200). Si le nouvel achat échoue après le remboursement, l’ancien reste trocado : interrogez-le et traitez l’erreur. Cet exemple remplace un numéro teste_falha après deux minutes.

curl -X POST "https://www.ativasms.store/api/v1/numeros/5f0c0000-0000-4000-8000-000000000001/trocar" \
  -H "Authorization: Bearer $ATIVA_API_KEY" \
  -H "Idempotency-Key: troca_abc123" \
  -H "Content-Type: application/json" \
  -d '{"preco_maximo_centavos":83,"motivo":"banido"}'

HTTP 201

{
  "id": "5f0c0000-0000-4000-8000-000000000002",
  "servico": "teste_falha",
  "servico_nome": "Teste sem código",
  "pais": 73,
  "telefone": "+12025550124",
  "status": "aguardando_sms",
  "motivo": null,
  "preco_centavos": 83,
  "preco_site_centavos": 120,
  "moeda": "BRL",
  "criado_em": "2026-10-08T12:02:01.000Z",
  "cancelavel_em": "2026-10-08T12:04:01.000Z",
  "expira_em": "2026-10-08T12:22:01.000Z",
  "encerrado_em": null,
  "substitui_id": "5f0c0000-0000-4000-8000-000000000001",
  "cliente_ref": "cliente_abc123",
  "sms": []
}

POST /numeros/{id}/concluir

Chemin : /numeros/{id}/concluir. Sans corps. Confirme que le code a été utilisé et ferme un numéro recebido sans remboursement. Renvoie 200 avec le numéro concluido ; répéter la finalisation ou finaliser sans code renvoie 409 nao_concluivel.

curl -X POST "https://www.ativasms.store/api/v1/numeros/5f0c0000-0000-4000-8000-000000000001/concluir" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

{
  "id": "5f0c0000-0000-4000-8000-000000000001",
  "servico": "wa",
  "servico_nome": "WhatsApp",
  "pais": 73,
  "telefone": "+12025550123",
  "status": "concluido",
  "motivo": null,
  "preco_centavos": 83,
  "preco_site_centavos": 120,
  "moeda": "BRL",
  "criado_em": "2026-10-08T12:00:00.000Z",
  "cancelavel_em": "2026-10-08T12:02:00.000Z",
  "expira_em": "2026-10-08T12:20:00.000Z",
  "encerrado_em": "2026-10-08T12:01:00.000Z",
  "substitui_id": null,
  "cliente_ref": "cliente_abc123",
  "sms": [
    {
      "codigo": "123456",
      "texto": "Seu código de teste: 123456",
      "recebido_em": "2026-10-08T12:00:20.000Z"
    }
  ]
}

GET /extrato

Paramètre de requête facultatif : pagina (1–1000, par défaut 1). Renvoie jusqu’à 50 opérations du compte, les plus récentes en premier (achat, remboursement, recharge, ajustement), y compris les opérations du site en mode live. valor_centavos est signé : négatif pour les débits, positif pour les crédits ; saldo_apos_centavos est le solde après l’opération. numero_id vaut null si l’opération n’est pas liée à un numéro ; descricao peut être null. Le relevé n’inclut pas moeda : utilisez la devise de GET /saldo. Les relevés de test sont distincts et utilisent toujours BRL.

curl "https://www.ativasms.store/api/v1/extrato?pagina=1" \
  -H "Authorization: Bearer $ATIVA_API_KEY"

HTTP 200

{
  "movimentacoes": [
    {
      "id": 42,
      "tipo": "compra",
      "valor_centavos": -83,
      "saldo_apos_centavos": 9917,
      "descricao": "Número WhatsApp",
      "numero_id": "5f0c0000-0000-4000-8000-000000000001",
      "criado_em": "2026-10-08T12:00:00.000Z"
    }
  ],
  "pagina": 1,
  "tem_mais": false
}

7. Cycle de vie du numéro

Après l’achat, montrez le numéro de téléphone à votre client et demandez-lui de solliciter le code dans l’application de destination. Acheter un numéro ne demande pas automatiquement un SMS. Le mode de test simule la livraison.

Status Signification
aguardando_sms En attente ; un téléphone null peut indiquer une réservation en cours.
recebido Au moins un SMS est arrivé ; annulation et remplacement indisponibles.
concluido Fermé après réception d’un code, manuellement ou à l’expiration du délai.
cancelado Annulé sans code et remboursé.
trocado Le numéro précédent a été fermé et remboursé lors d’un remplacement.
expirado Délai écoulé sans code ; remboursement automatique.
falhou Achat/livraison échoué ; fonds restitués.

Les objets numéro incluent servico_nome, motivo, moeda, preco_site_centavos (peut être null), encerrado_em, substitui_id (l’id du numéro précédent lors du remplacement) et sms. Les dates d’annulation, d’expiration et de fermeture peuvent être null. Chaque SMS inclut codigo et texto (chacun peut être null), ainsi que recebido_em.

Respectez cancelavel_em : certaines options autorisent l’annulation immédiate ; d’autres imposent 2 min. Une erreur cedo précise l’attente restante :

{
  "erro": "cedo",
  "mensagem": "Este número ainda não pode ser cancelado. Tente de novo após `segundos`.",
  "segundos": 45
}

Sans SMS avant le délai, les fonds reviennent automatiquement au solde. Les numéros live peuvent bénéficier d’un délai de grâce de 5 min pour les SMS tardifs avant fermeture. Suivez expira_em et l’état renvoyé. Terminer un numéro après l’arrivée d’un code le ferme sans remboursement ; les numéros ayant reçu un code ne peuvent être annulés ni remplacés. Lors d’un remplacement, l’échec de l’achat du nouveau numéro peut laisser l’ancien à l’état trocado et remboursé.

8. Erreurs

Les erreurs incluent erro et mensagem (en portugais). Traitez le code plutôt que de comparer le message. Les champs supplémentaires peuvent inclure campos, segundos, preco_centavos et opcoes.

Code HTTP Quand cela se produit
nao_autorizado 401 Clé absente, invalide ou révoquée, IP non autorisée ou accès suspendu ; la même réponse générique masque la cause.
parametro_invalido 400 Champ, requête ou corps invalide ; peut inclure campos listant les champs à corriger.
servico_bloqueado 400 Un service bloqué a été demandé.
idempotencia_obrigatoria 400 Idempotency-Key absent ou mal formé lors de l’achat ou du remplacement.
saldo_insuficiente 402 Fonds insuffisants pour l’achat.
cliente_bloqueado 403 cliente_ref est bloqué pour ce compte.
nao_encontrado 404 UUID invalide ou numéro indisponible pour ce compte et ce mode.
sem_numeros 409 Aucun numéro disponible pour ce service et ce pays ; aucun débit.
opcao_indisponivel 409 L’option choisie n’est plus disponible ; inclut les opcoes actuelles.
preco_acima_do_maximo 409 Le prix actuel dépasse le maximum ; inclut preco_centavos et opcoes, sans débit.
preco_subiu 409 Le prix a changé pendant l’achat ; le montant a été remboursé.
cedo 409 Trop tôt pour annuler ou remplacer ; inclut l’attente dans segundos.
codigo_recebido 409 Un code reçu empêche le remplacement, ou est arrivé pendant l’annulation.
nao_cancelavel 409 L’état ne permet pas l’annulation, y compris un numéro déjà recebido.
nao_trocavel 409 Pas de téléphone, numéro fermé ou état incompatible avec le remplacement.
nao_concluivel 409 Le numéro n’est pas recebido, y compris lors d’une deuxième demande de finalisation.
muitas_requisicoes 429 Débit de requêtes ou d’achats dépassé ; Retry-After: 60.
limite_abertas 429 Trop de numéros en attente de SMS ; pas de Retry-After.
limite_tentativas 429 Tentatives sans code du compte épuisées sur 24 h ; pas de Retry-After.
limite_tentativas_cliente 429 Tentatives sans code épuisées pour ce cliente_ref sur 24 h ; pas de Retry-After.
erro_fornecedor 502 La livraison du numéro a échoué ; le montant a été remboursé.
sem_confirmacao 502 Achat non confirmé ; réessayez avec la même Idempotency-Key. Si aucun numéro n’est livré sous 10 min, le montant est remboursé.
erro_interno 500 Défaillance inattendue ; réessayez les achats et remplacements avec la même clé d’idempotence.

9. Webhooks et signatures

Configurez une URL HTTPS publique dans Tableau de bord → API. Les adresses internes, identifiants et fragments d’URL sont refusés ; les redirections ne sont pas suivies. Le secret est affiché une fois à sa création ou à son renouvellement : conservez-le sur votre serveur. Modifier uniquement l’URL préserve le secret ; supprimer le webhook efface sa configuration. Chaque tentative de livraison utilise l’URL et le secret actuels.

Les événements sont envoyés sous forme de requêtes POST JSON :

  • numero.codigo_recebido : une notification par nouveau SMS ; dados.sms est un objet avec codigo, texto et recebido_em, et non un tableau de SMS. Un numéro peut recevoir plusieurs notifications, y compris des SMS tardifs après fermeture.
  • numero.encerrado : concluido, cancelado, trocado, expirado ou falhou ; inclut motivo (peut être null), encerrado_em et estornado_centavos (zéro sans remboursement), dans la devise indiquée.
  • teste.ping : un test de livraison demandé dans le tableau de bord, sans numéro ni débit.

L’enveloppe contient id (l’UUID de la notification, différent de dados.id), evento, criado_em et dados. Les événements de numéros incluent id, cliente_ref, servico, pais, telefone (ou null), status et moeda dans dados. Les événements des numéros de test ajoutent modo: "test" à l’enveloppe ; les événements live et le ping du tableau de bord omettent ce champ. Exemples :

{
  "id": "a7100000-0000-4000-8000-000000000001",
  "evento": "numero.codigo_recebido",
  "criado_em": "2026-10-08T12:00:20.000Z",
  "modo": "test",
  "dados": {
    "id": "5f0c0000-0000-4000-8000-000000000001",
    "cliente_ref": "cliente_abc123",
    "servico": "wa",
    "pais": 73,
    "telefone": "+12025550123",
    "status": "recebido",
    "moeda": "BRL",
    "sms": {
      "codigo": "123456",
      "texto": "Seu código de teste: 123456",
      "recebido_em": "2026-10-08T12:00:20.000Z"
    }
  }
}
{
  "id": "a7100000-0000-4000-8000-000000000002",
  "evento": "numero.encerrado",
  "criado_em": "2026-10-08T12:20:00.000Z",
  "modo": "test",
  "dados": {
    "id": "5f0c0000-0000-4000-8000-000000000001",
    "cliente_ref": "cliente_abc123",
    "servico": "teste_falha",
    "pais": 73,
    "telefone": "+12025550123",
    "status": "expirado",
    "moeda": "BRL",
    "motivo": "prazo",
    "encerrado_em": "2026-10-08T12:20:00.000Z",
    "estornado_centavos": 83
  }
}
{
  "id": "a7100000-0000-4000-8000-000000000003",
  "evento": "teste.ping",
  "criado_em": "2026-10-08T12:00:00.000Z",
  "dados": {
    "mensagem": "Teste de webhook do Ativa SMS"
  }
}

En-têtes :

  • X-Ativa-Evento : nom de l’événement.
  • X-Ativa-Timestamp : horodatage Unix en secondes, renouvelé à chaque tentative.
  • X-Ativa-Assinatura : HMAC-SHA256 en hexadécimal minuscule sur les octets UTF-8 de timestamp + "." + body, en utilisant le secret comme texte, sans le décoder depuis l’hexadécimal.

Vérifiez le corps brut avant d’analyser le JSON ; une nouvelle sérialisation peut modifier la signature. Refusez les horodatages distants de plus de 5 minutes de l’heure actuelle, dans les deux sens. Comparez avec crypto.timingSafeEqual après vérification des longueurs. Cet exemple Node.js utilise Request/Headers et un Buffer contenant le corps d’origine :

import { createHmac, timingSafeEqual } from "node:crypto";

export function verificarWebhook(headers, corpoBruto, segredo) {
  const timestamp = headers.get("x-ativa-timestamp") ?? "";
  const assinatura = headers.get("x-ativa-assinatura") ?? "";
  if (!/^\d{1,12}$/.test(timestamp)) return false;
  const agora = Math.floor(Date.now() / 1000);
  if (Math.abs(agora - Number(timestamp)) > 300) return false;
  if (!/^[0-9a-f]{64}$/.test(assinatura)) return false;
  const esperada = createHmac("sha256", segredo)
    .update(timestamp + ".", "utf8")
    .update(corpoBruto)
    .digest();
  const recebida = Buffer.from(assinatura, "hex");
  return recebida.length === esperada.length
    && timingSafeEqual(recebida, esperada);
}

export async function lerAviso(request, segredo) {
  const corpoBruto = Buffer.from(await request.arrayBuffer());
  if (!verificarWebhook(request.headers, corpoBruto, segredo)) {
    throw new Error("assinatura_invalida");
  }
  return JSON.parse(corpoBruto.toString("utf8"));
}

Après vérification, dédupliquez par l’id de la notification dans un stockage persistant et enregistrez la réception avant le traitement asynchrone. Renvoyez 2xx rapidement, y compris pour les doublons déjà acceptés. Le délai maximal de livraison est de 5 s. Une livraison exactement une fois n’est pas garantie : une réponse perdue peut entraîner une répétition.

Jusqu’à 8 tentatives au total. Après les échecs 1–7, les attentes sont de 1, 2, 4, 8, 16, 32 et 64 min, à compter de la fin de la tentative ; les livraisons sont vérifiées chaque minute. Seul HTTP 2xx confirme la livraison ; 3xx, autres erreurs et dépassements de délai déclenchent une nouvelle tentative. Après le huitième échec, aucun renvoi automatique n’a lieu. L’id et le corps restent identiques entre tentatives ; l’horodatage et la signature sont renouvelés. Interrogez GET /numeros/{id} si une notification manque.

10. Mode de test

Créez une clé ativa_test_ dans le tableau de bord et utilisez les mêmes endpoints et URL de base. Aucun fonds réel n’est débité. Le solde fictif initial est de R$ 100.00 (10000 centimes BRL), quelle que soit la devise réelle du compte. L’option est op_000000000001 : prix fictif R$ 0.83, référence du site R$ 1.20, durée de 20 min et annulation après 2 min.

Les téléphones sont des numéros fictifs de la plage +1 202 555-01xx, renvoyés sans espaces, comme +12025550123 ; le pays choisi reste dans pais. Le code 123456 devient disponible après 20 s ; interroger le numéro ensuite met à jour son état, tandis que le webhook peut arriver à la minute suivante.

Utilisez servico=teste_falha pour ne recevoir aucun code et tester l’annulation, le remplacement (après 2 min) ou l’expiration (après 20 min), avec remboursement fictif. Les autres services reçoivent le code avant la fin de l’attente d’annulation. Les numéros de test avec code deviennent concluido à l’expiration.

Réinitialisez le solde à R$ 100.00 dans le tableau de bord : les numéros ouverts sans code sont annulés ; ceux avec code sont terminés. La réinitialisation préserve l’historique, les tentatives, l’idempotence et les clés ; elle ne réinitialise pas les limites par minute. Utilisez une nouvelle Idempotency-Key pour l’achat suivant. L’historique de test est nettoyé après 30 jours.

Les clés live et test ne se mélangent pas : numéros, soldes, relevés, compteurs d’utilisation et idempotence sont distincts. Les limites configurées et les blocages de cliente_ref s’appliquent aux deux modes. Le ping du tableau de bord teste uniquement la livraison ; achetez un numéro avec une clé de test pour exercer modo: "test".

11. Conseils d’intégration

  • Réutilisez Idempotency-Key après une panne réseau ; vérifiez le résultat avant de choisir une nouvelle clé.
  • Interrogez GET /numeros/{id} si un webhook manque, et respectez les limites d’utilisation.
  • Traitez preco_acima_do_maximo : affichez le prix actualisé et obtenez la décision du client avant un nouvel achat.
  • Gardez votre prix d’acquisition (preco_centavos) confidentiel ; protégez aussi vos clés et le secret du webhook.
  • Envoyez uniquement un cliente_ref anonyme ; conservez le lien avec votre client dans votre propre système.

Consultez les Conditions de revente. Contact : contato@ativasms.store.

Assistance