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

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

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 dit handboek bestaat

De meeste berichten van het type "SMS-Activate is dood, hier is een lijst met alternatieven" missen het deel dat daadwerkelijk ontwikkelaarsuren kost: de code. Aanmelden bij een nieuwe provider duurt vijf minuten. Het herschrijven van een integratie die al jaren stilzwijgend in productie draait, met ongeteste hoekgevallen en een kosten-tracker eraan gekoppeld, duurt langer dan je denkt.

Nadat we in januari 2026 onze compatibiliteitslaag hebben uitgebracht, kregen we van elk team dat deze probeerde dezelfde drie vragen:

  1. Welke endpoints mappen schoon en welke vereisen handmatige wijzigingen?
  2. Hoe kan ik mijn kostenbewaking en terugbetalingsstroom laten werken zonder herschrijven?
  3. Wat gaat er stil kapot en verschijnt een week later als een factuurverrassing?

Dit handboek beantwoordt deze drie vragen op volgorde en biedt je kant-en-klare code die je kunt auditen voordat je deze uitvoert.

De 48-uurs samenvatting: wat er werkelijk gebeurde

SMS-Activate ging offline op 29 december 2025. Er was geen banner voor gepland onderhoud, geen migratietool en geen openbare aankondiging. Gebruikers die probeerden in te loggen, kregen één pagina te zien waarop stond dat de service permanent gesloten was. De API retourneerde binnen enkele uren connection resets op elk endpoint.

Drie dingen gebeurden snel:

De nasleep is nog steeds gaande. Vanaf april 2026 zijn er actieve rechtszaken voor kleine vorderingen in Rusland en proberen minstens twee gecoördineerde gemeenschapsrechtszaken bevroren saldi te herstellen. Niets daarvan helpt je code, daarom richten we ons op het deel dat je daadwerkelijk kunt oplossen.

Deel 1: API-afschrijvingskaart

SMS-Activate leverde één openbaar endpoint op https://sms-activate.org/stubs/handler_api.php. Elke actie was een query string parameter op die URL. De onderstaande tabel koppelt elke belangrijke actie aan het VerifySMS-equivalent. De compat-laag op https://api.verifysms.app/compat/handler_api.php accepteert exact dezelfde query string vorm.

SMS-Activate actieDoelVerifySMS compat-laagNative VerifySMS API
getBalanceUSD-saldo als tekst retournerenWerkt ongewijzigd. Retourneert ACCESS_BALANCE:X.YYGET /v1/balance retourneert JSON
getNumbersStatusBeschikbaarheid per landWerkt. Retourneert het legacy mapformaatGET /v1/countries/availability
getNumberEen nummer leasen voor een serviceWerkt. Retourneert ACCESS_NUMBER:id:+phonePOST /v1/rentals
setStatusEen lease bevestigen of annulerenWerkt. Statuscodes 1/3/6/8 gedragen zich identiekPOST /v1/rentals/{id}/status
getStatusPoll voor SMS-aankomstWerkt. Retourneert STATUS_WAIT_CODE, STATUS_OK:CODE, STATUS_WAIT_RETRYGET /v1/rentals/{id}
getPricesPrijslijst ophalenWerkt. Retourneert VerifySMS-prijzen in de legacy JSON-vormGET /v1/prices
getCountriesLandcode-kaartWerkt. Retourneert zowel legacy numerieke ID's als ISO-3166-codesGET /v1/countries
getTopCountriesByServiceToplanden per serviceRetourneert realtime VerifySMS-gegevens in plaats van gecachte SMS-Activate-ranglijstenGET /v1/services/{id}/top-countries

Een handvol minder gebruikte SMS-Activate-acties mappen niet één op één. getRentServicesAndCountries en de long-lease rental API waren specifiek voor SMS-Activate en hebben geen compat-laag. Als je integratie die gebruikte, moet je overstappen naar het native VerifySMS long-lease endpoint op POST /v1/rentals/long, dat apart gedocumenteerd is.

Deel 2: Code migratieworkflows

De volgende snippets zijn exact zoals ik ze in januari heb getest tegen onze eigen staging-omgeving. Ik heb ze bewust saai gehouden, zodat je ze kunt lezen tegen je eigen code zonder contextwisseling.

