← Blog
Developer playbook · 16 min read · Published April 2026 · Author Serhat Dogan

SMS-Activate Migratie Hub 2026: Ontwikkelaarscontrolelijst, API-toewijzing & Refundvergelijking

If you are still carrying handler_api.php calls in your repo pointed at sms-activate.org, this is the hub you open at 9pm on a Friday and close with a working integration before midnight. Real endpoint mapping, real code diffs, refund policy comparison, and the gotchas that will bite you between the line you change and the alert that wakes you up on Monday morning.

Waarom deze playbook bestaat

De meeste "SMS-Activate is dood, hier is een lijst met alternatieven" posts missen het deel dat daadwerkelijk ontwikkelaarstijd kost: de code. Het aanmelden voor een nieuwe provider duurt vijf minuten. Het herschrijven van een integratie die al jaren stilzwijgend in productie draait, met ongeteste randgevallen en een kostentracker eraan gekoppeld, duurt langer dan je denkt.

Nadat we onze compatibiliteitslaag in januari 2026 hebben verzonden, begonnen we dezelfde drie vragen te krijgen van elk team dat het probeerde:

  1. Welke eindpunten kaarten schoon en welke hebben handmatige wijzigingen nodig?
  2. Hoe kan ik mijn kostentracking en terugbetalingsstroom laten werken zonder een herschrijving?
  3. Wat breekt stil en komt een week later op als een factureringsverrassing?

Deze playbook beantwoordt die drie vragen in volgorde en geeft je kopieer-en-plakcode die je kunt controleren voordat je hem uitvoert.

De 48-uurs samenvatting: wat gebeurde er eigenlijk

SMS-Activate ging donker op 29 december 2025. Er was geen geplande onderhoudsbanner, geen migratietool en geen openbare kennisgeving. Gebruikers die probeerden in te loggen, troffen een enkele pagina aan die zei dat de service permanent was gesloten. De API retourneerde verbindingsresets op elk eindpunt binnen een paar uur.

Er gebeurden drie dingen snel:

De nasleep speelt zich nog steeds af. Vanaf april 2026 zijn er actieve kleine claims-zaken in Rusland en ten minste twee gecoördineerde communautaire rechtszaken die proberen bevroren saldos terug te krijgen. Niets daarvan helpt je code, daarom gaan we ons richten op het deel dat je daadwerkelijk kunt oplossen.

Deel 1: API-deprecatiekaart

SMS-Activate verzond een enkel openbaar eindpunt op https://sms-activate.org/stubs/handler_api.php. Elke actie was een queryreeksparameter op die URL. De tabel hieronder kaarten elke grote actie naar zijn VerifySMS-equivalent. De compatibiliteitslaag op https://api.verifysms.app/compat/handler_api.php accepteert dezelfde queryreeksvorm.

SMS-Activate-actieDoelVerifySMS-compatibiliteitslaagNative VerifySMS-API
getBalanceRetourneer USD-saldo als tekstWerkt ongewijzigd. Retourneert ACCESS_BALANCE:X.YYGET /v1/balance retourneert JSON
getNumbersStatusPer-land beschikbaarheidWerkt. Retourneert het oude kaartformaatGET /v1/countries/availability
getNumberLeas een nummer voor een serviceWerkt. Retourneert ACCESS_NUMBER:id:+phonePOST /v1/rentals
setStatusBevestig of annuleer een leaseWerkt. Statuscodes 1/3/6/8 gedragen zich identiekPOST /v1/rentals/{id}/status
getStatusPoll voor SMS-berichtWerkt. Retourneert STATUS_WAIT_CODE, STATUS_OK:CODE, STATUS_WAIT_RETRYGET /v1/rentals/{id}
getPricesHaal prijstabel opWerkt. Retourneert VerifySMS-prijzen in het oude JSON-formaatGET /v1/prices
getCountriesLandencodekaartWerkt. Retourneert zowel oude numerieke ID's als ISO-3166-codesGET /v1/countries
getTopCountriesByServiceTop-landen per serviceRetourneert realtime VerifySMS-gegevens in plaats van gecachte SMS-Activate-rangenGET /v1/services/{id}/top-countries

