Accès programmatique aux numéros virtuels, commandes et solde du compte.
Recommandé
⟩Commencez avec les SDK officiels
Utilisez le SDK TypeScript/JavaScript ou Python pour les nouvelles intégrations. Les deux SDK utilisent par défaut l'API publique /v2, conservent les clés d'idempotence lors des reprises sûres, exposent des erreurs typées et gardent le cycle de vie OTP cohérent.
Créez une commande avec product_id pour un créneau de palier exact et stable, ou avec catalog_product_id, operator_id optionnel, min_price/max_price et une clé d’idempotence pour des appels payants routés sûrs en cas de nouvelle tentative.
catalog_product_idmax_priceIdempotency-Key
02
Utiliser l'OTP
Attendez l'OTP, soumettez-le dans votre application cible, puis appelez finish pour fermer la commande.
waitForOtpwait_for_otpfinish
03
Renvoyer seulement si nécessaire
Après un renvoi, attendez un nouveau code avec afterCode en TypeScript ou after_code en Python.
can_resendresend_available_at
import { SmscodeClient, OtpTimeoutError } from "@smscode/sdk";const client = new SmscodeClient({ token: process.env.SMSCODE_TOKEN! });let orderId: number | undefined;try { const created = await client.orders.create({ catalog_product_id: Number(process.env.SMSCODE_CATALOG_PRODUCT_ID), max_price: "0.50", quantity: 1, }); const order = created.orders[0]!; orderId = order.id; const first = await client.orders.waitForOtp(orderId, { timeoutMs: 120_000 }); console.log(first.otpCode); // Soumettre ce code dans l'application cible. await client.orders.finish(orderId);} catch (err) { if (err instanceof OtpTimeoutError && orderId !== undefined) { const current = await client.orders.get(orderId); if (current.can_cancel) await client.orders.cancel(orderId); } throw err;}
import osfrom smscode import OtpTimeoutError, SmscodeClientwith SmscodeClient(token=os.environ["SMSCODE_TOKEN"]) as client: created = client.orders.create( catalog_product_id=int(os.environ["SMSCODE_CATALOG_PRODUCT_ID"]), max_price="0.50", quantity=1, ) order = created.orders[0] order_id = int(order["id"]) try: first = client.orders.wait_for_otp(order_id, timeout_ms=120_000) print(first.otp_code) # Soumettre ce code dans l'application cible. client.orders.finish(order_id) except OtpTimeoutError: current = client.orders.get(order_id) if current["can_cancel"]: client.orders.cancel(order_id) raise
Utilisez can_resend et resend_available_at pour le timing de renvoi. Les timestamps de renvoi de bas niveau sont internes et ne sont pas des champs de réponse publics.
⟩Apercu
Tous les champs monétaires de l'API /v1 sont en IDR (roupie indonésienne), sous forme d'unités entières — par exemple, "price": 15000 et "balance": 500000 signifient Rp 15 000 et Rp 500 000. Pour une projection native en USD du même registre, passez à l'API v2 à l'aide du sélecteur de version ci-dessus.
⟩Authentification
Toutes les requêtes API nécessitent un Bearer token. Générez-en un depuis les Paramètres du compte dans le tableau de bord, puis incluez-le dans chaque requête :
Authorization:Bearer YOUR_API_TOKEN
Les requêtes sans token valide reçoivent une réponse 401 UNAUTHORIZED.
⟩URL de base
Tous les chemins d'endpoints ci-dessous sont relatifs à :
https://api.smscode.gg/v1
⟩Format de réponse
Chaque réponse renvoie du JSON avec une enveloppe cohérente. Toutes les réponses incluent un en-tête x-request-id pour le débogage.
Tous les champs monétaires de l'API /v1 sont en IDR (roupie indonésienne), sous forme d'unités entières — par exemple, "price": 15000 et "balance": 500000 signifient Rp 15 000 et Rp 500 000. Pour une projection native en USD du même registre, passez à l'API v2 à l'aide du sélecteur de version ci-dessus.
Renvoie les opérateurs sélectionnables pour un pays + service. Si des opérateurs réels et du stock Any sont disponibles, la réponse inclut une ligne Any avec operator_id null ; s’il n’existe aucun produit spécifique à un opérateur, la liste est vide.
Crée une nouvelle commande de numéro virtuel. Débite le solde automatiquement. Prend en charge un en-tête Idempotency-Key optionnel pour éviter les commandes en double lors des nouvelles tentatives réseau.
Corps de la requête
Nom
Type
Requis
Description
product_id
integer
Non
ID produit stable d’un créneau de palier exact pour commander directement. Fournissez SOIT celui-ci SOIT catalog_product_id, pas les deux.
catalog_product_id
integer
Non
ID umbrella routé pays+plateforme. Le serveur choisit un palier actuel correspondant. Fournissez celui-ci ou product_id.
operator_id
integer
Non
ID d’opérateur optionnel depuis /catalog/operators. Valide uniquement avec catalog_product_id ; omettez-le pour Any.
min_price
integer
Non
Prix plancher optionnel. Entier IDR. Valide uniquement avec catalog_product_id.
max_price
integer
Non
Prix plafond optionnel. Entier IDR. Valide uniquement avec catalog_product_id.
prefer_provider
string
Non
Code de fournisseur facultatif à privilégier en cas d'offres équivalentes.
policy
string
Non
Politique de routage facultative, valable uniquement avec catalog_product_id. Valeurs : cheapest (par défaut) choisit l'offre saine la moins chère ; best_success classe les offres d'abord selon le succès de livraison récent. best_success note chaque fournisseur sur la proportion de commandes ayant reçu un OTP au cours des 30 derniers jours complets, par tranches de 10%, et ne le comptabilise qu'à partir d'au moins 20 commandes sur cette période — les fournisseurs sous ce seuil ou sans historique sont considérés comme neutres, de sorte que les nouvelles offres ne sont jamais écartées (sur option ; le signal démarre neutre). Si prefer_provider est également défini, le fournisseur préféré reste en première position.
quantity
integer
Non
Quantité (1-100, défaut 1)
Passez un en-tête Idempotency-Key pour réessayer en toute sécurité sans créer de doublons. La clé peut contenir des lettres, des chiffres, un trait d'union et un tiret bas (A-Z a-z 0-9 _ -), jusqu'à 128 caractères ; une clé invalide est rejetée avec 422 VALIDATION_ERROR. Réessayer avec la même clé et le même corps rejoue le résultat d'origine (y compris le failed_count d'un succès partiel). Une nouvelle tentative qui atteint le fournisseur mais échoue est enregistrée et rejoue cette même erreur — utilisez une NOUVELLE clé pour réessayer. Les échecs sans effet de bord (solde insuffisant, aucune offre disponible) libèrent la clé, vous pouvez donc recharger votre solde et réessayer avec la même clé. Réutiliser une clé avec un corps différent renvoie 422 IDEMPOTENCY_KEY_REUSED, et une requête encore en cours avec cette clé renvoie 409 REQUEST_IN_PROGRESS. Le champ failed_reason dans les réponses de create est toujours null — il n'est renseigné qu'à la consultation/au listage des commandes.
Exemple de requête
curl -s -X POST https://api.smscode.gg/v1/orders/create \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: unique-request-id-123" \ -d '{"product_id":142,"quantity":1}'# Or route by catalog_product_id — the server picks a current tier.# product_id is the stable exact tier-slot id. catalog_product_id is the# country+platform umbrella for routed ordering. Optional min_price/max_price# bound the tier; operator_id scopes to a carrier from /catalog/operators.# Pass EITHER product_id OR catalog_product_id.curl -s -X POST https://api.smscode.gg/v1/orders/create \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: unique-request-id-124" \ -d '{"catalog_product_id":87,"min_price":12000,"max_price":20000,"operator_id":433}'
Demande à la plateforme de renvoyer le SMS au numéro loué. Toutes les plateformes ne prennent pas en charge le renvoi — vérifiez le champ resent dans la réponse.
Réactive un numéro terminé — commande à nouveau le même numéro pour un autre code de vérification, sans louer un nouveau numéro. Seule une commande terminée dont le numéro prend en charge la réactivation est éligible (vérifiez can_reactivate sur la commande, ou obtenez un aperçu avec reactivate-options). La commande enfant réactivée est une NOUVELLE commande, renvoyée sous la même forme que create ; le solde est débité automatiquement.
Corps de la requête
Nom
Type
Requis
Description
id
integer
Oui
La commande terminée à réactiver.
max_price
integer
Non
Plafond de coût optionnel. Entier IDR. La réactivation est refusée avec 422 VALIDATION_ERROR si le coût actuel le dépasse.
Comme create, il s'agit d'une mutation monétaire — passez un en-tête Idempotency-Key pour réessayer en toute sécurité (un create et un reactivate ne peuvent jamais entrer en conflit sur une même clé). Réutiliser une clé avec un corps différent renvoie 422 IDEMPOTENCY_KEY_REUSED, et une requête encore en cours de traitement avec cette clé renvoie 409 REQUEST_IN_PROGRESS. Un numéro qui ne peut pas être réactivé renvoie 409 CONFLICT ; un solde trop faible renvoie 409 INSUFFICIENT_BALANCE.
Prévisualise ce qu'une réactivation coûterait maintenant. En lecture seule — ne consomme aucun Idempotency-Key et ne crée rien. Renvoie le coût sous forme d'entier IDR. Disponible uniquement pour une commande terminée dont le numéro prend en charge la réactivation.
Paramètres de chemin
Nom
Type
Requis
Description
id
integer
Oui
Identifiant de la commande pour laquelle prévisualiser le coût de réactivation (paramètre de chemin).
Met à jour votre URL webhook et/ou votre secret. Un secret est généré automatiquement lorsque vous définissez une URL pour la première fois. Envoyez une chaîne vide pour effacer. L'URL doit utiliser HTTPS.
Corps de la requête
Nom
Type
Requis
Description
webhook_url
string
Non
URL HTTPS pour recevoir les événements webhook (chaîne vide pour effacer)
webhook_secret
string
Non
Secret partagé pour la signature HMAC-SHA256 (généré automatiquement s'il est omis lors de la première configuration)
Envoie un événement test à votre URL webhook configurée. Renvoie le code de statut HTTP de votre serveur. Utile pour vérifier que votre endpoint fonctionne avant la mise en production.
Paramètres
Aucun
Exemple de requête
curl -s -X POST https://api.smscode.gg/v1/webhook/test \ -H "Authorization: Bearer YOUR_API_TOKEN"
const res = await fetch("https://api.smscode.gg/v1/webhook/test", { method: "POST", headers: { Authorization: "Bearer YOUR_API_TOKEN" },});const data = await res.json();
Configurez une URL webhook pour recevoir des notifications push en temps réel pour les événements de commande au lieu du polling. C'est l'approche recommandée pour les scripts de bot.
Événements
Événement
Déclencheur
order.otp_received
Nouveau SMS reçu ; le code détecté peut être null
order.completed
Commande marquée comme terminée (manuellement ou par expiration)
order.expired
Commande expirée avant la réception d'un SMS (solde remboursé)
order.canceled
Commande annulée par l'utilisateur (solde remboursé)
Chaque nouveau SMS émet cet événement. otp_code peut être null lorsque otp_message est présent. Plusieurs événements SMS peuvent arriver dans le désordre ; utilisez sms_revision pour ignorer une paire agrégée plus ancienne.
Chaque requête webhook inclut un en-tête X-Webhook-Signature avec une signature HMAC-SHA256 du corps de la requête, utilisant votre webhook_secret comme clé :
Vérifiez cette signature côté serveur pour vous assurer que la requête est authentique. La livraison est de type « fire-and-forget » avec un délai d'expiration de 3 secondes et sans nouvelle tentative.
⟩Limites de débit
Les requêtes API sont soumises à des limites de débit par groupe d'endpoints. Le dépassement de la limite renvoie un code 429 Too Many Requests avec un en-tête Retry-After indiquant le nombre de secondes à attendre.
Groupe d'endpoints
Limite
Fenêtre
Catalogue (pays, services, produits, taux de change)
5 000 requêtes
60 secondes
Solde
600 requêtes
60 secondes
Lecture de commandes (liste, détail, actives)
5 000 requêtes
60 secondes
Création de commande
3 000 requêtes
60 secondes
Annulation de commande
1 000 requêtes
60 secondes
Actions sur les commandes (finaliser, renvoyer)
1 000 requêtes
60 secondes
Configuration webhook (lecture, mise à jour)
600 requêtes
60 secondes
Test webhook
10 requêtes
60 secondes
⟩Codes d'erreur
Les réponses d'erreur incluent l'un de ces codes dans error.code :
Code
HTTP
Description
UNAUTHORIZED
401
Token API manquant ou invalide
FORBIDDEN
403
Accès refusé
NOT_FOUND
404
Ressource introuvable (commande, taux de change, etc.)
CONFLICT
409
Requête en double ou conflit de ressource
INSUFFICIENT_BALANCE
409
Solde insuffisant pour créer la commande
VALIDATION_ERROR
422
Les paramètres de la requête n'ont pas passé la validation
RATE_LIMIT_EXCEEDED
429
Trop de requêtes (vérifiez l'en-tête Retry-After)
INTERNAL_ERROR
500
Erreur interne du serveur
PROVIDER_ERROR
422
Le fournisseur SMS en amont a rejeté la requête. En cas d'échec de création de commande, l'erreur peut inclure details : cause_counts (commandes avec product_id hérité — un décompte regroupé par cause) ou attempts (commandes avec catalog_product_id — résultats par tentative), avec les valeurs ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error.
NO_OFFER_AVAILABLE
422
Aucune offre active ne correspond au produit et à la politique demandés (plafond de prix, disponibilité).
CANCEL_TOO_EARLY
409
Commande trop récente pour être annulée — patientez 2 minutes
REQUEST_IN_PROGRESS
409
Une requête de création avec cette clé d'idempotence est encore en cours
IDEMPOTENCY_KEY_REUSED
422
Cette clé d'idempotence a déjà été utilisée avec un corps de requête différent
SERVICE_UNAVAILABLE
503
Service temporairement indisponible (maintenance)
⟩Apercu
Tous les champs monétaires de l'API /v2 sont en USD et renvoyés sous forme d'objet monétaire — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount est une chaîne décimale ; canonical_amount est la valeur IDR exacte du registre (utilisez-la pour le rapprochement). Le rate USD/IDR appliqué est communiqué une seule fois par réponse dans meta.fx. v2 est une projection en USD au moment du rendu, par-dessus le même registre IDR que v1 — elle ne stocke ni ne traite jamais d'USD.
Toutes les requêtes API nécessitent un Bearer token. Générez-en un depuis les Paramètres du compte dans le tableau de bord, puis incluez-le dans chaque requête :
Authorization:Bearer YOUR_API_TOKEN
Les requêtes sans token valide reçoivent une réponse 401 UNAUTHORIZED.
⟩URL de base
Tous les chemins d'endpoints ci-dessous sont relatifs à :
https://api.smscode.gg/v2
⟩Format de réponse
Chaque réponse renvoie du JSON avec une enveloppe cohérente. Toutes les réponses incluent un en-tête x-request-id pour le débogage.
Tous les champs monétaires de l'API /v2 sont en USD et renvoyés sous forme d'objet monétaire — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount est une chaîne décimale ; canonical_amount est la valeur IDR exacte du registre (utilisez-la pour le rapprochement). Le rate USD/IDR appliqué est communiqué une seule fois par réponse dans meta.fx. v2 est une projection en USD au moment du rendu, par-dessus le même registre IDR que v1 — elle ne stocke ni ne traite jamais d'USD.
Identique à v1 — seul le chemin de base change (/v1 → /v2).
GET/catalog/operators
Renvoie les opérateurs sélectionnables pour un pays + service. Si des opérateurs réels et du stock Any sont disponibles, la réponse inclut une ligne Any avec operator_id null ; s’il n’existe aucun produit spécifique à un opérateur, la liste est vide.
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
GET/catalog/exchange-rate
Renvoie le taux de change USD/IDR actuel utilisé pour la conversion de devises.
Paramètres
Aucun — v2 renvoie toujours USD/IDR ; le paramètre ?pair de v1 est ignoré.
v2 : renvoie { pair, rate, rate_as_of } (pas de base_currency/quote_currency, pas d'enveloppe meta — le taux est la donnée). ?pair est ignoré — v2 renvoie toujours USD/IDR (v1 prend en compte ?pair). Renvoie 503 FX_RATE_UNAVAILABLE s'il n'existe aucun taux utilisable.
GET/balance
Renvoie le solde du compte de l'utilisateur authentifié.
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
GET/orders
Renvoie la liste des commandes de l'utilisateur authentifié, triées par date décroissante. Filtrage par statut et pagination via offset.
Paramètres de requête
Nom
Type
Requis
Description
limit
integer
Non
Nombre max. de résultats (1-100, défaut 20)
offset
integer
Non
Nombre de résultats à ignorer (défaut 0)
status
string
Non
Filtrer par statut : ACTIVE, OTP_RECEIVED, COMPLETED, CANCELED, EXPIRED (insensible à la casse)
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
GET/orders/{id}
Renvoie une commande par son identifiant. Ne renvoie que les commandes de l'utilisateur authentifié.
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
GET/orders/active
Liste toutes les commandes actuellement actives (ACTIVE + OTP_RECEIVED). Utilisez cet endpoint pour surveiller les mises à jour de statut OTP.
v2 : cet endpoint n'est pas porteur de montant — il ne renvoie ni amount ni meta.fx (même structure que v1, sous /v2).
POST/orders/create
Crée une nouvelle commande de numéro virtuel. Débite le solde automatiquement. Prend en charge un en-tête Idempotency-Key optionnel pour éviter les commandes en double lors des nouvelles tentatives réseau.
Corps de la requête
Nom
Type
Requis
Description
product_id
integer
Non
ID produit stable d’un créneau de palier exact pour commander directement. Fournissez SOIT celui-ci SOIT catalog_product_id, pas les deux.
catalog_product_id
integer
Non
ID umbrella routé pays+plateforme. Le serveur choisit un palier actuel correspondant. Fournissez celui-ci ou product_id.
operator_id
integer
Non
ID d’opérateur optionnel depuis /catalog/operators. Valide uniquement avec catalog_product_id ; omettez-le pour Any.
min_price
string
Non
Prix plancher optionnel. Chaîne décimale USD (par ex. "0.30"). Valide uniquement avec catalog_product_id.
max_price
string
Non
Prix plafond optionnel. Chaîne décimale USD (par ex. "0.50"). Valide uniquement avec catalog_product_id.
prefer_provider
string
Non
Code de fournisseur facultatif à privilégier en cas d'offres équivalentes.
policy
string
Non
Politique de routage facultative, valable uniquement avec catalog_product_id. Valeurs : cheapest (par défaut) choisit l'offre saine la moins chère ; best_success classe les offres d'abord selon le succès de livraison récent. best_success note chaque fournisseur sur la proportion de commandes ayant reçu un OTP au cours des 30 derniers jours complets, par tranches de 10%, et ne le comptabilise qu'à partir d'au moins 20 commandes sur cette période — les fournisseurs sous ce seuil ou sans historique sont considérés comme neutres, de sorte que les nouvelles offres ne sont jamais écartées (sur option ; le signal démarre neutre). Si prefer_provider est également défini, le fournisseur préféré reste en première position.
quantity
integer
Non
Quantité (1-100, défaut 1)
Passez un en-tête Idempotency-Key pour réessayer en toute sécurité sans créer de doublons. La clé peut contenir des lettres, des chiffres, un trait d'union et un tiret bas (A-Z a-z 0-9 _ -), jusqu'à 128 caractères ; une clé invalide est rejetée avec 422 VALIDATION_ERROR. Réessayer avec la même clé et le même corps rejoue le résultat d'origine (y compris le failed_count d'un succès partiel). Une nouvelle tentative qui atteint le fournisseur mais échoue est enregistrée et rejoue cette même erreur — utilisez une NOUVELLE clé pour réessayer. Les échecs sans effet de bord (solde insuffisant, aucune offre disponible) libèrent la clé, vous pouvez donc recharger votre solde et réessayer avec la même clé. Réutiliser une clé avec un corps différent renvoie 422 IDEMPOTENCY_KEY_REUSED, et une requête encore en cours avec cette clé renvoie 409 REQUEST_IN_PROGRESS. Le champ failed_reason dans les réponses de create est toujours null — il n'est renseigné qu'à la consultation/au listage des commandes.
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
POST/orders/cancel
Annule une commande active. Le coût de la location est remboursé sur le solde de votre compte.
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
POST/orders/finish
Marque une commande comme terminée après réception de l'OTP. Cela libère le numéro immédiatement au lieu d'attendre l'expiration.
Identique à v1 — seul le chemin de base change (/v1 → /v2).
POST/orders/resend
Demande à la plateforme de renvoyer le SMS au numéro loué. Toutes les plateformes ne prennent pas en charge le renvoi — vérifiez le champ resent dans la réponse.
Identique à v1 — seul le chemin de base change (/v1 → /v2).
POST/orders/reactivate
Réactive un numéro terminé — commande à nouveau le même numéro pour un autre code de vérification, sans louer un nouveau numéro. Seule une commande terminée dont le numéro prend en charge la réactivation est éligible (vérifiez can_reactivate sur la commande, ou obtenez un aperçu avec reactivate-options). La commande enfant réactivée est une NOUVELLE commande, renvoyée sous la même forme que create ; le solde est débité automatiquement.
Corps de la requête
Nom
Type
Requis
Description
id
integer
Oui
La commande terminée à réactiver.
max_price
string
Non
Plafond de coût optionnel. Chaîne décimale USD (par ex. "0.50"). La réactivation est refusée avec 422 VALIDATION_ERROR si le coût actuel le dépasse.
Comme create, il s'agit d'une mutation monétaire — passez un en-tête Idempotency-Key pour réessayer en toute sécurité (un create et un reactivate ne peuvent jamais entrer en conflit sur une même clé). Réutiliser une clé avec un corps différent renvoie 422 IDEMPOTENCY_KEY_REUSED, et une requête encore en cours de traitement avec cette clé renvoie 409 REQUEST_IN_PROGRESS. Un numéro qui ne peut pas être réactivé renvoie 409 CONFLICT ; un solde trop faible renvoie 409 INSUFFICIENT_BALANCE.
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
GET/orders/{id}/reactivate-options
Prévisualise ce qu'une réactivation coûterait maintenant. En lecture seule — ne consomme aucun Idempotency-Key et ne crée rien. Renvoie le coût sous forme d'objet monétaire USD avec un reçu FX. Disponible uniquement pour une commande terminée dont le numéro prend en charge la réactivation.
Paramètres de chemin
Nom
Type
Requis
Description
id
integer
Oui
Identifiant de la commande pour laquelle prévisualiser le coût de réactivation (paramètre de chemin).
v2 : les champs monétaires sont des objets monétaires en USD et la réponse contient un unique meta.fx { pair, rate, rate_as_of }. rate est le montant entier en IDR pour 1 USD, donc USD = canonical_amount / rate. Les totaux utilisent 2 décimales ; les prix/remboursements par article en utilisent 4. Un montant strictement positif n'est jamais arrondi à 0.00. rate_as_of est l'horodatage RFC3339 du taux (au format +00:00) ou null lorsqu'aucun horodatage n'est enregistré.
v2 uniquement : s'il n'existe aucun taux USD/IDR utilisable, les endpoints monétaires renvoient 503 FX_RATE_UNAVAILABLE avec un en-tête Retry-After au lieu d'un corps monétaire. v1 ne renvoie jamais cela.
GET/webhook
Renvoie votre configuration actuelle de notifications webhook.
Identique à v1 — seul le chemin de base change (/v1 → /v2).
PATCH/webhook
Met à jour votre URL webhook et/ou votre secret. Un secret est généré automatiquement lorsque vous définissez une URL pour la première fois. Envoyez une chaîne vide pour effacer. L'URL doit utiliser HTTPS.
Corps de la requête
Nom
Type
Requis
Description
webhook_url
string
Non
URL HTTPS pour recevoir les événements webhook (chaîne vide pour effacer)
webhook_secret
string
Non
Secret partagé pour la signature HMAC-SHA256 (généré automatiquement s'il est omis lors de la première configuration)
Identique à v1 — seul le chemin de base change (/v1 → /v2).
POST/webhook/test
Envoie un événement test à votre URL webhook configurée. Renvoie le code de statut HTTP de votre serveur. Utile pour vérifier que votre endpoint fonctionne avant la mise en production.
Paramètres
Aucun
Exemple de requête
curl -s -X POST https://api.smscode.gg/v2/webhook/test \ -H "Authorization: Bearer YOUR_API_TOKEN"
const res = await fetch("https://api.smscode.gg/v2/webhook/test", { method: "POST", headers: { Authorization: "Bearer YOUR_API_TOKEN" },});const data = await res.json();
Identique à v1 — seul le chemin de base change (/v1 → /v2).
⟩Notifications webhook
Configurez une URL webhook pour recevoir des notifications push en temps réel pour les événements de commande au lieu du polling. C'est l'approche recommandée pour les scripts de bot.
Événements
Événement
Déclencheur
order.otp_received
Nouveau SMS reçu ; le code détecté peut être null
order.completed
Commande marquée comme terminée (manuellement ou par expiration)
order.expired
Commande expirée avant la réception d'un SMS (solde remboursé)
order.canceled
Commande annulée par l'utilisateur (solde remboursé)
Chaque nouveau SMS émet cet événement. otp_code peut être null lorsque otp_message est présent. Plusieurs événements SMS peuvent arriver dans le désordre ; utilisez sms_revision pour ignorer une paire agrégée plus ancienne.
Chaque requête webhook inclut un en-tête X-Webhook-Signature avec une signature HMAC-SHA256 du corps de la requête, utilisant votre webhook_secret comme clé :
Vérifiez cette signature côté serveur pour vous assurer que la requête est authentique. La livraison est de type « fire-and-forget » avec un délai d'expiration de 3 secondes et sans nouvelle tentative.
⟩Limites de débit
Les requêtes API sont soumises à des limites de débit par groupe d'endpoints. Le dépassement de la limite renvoie un code 429 Too Many Requests avec un en-tête Retry-After indiquant le nombre de secondes à attendre.
Groupe d'endpoints
Limite
Fenêtre
Catalogue (pays, services, produits, taux de change)
5 000 requêtes
60 secondes
Solde
600 requêtes
60 secondes
Lecture de commandes (liste, détail, actives)
5 000 requêtes
60 secondes
Création de commande
3 000 requêtes
60 secondes
Annulation de commande
1 000 requêtes
60 secondes
Actions sur les commandes (finaliser, renvoyer)
1 000 requêtes
60 secondes
Configuration webhook (lecture, mise à jour)
600 requêtes
60 secondes
Test webhook
10 requêtes
60 secondes
⟩Codes d'erreur
Les réponses d'erreur incluent l'un de ces codes dans error.code :
Code
HTTP
Description
UNAUTHORIZED
401
Token API manquant ou invalide
FORBIDDEN
403
Accès refusé
NOT_FOUND
404
Ressource introuvable (commande, taux de change, etc.)
CONFLICT
409
Requête en double ou conflit de ressource
INSUFFICIENT_BALANCE
409
Solde insuffisant pour créer la commande
VALIDATION_ERROR
422
Les paramètres de la requête n'ont pas passé la validation
RATE_LIMIT_EXCEEDED
429
Trop de requêtes (vérifiez l'en-tête Retry-After)
INTERNAL_ERROR
500
Erreur interne du serveur
PROVIDER_ERROR
422
Le fournisseur SMS en amont a rejeté la requête. En cas d'échec de création de commande, l'erreur peut inclure details : cause_counts (commandes avec product_id hérité — un décompte regroupé par cause) ou attempts (commandes avec catalog_product_id — résultats par tentative), avec les valeurs ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error.
NO_OFFER_AVAILABLE
422
Aucune offre active ne correspond au produit et à la politique demandés (plafond de prix, disponibilité).
CANCEL_TOO_EARLY
409
Commande trop récente pour être annulée — patientez 2 minutes
REQUEST_IN_PROGRESS
409
Une requête de création avec cette clé d'idempotence est encore en cours
IDEMPOTENCY_KEY_REUSED
422
Cette clé d'idempotence a déjà été utilisée avec un corps de requête différent
SERVICE_UNAVAILABLE
503
Service temporairement indisponible (maintenance)
FX_RATE_UNAVAILABLE
503
Taux de change USD/IDR indisponible (endpoints monétaires v2) — renvoie 503 avec un en-tête Retry-After.
v1 → v2
⟩Migration de v1 vers v2
v1 sert l'IDR ; v2 sert l'USD. Les deux versions coexistent en permanence — il n'y a pas d'arrêt prévu. Choisissez une version par intégration ; ne mélangez pas les chemins de base. v2 est identique à v1, à l'exception de la façon dont l'argent est représenté.
product_id est l’ID stable du créneau de palier SMSCode. Conservez-le si vous voulez commander exactement ce palier ; son prix et sa disponibilité peuvent changer sur la même ligne. catalog_product_id est l’umbrella stable pays+plateforme pour les commandes routées ; utilisez-le avec operator_id, min_price, max_price, prefer_provider et policy optionnels lorsque vous voulez que le serveur choisisse un palier actuel correspondant.
Analysez les champs monétaires comme des objets — lisez amount comme une chaîne décimale ; currency vaut "USD".
Pour le rapprochement du registre, utilisez canonical_amount (IDR exact) ; le amount en USD est une projection au moment du rendu et le rate est communiqué une seule fois dans meta.fx.
Gérez le nouveau FX_RATE_UNAVAILABLE (503) — réessayez après Retry-After. v1 ne renvoie jamais cela.