Python (requests)

De enige vereiste wijziging is de basis-URL. Als je de API al hebt ingepakt in een kleine clientmodule, is de diff één 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"onverwachte response: {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)
    # Markeren 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(`onverwachte response: ${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 bewust geschreven zonder externe clients, zodat je het kunt integreren in een minimale service zonder afhankelijkheden toe te voegen. Hetzelfde patroon geldt in elke andere taal: wissel de URL, behoud de rest en laat je bestaande foutafhandeling overnemen.

Deel 3: Valstrikken die je zullen opbreken

Dit zijn de plekken waar de compat-laag trouw is aan de eigenaardigheden van SMS-Activate, maar de eigenaardigheden zelf je zullen verrassen als je deze code al een tijdje niet hebt aangeraakt.

Land-ID's zijn geen ISO-codes

SMS-Activate nummerde landen in hun eigen volgorde: Rusland was 0, VS was 187, Indonesië was 6, enzovoort. Als je integratie deze magische getallen hardcoded heeft, werken ze nog steeds op de compat-laag. Als je nieuwe code schrijft, geef dan de voorkeur aan de ISO-3166 alpha-2 vorm (RU, US, ID) die de compat-laag ook accepteert. Mix beide stijlen niet op dezelfde aanroeplocatie, want toekomstige debugging zal pijnlijk zijn.

Statuscode 3 versus 6

setStatus actiecode 3 betekende "vraag een nieuwe SMS aan" in de wereld van SMS-Activate, en code 6 betekende "accepteer de code als geldig". Deze twee codes zijn gemakkelijk te verwisselen in haast en ze hebben tegengestelde factureringsresultaten: 3 houdt je gefactureerd, 6 bevestigt de succesvolle verificatie. De compat-laag gedraagt zich op dezelfde manier. Grep je code op setStatus en zorg ervoor dat de branch die code 6 gebruikt alleen wordt uitgevoerd nadat je er zeker van bent dat de verificatie is gelukt.

Time-outs en circuit breakers

SMS-Activate retourneerde onder belasting soms een 200 met een lege body in plaats van een HTTP-fout. Defensieve clients verpakken de aanroep in een time-out en behandelen de lege body als een retry-signaal. VerifySMS retourneert nooit een lege body op de compat-laag. Als je client nog steeds lege body als retry behandelt, verspil je budget aan een onstabiel netwerk omdat de retry een ander huur-ID zal raken. Het veiligere patroon is om te controleren op de bekende response-prefixed (ACCESS_, STATUS_, BAD_) en alles anders te behandelen als een harde storing, niet als een tijdelijke.

Rate limits verschuiven van per-sleutel naar per-IP

SMS-Activate rate limits waren gekoppeld aan de API-token. VerifySMS rate limits 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 productie verkeer vanaf één 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 de eerste keer dat de hele vloot tegelijkertijd opwarmt. De oplossing is om de canary een dag lang vanaf één node te laten draaien voordat je uitwaaiert.

Terugbetalingstijd voelt direct omdat het daadwerkelijk direct is

Dit is niet zozeer een valkuil als wel een aangename verrassing. Waar SMS-Activate terugbetalingen enkele uren nodig hadden om op je saldo te verschijnen, verschijnen VerifySMS terugbetalingen binnen 60 seconden. Als je kosten-tracker het saldo op schema leest, registreert het de terugbetaling als een tegoed dat het oude systeem zou hebben gemist. Afstemmingsdashboards markeren dit soms als een anomalie op de eerste dag.

Deel 4: Prijsvergelijking realiteitscheck

Vóór de sluiting was SMS-Activate de bodem van de markt. Russische nummers kostten $0,03 tot $0,05 per verificatie, en kopers van grote volumes betaalden nog minder. Die bodem is weg. Hier zitten de resterende providers in april 2026 voor de meest voorkomende services, gehaald uit de publieke prijslijst van elke provider op :

Service5simTextVerifiedSMSPVASMS-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 simpel: 5sim en SMS-MAN winnen op extreem lage Russische prijzen, TextVerified is de premium tier voor de VS, en VerifySMS zit in het midden met een vaste basisprijs van $0,10 voor alles behalve de duurste Amerikaanse niet-VoIP-nummers. Als je budget was afgestemd op de bodemprijzen van SMS-Activate, verwacht dan twee tot vijf keer meer per verificatie te betalen, ongeacht welke vervanging je kiest.