Een handvol minder gebruikte SMS-Activate-acties kaarten niet één-op-één. getRentServicesAndCountries en de long-lease-verhuur-API waren SMS-Activate-specifiek en hebben geen compatibiliteitslaag. Als je integratie die gebruikte, moet je naar het native VerifySMS long-lease-eindpunt op POST /v1/rentals/long, dat apart is gedocumenteerd.

Deel 2: Code-migratiewalkthroughs

De volgende fragmenten zijn de exacte vorm die ik heb getest tegen onze eigen staging-omgeving in januari. Ik heb ze opzettelijk saai gehouden zodat je ze kunt lezen tegen je eigen code zonder context te hoeven schakelen.

Python (requests)

De enige vereiste wijziging is de basis-URL. Als je de API al hebt verpakt in een kleine clientmodule, is de diff een enkele regel.

import os
import requests

# VOOR
# BASE_URL = "https://sms-activate.org/stubs/handler_api.php"
# NA
BASE_URL = "https://api.verifysms.app/compat/handler_api.php"
API_KEY = os.environ["SMS_API_KEY"]

def get_number(service: str, country: int) -> tuple[str, str]:
    resp = requests.get(BASE_URL, params={
        "api_key": API_KEY,
        "action": "getNumber",
        "service": service,
        "country": country,
    }, timeout=30)
    resp.raise_for_status()
    # ACCESS_NUMBER:12345:+441234567890
    status, rental_id, phone = resp.text.split(":", 2)
    if status != "ACCESS_NUMBER":
        raise RuntimeError(f"onverwacht antwoord: {resp.text}")
    return rental_id, phone

def wait_for_code(rental_id: str, deadline_seconds: int = 180) -> str:
    import time
    start = time.monotonic()
    while time.monotonic() - start < deadline_seconds:
        resp = requests.get(BASE_URL, params={
            "api_key": API_KEY,
            "action": "getStatus",
            "id": rental_id,
        }, timeout=15).text
        if resp.startswith("STATUS_OK:"):
            return resp.split(":", 1)[1]
        time.sleep(4)
    # Markeer als ongebruikt zodat we de terugbetaling krijgen
    requests.get(BASE_URL, params={
        "api_key": API_KEY,
        "action": "setStatus",
        "status": 8,
        "id": rental_id,
    }, timeout=15)
    raise TimeoutError(f"geen code na {deadline_seconds}s")

Node.js (axios)

import axios from "axios";

// VOOR
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// NA
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";
const API_KEY = process.env.SMS_API_KEY;

export async function getNumber(service, country) {
  const { data } = await axios.get(BASE_URL, {
    params: { api_key: API_KEY, action: "getNumber", service, country },
    timeout: 30_000,
  });
  const [status, rentalId, phone] = data.split(":");
  if (status !== "ACCESS_NUMBER") {
    throw new Error(`onverwacht antwoord: ${data}`);
  }
  return { rentalId, phone };
}

export async function waitForCode(rentalId, deadlineMs = 180_000) {
  const start = Date.now();
  while (Date.now() - start < deadlineMs) {
    const { data } = await axios.get(BASE_URL, {
      params: { api_key: API_KEY, action: "getStatus", id: rentalId },
      timeout: 15_000,
    });
    if (data.startsWith("STATUS_OK:")) return data.split(":")[1];
    await new Promise((r) => setTimeout(r, 4000));
  }
  await axios.get(BASE_URL, {
    params: { api_key: API_KEY, action: "setStatus", status: 8, id: rentalId },
    timeout: 15_000,
  });
  throw new Error(`geen code na ${deadlineMs}ms`);
}

PHP (curl)

<?php
// VOOR
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// NA
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";

function sms_call(array $params): string {
    $params["api_key"] = getenv("SMS_API_KEY");
    $url = BASE_URL . "?" . http_build_query($params);
    $ch  = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    $body = curl_exec($ch);
    curl_close($ch);
    return $body;
}