Twee opmerkingen over deze tabel. Ten eerste verhogen en verlagen alle providers (inclusief VerifySMS) individuele landprijzen als reactie op netwerkkosten, dus bevestig de huidige prijs in je eigen dashboard voordat je een budget toezegt. Ten tweede hangt de *effectieve* prijs per succesvolle verificatie af van de terugbetalingsratio. Een provider met een prijskaartje van $0,08 en een slagingspercentage van 70% kost je meer per succes dan een provider van $0,10 met automatische terugbetalingen en een slagingspercentage van 90%.

Deel 5: De 10-stappen migratiechecklist

Dit is de daadwerkelijke volgorde die we onze eigen gebruikers in januari hebben laten doorlopen. Het gaat uit van één ontwikkelaar met repository-toegang, één productieservice en een staging-omgeving. Schaal de canary-percentages omhoog als je meerdere services of een monorepo draait.

  1. Inventariseer elke aanroeplocatie. Voer git grep -n 'sms-activate\.org\|handler_api\.php\|getNumber\|setStatus' uit en lijst elk bestand op dat de oude API aanroept. Als je meer dan een dozijn vindt, kies dan eerst een wrapper-module en centraliseer de aanroepen voordat je migreert.
  2. Verkrijg een VerifySMS API-sleutel. Meld je aan, voeg een klein bedrag toe aan je saldo en genereer een gescripte 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 query string niet. Commit dit alleen, zodat de diff schoon is.
  4. Voer je bestaande tests uit. Als de tests de echte API aanroepen, wijs ze dan naar staging en let op vormverschillen. Als ze de API mocken, voer ze dan ook uit tegen het live staging-endpoint, zodat je contractafwijkingen opvangt.
  5. Herbevestig land-ID's. Scan je code op landconstanten. Als je de legacy numerieke ID's gebruikt, werken ze nog steeds. Als je de kans krijgt, vervang ze dan door ISO-3166-codes, want de volgende ontwikkelaar die dit bestand aanraakt, zal je dankbaar zijn.
  6. Koppel terugbetalingsclaims. Bevestig dat je time-outpad setStatus aanroept met status=8. Zonder dit krijg je nog steeds terugbetalingen (we betalen verlopen leases automatisch terug), maar je kosten-tracker loopt achter op de realiteit.
  7. Update je kosten-tracker. Lees de kosten uit de X-VerifySMS-Cost response header in plaats van ze uit de prijslijst te parsen. Deze enkele wijziging maakt je financiële dashboard tot op de cent nauwkeurig.
  8. Monitoring. Voeg alerts toe voor slagingspercentage, p95 latentie en terugbetalingsratio tegen je bestaande baseline. Kies drempels die je kunt verdedigen, niet degene waarvan je denkt dat ze "goed" zullen zijn.
  9. Canary 5 procent gedurende 24 uur. Routeer een klein deel van het productie verkeer via het nieuwe endpoint. Bekijk het dashboard, niet alleen de alerts.
  10. Schakel de rest over. Zodra het canary-venster schoon is, verplaats je de resterende 95 procent en laat je de oude clientcode uitcommentariëren (niet verwijderen) voor één releasecyclus, zodat je een snelle rollback hebt.

Verwijder de oude code in de volgende release daarna. Laat geen dode aanroeplocaties langer dan een week staan, want de volgende persoon die de module aanraakt, zal ze per ongeluk weer in een nieuwe integratie plakken.

Veelgestelde vragen

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

Het is een echt endpoint op api.verifysms.app/compat/handler_api.php dat elke belangrijke actie uit de publieke documentatie van SMS-Activate accepteert: getBalance, getNumber, getStatus, setStatus, getPrices en getCountries. Verzoeken worden onder de motorkap doorgestuurd naar onze native API, zodat je VerifySMS-prijzen, dekking en terugbetalingsgedrag krijgt zonder code-wijzigingen aan jouw kant.

Werkt mijn oude API-sleutel nog?

Nee. SMS-Activate API-sleutels stopten met authenticeren op de dag dat de service werd gesloten. Je hebt een nieuwe sleutel van VerifySMS nodig. Meld je aan, voeg een klein bedrag toe aan je saldo en genereer een sleutel vanuit het dashboard. Het sleutelformaat is identiek in lengte, zodat je het in dezelfde omgevingsvariabele kunt plakken.

Hoe werken terugbetalingen vergeleken met SMS-Activate?

SMS-Activate vereiste dat je setStatus aanriep met statuscode 8 binnen 20 minuten om een nummer als ongebruikt te markeren, en terugbetalingen werden handmatig binnen enkele uren verwerkt. VerifySMS accepteert dezelfde setStatus-aanroep en betaalt het volledige bedrag binnen 60 seconden terug op je saldo. Als je vergeet setStatus aan te roepen, betalen we automatisch elk nummer terug dat nooit een SMS heeft ontvangen na het verlopen van het leasevenster.

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. Je kunt je bestaande land-ID-mapping behouden of migreren naar ISO-3166 alpha-2 codes wanneer je maar wilt.

Zijn de prijzen hetzelfde?

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

Moet ik mijn polling-logica wijzigen?

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

Wat gebeurt er als de compat-laag ooit wordt afgeschaft?

De compatibiliteitslaag wordt beschouwd als een permanente publieke interface. Als we het gedrag ooit wijzigen, publiceren we een minimale afschaffingsperiode van zes maanden met een volledige migratienotitie. De native VerifySMS JSON API is ook gedocumenteerd, zodat je op je eigen tempo van de compat-laag kunt migreren wanneer dat zinvol is.

Hoe test ik zonder geld uit te geven?

Het VerifySMS-dashboard biedt een sandbox-modus die gesimuleerde telefoonnummers en standaard SMS-codes retourneert zonder je saldo te belasten. Zet de sandbox-vlag in het dashboard aan of stuur de X-Sandbox-Mode header mee met elke aanvraag om je code-paden te testen voordat je live gaat.

Kan ik ook migreren van andere services?

Ja. Dit handboek is geschreven rond de SMS-Activate API omdat daar de meeste gestrande code leeft, maar dezelfde checklist is van toepassing op migraties van 5sim, SMS-MAN of elke andere handler_api-compatibele service. De compat-laag herkent handler_api.php parameters, ongeacht welke service je eerder aanriep.

Hoe lang duurt een echte migratie?

Voor een enkele service-integratie met enkele tientallen aanroeplocaties, plan je twee tot vier uur gerichte werkzaamheden, plus een canary-venster van 24 uur voordat je het volledige verkeer overschakelt. Grotere multi-service migraties met aangepaste foutafhandeling, analyse en retries kunnen langer duren, maar zijn meestal binnen één werkdag voltooid.

Verlies ik mijn historische gegevens?

De verificatiegeschiedenis van SMS-Activate ging offline toen de service werd gesloten en is niet herstelbaar. VerifySMS onderhoudt een volledig auditlogboek van elke verificatiepoging op je account gedurende 12 maanden, toegankelijk via het dashboard en via de extensie /compat/handler_api.php?action=getHistory.

Heeft dit invloed op mijn GDPR- of compliance-houding?

VerifySMS is geregistreerd in het Verenigd Koninkrijk en volgt de Britse GDPR. We publiceren ons gegevensretentiebeleid, sub-processors en DPA op de privacy-pagina. Als je vorige opstelling een DPA met SMS-Activate vereiste, neem dan contact met ons op en we zullen dezelfde overeenkomst binnen één werkdag mede ondertekenen.

Volgende stappen

Als je tot hier hebt gelezen, heb je de benodigde onderdelen al. Begin met de inventarisatiestap, laat de basis-URL-wissel door een teamgenoot beoordelen en voer de canary 's nachts uit. Het handboek is opzettelijk klein; het moeilijke deel is de discipline om te stoppen na het canary-venster in plaats van alles in één commit over te zetten.

Gerelateerd lezen op de rest van de site:

Klaar om de migratie in één avond te voltooien?

Maak een VerifySMS API-sleutel aan →

Sandbox-modus inbegrepen · Gegarandeerde automatische terugbetaling · 200+ landen · SMS-Activate compat-laag 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