function get_number(string $service, int $country): array {
    $resp = sms_call(["action" => "getNumber", "service" => $service, "country" => $country]);
    [$status, $id, $phone] = explode(":", $resp, 3);
    if ($status !== "ACCESS_NUMBER") {
        throw new RuntimeException("onverwacht: $resp");
    }
    return ["id" => $id, "phone" => $phone];
}

Go (net/http)

package sms

import (
    "errors"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strings"
    "time"
)

// VOOR
// const baseURL = "https://sms-activate.org/stubs/handler_api.php"
// NA
const baseURL = "https://api.verifysms.app/compat/handler_api.php"

func call(params url.Values) (string, error) {
    params.Set("api_key", os.Getenv("SMS_API_KEY"))
    req, _ := http.NewRequest("GET", baseURL+"?"+params.Encode(), nil)
    client := &http.Client{Timeout: 30 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    return string(body), nil
}

func GetNumber(service string, country int) (id, phone string, err error) {
    body, err := call(url.Values{
        "action":  {"getNumber"},
        "service": {service},
        "country": {fmt.Sprint(country)},
    })
    if err != nil {
        return "", "", err
    }
    parts := strings.SplitN(body, ":", 3)
    if len(parts) != 3 || parts[0] != "ACCESS_NUMBER" {
        return "", "", errors.New("onverwacht: " + body)
    }
    return parts[1], parts[2], nil
}

Het Go-voorbeeld is opzettelijk geschreven zonder een externe client, zodat je het in een minimale service kunt plaatsen zonder afhankelijkheden toe te voegen. Hetzelfde patroon geldt in elke andere taal: wissel de URL, houd de rest, en laat je bestaande foutafhandelingsmechanismen over.

Deel 3: Gotchas die je zullen verbranden

Dit zijn de plaatsen waar de compatibiliteitslaag trouw is aan de eigenaardigheden van SMS-Activate, maar de eigenaardigheden zelf je zullen verrassen als je deze code niet hebt aangeraakt in een tijdje.

Land-ID's zijn geen ISO-codes

SMS-Activate nummerde landen in hun eigen volgorde: Rusland was 0, VS was 187, Indonesië was 6, enzovoorts. Als je integratie deze magische nummers hard heeft gecodeerd, werken ze nog steeds op de compatibiliteitslaag. Als je nieuwe code schrijft, gebruik dan de ISO-3166 alpha-2-vorm (RU, US, ID) die de compatibiliteitslaag ook accepteert. Meng beide stijlen niet in dezelfde aanroep, omdat toekomstige foutopsporing pijnlijk zal zijn.

Statuscode 3 versus 6

setStatus actiecodes 3 betekende "vraag nog een SMS" in de wereld van SMS-Activate, en code 6 betekende "accepteer de code als geldig". Deze twee codes zijn gemakkelijk te verwisselen in een haast, en ze hebben tegenovergestelde factureringsresultaten: 3 houdt je gefactureerd, 6 bevestigt de succesvolle verificatie. De compatibiliteitslaag gedraagt zich op dezelfde manier. Grep je code voor setStatus en zorg ervoor dat de branch die code 6 alleen uitvoert nadat je zeker bent dat de verificatie is geslaagd.

Time-outs en circuitbreakers

SMS-Activate retourneerde onder belasting soms een 200 met een leeg lichaam in plaats van een HTTP-fout. Defensieve clients verpakken de aanroep in een time-out en behandelen het lege lichaam als een herhalingssignaal. VerifySMS retourneert nooit een leeg lichaam op de compatibiliteitslaag. Als je client nog steeds leeg behandelt als een herhaling, zal het budget verbranden op een onbetrouwbaar netwerk omdat de herhaling een ander verhuurnummer zal raken. Het veiligere patroon is om te controleren op de bekende responsvoorvoegsels (ACCESS_, STATUS_, BAD_) en alles anders te behandelen als een harde fout, niet als een tijdelijke.

Tariefslimieten gaan van per-sleutel naar per-IP

SMS-Activate-tariefslimieten waren gekoppeld aan de API-token. VerifySMS-tariefslimieten zijn gekoppeld aan de combinatie van API-token en bron-IP-adres, omdat we veel misbruik zien van scraper-scripts die één sleutel delen over een botnet. Voor normaal productieverkeer van een enkele server of een load-balanced pool is dit onzichtbaar. Als je gedistribueerde CI-taken uitvoert die allemaal één staging-sleutel delen, kun je een 429 zien wanneer de vloot voor het eerst samen opwarmt. De oplossing is om de canary uit te voeren vanaf één knooppunt voor een dag voordat je het uitbreidt.

Terugbetalingstiming voelt instant omdat het dat eigenlijk is

Dit is geen valstrik zo veel als een aangename verrassing. Waar SMS-Activate-terugbetalingen een paar uur duurden om in je saldo te verschijnen, verschijnen VerifySMS-terugbetalingen binnen 60 seconden. Als je kostentracker het saldo op een schema leest, zal het de terugbetaling registreren als een tegoed dat het oude systeem zou hebben gemist. Reconcilatie-dashboard's markeren dit soms als een anomalie op de eerste dag.

Deel 4: Prijsvergelijking realiteitscheck

Voor de afsluiting was SMS-Activate de bodem van de markt. Russische nummers kostten $0,03 tot $0,05 per verificatie, en grote-volume kopers betaalden nog minder. Die bodem is verdwenen. Hier is waar de resterende providers zitten in april 2026 voor de meest voorkomende diensten, getrokken uit de openbare prijslijst van elke provider op :

Dienst5simTextVerifiedSMSPVASMS-MANVerifySMS
WhatsApp / Rusland$0,014$0,05$0,035$0,10
WhatsApp / VS$0,27$0,25$0,28$0,22$0,18
Telegram / Rusland$0,016$0,05$0,04$0,10
Telegram / VS$0,35$0,40$0,38$0,30$0,20
Google / Indonesië$0,07$0,08$0,06$0,10

Het patroon is eenvoudig: 5sim en SMS-MAN winnen op bodemprijzen voor Russische nummers, TextVerified is de Amerikaanse premiumlaag, en VerifySMS zit in het midden met een vlakke $0,10 baselijn voor alles behalve de duurste Amerikaanse niet-VoIP-nummers. Als uw budget was afgestemd op de bodemprijzen van SMS-Activate, verwacht dan twee tot vijf keer meer per verificatie te betalen, ongeacht welke vervanging u kiest.

Twee opmerkingen over deze tabel. Ten eerste verhoogt elke provider (inclusief VerifySMS) individuele landprijzen als reactie op dragerkosten, dus bevestig de huidige prijs in uw eigen dashboard voordat u een budget vastlegt. Ten tweede hangt de effectieve prijs per succesvolle verificatie af van de terugbetalingsratio. Een provider met een stickerprijs van $0,08 en een succespercentage van 70% kost u meer per succes dan een provider met een stickerprijs van $0,10 en automatische terugbetalingen en een succespercentage van 90%.

Deel 5: De 10-stappengids voor migratie

Dit is de daadwerkelijke sequentie die we onze eigen gebruikers doorliepen in januari. Het gaat ervan uit dat een enkele ontwikkelaar toegang heeft tot de repository, één productiedienst en een staging-omgeving. Schaal de canary-percentages op als u meerdere diensten of een monorepo uitvoert.

  1. Inventariseer elke aanroepplaats. Voer git grep -n 'sms-activate\.org\|handler_api\.php\|getNumber\|setStatus' uit en vermeld elk bestand dat de oude API raakt. Als u meer dan een dozijn vindt, kiest u een wrapper-module en centraliseert u de aanroepen eerst voordat u migreert.
  2. Haal een VerifySMS API-sleutel. Meld u aan, voeg een klein bedrag toe en genereer een scoped sleutel voor staging. Houd de productiesleutel buiten de repository.
  3. Wijzig de basis-URL. Vervang de SMS-Activate-host door api.verifysms.app/compat/handler_api.php. Wijzig de queryreeks niet. Sla deze wijziging alleen op zodat de diff schoon is.
  4. Voer uw bestaande tests uit. Als de tests de echte API raken, wijs ze dan naar staging en kijk uit voor vormmismatches. Als ze de API mocken, voer ze dan uit tegen het live staging-eindpunt om te controleren of het contract drift is.
  5. Bevestig land-ID's opnieuw. Scrol door uw code voor landconstanten. Als u de oude numerieke ID's gebruikt, werken ze nog steeds. Als u de kans krijgt, vervang ze dan door ISO-3166-codes omdat de volgende ontwikkelaar die dit bestand aanraakt, u zal bedanken.
  6. Sluit terugbetalingsclaims aan. Bevestig dat uw time-outpad setStatus aanroept met status=8. Zonder deze wordt u nog steeds terugbetaald (we betalen automatisch verlopen leases), maar uw kostentracker zal achterblijven bij de realiteit.
  7. Werk uw kostentracker bij. Lees de kosten uit het X-VerifySMS-Cost responsheader in plaats van het uit de prijslijst te parseren. Deze enkele wijziging maakt uw financieel dashboard nauwkeurig tot op de cent.
  8. Monitoring. Voeg succespercentage, p95 latentie en terugbetalingsratio waarschuwingen toe tegen uw bestaande basislijn. Kies drempels die u kunt verdedigen, niet die waarvan u denkt dat ze "goed" zullen zijn.
  9. Canary 5 procent voor 24 uur. Leid een klein deel van de productieverkeer door het nieuwe eindpunt. Kijk naar het dashboard, niet alleen naar de waarschuwingen.
  10. Snij de rest over. Als het canary-venster schoon is, verplaatst u de resterende 95 procent en laat u de oude clientcode commentaarloos (niet verwijderd) voor één releasecyclus zodat u een snelle rollback hebt.

Verwijder de oude code in de volgende release daarna. Laat geen dode aanroepplaatsen langer dan een week achter omdat de volgende persoon die de module aanraakt, ze rechtstreeks in een nieuwe integratie zal plakken.

Veelgestelde vragen

Is de SMS-Activate-compatibiliteitslaag een echte API of gewoon een stub?

Het is een echt eindpunt op api.verifysms.app/compat/handler_api.php dat elke grote actie van de SMS-Activate openbare documenten accepteert: getBalance, getNumber, getStatus, setStatus, getPrices en getCountries. Verzoeken worden onder de motorkap doorgestuurd naar onze native API, zodat u VerifySMS-prijzen, dekking en terugbetalingsgedrag krijgt zonder codewijzigingen aan uw kant.

Werkt mijn oude API-sleutel?

Nee. SMS-Activate API-sleutels stopten met authenticeren de dag dat de dienst werd afgesloten. U hebt een nieuwe sleutel van VerifySMS nodig. Meld u aan, voeg een klein bedrag toe en genereer een sleutel uit het dashboard. De sleutelvorm is identiek in lengte zodat u deze in dezelfde omgevingsvariabele kunt plakken.

Hoe werken terugbetalingen in vergelijking met SMS-Activate?

SMS-Activate vereiste dat u setStatus aanroept met statuscode 8 binnen 20 minuten om een nummer als ongebruikt te markeren, en terugbetalingen werden handmatig verwerkt binnen enkele uren. VerifySMS accepteert dezelfde setStatus aanroep en betaalt het volledige bedrag terug aan uw saldo binnen 60 seconden. Als u setStatus helemaal vergeet aan te roepen, betaalt ons systeem nog steeds automatisch terug elk nummer dat nooit een SMS ontving nadat het leasevenster was verlopen.

Welke landen worden ondersteund?

VerifySMS dekt meer dan 200 landen. Elk land dat SMS-Activate aanbood, is beschikbaar op VerifySMS, inclusief Rusland, Indonesië, Vietnam, Nigeria en de VS. U kunt uw bestaande land-ID-toewijzing behouden of op elk moment migreren naar ISO-3166 alpha-2-codes.

Is de prijsstelling hetzelfde?

Nee. De bodemprijzen van SMS-Activate van $0,03 tot $0,05 per verificatie voor Russische nummers zijn verdwenen van de open markt. Huidige marktprijzen variëren van $0,10 voor gewone diensten tot $0,25 voor Amerikaanse niet-VoIP-nummers op strengere platforms. VerifySMS rekent $0,10 als baselijn en publiceert prijzen per land op het dashboard.

Moet ik mijn pollinglogica veranderen?

Nee. De getStatus aanroep retourneert STATUS_WAIT_CODE en STATUS_OK in hetzelfde formaat dat SMS-Activate gebruikte. Pollingintervallen van 3 tot 5 seconden werken nog steeds. Het enige nieuwe gedrag is dat VerifySMS ook een webhook-URL in het dashboard blootlegt, zodat u kunt stoppen met pollen als u een gebeurtenisgestuurde stroom verkiest.

Wat gebeurt er als de compatibiliteitslaag ooit wordt afgeschaft?

De compatibiliteitslaag wordt beschouwd als een permanente openbare interface. Als we ooit het gedrag veranderen, publiceren we een deprecatievenster van minimaal zes maanden met een volledige migratienote. De native VerifySMS JSON API is ook gedocumenteerd, zodat u kunt migreren van de compatibiliteitslaag op uw eigen tempo wanneer het maar zinvol is.

Hoe test ik zonder geld uit te geven?

Het VerifySMS-dashboard exposeert een sandbox-modus die gesimuleerde telefoonnummers en ingeblikte SMS-codes retourneert zonder uw saldo te debiteren. Flip de sandbox-vlag in het dashboard of stuur de X-Sandbox-Mode header met elk verzoek om uw codepaden te oefenen voordat u live gaat.

Kan ik ook vanuit andere diensten migreren?

Ja. Deze playbook is geschreven rond de SMS-Activate API omdat dat is waar de meeste gestrande code leeft, maar dezelfde checklist is van toepassing op migraties van 5sim, SMS-MAN of elke andere handler_api-compatibele dienst. De compatibiliteitslaag herkent handler_api.php parameters ongeacht welke dienst u eerder aanriep.

Hoe lang duurt een echte migratie?

Voor een enkele service-integratie met enkele tientallen aanroepplaatsen, plan dan twee tot vier uur gefocuste arbeid, plus een 24-uurs canary-venster voordat u de volledige verkeer oversteekt. Grotere multi-service migraties met aangepaste foutafhandeling, analytics en retries kunnen langer duren maar meestal nog steeds binnen één werkdag eindigen.

Verlies ik mijn historische gegevens?

SMS-Activate verificatiegeschiedenis ging offline toen de dienst werd afgesloten en is niet terughaalbaar. VerifySMS onderhoudt een volledige auditlog van elke verificatiepoging op uw account voor 12 maanden, toegankelijk vanuit het dashboard en via de /compat/handler_api.php?action=getHistory extensie.

Beïnvloedt dit mijn GDPR- of nalevingsstatus?

VerifySMS is geregistreerd in het Verenigd Koninkrijk en volgt de UK GDPR. We publiceren ons gegevensretentiebeleid, sub-processors en DPA op de privacypagina. Als uw vorige installatie een DPA met SMS-Activate vereiste, neem dan contact met ons op en wij zullen dezelfde overeenkomst binnen één werkdag ondertekenen.

Volgende stappen

Als u hier ver heeft gelezen, hebt u al de stukken die u nodig hebt. Begin met de inventarisatie, krijg de basis-URL-wijziging voor een collega om te beoordelen en voer de canary 's nachts uit. De playbook is klein opzettelijk; het moeilijke deel is de discipline om na het canary-venster te stoppen in plaats van alles in één commit over te steken.

Gerelateerde lectuur op de rest van de site:

Klaar om de migratie in één avond af te ronden?

Maak een VerifySMS API-sleutel aan →

Sandbox-modus inbegrepen · Automatische terugbetalingsgarantie · 200+ landen · SMS-Activate-compatibiliteitslaag op /compat/handler_api.php

Next steps

If you have read this far, you already have the pieces you need. Start with the inventory step, get the base URL swap in front of a teammate for review, and run the canary overnight. The playbook is small on purpose; the hard part is the discipline to stop after the canary window instead of cutting everything over in one commit.

Related reading on the rest of the site:

Ready to cut the migration to a single evening?

Create a VerifySMS API key →

Sandbox mode included · Auto-refund guarantee · 200+ countries · SMS-Activate compat layer on /compat/handler_api.php