Apply custom discountAnyone who rents OTP Gmail > 2000 Gmails per day for any service (except Facebook, Instagram, and Apple) and wants a private discount, feel free to message me! Discounts can be up to 50% off the original price for those registering accounts in large enough volumes.
New update 29/09I have just added 4 new warehouses for all services + short-live accounts shop (24h) with email + pass for you to log in, feel free to check them out when you have time

API-Dokumentation

Öffentliche API /v1 – authentifiziere dich mit einem API-Schlüssel und automatisiere Postfachmieten aus deiner eigenen Software

Dein API-Schlüssel
Du bist nicht angemeldet – melde dich an, um deinen eigenen API-Schlüssel zu erhalten.
Sende diesen Schlüssel im Header Authorization: Bearer <key> oder im Query-Parameter ?api_key=<key>. Klicke auf das Augen-Symbol, um den ganzen Schlüssel anzuzeigen; klicke auf Neuen Schlüssel erzeugen, wenn du ihn ersetzen musst (der alte Schlüssel funktioniert sofort nicht mehr).

Überblick

Basis-URL
https://otpgmail.net
Authentifizierung
Füge deinen API-Schlüssel jeder Anfrage hinzu, entweder im Header Authorization: Bearer og_xxx... (empfohlen) oder im Query-String ?api_key=og_xxx....
Idempotency-Key (verhindert doppelte Bestellungen)
Jede Anfrage POST /v1/orders sollte den Header Idempotency-Key: <neue UUID pro Bestellung> enthalten. Läuft die Anfrage in ein Timeout oder bricht die Verbindung ab, sende sie erneut mit dem gleichen Schlüssel – der Server liefert dann die ursprüngliche Antwort zurück, statt eine weitere Bestellung anzulegen. Erzeuge für jede neue Bestellung eine neue UUID und verwende eine UUID nur bei einem erneuten Versuch wieder.
Viele Bestellungen auf einmal: Status in einem Aufruf
Du mietest Dutzende oder Hunderte Postfächer? Frage ihren Status gemeinsam ab: POST /v1/orders/status liefert bis zu 100 Bestellungen pro Aufruf (zählt als eine einzige Anfrage für dein Rate-Limit), und GET /v1/orders listet deine Bestellungen – nach einem Neustart liefert ?status=waiting_code alle Bestellungen zurück, die noch auf einen Code warten. Jeder Fehler sagt außerdem, ob sich ein erneuter Versuch lohnt (error.retryable, error.retryAfterSec). Details: Abschnitt Status vieler Bestellungen.
Postfachtyp: Gmail oder iCloud
Standardmäßig erhältst du ein @gmail.com-Postfach. Um ein @icloud.com-Postfach zu mieten, füge "domain": "icloud.com" zum Body von POST /v1/orders hinzu. Ohne domain gilt gmail.com – bestehende Integrationen funktionieren ohne Änderungen weiter. iCloud ist nur für Dienste verfügbar, deren Feld icloud in GET /v1/services nicht null ist; der iCloud-Preis kann vom Gmail-Preis abweichen (siehe icloud.price). Jede Bestellantwort enthält ein Feld domain, das angibt, welchen Postfachtyp die Bestellung nutzt. Einige Dienste bieten auch @gmx.com-Postfächer an (derzeit nur Pokemon, Code pkm, Lager 2): Sende "domain": "gmx.com" – ein Dienst bietet es an, wenn sein offers[] in GET /v1/services einen Eintrag mit domain = "gmx.com" hat; bei jedem anderen Dienst ⇒ DOMAIN_UNAVAILABLE. gmx.com ist für Sammelbestellungen nicht verfügbar.
Lager: ein Dienst, mehrere Postfachquellen
Ein Dienst kann aus mehreren Lagern verkauft werden, jedes mit eigenem Preis, Bestand und eigenen Coderegeln (das Array offers[] in GET /v1/services). Sende beim Anlegen einer Bestellung "warehouse": 2, um eines zu wählen; "warehouse": 99 ⇒ das System wählt das günstigste Lager mit Bestand und geht zum nächsten über, wenn es leer ist (ideal für Dienste, die oft ausverkauft sind, wie Facebook und Instagram); 0 ⇒ das günstigste Lager mit Bestand. Bestehende Clients brauchen keine Änderungen: ohne warehouse ⇒ wie bisher Lager 1, und price, stock und icloud beschreiben Lager 1 (Dienste ohne Lager 1 ⇒ das günstigste Lager mit Bestand). Details: siehe Lager.
Sammelmiete (Sammelbestellungen)
Sammelbestellungen werden im Web gekauft (Seite Sammelmiete); über die API kannst du Sammelbestellungen auflisten und Codes lesen. Gmail (Lager 1): Jedes Postfach hat eine proxyUrl – einen Code-Link, der keinen API-Schlüssel braucht. iCloud (Lager 3): private Postfächer; lies Codes mit GET /v1/batches/{id}/mails/{mailId}/codes oder öffne readUrl im Web. Details: Abschnitt Sammelmiete.
Kurzlebige Gmail-Konten kaufen (Mail-Shop)
Neben dem Mieten von Postfächern kannst du auch komplette Gmail-Konten kaufen – aus 2 Lagern: Lager 1 liefert E-Mail, Passwort, Cookies + Postfach-Link (auch als JSON), Lager 2 E-Mail, Passwort + schnellen Login-Link; Lager 99 nimmt zuerst das günstigste (die Seite Kurzzeit-Gmail kaufen im Web). Die API kann alles, was diese Seite kann: Lager, Preise und Bestand ansehen, kaufen (Idempotency-Key erforderlich), die Lieferung verfolgen, die Konten herunterladen und ihre E-Mails lesen. Details: Abschnitt Mail-Shop.
Deine Kunden Codes selbst lesen lassen (die Seite /otp)
Du verkaufst gemietete Postfächer weiter (im Web oder über die API)? Schalte auf deiner Kontoseite einen Teilen-Code ein und schick deinem Kunden dann den Link https://otpgmail.net/de/otp#k7m2q9:[email protected] (k7m2q9 = dein Teilen-Code) – er sieht die neuesten Codes dieser E-Mail ohne Anmeldung und sieht nie Preise oder dein Guthaben; die Seite führt nur kostenlose Aktionen aus (sie berechnet dir nie etwas).
JSON-Link für Tools / Bots (deine oder die deines Kunden): Nimm den Link in Pfadform https://otpgmail.net/de/otp/k7m2q9:[email protected] (er öffnet die Seite /otp genau wie der #-Link) und hänge ?json an ⇒ der Server liefert JSON mit genau den Daten, die die Seite zeigt: cards[] – eine Karte pro Dienst, der auf dieser E-Mail gemietet ist (email, serviceName, state, items[] = bis zu 5 neueste Codes / Links, neueste zuerst) – plus serverTime. state: waiting wartet auf den ersten Code · listening wartet auf einen neuen Code · received Codes sind da · needs_owner der Verkäufer muss verlängern · expired abgelaufen · banned Postfach gesperrt. Mehrere E-Mails: durch Kommas trennen (höchstens 50). Kein API-Schlüssel, keine Cookies, CORS ist offen; nur lesen („Neuen OTP laden“ / „Diese E-Mail erneut mieten“ bleiben auf der Seite /otp). Dieselben Schutzgrenzen gegen Raten wie auf der Seite: falscher Code / keine anzeigbare Bestellung für die E-Mail ⇒ 404 NOT_FOUND; wiederholte Fehlversuche oder mehr als 30 Abrufe pro Minute für eine E-Mail ⇒ 429 RATE_LIMITED mit retryAfter (Sekunden). ?json an einen /otp#…-Link anzuhängen liefert KEIN JSON: Browser schicken den Teil nach # nie an den Server.
curl "https://otpgmail.net/de/otp/k7m2q9:[email protected]?json"
{
  "success": true,
  "data": {
    "cards": [
      {
        "ref": "Q3vX9kL2mP0aB7cD",
        "serviceCode": "fb",
        "serviceName": "Facebook",
        "logo": "/logos/fb.png?v=20261007",
        "domain": "gmail.com",
        "email": "[email protected]",
        "state": "received",
        "until": null,
        "items": [
          {
            "kind": "code",
            "value": "529170",
            "label": null,
            "at": 1760000405
          },
          {
            "kind": "code",
            "value": "381904",
            "label": null,
            "at": 1759999030
          }
        ],
        "total": 2,
        "canLoad": true
      }
    ],
    "serverTime": 1760000530
  }
}
Vollständiger Beispielcode auf GitHub
Sofort lauffähige Clients für Python, Node.js, PHP, C# (.NET) und curl – Postfach mieten, auf den Code warten, für eine Erstattung automatisch stornieren und zwischen iCloud und Gmail wechseln, wenn der Bestand ausgeht: github.com/letoan0902/otpgmail-email-otp-api (Englisch) · thue-otp-gmail (Vietnamesisch). Hinweis zu Python: Nutze requests statt des einfachen urllib (dessen Standard-User-Agent bekommt vom CDN ein 403).
Webhooks
Statt jede Bestellung ständig abzufragen, registrieren Sie eine URL und erhalten einen signierten POST, sobald eine Bestellung einen neuen Code erhält oder storniert / erstattet wird — siehe Webhooks.

Endpunkte

GET/v1/balance
Gibt das Kontoguthaben (in VND) für den in der Anfrage verwendeten API-Schlüssel zurück.
Beispiel
curl "https://otpgmail.net/v1/balance" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "balance": 25000
  }
}
Mögliche Fehler: UNAUTHORIZED
GET/v1/services
Listet die aktivierten Dienste auf – Code, Name, Gmail-Preis (in VND) und aktuellen Gmail-Bestand, einen icloud-Block mit Preis und Bestand für @icloud.com-Postfächer sowie ein Array offers[]: jedes Lager, das den Dienst verkauft, ein Eintrag pro Lager × Postfachtyp (Preis, Bestand, Anzahl der Codes, Regeln). Die Daten sind zwischengespeichert: Der Bestand wird etwa alle 30–60 Sekunden aktualisiert, Preise seltener.
icloud = null bedeutet, dass der Dienst iCloud noch nicht unterstützt (der Anbieter bietet es nicht an oder es ist vorübergehend deaktiviert) – es kann nur Gmail gemietet werden. Bei Diensten mit einem icloud-Block sendest du beim Anlegen der Bestellung "domain": "icloud.com", um ein @icloud.com-Postfach zu icloud.price zu erhalten. price / stock / icloud.price beschreiben Lager 1 – dorthin gehen Bestellungen ohne warehouse (Dienste ohne Lager 1 ⇒ das günstigste Lager mit Bestand); Preise und Bestand pro Lager stehen in offers[] (siehe Lager). stock ist die Zahl, die die Quelle veröffentlicht (Lager 1: etwa alle 30 Sekunden aktualisiert), kein Versprechen, dass ein einzelner Aufruf so viele mieten kann: Postfächer werden einzeln zugewiesen, und die Quelle kann mittendrin leerlaufen – dann liefert POST /v1/orders weniger Bestellungen plus ein Feld partial. Lager 2 und 4 zeigen ebenso die Zahlen ihrer Quellen.
Beispiel
curl "https://otpgmail.net/v1/services" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": [
    {
      "code": "dr",
      "name": "OpenAI (ChatGPT)",
      "price": 350,
      "stock": 616525,
      "icloud": {
        "price": 630,
        "stock": 12464
      },
      "offers": [
        {
          "warehouse": 2,
          "domain": "gmail.com",
          "price": 350,
          "stock": 616525,
          "maxCodes": null,
          "rules": null,
          "fee": {
            "freeHours": 6,
            "paidPct": 50,
            "maxHours": 24
          },
          "isDefault": true
        },
        {
          "warehouse": 1,
          "domain": "gmail.com",
          "price": 630,
          "stock": 12464,
          "maxCodes": 3,
          "rules": null,
          "fee": null,
          "isDefault": false
        },
        {
          "warehouse": 1,
          "domain": "icloud.com",
          "price": 630,
          "stock": 12464,
          "maxCodes": 5,
          "rules": null,
          "fee": null,
          "isDefault": true
        }
      ],
      "note": null
    },
    {
      "code": "fb",
      "name": "Facebook",
      "price": 1590,
      "stock": 88,
      "icloud": null,
      "offers": [
        {
          "warehouse": 1,
          "domain": "gmail.com",
          "price": 1590,
          "stock": 88,
          "maxCodes": 10,
          "rules": null,
          "fee": null,
          "isDefault": false
        },
        {
          "warehouse": 4,
          "domain": "gmail.com",
          "price": 1580,
          "stock": 2310,
          "maxCodes": null,
          "rules": null,
          "fee": null,
          "isDefault": true
        }
      ],
      "note": null
    }
  ]
}
Mögliche Fehler: UNAUTHORIZED
POST/v1/orders
Legt eine Bestellung für ein Postfach an, das Bestätigungscodes empfängt – standardmäßig Gmail, oder iCloud, wenn du domain: "icloud.com" sendest. Dein Guthaben wird belastet, sobald der Anbieter das Postfach zuweist. Gibt eine Liste von Bestellungen zurück (eine, oder mehrere bei quantity > 1).
Teillieferung: quantity ist eine Obergrenze
  • Postfächer werden einzeln nacheinander zugewiesen. Gehen dem Anbieter mittendrin die Postfächer aus (z. B. hat Lager 1 in diesem Moment keine Postfächer mehr für diesen Dienst), stoppt der Aufruf und liefert HTTP 200 mit den bis dahin erstellten Bestellungen – data kann kürzer als quantity sein, und berechnet werden nur die Bestellungen in data. Der stock in GET /v1/services garantiert nicht, wie viele ein einzelner Aufruf bekommt.
  • Die Antwort enthält dann zusätzlich ein Feld partial auf oberster Ebene neben data (fehlt, wenn alles geliefert wurde – Bots, die nur data lesen, laufen unverändert weiter): requested, delivered, missing und reason = {code, message, retryable, retryAfterSec}; code ist der Fehler, der den Aufruf gestoppt hat (NO_MAILS_AVAILABLE, OUT_OF_STOCK, ALL_WAREHOUSES_EXHAUSTED, INSUFFICIENT_BALANCE, WAITING_LIMIT_REACHED…).
  • Leerlaufen ist meist nur vorübergehend – bei Lager 1 hat der Anbieter oft schon wenige Sekunden später wieder Postfächer. Bei reason.retryable = true warte reason.retryAfterSec Sekunden (3–5 s) und fordere die fehlenden Postfächer (missing) mit einem NEUEN Idempotency-Key erneut an (der alte Schlüssel liefert nur genau diese Antwort noch einmal); wiederhole das, bis du alle hast.
  • Direkt nach einer Teillieferung kann der nächste Aufruf 409 WAREHOUSE_UNAVAILABLE / OUT_OF_STOCK oder 503 NO_MAILS_AVAILABLE liefern – das ist normal: halte weiter 3–5 s Pause zwischen den Aufrufen und gib erst auf, wenn viele Aufrufe hintereinander scheitern (z. B. 10 Versuche, etwa eine Minute); versuche es dann später erneut oder sende "warehouse": 99, damit das System auf andere Lager ausweicht. retryable = false (INSUFFICIENT_BALANCE, WAITING_LIMIT_REACHED…) ⇒ behebe zuerst die Ursache (Guthaben aufladen oder warten, bis offene Bestellungen abgeschlossen sind).
  • Fehlerantworten enthalten dieselben zwei Felder in error: error.retryable und error.retryAfterSec (z. B. 409 OUT_OF_STOCK ⇒ true, 5; 402 INSUFFICIENT_BALANCE ⇒ false, null) – siehe Fehlercodes.
Der Header Idempotency-Key (eine UUID) ist erforderlich. Erzeuge für jede neue Miete eine neue UUID; bei einem erneuten Versuch nach einem Timeout verwende DENSELBEN Schlüssel erneut, um keine doppelte Bestellung anzulegen. Dieser Aufruf wartet, bis der Anbieter ein Postfach zuweist, dauert also meist etwa 0,3 Sekunden, kann zu Spitzenzeiten (selten) aber bis zu 10 Sekunden dauern – stelle das Timeout deines Clients auf etwa 15 Sekunden ein, statt nach 5 Sekunden aufzugeben und zu früh erneut zu versuchen.
Parameter
NameTypErforderlichBeschreibung
servicestringJaDienstcode (aus GET /v1/services, z. B. "ig", "tg", "fb")
quantitynumberNeinHÖCHSTZAHL der zu mietenden Postfächer (Standard 1; höchstens 200 pro Anfrage) – geht das Lager mittendrin aus, bekommst du weniger: zähle data[] und lies partial
domainstringNeinPostfachtyp: "gmail.com" (Standard), "icloud.com" oder "gmx.com". Ohne Angabe = gmail.com – bestehende Integrationen brauchen keine Änderungen. iCloud kann nur für Dienste gemietet werden, deren Feld icloud in GET /v1/services nicht null ist; "gmx.com" nur für Dienste mit offers[].domain = "gmx.com" (derzeit nur Pokemon, Code pkm) – andernfalls schlägt der Aufruf mit DOMAIN_UNAVAILABLE fehl. Jeder andere Wert ⇒ VALIDATION_ERROR.
warehousenumberNeinLager, aus dem gemietet wird. Ohne Angabe = LAGER 1, genau wie vor der Einführung mehrerer Lager (Dienste ohne Lager 1 weichen auf das günstigste aus) – bestehende Clients brauchen keine Änderungen. 0 = das günstigste Lager mit Bestand (es wird nur ein Lager versucht). 1–4 = genau dieses Lager (aus offers[].warehouse in GET /v1/services); wenn es diesen Dienst/Postfachtyp nicht verkauft, pausiert oder leer ist ⇒ WAREHOUSE_UNAVAILABLE. 99 = AUTOMATISCH: Lager mit Bestand werden vom günstigsten zum teuersten versucht und es geht weiter, wenn eines leer ist oder ausfällt; bleibt keines übrig ⇒ ALL_WAREHOUSES_EXHAUSTED – empfohlen für Dienste, die oft ausverkauft sind (Facebook, Instagram …). Siehe Lager.
Beispiel
# Erzeuge für jede NEUE Bestellung einen zufälligen Idempotency-Key
# Verwende bei einem erneuten Versuch nach einem Timeout DENSELBEN Schlüssel erneut (verhindert doppelte Bestellungen)
# domain: "gmail.com" (Standard, optional) | "icloud.com" (ein @icloud.com-Postfach mieten) | "gmx.com" (nur Dienste, die es anbieten, z. B. Pokemon)
# warehouse: 0 | 1–4 | 99 (optional – ohne Angabe bleibt es wie bisher bei Lager 1; 0 = günstigstes Lager mit Bestand; 99 = automatisch, geht weiter, wenn ein Lager leer ist; siehe offers[] in GET /v1/services)
curl -X POST "https://otpgmail.net/v1/orders" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"service":"ig","quantity":1,"domain":"gmail.com"}'
Beispielantwort
{
  "success": true,
  "data": [
    {
      "orderId": "abc123xyz",
      "service": "ig",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "waiting_code",
      "price": 1060,
      "otp": [],
      "codeDeadlineAt": 1753270000,
      "rentExpiresAt": 1753356400,
      "createdAt": 1753268200,
      "warehouse": 1,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    }
  ]
}
Beispielantwort – Teillieferung (quantity 5, 2 Postfächer geliefert)
{
  "success": true,
  "data": [
    {
      "orderId": "abc123xyz",
      "service": "ig",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "waiting_code",
      "price": 1060,
      "otp": [],
      "codeDeadlineAt": 1753270000,
      "rentExpiresAt": 1753356400,
      "createdAt": 1753268200,
      "warehouse": 1,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    },
    {
      "orderId": "jkl012mno",
      "service": "ig",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "waiting_code",
      "price": 1060,
      "otp": [],
      "codeDeadlineAt": 1753270001,
      "rentExpiresAt": 1753356401,
      "createdAt": 1753268201,
      "warehouse": 1,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    }
  ],
  "partial": {
    "requested": 5,
    "delivered": 2,
    "missing": 3,
    "reason": {
      "code": "NO_MAILS_AVAILABLE",
      "message": "No inboxes available for this service right now, please try again later",
      "retryable": true,
      "retryAfterSec": 5
    }
  }
}
Mögliche Fehler: UNAUTHORIZED, VALIDATION_ERROR, SERVICE_DISABLED, INSUFFICIENT_BALANCE, OUT_OF_STOCK, DOMAIN_UNAVAILABLE, WAREHOUSE_UNAVAILABLE, ALL_WAREHOUSES_EXHAUSTED, NO_MAILS_AVAILABLE, QUANTITY_EXCEEDED, WAITING_LIMIT_REACHED, RATE_LIMITED, DUPLICATE_REQUEST, PROVIDER_ERROR, MAINTENANCE
GET/v1/orders/{id}
Gibt eine Bestellung anhand der ID zurück, einschließlich der bisher empfangenen Codes (otp[]). Frage diesen Endpunkt alle 3–5 Sekunden ab, um den Code abzuholen, solange die Bestellung den Status waiting_code hat.
Das Array otp[] ist nach Eingang sortiert: ältester zuerst, NEUESTER ZULETZT – lies den neuesten Code mit otp[otp.length - 1]. Bei jedem Aufruf prüft das System beim Anbieter noch offene Bestellungen, im eigenen Takt jedes Lagers (alle paar Sekunden) – häufigeres Aufrufen liefert nur die gespeicherten Daten. Frage nach dem ersten Code weiter ab: Weitere Codes (wenn der Dienst mehr als einen sendet) werden ohne Aufpreis innerhalb des Bestellzeitraums ans Ende von otp[] angehängt – das Limit und das Zeitfenster werden vor dem Mieten angezeigt (maxCodes und fee in offers[]); um sicherzustellen, dass das System auf einen neuen Code wartet, rufe POST /v1/orders/{id}/next-code auf (jedes Lager; nur Lager 2 berechnet außerhalb seines kostenlosen Zeitraums eine Gebühr). Erlaubt der Dienst nur einen Code pro Postfach, endet otp[] bei diesem Code – die Antworten bleiben normal, ohne Fehler. Das Feld warehouse sagt dir, welches Lager das Postfach geliefert hat (1–4); nextCodeFee ist die Gebühr für weitere Codes (Bestellungen aus Lager 2 und 3; null für die Lager 1 und 4) – siehe Lager. Seit 2026-10-02 enthalten Bestellungen außerdem nextCodeBlocked (true = das Postfach kann keine weiteren Codes mehr empfangen) und nextCodeBlockReason – den Grund: "email_banned" (die E-Mail wurde vom Dienst gesperrt – miete eine neue), "activation_gone" (der Anbieter hat das Postfach geschlossen), "hard_expired" (das Code-Fenster ist beendet), "max_codes" (die maximale Anzahl an Codes wurde empfangen), null (nicht blockiert / ältere Bestellung ohne erfassten Grund). Das sind zusätzliche Felder – bestehende Clients brauchen keine Änderungen. Du fragst viele Bestellungen ab? Sende bis zu 100 IDs pro Aufruf an POST /v1/orders/status statt eines GET pro Bestellung – dieselben Bestellobjekte, nur eine Anfrage für dein Rate-Limit.
Parameter
NameTypErforderlichBeschreibung
idstringJaBestell-ID (die orderId, die beim Anlegen der Bestellung zurückgegeben wurde)
Beispiel
curl "https://otpgmail.net/v1/orders/ORDER_ID" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "orderId": "abc123xyz",
    "service": "ig",
    "domain": "gmail.com",
    "email": "[email protected]",
    "status": "completed",
    "price": 1060,
    "otp": [
      {
        "code": "123456",
        "receivedAt": 1753268500
      },
      {
        "code": "789012",
        "receivedAt": 1753269000
      }
    ],
    "codeDeadlineAt": 1753270000,
    "rentExpiresAt": 1753356400,
    "createdAt": 1753268200,
    "warehouse": 1,
    "nextCodeFee": null,
    "listening": true,
    "listenUntil": 1753270500,
    "nextCodeBlocked": false,
    "nextCodeBlockReason": null
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND
POST/v1/orders/{id}/cancel
Storniert die Bestellung und erstattet 100 % des Preises. Nur erlaubt, solange die Bestellung den Status waiting_code hat UND noch kein Code empfangen wurde.
Eine Bestellung, die einen Code erhalten hat (auch nur einen), kann nicht storniert oder erstattet werden – das gilt für die Website und die API gleichermaßen. Bestellungen mit rules.cancellable = false (Dienste aus eigenem Bestand wie Cointiply) können nicht storniert werden: Sobald das Postfach zugewiesen ist, gibt es keine Stornierung und keine Erstattung – der Stornoaufruf liefert ORDER_NOT_CANCELLABLE. Bestellungen aus Lager 3 bei regulären Diensten (rules.cancellable = true) können wie jede andere Bestellung vor dem Eintreffen eines Codes storniert werden.
Parameter
NameTypErforderlichBeschreibung
idstringJaID der zu stornierenden Bestellung
Beispiel
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/cancel" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json"
Beispielantwort
{
  "success": true,
  "data": {
    "orderId": "abc123xyz",
    "service": "ig",
    "domain": "gmail.com",
    "email": "[email protected]",
    "status": "cancelled",
    "price": 1060,
    "otp": [],
    "codeDeadlineAt": 1753270000,
    "rentExpiresAt": 1753356400,
    "createdAt": 1753268200,
    "warehouse": 1,
    "nextCodeFee": null,
    "listening": false,
    "listenUntil": null,
    "nextCodeBlocked": false,
    "nextCodeBlockReason": null
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, ORDER_NOT_CANCELLABLE, PROVIDER_ERROR
POST/v1/orders/{id}/rerent
Mietet dasselbe Postfach erneut für eine Bestellung eines Dienstes aus eigenem Bestand (provider = "inventory", z. B. Cointiply): legt eine NEUE Bestellung auf diesem Postfach mit einem frischen Empfangsfenster an, zum rerentPriceVnd der alten Bestellung.
Funktioniert nur, wenn die alte Bestellung canRerent = true hat (die neueste Bestellung dieses Postfachs, deren vorheriges Fenster beendet ist oder alle E-Mails empfangen hat). Du wirst belastet, sobald es gelingt – sende einen Idempotency-Key und verwende ihn bei einem erneuten Versuch wieder, damit dir nie doppelt berechnet wird. Die Antwort ist { order, message }; order hat dieselbe Form wie GET /v1/orders/{id}. Lager 2 (seit 2026-10-09): eine Bestellung, die storniert wurde, in ein Timeout lief oder abgelaufen ist (Postfach geschlossen) – rufe es auf, wenn canRerent = true ist (die neueste Bestellung dieser E-Mail, und die E-Mail hat keine andere aktive Bestellung). Das System öffnet genau diese E-Mail als NEUE Bestellung (status = waiting_code) zum Preis rerentPriceVnd (derzeit 100 % deines Preises) und wartet wie bei jeder Bestellung aus Lager 2 20 Minuten auf eine E-Mail – kommt keine, wird sie storniert und zu 100 % erstattet. E-Mail vom Dienst gesperrt ⇒ RERENT_EMAIL_BANNED; der Anbieter kann sie nicht mehr öffnen ⇒ RERENT_MAIL_GONE; Lager 2 vorübergehend ausgefallen ⇒ PROVIDER_ERROR – bei keinem der drei wird dir etwas berechnet.
Parameter
NameTypErforderlichBeschreibung
idstringJaID der vorherigen Bestellung des Postfachs (die mit canRerent = true)
Beispiel
# Verwende bei einem erneuten Versuch nach einem Timeout DENSELBEN Schlüssel erneut (verhindert doppelte Bestellungen)
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/rerent" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Idempotency-Key: $(uuidgen)"
Beispielantwort
{
  "success": true,
  "data": {
    "order": {
      "orderId": "def456uvw",
      "service": "ctp",
      "domain": "icloud.com",
      "email": "[email protected]",
      "status": "waiting_code",
      "price": 3500,
      "otp": [],
      "codeDeadlineAt": 1753273500,
      "rentExpiresAt": 1753273500,
      "createdAt": 1753269000,
      "provider": "inventory",
      "rules": {
        "windowMinutes": 75,
        "maxCodes": 4,
        "cancellable": false,
        "refundOnTimeout": false,
        "rerentRatio": 0.5
      },
      "windowEndsAt": 1753273500,
      "canRerent": false,
      "rerentPriceVnd": 3500,
      "warehouse": 3,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    },
    "message": "Inbox rented again — a new 75-minute message window has started"
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, RERENT_NOT_AVAILABLE, RERENT_EMAIL_BANNED, RERENT_MAIL_GONE, INSUFFICIENT_BALANCE, WAITING_LIMIT_REACHED, RATE_LIMITED, DUPLICATE_REQUEST, PROVIDER_ERROR, MAINTENANCE
POST/v1/orders/{id}/next-code
Fordert den nächsten Code auf demselben Postfach an – funktioniert für Bestellungen aus JEDEM Lager (Lager 1–4), die ihren ersten Code bereits erhalten haben (status = completed). Kostenlos, außer bei Bestellungen aus Lager 2, deren kostenloser Zeitraum beendet ist (nextCodeFee.feePriceVnd > 0): Dann sende confirmFee: true, um die Gebühr zu zahlen und ein weiteres Code-Fenster zu öffnen (bis zur harten Grenze nextCodeFee.hardUntil). Dein Bot muss das Lager nicht kennen: Frage GET /v1/orders/{id} weiter ab – neue Codes werden an otp[] angehängt – und rufe diesen Endpunkt auf, wenn das System auf einen neuen Code warten soll.
Was dieser Aufruf in jedem Lager tut
  • Lager 1: Das System wartet nach jedem Code innerhalb des Bestellzeitraums (rentExpiresAt) selbst auf den nächsten Code – der Aufruf liefert 200 und ändert nichts; kann das Postfach keine Codes mehr aufnehmen (Code-Limit erreicht, Postfach geschlossen, abgelaufen) ⇒ 409 mit nextCodeBlockReason.
  • Lager 2: öffnet das Postfach beim Anbieter erneut (reordered = true) und wartet 20 Minuten auf eine E-Mail; innerhalb des kostenlosen Zeitraums kostenlos (nextCodeFee.feePriceVnd = 0), außerhalb musst du confirmFee = true senden (fehlt es ⇒ 409 NEXT_CODE_FEE_CONFIRM, nichts berechnet). Die Gebühr bleibt nur bestehen, wenn ein neuer Code ankommt – kein Code innerhalb von 20 Minuten ⇒ automatische Erstattung. Nach jedem Code wartet das System weitere 25 Minuten auf den nächsten (listening = true, listenUntil).
  • Lager 3 (reguläre Dienste): öffnet ein kurzes Abhörfenster, kostenlos innerhalb des kostenlosen Zeitraums (nextCodeFee.freeUntil); danach ⇒ 409 – nutze POST /v1/orders/{id}/rerent. Dienste aus eigenem Bestand wie Cointiply (rules.cancellable = false) haben keinen nächsten Code (409): Jede E-Mail erscheint während des Fensters von selbst.
  • Lager 4: liest die Bestellung beim Anbieter neu und wartet auf einen neuen Code, kostenlos bis rentExpiresAt.
200 = das System wartet auf einen neuen Code für diese Bestellung (listening = true, listenUntil = wann es aufhört; reordered = true, wenn das Postfach gerade erneut geöffnet wurde; chargedVnd, wenn eine Gebühr erhoben wurde), oder genau beim Aufruf ist ein neuer Code angekommen (bereits in order.otp). 409 = es konnte kein neues Wartefenster geöffnet werden – lies error.code; warum das Postfach keine Codes mehr aufnimmt, steht in order.nextCodeBlockReason (GET /v1/orders/{id}). Eine Bestellung, die noch auf ihren ersten Code wartet (waiting_code) ⇒ 409, du musst noch nicht aufrufen. Sende bei kostenpflichtigen Aufrufen einen Idempotency-Key (verwende ihn bei einem erneuten Versuch wieder, damit dir nie doppelt berechnet wird). Anbieter nicht erreichbar ⇒ 502 PROVIDER_ERROR, versuche es später erneut. 409-Fehlercodes dieses Aufrufs: NEXT_CODE_FEE_CONFIRM (Lager 2 verlangt die Gebühr – rufe erneut mit confirmFee: true auf), EMAIL_BANNED (die E-Mail wurde vom Dienst gesperrt – miete eine neue; error.details.blockReason = "email_banned"), ORDER_EXPIRED (abgelaufen, Postfach geschlossen, Code-Limit erreicht oder der Dienst hat keinen „nächsten Code“ – der Grund steht in order.nextCodeBlockReason), ORDER_NOT_CANCELLABLE (die Bestellung hat ihren ersten Code noch nicht erhalten). Bots, die jedes 409 dieses Aufrufs als „keine Codes mehr“ behandeln, funktionieren unverändert weiter. refresh: true (Lager 2): prüft zuerst auf eine neue E-Mail (eine gerade eingetroffene wird zugestellt, kein Neuöffnen), öffnet dann das Postfach beim Anbieter neu – im kostenlosen Zeitraum beginnt die Wartezeit erneut für 20 Minuten; außerhalb behält sie ihr Ende (listenUntil) und es wird nichts berechnet. Die Antwort enthält zusätzlich refreshed (true = gerade neu geöffnet) und refreshAvailableAt (Unix-Sekunden; davor prüft ein weiteres Erneuern nur auf E-Mails; null = sofort möglich). Andere Lager: keine Wirkung (refreshed = false). Ohne refresh verhält sich der Aufruf genau wie bisher.
Parameter
NameTypErforderlichBeschreibung
idstringJaBestell-ID (orderId) – jedes Lager
confirmFeebooleanNeintrue = du bist einverstanden, nextCodeFee.feePriceVnd zu zahlen, um ein weiteres Code-Fenster zu öffnen. Nicht nötig, solange feePriceVnd = 0.
refreshbooleanNeintrue = Lager 2, die Bestellung wartet auf einen neuen Code (listening = true), aber es kommt keiner: das Postfach beim Anbieter sofort neu öffnen (Wartezeit erneuern) – die alte Wartezeit nimmt beim Anbieter eventuell keine E-Mails mehr an. Kostenlos; höchstens einmal alle 5 s pro Bestellung. Wartet die Bestellung nicht ⇒ wie ein normaler Aufruf.
Beispiel
# Kostenlos (nextCodeFee ist null oder feePriceVnd = 0 – jedes Lager außer Lager 2 nach seinem kostenlosen Zeitraum): kein confirmFee nötig
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/next-code" \
  -H "Authorization: Bearer og_xxx..."

# Lager 2 nach seinem kostenlosen Zeitraum: sende confirmFee = true, um nextCodeFee.feePriceVnd zu zahlen
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/next-code" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"confirmFee":true}'
Beispielantwort
{
  "success": true,
  "data": {
    "order": {
      "orderId": "ghi789rst",
      "service": "dr",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "completed",
      "price": 350,
      "otp": [
        {
          "code": "482913",
          "receivedAt": 1753268300
        }
      ],
      "codeDeadlineAt": 1753269700,
      "rentExpiresAt": 1753354200,
      "createdAt": 1753267800,
      "warehouse": 2,
      "nextCodeFee": {
        "feePriceVnd": 0,
        "freeUntil": 1753311000,
        "hardUntil": 1753354200,
        "paidPct": 50,
        "freeHours": 6
      },
      "listening": true,
      "listenUntil": 1753269500,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    },
    "message": "Another code window is open — waiting for a new code",
    "listening": true,
    "listenUntil": 1753269500,
    "reordered": true
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, NEXT_CODE_FEE_CONFIRM, INSUFFICIENT_BALANCE, EMAIL_BANNED, ORDER_EXPIRED, ORDER_NOT_CANCELLABLE, PROVIDER_ERROR, DUPLICATE_REQUEST, MAINTENANCE

Status vieler Bestellungen in einem Aufruf

Wiederverkäufer und Bots, die viele Postfächer mieten, sollten nicht für jede Bestellung ein eigenes GET /v1/orders/{id} senden: POST /v1/orders/status liest bis zu 100 Bestellungen pro Aufruf mit denselben Bestellobjekten, und GET /v1/orders listet deine Bestellungen aus den gespeicherten Daten.
  • Frage alle 3–5 Sekunden mit POST /v1/orders/status ab, bis zu 100 Bestell-IDs pro Aufruf – jeder Aufruf zählt als eine Anfrage für dein Rate-Limit.
  • Pro Aufruf prüft das System beim Anbieter höchstens 20 offene Bestellungen der Liste (zuerst die, die auf ihren ersten Code warten, dann die am längsten nicht geprüften) und liefert den Rest aus den gespeicherten Daten; diese werden bei den nächsten Aufrufen geprüft. Ein Aufruf antwortet in etwa 3 Sekunden.
  • Nimm eine Bestellung aus deiner Liste, sobald sie abgeschlossen ist: cancelled (erstattet) oder completed, wenn du keine weiteren Codes brauchst.
  • IDs, die es nicht gibt oder die zu einem anderen Konto gehören, kommen in missing zurück (kein 404) – nimm sie ebenfalls heraus.
  • Hole dir nach einem Neustart alle offenen Bestellungen mit GET /v1/orders?status=waiting_code zurück (folge nextCursor) und frage dann weiter mit POST /v1/orders/status ab.
POST/v1/orders/status
Liefert bis zu 100 deiner Bestellungen in einem Aufruf. Jedes Element ist genau das, was GET /v1/orders/{id} für diese Bestellung zurückgibt (status, otp[] mit allen empfangenen Codes, warehouse …). Damit fragst du viele offene Bestellungen gleichzeitig ab.
data behält die Reihenfolge von ids bei; doppelte IDs werden ignoriert. IDs, die es nicht gibt oder die zu einem anderen Konto gehören, stehen in missing (kein 404). Bei offenen Bestellungen prüft das System beim Anbieter wie GET /v1/orders/{id}, aber höchstens für 20 Bestellungen pro Aufruf – zuerst die, die auf ihren ersten Code warten, dann die am längsten nicht geprüften – und liefert die übrigen aus den gespeicherten Daten (sie werden bei den nächsten Aufrufen geprüft); ein Aufruf antwortet in etwa 3 Sekunden. Ein Aufruf zählt als eine Anfrage für dein Rate-Limit, egal wie viele IDs er enthält. Mehr als 100 IDs, eine leere Liste oder IDs, die keine Strings sind ⇒ 400 VALIDATION_ERROR.
Parameter
NameTypErforderlichBeschreibung
idsstring[]JaBestell-IDs (orderId) – 1 bis 100 pro Aufruf
Beispiel
# IDs der Bestellungen, auf die du wartest (orderId aus POST /v1/orders) – bis zu 100 pro Aufruf
curl -X POST "https://otpgmail.net/v1/orders/status" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json" \
  -d '{"ids":["ORDER_ID_1","ORDER_ID_2"]}'
Beispielantwort
{
  "success": true,
  "data": [
    {
      "orderId": "abc123xyz",
      "service": "ig",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "completed",
      "price": 1060,
      "otp": [
        {
          "code": "123456",
          "receivedAt": 1753268500
        }
      ],
      "codeDeadlineAt": 1753270000,
      "rentExpiresAt": 1753356400,
      "createdAt": 1753268200,
      "warehouse": 1,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    },
    {
      "orderId": "jkl012mno",
      "service": "ig",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "waiting_code",
      "price": 1060,
      "otp": [],
      "codeDeadlineAt": 1753270001,
      "rentExpiresAt": 1753356401,
      "createdAt": 1753268201,
      "warehouse": 1,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    }
  ],
  "missing": [
    "zzz999qqq"
  ]
}
Mögliche Fehler: UNAUTHORIZED, VALIDATION_ERROR, RATE_LIMITED
GET/v1/orders
Listet deine Bestellungen, neueste zuerst, nur aus gespeicherten Daten – es fragt den Anbieter nicht nach neuen Codes (das macht POST /v1/orders/status). Filtere nach Status und Erstellungszeit; blättere mit nextCursor.
Übergib nextCursor als cursor, um die nächste Seite zu bekommen; nextCursor = null ⇒ letzte Seite. Seiten wiederholen oder überspringen nie eine Bestellung, selbst wenn viele Bestellungen in derselben Sekunde angelegt wurden. Interne Zustände werden nie gelistet (nur waiting_code, completed, cancelled und queued). Nach einem Neustart deines Bots: GET /v1/orders?status=waiting_code&limit=200, folge nextCursor und frage diese Bestellungen dann mit POST /v1/orders/status ab.
Parameter
NameTypErforderlichBeschreibung
statusstringNeinNur dieser Status: waiting_code | completed | cancelled | queued (weggelassen ⇒ alle)
sincenumberNeinNur Bestellungen, die zu oder nach dieser Unix-Zeit (Sekunden) angelegt wurden
limitnumberNeinBestellungen pro Seite: 1–200 (Standard 50)
cursorstringNeinnextCursor der vorigen Seite, unverändert
Beispiel
# Nach einem Neustart: alle Bestellungen, die noch auf einen Code warten, Seite für Seite einsammeln
curl "https://otpgmail.net/v1/orders?status=waiting_code&limit=200" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": [
    {
      "orderId": "jkl012mno",
      "service": "ig",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "waiting_code",
      "price": 1060,
      "otp": [],
      "codeDeadlineAt": 1753270001,
      "rentExpiresAt": 1753356401,
      "createdAt": 1753268201,
      "warehouse": 1,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    },
    {
      "orderId": "abc123xyz",
      "service": "ig",
      "domain": "gmail.com",
      "email": "[email protected]",
      "status": "waiting_code",
      "price": 1060,
      "otp": [],
      "codeDeadlineAt": 1753270000,
      "rentExpiresAt": 1753356400,
      "createdAt": 1753268200,
      "warehouse": 1,
      "nextCodeFee": null,
      "listening": false,
      "listenUntil": null,
      "nextCodeBlocked": false,
      "nextCodeBlockReason": null
    }
  ],
  "nextCursor": "1753268200:abc123xyz"
}
Mögliche Fehler: UNAUTHORIZED, VALIDATION_ERROR, RATE_LIMITED

Lager

Jeder Eintrag in offers[] ist ein Angebot = (Dienst, Lager, Postfachtyp). Es werden nur Lager aufgelistet, die gerade verkaufen; ein Lager, das vorübergehend leer ist, bleibt mit stock = 0 in der Liste. Die Lagernummern sind fest: 1, 2, 3, 4 – übergib dieselbe Nummer im Parameter warehouse, wenn du eine Bestellung anlegst.
Bestehende Clients brauchen keine Änderungen. Ohne warehouse ⇒ dasselbe Verhalten wie bisher (Lager 1: 25 Minuten Wartezeit, zusätzliche Codes kommen automatisch innerhalb des 24-Stunden-Bestellzeitraums an, wenn der Dienst mehr als einen sendet); sende 0 ⇒ günstigstes Lager mit Bestand; sende 99 ⇒ automatisches Lager, das weitergeht, wenn eines leer ist (siehe direkt unten); jedes bestehende Feld bleibt, wie es ist – es kommen nur neue Felder hinzu (offers[], warehouse, nextCodeFee, listening, nextCodeBlocked …).
Automatisches Lager: warehouse = 99 (empfohlen für Dienste, die oft ausverkauft sind)
Sende "warehouse": 99 und das System mietet aus dem günstigsten Lager mit Bestand; ist dieses Lager leer oder fehlerhaft, geht es im selben Aufruf zum nächsten über. Ideal für Dienste, die oft ausverkauft sind, wie Facebook und Instagram – dein Bot muss offers[] nicht mehr lesen und Lager für Lager erneut versuchen.
  • Reihenfolge der Versuche: Preis von niedrig nach hoch (z. B. 1.500 → 1.600 → 1.700 VND); gleiche Preise ⇒ zuerst die niedrigere Lagernummer.
  • Lager, die keinen Bestand melden (stock = 0 in offers[]), werden nie aufgerufen – keine verschwendete Zeit.
  • Geht weiter, wenn das aktuelle Lager leer oder fehlerhaft ist (OUT_OF_STOCK, NO_MAILS_AVAILABLE, WAREHOUSE_UNAVAILABLE, PROVIDER_ERROR).
  • Andere Fehler werden sofort zurückgegeben, ohne das nächste Lager zu versuchen: INSUFFICIENT_BALANCE, WAITING_LIMIT_REACHED, QUANTITY_EXCEEDED, MAINTENANCE, SERVICE_DISABLED, DOMAIN_UNAVAILABLE …
  • quantity > 1: Reicht das erste Lager nicht, kommt der Rest aus dem nächsten – Bestellungen können mehrere Lager umfassen; jede Bestellung trägt das warehouse und den price des Lagers, das sie ausgegeben hat (und folgt dessen Coderegeln).
  • Wurden alle Lager versucht und konnte kein Postfach gemietet werden ⇒ ALL_WAREHOUSES_EXHAUSTED (HTTP 409, nichts berechnet) – versuche es später erneut. Wurden nur einige Postfächer gemietet, erhältst du trotzdem 200 mit diesen Bestellungen.
Beispiel: Facebook-Gmail mit warehouse = 99 mieten
# warehouse: 99 – günstigstes Lager mit Bestand; leer oder fehlerhaft ⇒ automatisch das nächste Lager
curl -X POST "https://otpgmail.net/v1/orders" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"service":"fb","quantity":1,"domain":"gmail.com","warehouse":99}'
Felder von offers[]
FeldTypBeschreibung
warehouse1 | 2 | 3 | 4Lagernummer: 1, 2, 3 oder 4.
domainstringPostfachtyp des Angebots: gmail.com, icloud.com oder gmx.com (zusätzliche Domain – nur bei einigen Diensten, derzeit Pokemon).
pricenumberPreis eines Postfachs aus diesem Lager (VND).
stocknumberÜbrige Postfächer. 0 = nicht auf Lager – Lager 1 oder 2 liefert dann WAREHOUSE_UNAVAILABLE; Lager 4 wird trotzdem versucht (seine Quellen liefern oft, obwohl sie 0 melden) und gibt OUT_OF_STOCK zurück, wenn nichts verfügbar ist.
maxCodesnumber | nullMaximale Anzahl an Codes, die ein Postfach empfangen kann; null = unbegrenzt / unbekannt (siehe fee).
rulesobject | nullFensterregeln für Lager 3 (windowMinutes, maxCodes, cancellable, refundOnTimeout, rerentRatio); null für die Lager 1, 2 und 4.
feeobject | nullFenster für zusätzliche Codes: freeHours kostenlose Stunden ab der Miete, danach kostet jede Verlängerung paidPct % des Mietpreises, bis zu maxHours Stunden ab der Miete (null = kein Limit). fee = null: keine Gebühr – zusätzliche Codes kosten innerhalb des Bestellzeitraums nichts, wenn der Dienst mehr als einen sendet (bis zu maxCodes), wie bei Lager 1.
isDefaultbooleantrue für das günstigste Lager mit Bestand (bei Gleichstand ⇒ niedrigere Lagernummer) – das, das bei warehouse = 0 verwendet wird, und das erste, das bei 99 versucht wird.
Regeln nach Lager
RegelLager 1Lager 2Lager 3Lager 4
Wartezeit auf den ersten Code25 Minuten20 Minuten25 Minuten ¹Bis zu 25 Minuten (bei manchen Quellen kürzer – die tatsächliche Frist ist das codeDeadlineAt der Bestellung)
Kein CodeAutomatisch storniert, 100 % ErstattungAutomatisch storniert, 100 % ErstattungAutomatisch storniert, 100 % Erstattung ¹Automatisch storniert, 100 % Erstattung
Stornieren vor einem CodeJaJaJa ¹Ja
Codes pro PostfachLaut maxCodesUnbegrenztUnbegrenzt ¹Laut maxCodes (meist null = kein Limit; 1 bei einigen Angeboten)
Weitere CodesOhne Aufpreis innerhalb des 24-Stunden-Bestellzeitraums, wenn der Dienst weitere Codes sendet; automatisch (kein zusätzlicher Aufruf). Das Postfach bleibt nach dem letzten Code meist etwa 25 Minuten offen.fee.freeHours Stunden kostenlos (derzeit Gmail 6 Stunden, iCloud 12 Stunden); danach kostet jede Verlängerung fee.paidPct % des Preises über next-code, bis zu fee.maxHours (Gmail 24 Stunden, iCloud 10 Tage)24 Stunden ohne Aufpreis; danach dasselbe Postfach erneut mieten (rerent) für 50 % des PreisesOhne Aufpreis innerhalb des 24-Stunden-Bestellzeitraums, wie bei Lager 1 – neue Codes erscheinen beim Lesen der Bestellung; next-code liest sie sofort neu.
¹ Dienste, die nur aus dem eigenen Bestand verkauft werden, wie Cointiply (provider = "inventory", rules.cancellable = false), folgen rules: bis zu rules.maxCodes E-Mails innerhalb von rules.windowMinutes Minuten, keine Stornierung und keine Erstattung, wenn die Zeit ohne E-Mail abläuft.
nextCodeFee – Gebühr für weitere Codes (Bestellungen aus Lager 2 und 3)
Enthalten in GET /v1/orders/{id} und in jeder Bestellantwort; null bei Bestellungen aus Lager 1 und 4 (weitere Codes sind kostenlos). Lager 2: Rufe POST /v1/orders/{id}/next-code auf, wenn du einen weiteren Code brauchst – ist feePriceVnd größer als 0, musst du confirmFee = true senden. Lager 3 (reguläre Dienste): Ist der kostenlose Zeitraum vorbei, ist feePriceVnd der Preis, um dasselbe Postfach erneut zu mieten (rerent).
FeldTypBeschreibung
feePriceVndnumberBetrag, der berechnet wird, wenn du jetzt next-code aufrufst; 0 = noch innerhalb des kostenlosen Zeitraums.
freeUntilnumber | nullEnde des aktuellen kostenlosen Zeitraums (Unix-Sekunden).
hardUntilnumber | nullHarte Grenze (Unix-Sekunden) – danach kommen keine Codes mehr.
paidPctnumber% des Mietpreises, die für jedes zusätzliche Fenster berechnet werden.
freeHoursnumberKostenlose Stunden eines Code-Fensters (Lager 2: Gmail 6, iCloud 12; Lager 3: 24).
Beispiel: Lager auflisten und dann aus einem mieten
# warehouse: 0 | 1–4 | 99 (optional – ohne Angabe bleibt es wie bisher bei Lager 1; 0 = günstigstes Lager mit Bestand; 99 = automatisch, geht weiter, wenn ein Lager leer ist; siehe offers[] in GET /v1/services)
curl -X POST "https://otpgmail.net/v1/orders" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"service":"dr","quantity":1,"domain":"gmail.com","warehouse":2}'
Beispielbestellung aus Lager 2 (GET /v1/orders/{id})
{
  "success": true,
  "data": {
    "orderId": "ghi789rst",
    "service": "dr",
    "domain": "gmail.com",
    "email": "[email protected]",
    "status": "completed",
    "price": 350,
    "otp": [
      {
        "code": "482913",
        "receivedAt": 1753268300
      }
    ],
    "codeDeadlineAt": 1753269700,
    "rentExpiresAt": 1753354200,
    "createdAt": 1753267800,
    "warehouse": 2,
    "nextCodeFee": {
      "feePriceVnd": 175,
      "freeUntil": 1753289400,
      "hardUntil": 1753354200,
      "paidPct": 50,
      "freeHours": 6
    },
    "listening": false,
    "listenUntil": null,
    "nextCodeBlocked": false,
    "nextCodeBlockReason": null
  }
}

Sammelmiete (Sammelbestellungen)

Sammelbestellungen werden im Web gekauft (Seite Sammelmiete) – über die API kannst du Sammelbestellungen auflisten und Codes lesen. Die E-Mail-Domain bestimmt das Lager: Gmail = Lager 1, iCloud = Lager 3 (private Postfächer).
  • Eine Sammelbestellung wird ab dem Kauf 24 Stunden lang aufbewahrt – lies in dieser Zeit Codes kostenlos, ohne Begrenzung der Codeanzahl.
  • Postfächer, die keinen Code erhalten, werden nicht erstattet; nur eine Fehlmenge wird (automatisch) erstattet.
  • Lager 1 (Gmail): Jedes Postfach hat eine proxyUrl – ein GET liefert {"status":1,"code":"482913","all_codes":[...]} ohne API-Schlüssel (der Link ist das Geheimnis – halte ihn privat). Der Code-Endpunkt unten funktioniert ebenfalls.
  • Lager 3 (iCloud): proxyUrl = null – lies Codes mit dem Endpunkt unten oder öffne readUrl (Webseite). Jedes Postfach ist für diesen Dienst an dein Konto gebunden; nach Ablauf kannst du es im Web weiterhin erneut mieten.
  • E-Mails ohne extrahierbaren Code (Willkommens-E-Mails, Newsletter …) kommen unverändert in code mit isRaw: true zurück (HTML oder Text, bis zu 20.000 Zeichen).
Felder eines Postfachs in einer Sammelbestellung (mails[])
FeldTypBeschreibung
idnumberPostfach-ID – der Parameter {mailId} des Code-Endpunkts
emailstringE-Mail-Adresse
urlstringLager 1: = proxyUrl · Lager 3: = readUrl (für ältere Skripte / Exportdateien beibehalten)
proxyUrlstring | nullLager 1: Code-Link, der keine Anmeldung braucht · Lager 3: null
readUrlstringWebseite zum Lesen der Codes der Sammelbestellung
codeCountnumberAnzahl der empfangenen Codes
lastCodestring | nullNeuester Code (rohe E-Mail: die ersten 300 Zeichen – der volle Text steht im Code-Endpunkt)
lastIsRawbooleantrue = der neueste Code ist eine rohe E-Mail
checkedAtnumber | nullLetzter Zeitpunkt, an dem das System den Anbieter für dieses Postfach geprüft hat (Unix-Sekunden; null = nie)
GET/v1/batches
Deine Sammelbestellungen, neueste zuerst (seitenweise mit page / pageSize, bis zu 100). Jede Sammelbestellung hat warehouse (1 = Gmail, 3 = iCloud), status (active | expired | failed), expiresAt, codeCount und readUrl.
Parameter
NameTypErforderlichBeschreibung
pagenumberNeinSeite (Standard 1)
pageSizenumberNeinSammelbestellungen pro Seite (Standard 20, max. 100)
Beispiel
curl "https://otpgmail.net/v1/batches?page=1&pageSize=20" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "k3v9m2x7q1w8e5r4",
        "serviceCode": "lf",
        "serviceName": "Tiktok",
        "domain": "icloud.com",
        "warehouse": 3,
        "status": "active",
        "countRequested": 3,
        "countReceived": 3,
        "countDuplicate": 0,
        "countCharged": 3,
        "unitPriceVnd": 600,
        "quotedVnd": 1800,
        "chargedVnd": 1800,
        "refundVnd": 0,
        "holdHours": 24,
        "expiresAt": 1753354200,
        "createdAt": 1753267800,
        "readUrl": "https://otpgmail.net/app/batch/k3v9m2x7q1w8e5r4",
        "codeCount": 1,
        "rerentPriceVnd": null
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}
Mögliche Fehler: UNAUTHORIZED
GET/v1/batches/{id}
Eine Sammelbestellung mit ihren mails[] – nimm die id jedes Postfachs, um seine Codes zu lesen.
Gmail-Sammelbestellungen (Lager 1): Die proxyUrl jedes Postfachs funktioniert in deinen Skripten genau wie bisher. iCloud-Sammelbestellungen (Lager 3): proxyUrl = null, lies Codes mit dem Endpunkt unten. Abgelaufene iCloud-Sammelbestellungen enthalten rerentPriceVnd – den Preis, um ein Postfach einzeln erneut zu mieten (im Web).
Parameter
NameTypErforderlichBeschreibung
idstringJaSammelbestell-ID (angezeigt auf der Seite Sammelmiete und in readUrl)
Beispiel
curl "https://otpgmail.net/v1/batches/BATCH_ID" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "id": "k3v9m2x7q1w8e5r4",
    "serviceCode": "lf",
    "serviceName": "Tiktok",
    "domain": "icloud.com",
    "warehouse": 3,
    "status": "active",
    "countRequested": 3,
    "countReceived": 3,
    "countDuplicate": 0,
    "countCharged": 3,
    "unitPriceVnd": 600,
    "quotedVnd": 1800,
    "chargedVnd": 1800,
    "refundVnd": 0,
    "holdHours": 24,
    "expiresAt": 1753354200,
    "createdAt": 1753267800,
    "readUrl": "https://otpgmail.net/app/batch/k3v9m2x7q1w8e5r4",
    "codeCount": 1,
    "rerentPriceVnd": null,
    "mails": [
      {
        "id": 812,
        "email": "[email protected]",
        "url": "https://otpgmail.net/app/batch/k3v9m2x7q1w8e5r4",
        "proxyUrl": null,
        "readUrl": "https://otpgmail.net/app/batch/k3v9m2x7q1w8e5r4",
        "fresh": true,
        "codeCount": 1,
        "lastCode": "482913",
        "lastCodeAt": 1753268500,
        "lastIsRaw": false,
        "checkedAt": 1753268507
      }
    ]
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND
GET/v1/batches/{id}/mails/{mailId}/codes
Lies die Codes eines Postfachs in einer Sammelbestellung: Das System prüft beim Anbieter, wenn das Postfach fällig ist, und liefert dann JEDEN empfangenen Code, ältester zuerst – der neueste Code steht zuletzt in codes[].
Frage während des Wartens alle 5–10 Sekunden ab (häufigeres Aufrufen liefert nur die gespeicherten Daten – polledAt ändert sich nicht). E-Mails ohne extrahierbaren Code kommen unverändert in code mit isRaw: true zurück. Abgelaufene Sammelbestellungen liefern weiterhin ihre gespeicherten Codes, werden aber nicht mehr geprüft.
Parameter
NameTypErforderlichBeschreibung
idstringJaSammelbestell-ID
mailIdnumberJaPostfach-ID (mails[].id)
poll0 | 1Nein0 = nur gespeicherte Codes lesen, den Anbieter nicht prüfen (Standard 1)
Beispiel
# Rufe es während des Wartens alle 5–10 Sekunden erneut auf; der neueste Code steht zuletzt in codes[]
curl "https://otpgmail.net/v1/batches/BATCH_ID/mails/MAIL_ID/codes" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "mailId": 812,
    "codes": [
      {
        "code": "482913",
        "receivedAt": 1753268500,
        "isRaw": false
      }
    ],
    "polledAt": 1753268507,
    "mail": {
      "id": 812,
      "email": "[email protected]",
      "url": "https://otpgmail.net/app/batch/k3v9m2x7q1w8e5r4",
      "proxyUrl": null,
      "readUrl": "https://otpgmail.net/app/batch/k3v9m2x7q1w8e5r4",
      "fresh": true,
      "codeCount": 1,
      "lastCode": "482913",
      "lastCodeAt": 1753268500,
      "lastIsRaw": false,
      "checkedAt": 1753268507
    }
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, RATE_LIMITED

Buy live Gmail accounts (2 warehouses)

Buy short-lived Gmail accounts outright: you are charged when you order and the accounts arrive within seconds. There are two mail-shop warehouses (numbered separately from the OTP-rental warehouses 1–4), each with ONE price for any quantity. Prices returned by the API are YOUR account's prices (custom price if the admin set one) — exactly what you are charged.
Warehouses
WarehouseEach account deliversDefault priceStock
1gm_cookieemail · password · cookies · inboxUrl (inbox link) · inboxJsonUrl (emails as JSON) · twoFaUrl (2FA link — just the 6 digits)3,300 VNDA number — published by the source, refreshed every minute. The source checks that accounts are live at purchase time, so you may get fewer — the shortfall is refunded (a partial order; warehouse 99: the rest goes to the next warehouse)
2gm_loginemail · password · loginUrl (quick login link — open it in a desktop browser) · otpUrl (Vinted OTP link — when delivers lists it)6,000 VNDA REFERENCE number from a source sharing the same stock (refreshed every minute; null when it cannot be read) — may be off by a few, never blocks a purchase; a shortfall becomes a partial order with the rest refunded
99As the warehouse that supplied each account (items[].warehouse)Price of the cheapest warehouse on sale (auto.priceVnd)Buys the cheapest warehouse first (auto.order); if it runs out midway the rest goes to the next warehouse at that warehouse's price (the difference is charged — refunded instead if your balance is short); every warehouse out ⇒ MAILSHOP_OUT_OF_STOCK, nothing charged
Anmeldung bei Konten aus Lager 1 (ohne Wiederherstellungs-E-Mail)
  • Konten aus Lager 1 (gm_cookie) kommen ohne Wiederherstellungs-E-Mail – du bekommst E-Mail, Passwort, Cookies und den Postfach-Link (inboxUrl / inboxJsonUrl), mehr nicht.
  • Empfohlene Anmeldung: Cookies importieren (cookies, JSON) – mit einer Cookie-Import-Erweiterung oder dem Cookie-Import deines Antidetect-Browsers – und dann Gmail öffnen: Die Sitzung ist bereits angemeldet, meist ohne Passwortabfrage und ohne Bestätigung.
  • Brauchst du nur die Codes oder E-Mails? Lies sie über inboxUrl (Webseite) oder inboxJsonUrl (JSON) – ohne API-Schlüssel und ohne Google-Anmeldung.
  • Die Anmeldung mit dem Passwort auf einem neuen Gerät oder unter einer neuen IP kann dazu führen, dass Google eine Bestätigung verlangt (Wiederherstellungs-E-Mail, Telefon oder Code) – diese Konten haben nichts, womit sie sie bestehen könnten; nutze stattdessen die Cookies oder den Postfach-Link.
  • Du verkaufst weiter? Gib deinen Kunden die Cookies und den Postfach-Link – der .txt-Download (GET /v1/mail-shop/orders/{id}/download) enthält email|password|cookies|inboxUrl in einer Zeile – und sag ihnen, dass sie sich mit den Cookies anmelden sollen, nicht mit dem Passwort.
  • Konten aus Lager 2 (gm_login) kommen stattdessen mit einem Schnell-Login-Link (loginUrl) – öffne ihn zum Anmelden in einem Desktop-Browser.
3 steps
  1. See warehouses & prices: GET /v1/mail-shop/products ⇒ products[] (per warehouse: priceVnd, stock, available, maxQty) + auto (warehouse 99: order, priceVnd). To see the total first, call GET /v1/mail-shop/quote.
  2. Buy: POST /v1/mail-shop/orders with {"warehouse": 1 | 2 | 99, "quantity": N} + header Idempotency-Key (a new UUID per purchase). The response is the order, usually already completed with items[].
  3. Get the accounts: read items[] (table below) or download the TXT GET /v1/mail-shop/orders/{id}/download. status = pending ⇒ poll GET /v1/mail-shop/orders/{id} every 5 seconds until completed / partial / failed.
# 1) warehouses, your prices, stock
curl "https://otpgmail.net/v1/mail-shop/products" -H "Authorization: Bearer og_xxx..."

# 2) buy (warehouse 1 | 2 | 99) — a NEW Idempotency-Key per purchase
curl -X POST "https://otpgmail.net/v1/mail-shop/orders" \
  -H "Authorization: Bearer og_xxx..." -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" --max-time 60 \
  -d '{"warehouse":99,"quantity":2}'

# 3) status "pending"? read the order again (or download the .txt)
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID" -H "Authorization: Bearer og_xxx..."
Fields of an account (items[])
FeldTypBeschreibung
lineNonumberPosition in the order (1, 2, …) — the {lineNo} of the inbox endpoints
warehouse1 | 2Warehouse that supplied this account
emailstringGmail address
passwordstringPassword
recoverystring?Recovery email — only when the source provides one; warehouse 1 (gm_cookie) accounts have NONE (sign in with the cookies, see the sign-in notes above)
cookiesstring | nullWarehouse 1: login cookies (JSON)
inboxUrlstring | nullWarehouse 1: inbox page without an API key (https://otpgmail.net/mail/{code}); null = link turned off
inboxJsonUrlstring | nullWarehouse 1: the same link + ?json ⇒ emails as JSON (section below)
twoFaUrlstring | nullWarehouse 1: the 2FA LINK (https://otpgmail.net/2fa/{code} — same code + expiry as inboxUrl): GET / open it in a browser ⇒ JUST the 6-digit 2FA code (section below). null = link turned off. The .txt file has this column when downloaded with ?twofa=1
inboxLinkExpiresAtnumber | nullWarehouse 1: when the link expires (unix seconds); null = no limit
loginUrlstring | nullWarehouse 2: quick login link
otpUrlstring | nullWarehouse 2: the account's Vinted OTP link — open it in a browser to see the code, ?json ⇒ JSON (section below). null = this account has no link
otpService"vinted" | nullWarehouse 2: the service of otpUrl
rawLinestringThe whole account on one line (cookies compacted) — copy it as is
Read the inbox as JSON (inboxJsonUrl)
GET {inboxJsonUrl} (= inboxUrl + ?json) returns the same JSON as GET /api/mailbox/{code}: email, letters[] (≤ max latest emails: from, subject, receivedAt, code — extracted code / link, content — sanitized HTML), total, etag, nextAt. No API key needed (the code in the link is the key — keep it private), CORS is open. Add &max=20|50|100|200 for more emails (default 10), &etag={previous etag} ⇒ unchanged emails come back as empty letters + unchanged: true. Reading one link faster than every 10 seconds ⇒ 429 (retryAfter); link off / wrong / expired ⇒ 404; account locked ⇒ 410.
curl "https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1d?json&max=20"
{
  "success": true,
  "data": {
    "email": "[email protected]",
    "letters": [
      {
        "key": "h:9f2c4e1a7b3d5c80",
        "from": "Instagram [email protected]",
        "subject": "350038 is your Instagram code",
        "receivedAt": 1760000520,
        "code": "350038",
        "content": "<html><body><p>Enter this confirmation code in the app: 350038</p></body></html>"
      }
    ],
    "max": 10,
    "total": 8,
    "fetchedAt": 1760000530,
    "cached": false,
    "etag": "f1c2a9d4e0b37a65",
    "unchanged": false,
    "nextAt": 1760000540,
    "expiresAt": 1762592400,
    "serverTime": 1760000530
  }
}
Read the Vinted OTP code (otpUrl)
A warehouse-2 account with otpUrl ⇒ open it in a browser to see the latest Vinted code (the page refreshes itself); GET {otpUrl}?json returns {status, code, service, email, checkedAt, retryAfter}: status = waiting (no code yet — ask again after retryAfter seconds) · ok (code = the latest code) · dead (codes can no longer be read for this account) · error (temporarily unreadable — ask again). No API key needed (the code in the link is the key — keep it private), CORS is open; the server reads the source at most every few seconds per account; more than 120 calls / minute / IP ⇒ 429.
curl "https://otpgmail.net/api/mail-shop/otp/p3x9k2m7q1w8e5r4.2.Zx8Kq2Lm4Np6Rt0Vw3Yb5c?json"
{
  "success": true,
  "data": {
    "status": "ok",
    "code": "4829",
    "service": "vinted",
    "email": "[email protected]",
    "checkedAt": 1760000530,
    "retryAfter": 5
  }
}
The 2FA code (twoFaUrl)
Some warehouse-1 accounts have 2-step verification (2FA) on — a password sign-in makes Google ask for a 6-digit code. GET {twoFaUrl} (or open it in a browser) returns text/plain with JUST the current 6 digits (no newline), a Refresh header = seconds until the next code + 1 (browsers reload by themselves), Cache-Control: no-store, open CORS — you can hand the link to your customers. Account without 2FA ⇒ 404 no 2fa; wrong / off / expired link ⇒ 404 not found; locked account ⇒ 410 gone; too fast / source hiccup ⇒ 429 / 502 retry (Retry-After header). With an API key: GET /v1/mail-shop/orders/{id}/items/{lineNo}/2fa ⇒ {none, code, expiresIn}. The server caches the code until it expires. The .txt file (GET /v1/mail-shop/orders/{id}/download): add ?twofa=1 ⇒ a last twoFaUrl column (off by default — the old layout is unchanged).
curl "https://otpgmail.net/2fa/Q7xK2mP9vL4tR8wN3cB6yH1d"
# 424819
Order statuses
StatusBeschreibung
pendingBeing delivered — accounts that arrived are in items[]; poll again in 5 seconds
completedFully delivered. Final.
partialDelivered short — the shortfall was refunded (refundVnd; message = the reason + the source's own words when it gave any). Final.
failedNothing delivered — fully refunded (message as above). Final.
refundedRefunded by an admin (refundVnd). Final.
Rules
  • Money: chargedVnd = Σ route[].quantity × unitPriceVnd (a 99 order split across warehouses ⇒ two legs in route). Shortfalls are refunded automatically (refundVnd). Delivered accounts are not refunded — the only warranty is the warehouse's terms (read them first).
  • Idempotency-Key is required (400 without it): retrying with the SAME key after a timeout returns the same order and charges nothing more; a new key buys again. Use a client timeout of at least 60 seconds.
  • Limits: at most 500 accounts per order (and the warehouse's maxQty), 10 purchases per minute for your account (web + API share it), 180 inbox reads per hour. Above ⇒ 429 RATE_LIMITED.
  • Common errors: MAILSHOP_OUT_OF_STOCK 409 (details.stock = how many you can buy now) · MAILSHOP_QTY_LIMIT 400 (details.max) · INSUFFICIENT_BALANCE 402 · MAILSHOP_DISABLED 503 (warehouse / section paused) · MAILSHOP_SUPPLIER_ERROR 502 (the source refused at once — a failed order was recorded and refunded, details.orderId) · VALIDATION_ERROR 400 (warehouse not 1 | 2 | 99, warehouse and product disagree, missing Idempotency-Key…).
  • Terms (warranty only for the first sign-in within 2 hours, accounts last ~6–24 hours) are in each warehouse's terms; the API does not ask you to accept them (the web asks once). API orders show up in your web history (channel = api) and vice versa.
GET/v1/mail-shop/products
The two mail-shop warehouses + automatic warehouse 99. products[]: warehouse (1 | 2), code (gm_cookie | gm_login), delivers (fields of every account), priceVnd (YOUR price), listPriceVnd (list price), stock, available, maxQty (most one order can take now). auto: order (the order warehouse 99 buys in), priceVnd (cheapest warehouse), available.
Warehouse 1 stock = what the source publishes (refreshed every minute; the source checks accounts are live at purchase time, so you may get fewer — the shortfall is refunded). Warehouse 2 = a reference number from a source sharing the same stock, may be off by a few (null = could not be read — just buy); a shortfall becomes a partial order with the rest refunded. available = false ⇒ do not buy from that warehouse (out of stock / paused). Prices can change — read the catalog again before a large purchase.
Beispiel
curl "https://otpgmail.net/v1/mail-shop/products" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "products": [
      {
        "warehouse": 1,
        "code": "gm_cookie",
        "name": "Short-lived live Gmail — with cookies + inbox link",
        "description": "Short-lived personal Gmail account. You get the email · password · login cookies · an inbox link (add ?json to read the emails as JSON).",
        "terms": "• Warranty covers ONLY the first successful sign-in, within 2 hours of purchase.\n• These Gmail accounts typically last 6–24 hours: use them right after purchase.",
        "delivers": [
          "email",
          "password",
          "cookies",
          "inboxUrl",
          "inboxJsonUrl"
        ],
        "priceVnd": 3300,
        "listPriceVnd": 3300,
        "stock": 1840,
        "available": true,
        "maxQty": 500
      },
      {
        "warehouse": 2,
        "code": "gm_login",
        "name": "Short-lived live Gmail — with a quick login link",
        "description": "Short-lived personal Gmail account that has been used on a few services. You get the email · password · a quick login link.",
        "terms": "• Warranty covers ONLY the first successful sign-in, within 2 hours of purchase.\n• Sign in with the link in a DESKTOP BROWSER.",
        "delivers": [
          "email",
          "password",
          "loginUrl"
        ],
        "priceVnd": 6000,
        "listPriceVnd": 6000,
        "stock": 141,
        "available": true,
        "maxQty": 100
      }
    ],
    "auto": {
      "warehouse": 99,
      "priceVnd": 3300,
      "available": true,
      "order": [
        1,
        2
      ]
    }
  }
}
Mögliche Fehler: UNAUTHORIZED
GET/v1/mail-shop/quote
Call quote to see the total first (nothing is reserved). Warehouse 99 ⇒ route[] = planned split by known stock; available = false ⇒ that quantity cannot be bought right now.
Parameter
NameTypErforderlichBeschreibung
warehouse1 | 2 | 99NeinMail-shop warehouse: 1 = email + password + cookies + inbox link · 2 = email + password + login link · 99 = automatic, cheapest first. Send warehouse OR product
productstringNeinInstead of warehouse: gm_cookie (= warehouse 1) | gm_login (= warehouse 2)
quantitynumberNeinNumber of accounts (default 1)
Beispiel
curl "https://otpgmail.net/v1/mail-shop/quote?warehouse=99&quantity=10" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "warehouse": 99,
    "quantity": 10,
    "unitPriceVnd": 3300,
    "totalVnd": 38400,
    "available": true,
    "stock": null,
    "maxQty": 500,
    "route": [
      {
        "warehouse": 1,
        "quantity": 8,
        "unitPriceVnd": 3300
      },
      {
        "warehouse": 2,
        "quantity": 2,
        "unitPriceVnd": 6000
      }
    ]
  }
}
Mögliche Fehler: UNAUTHORIZED, VALIDATION_ERROR, MAILSHOP_PRODUCT_NOT_FOUND
POST/v1/mail-shop/orders
Buy: you are charged at once, then the system buys from the source and delivers. The response is the order — usually already completed with items[]; status = pending ⇒ poll GET /v1/mail-shop/orders/{id} every 5 seconds. The call waits up to about 16 seconds for the delivery.
Idempotency-Key is required (400 without it). Not enough stock ⇒ MAILSHOP_OUT_OF_STOCK (error.details.stock = how many you can buy now, nothing charged); over the per-order limit ⇒ MAILSHOP_QTY_LIMIT (error.details.max). 502 MAILSHOP_SUPPLIER_ERROR = the source refused at once: a failed order was recorded and fully refunded (error.details.orderId) — retry with a new key.
Parameter
NameTypErforderlichBeschreibung
warehouse1 | 2 | 99NeinMail-shop warehouse: 1 = email + password + cookies + inbox link · 2 = email + password + login link · 99 = automatic, cheapest first. Send warehouse OR product
productstringNeinInstead of warehouse: gm_cookie (= warehouse 1) | gm_login (= warehouse 2)
quantitynumberNeinNumber of accounts (default 1)
Idempotency-KeystringJaHeader: a new UUID for every new purchase — reuse it only to retry that same purchase
Beispiel
# Idempotency-Key ist erforderlich: eine neue UUID pro Kauf – verwende denselben Schlüssel nur für einen erneuten Versuch nach einem Timeout
# warehouse: 1 = cookies + inbox link · 2 = login link · 99 = cheapest first
curl -X POST "https://otpgmail.net/v1/mail-shop/orders" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  --max-time 60 \
  -d '{"warehouse":99,"quantity":5}'
Beispielantwort
{
  "success": true,
  "data": {
    "id": "p3x9k2m7q1w8e5r4",
    "requestedWarehouse": 99,
    "warehouse": 1,
    "productCode": "gm_cookie",
    "quantity": 2,
    "deliveredCount": 2,
    "unitPriceVnd": 3300,
    "chargedVnd": 9300,
    "refundVnd": 0,
    "status": "completed",
    "channel": "api",
    "route": [
      {
        "warehouse": 1,
        "quantity": 1,
        "unitPriceVnd": 3300
      },
      {
        "warehouse": 2,
        "quantity": 1,
        "unitPriceVnd": 6000
      }
    ],
    "message": null,
    "createdAt": 1760000400,
    "completedAt": 1760000409,
    "items": [
      {
        "lineNo": 1,
        "warehouse": 1,
        "email": "[email protected]",
        "password": "Pw7Xq!k3d",
        "rawLine": "[email protected]|Pw7Xq!k3d||[{\"domain\":\".google.com\",\"name\":\"SID\",\"value\":\"g.a000…\"}]",
        "cookies": "[{\"domain\":\".google.com\",\"name\":\"SID\",\"value\":\"g.a000…\"}]",
        "inboxUrl": "https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1d",
        "inboxJsonUrl": "https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1d?json",
        "twoFaUrl": "https://otpgmail.net/2fa/Q7xK2mP9vL4tR8wN3cB6yH1d",
        "inboxLinkExpiresAt": 1762592400
      },
      {
        "lineNo": 2,
        "warehouse": 2,
        "email": "[email protected]",
        "password": "Qp9!rT2",
        "rawLine": "[email protected]!rT2----no----https://login.example/google/login?uid=s2c38a3b",
        "loginUrl": "https://login.example/google/login?uid=s2c38a3b",
        "otpUrl": "https://otpgmail.net/api/mail-shop/otp/p3x9k2m7q1w8e5r4.2.Zx8Kq2Lm4Np6Rt0Vw3Yb5c",
        "otpService": "vinted"
      }
    ]
  }
}
Mögliche Fehler: UNAUTHORIZED, VALIDATION_ERROR, MAILSHOP_PRODUCT_NOT_FOUND, MAILSHOP_DISABLED, MAILSHOP_QTY_LIMIT, MAILSHOP_OUT_OF_STOCK, INSUFFICIENT_BALANCE, MAILSHOP_SUPPLIER_ERROR, RATE_LIMITED, DUPLICATE_REQUEST, MAINTENANCE
GET/v1/mail-shop/orders
Your mail-shop orders, newest first (page / pageSize, up to 100 per page) — without items[]: fetch an order to get its accounts. Orders bought on the web are listed too (channel = web).
Parameter
NameTypErforderlichBeschreibung
pagenumberNeinPage (default 1)
pageSizenumberNeinOrders per page (default 20, max 100)
Beispiel
curl "https://otpgmail.net/v1/mail-shop/orders?page=1&pageSize=20" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "p3x9k2m7q1w8e5r4",
        "requestedWarehouse": 99,
        "warehouse": 1,
        "productCode": "gm_cookie",
        "quantity": 2,
        "deliveredCount": 2,
        "unitPriceVnd": 3300,
        "chargedVnd": 9300,
        "refundVnd": 0,
        "status": "completed",
        "channel": "api",
        "route": [
          {
            "warehouse": 1,
            "quantity": 1,
            "unitPriceVnd": 3300
          },
          {
            "warehouse": 2,
            "quantity": 1,
            "unitPriceVnd": 6000
          }
        ],
        "message": null,
        "createdAt": 1760000400,
        "completedAt": 1760000409
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}
Mögliche Fehler: UNAUTHORIZED
GET/v1/mail-shop/orders/{id}
One order with every account delivered so far (an order still delivering already shows what has arrived). route[] = which warehouse, how many, at what price.
Poll every 5 seconds while status = pending; completed / partial / failed / refunded are final. An order of many warehouse-1 accounts (cookies) can weigh several MB — stop polling once it is final.
Parameter
NameTypErforderlichBeschreibung
idstringJaOrder ID (data.id returned by the purchase)
Beispiel
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "id": "p3x9k2m7q1w8e5r4",
    "requestedWarehouse": 99,
    "warehouse": 1,
    "productCode": "gm_cookie",
    "quantity": 2,
    "deliveredCount": 2,
    "unitPriceVnd": 3300,
    "chargedVnd": 9300,
    "refundVnd": 0,
    "status": "completed",
    "channel": "api",
    "route": [
      {
        "warehouse": 1,
        "quantity": 1,
        "unitPriceVnd": 3300
      },
      {
        "warehouse": 2,
        "quantity": 1,
        "unitPriceVnd": 6000
      }
    ],
    "message": null,
    "createdAt": 1760000400,
    "completedAt": 1760000409,
    "items": [
      {
        "lineNo": 1,
        "warehouse": 1,
        "email": "[email protected]",
        "password": "Pw7Xq!k3d",
        "rawLine": "[email protected]|Pw7Xq!k3d||[{\"domain\":\".google.com\",\"name\":\"SID\",\"value\":\"g.a000…\"}]",
        "cookies": "[{\"domain\":\".google.com\",\"name\":\"SID\",\"value\":\"g.a000…\"}]",
        "inboxUrl": "https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1d",
        "inboxJsonUrl": "https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1d?json",
        "twoFaUrl": "https://otpgmail.net/2fa/Q7xK2mP9vL4tR8wN3cB6yH1d",
        "inboxLinkExpiresAt": 1762592400
      },
      {
        "lineNo": 2,
        "warehouse": 2,
        "email": "[email protected]",
        "password": "Qp9!rT2",
        "rawLine": "[email protected]!rT2----no----https://login.example/google/login?uid=s2c38a3b",
        "loginUrl": "https://login.example/google/login?uid=s2c38a3b",
        "otpUrl": "https://otpgmail.net/api/mail-shop/otp/p3x9k2m7q1w8e5r4.2.Zx8Kq2Lm4Np6Rt0Vw3Yb5c",
        "otpService": "vinted"
      }
    ]
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND
GET/v1/mail-shop/orders/{id}/download
The delivered accounts as .txt (text/plain), separated by |. The FIRST LINE is the column header: warehouse 1 email|password|cookies|inboxUrl · warehouse 2 email|password|loginUrl · a 99 order with both warehouses ⇒ email|password|cookies|inboxUrl|loginUrl (missing cells empty).
cookies is one-line JSON and may contain | — split email and password at the first two |, the link at the last | (with extra columns ⇒ the links after cookies at the last | separators — links never contain |; read the header line for their order). Nothing delivered yet ⇒ 409 VALIDATION_ERROR. Add <c>?otp=1</c> ⇒ warehouse 2 gets a LAST column <c>otpUrl</c> (the Vinted OTP link — empty when the account has none): email|password|loginUrl|otpUrl. Without it ⇒ exactly the columns above. Add <c>?twofa=1</c> ⇒ warehouse 1 gets a LAST column <c>twoFaUrl</c> (the 2FA link — same code as inboxUrl; GET ⇒ just the 6 digits; empty when the link is off / expired): email|password|cookies|inboxUrl|twoFaUrl. Combines with <c>otp=1</c> — fixed column order: …|loginUrl|otpUrl|twoFaUrl (a 99 order with both warehouses). Without it ⇒ the default layout above.
Parameter
NameTypErforderlichBeschreibung
idstringJaOrder ID (data.id returned by the purchase)
Beispiel
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/download" \
  -H "Authorization: Bearer og_xxx..." \
  -o mail-ORDER_ID.txt
Beispielantwort
email|password|cookies|inboxUrl
[email protected]|Pw7Xq!k3d|[{"domain":".google.com","name":"SID","value":"g.a000…"}]|https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1d
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR
GET/v1/mail-shop/orders/{id}/items/{lineNo}/inbox
Latest emails of a WAREHOUSE-1 account (sender, subject, time, extracted code / link, sanitized full email). Without an API key: GET the account's inboxJsonUrl (same emails, JSON).
max = 10 (default) | 20 | 50 | 100 | 200 — more emails take longer (up to about a minute). Cached for 15 seconds; every real read counts toward the hourly limit. 409 VALIDATION_ERROR = a warehouse-2 account (no inbox reading); 409 PROVIDER_ERROR with details.dead = the account was locked or removed.
Parameter
NameTypErforderlichBeschreibung
idstringJaOrder ID (data.id returned by the purchase)
lineNonumberJaAccount number within the order (items[].lineNo)
maxnumberNeinNumber of latest emails: 10 (default), 20, 50, 100 or 200
Beispiel
# Warehouse 1 accounts only; max = 10 | 20 | 50 | 100 | 200
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/items/1/inbox?max=20" \
  -H "Authorization: Bearer og_xxx..." \
  --max-time 70

# Same emails without an API key: GET the account's inboxJsonUrl
curl "https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1d?json&max=20"
Beispielantwort
{
  "success": true,
  "data": {
    "email": "[email protected]",
    "letters": [
      {
        "key": "h:9f2c4e1a7b3d5c80",
        "from": "Instagram [email protected]",
        "subject": "350038 is your Instagram code",
        "receivedAt": 1760000520,
        "code": "350038",
        "content": "<html><body><p>Enter this confirmation code in the app: 350038</p></body></html>"
      }
    ],
    "max": 10,
    "total": 8,
    "fetchedAt": 1760000530,
    "cached": false
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, PROVIDER_ERROR
GET/v1/mail-shop/orders/{id}/items/{lineNo}/status
Is a bought warehouse-1 account still alive: live = true / false, or null when it cannot be determined. Counts toward the hourly inbox-read limit.
Parameter
NameTypErforderlichBeschreibung
idstringJaOrder ID (data.id returned by the purchase)
lineNonumberJaAccount number within the order (items[].lineNo)
Beispiel
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/items/1/status" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "email": "[email protected]",
    "live": true,
    "reason": null,
    "checkedAt": 1760000600
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, PROVIDER_ERROR
GET/v1/mail-shop/orders/{id}/items/{lineNo}/2fa
The current 2FA code (6-digit TOTP, changes every 30 seconds) of a warehouse-1 account — some warehouse-1 accounts have 2-step verification on (Google asks for a code at a password sign-in). none = true ⇒ the account has no 2FA (code / expiresIn null). expiresIn = seconds the code stays valid — ask again after that. Without an API key: GET the account's twoFaUrl (the body is just the 6 digits).
The server caches the code until it expires (every call within those 30 seconds gets the same code) — it does NOT count toward the hourly inbox-read limit. Calling too fast ⇒ 429 (retryAfterSec); a warehouse-2 account ⇒ 409; a locked account ⇒ 409 (details.dead).
Parameter
NameTypErforderlichBeschreibung
idstringJaOrder ID (data.id returned by the purchase)
lineNonumberJaAccount number within the order (items[].lineNo)
Beispiel
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/items/1/2fa" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "none": false,
    "code": "424819",
    "expiresIn": 24
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, PROVIDER_ERROR

Lebenszyklus einer Bestellung

StatusBeschreibung
Wartet auf CodeNeu angelegte Bestellung, die auf den ersten Code wartet. Frage GET /v1/orders/{id} alle 3–5 Sekunden ab.
AbgeschlossenDer erste Code ist angekommen – der Status ändert sich sofort. Weitere Codes können noch ankommen, wenn der Dienst mehr als einen sendet (ohne Aufpreis innerhalb des Bestellzeitraums; Limit und Fenster je Lager); frage weiter ab, und jeder neue Code erscheint am Ende von otp[].
StorniertZu 100 % erstattet. Passiert, wenn du /cancel aufrufst (bevor ein Code ankommt) oder wenn bis codeDeadlineAt (höchstens 25 Minuten) kein Code angekommen ist.
Wichtige Hinweise:
  • Frage GET /v1/orders/{id} alle 3–5 Sekunden ab, solange die Bestellung waiting_code ist, damit du den Code zügig abholst.
  • Sobald der erste Code ankommt, wechselt der Status sofort auf completed.
  • Kein Code bis codeDeadlineAt (höchstens 25 Minuten; Lager 2: 20 Minuten; einige Quellen von Lager 4 sind kürzer) ⇒ das System storniert die Bestellung und erstattet 100 % (status = cancelled).
  • Das Array otp[] ist ältester zuerst, neuester zuletzt sortiert. Lies den neuesten Code mit otp[otp.length - 1] (Python: otp[-1]).
  • Eine completed-Bestellung empfängt weiter automatisch Codes: Frage GET /v1/orders/{id} weiter ab, und jeder neue Code erscheint am Ende von otp[]. Zusätzliche Codes kosten innerhalb des Bestellzeitraums nichts, wenn der Dienst mehr als einen sendet; das Limit und das Zeitfenster werden vor dem Mieten angezeigt (maxCodes und fee in offers[]; bei Lager 1 bleibt das Postfach nach dem letzten Code meist etwa 25 Minuten offen). Um ausdrücklich einen weiteren Code anzufordern (z. B. um dich nach der Registrierung anzumelden): POST /v1/orders/{id}/next-code – funktioniert in jedem Lager, und nur Lager 2 berechnet außerhalb seines kostenlosen Zeitraums eine Gebühr.
  • Erlaubt der Dienst nur einen Code pro Postfach, endet otp[] bei diesem Code. Die Antworten bleiben normal, ohne Fehler – du entscheidest, wann du mit dem Abfragen aufhörst.
  • otp[].code ist meist ein kurzer Code (Ziffern / Buchstaben) oder ein Link. Eine E-Mail ohne extrahierbaren Code (Willkommens-E-Mails, Newsletter, ungewöhnliche Codeformate …) wird unverändert (HTML oder Text) in otp[].code geliefert – behandle sie als vollständige E-Mail, wenn sie länger als 80 Zeichen ist, einen Zeilenumbruch enthält oder HTML ist, und suche den Code oder Link selbst darin.
  • POST /v1/orders wartet, bis der Anbieter ein Postfach zuweist, dauert also meist ~0,3 Sekunden, kann zu Spitzenzeiten (selten) aber bis zu 10 Sekunden dauern. Stelle ein Client-Timeout von ≈ 15 Sekunden ein, statt nach 5 Sekunden aufzugeben und zu früh erneut zu versuchen – und verwende bei einem erneuten Versuch denselben Idempotency-Key.
  • Eine Bestellung, die einen Code erhalten hat, kann nicht storniert oder erstattet werden – der Stornoaufruf liefert ORDER_NOT_CANCELLABLE.
  • Um ein @icloud.com-Postfach zu mieten, sende beim Anlegen der Bestellung "domain": "icloud.com" (für Gmail weglassen; bestehende Bots brauchen keine Änderungen). Unterstützt der Dienst iCloud nicht oder ist iCloud vorübergehend deaktiviert, erhältst du DOMAIN_UNAVAILABLE – wechsle zu Gmail, und die Miete geht sofort durch. Der Lebenszyklus, die Regeln für Stornierung und Erstattung und die Art, Codes zu lesen, sind genau dieselben wie bei Gmail.
  • Dienste aus eigenem Bestand (provider = "inventory", z. B. Cointiply – siehe GET /v1/services) gibt es nur mit iCloud; jede E-Mail kommt als Link in otp[].code an, zusammen mit kind, label und dem vollständigen E-Mail-Text in text. Eine Bestellung empfängt bis zu rules.maxCodes E-Mails innerhalb von rules.windowMinutes Minuten (bis windowEndsAt), kann nicht storniert werden (ORDER_NOT_CANCELLABLE), und läuft die Zeit ohne E-Mail ab, wird sie ohne Erstattung zu cancelled. Um auf demselben Postfach weiter zu empfangen, rufe POST /v1/orders/{id}/rerent auf, solange canRerent true ist (Preis: rerentPriceVnd). Alle anderen Dienste / Bestellungen haben provider = "standard" — seit dem 2026-10-24 hat provider nur zwei Werte, "standard" | "inventory" (das tatsächliche Lager steht in warehouse); vergleiche nur mit "inventory".
  • Bestellungen aus Lager 2 (warehouse = 2): Codes sind fee.freeHours Stunden lang kostenlos; schließt das kostenlose Fenster, kommen keine neuen Codes mehr an – rufe POST /v1/orders/{id}/next-code mit confirmFee = true auf (und zahle nextCodeFee.feePriceVnd), um ein weiteres Fenster zu öffnen, bis zu nextCodeFee.hardUntil. Bestellungen aus Lager 3 bei regulären Diensten: 24 Stunden kostenlose Codes, vor dem Eintreffen eines Codes stornierbar, danach nutze rerent. Bestellungen aus Lager 4: neue Codes landen beim Lesen der Bestellung in otp[], kostenlos bis rentExpiresAt; bei einer Quelle mit nur einem Code wird nextCodeBlockReason nach dem ersten Code zu "max_codes".
  • Viele Bestellungen gleichzeitig? Frage bis zu 100 pro Aufruf mit POST /v1/orders/status ab – siehe Status vieler Bestellungen in einem Aufruf.

Rate-Limits

Die Limits gelten pro API-Schlüssel (ein Konto kann eigene Limits haben; sonst gilt der Standard der Website):
  • 100 Anfragen/Sekunde – kurze Spitzen über wenige Sekunden
  • 3000 Anfragen/Minute – Gesamtverkehr pro Minute
Wird ein Limit überschritten, liefert die API HTTP 429 mit einem Header Retry-After: <seconds> und dem Body {"success":false,"error":{"code":"RATE_LIMITED",...}}. Warte vor dem nächsten Versuch die im Header angegebene Anzahl an Sekunden. Der Fehler-Body enthält dieselbe Sekundenzahl in error.retryAfterSec (mit error.retryable = true).

Fehlercodes

Jeder Fehler hat dieselbe Form: {"success":false,"error":{"code":"...","message":"...","retryable":false,"retryAfterSec":null}}
Seit 2026-10-17 hat jeder /v1-Fehler zusätzlich error.retryable und error.retryAfterSec (neue Felder – bestehende Clients müssen nichts ändern; details bleibt unverändert). retryable = true ⇒ derselbe Aufruf kann später klappen: warte retryAfterSec Sekunden (ganze Zahl ≥ 1) und sende ihn erneut – ausverkauft / keine Postfächer / Lager pausiert / Anbieterfehler ⇒ 5 s; 429 RATE_LIMITED ⇒ der Header Retry-After; 409 DUPLICATE_REQUEST, solange die erste Anfrage mit demselben Idempotency-Key noch läuft ⇒ 2 s. retryable = false (retryAfterSec = null) ⇒ erneutes Aufrufen hilft nicht, bis du die Ursache behebst (Guthaben, Limit wartender Bestellungen, Parameter, API-Schlüssel …). partial.reason von POST /v1/orders folgt denselben Regeln. Bei einer 409-Antwort „nicht vorrätig“ (OUT_OF_STOCK, WAREHOUSE_UNAVAILABLE, ALL_WAREHOUSES_EXHAUSTED) die vollen retryAfterSec Sekunden warten, bevor Sie es erneut versuchen — ein früherer Versuch liefert meist nur dieselbe Antwort — und vor dem nächsten Mieten stock / offers[].stock in GET /v1/services prüfen; bereits gemietete Bestellungen per Webhook verfolgen, statt sie ständig abzufragen.
Beispiel-Fehlerantworten
HTTP/1.1 409 Conflict
{
  "success": false,
  "error": {
    "code": "OUT_OF_STOCK",
    "message": "This service is currently out of stock",
    "retryable": true,
    "retryAfterSec": 5
  }
}

HTTP/1.1 429 Too Many Requests
Retry-After: 1
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded, retry after 1s",
    "retryable": true,
    "retryAfterSec": 1
  }
}

HTTP/1.1 402 Payment Required
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient balance to rent an inbox",
    "retryable": false,
    "retryAfterSec": null
  }
}
FehlercodeHTTPretryable · retryAfterSecBedeutung
UNAUTHORIZED401false · nullDer API-Schlüssel ist ungültig oder fehlt.
ACCOUNT_LOCKED403false · nullDas Konto ist gesperrt – jede Anfrage mit seinem Schlüssel wird abgelehnt. Wende dich an den Support.
FORBIDDEN403false · nullDas Konto war während der Verarbeitung der Miete nicht mehr aktiv (es wurde gerade gesperrt).
NOT_FOUND404false · nullKeine Bestellung / Sammelbestellung / kein Postfach mit dieser ID in deinem Konto (Bestellungen anderer Konten liefern ebenfalls 404).
VALIDATION_ERROR400false · nullDie Anfrageparameter sind ungültig (falscher Typ, fehlendes Pflichtfeld usw.).
INSUFFICIENT_BALANCE402false · nullDas Kontoguthaben reicht nicht aus, um die Bestellung zu bezahlen.
SERVICE_DISABLED409false · nullDer Dienstcode existiert nicht oder der Dienst ist vorübergehend deaktiviert. Nimm den genauen Code aus GET /v1/services.
OUT_OF_STOCK409true · 5Der Dienst hatte zum Zeitpunkt der Anfrage keine Postfächer verfügbar (für den gewählten Postfachtyp).
DOMAIN_UNAVAILABLE409false · nullDer Dienst bietet den gewählten Postfachtyp nicht an – iCloud wird noch nicht unterstützt / ist vorübergehend deaktiviert, oder der Dienst hat keine gmx.com-Postfächer. Sende domain "gmail.com" (oder lass das Feld weg), um Gmail zu mieten.
WAREHOUSE_UNAVAILABLE409true · 5 / falseDas gewählte Lager verkauft diesen Dienst/Postfachtyp nicht, ist pausiert oder gerade leer geworden. Wähle ein anderes Lager aus offers[] oder sende warehouse = 99, damit das System eines mit Bestand wählt. Ist dieses Lager (oder jedes Lager, das diesen Dienst verkauft) für dein Konto nicht verfügbar, ist retryable = false – ein erneuter Aufruf hilft nicht, nutze ein anderes Lager.
ALL_WAREHOUSES_EXHAUSTED409true · 5Nur mit warehouse = 99: Jedes Lager mit Bestand wurde versucht (das günstigste zuerst) und alle waren leer oder fehlerhaft – oder jedes Lager meldet keinen Bestand. Es wurde nichts berechnet; versuche es später erneut.
NO_MAILS_AVAILABLE503true · 5Der Anbieter hat für diesen Dienst keine Postfächer mehr (vorübergehend leer).
QUANTITY_EXCEEDED400false · nullquantity überschreitet das Maximum pro Anfrage (derzeit 200).
WAITING_LIMIT_REACHED429false · nullZu viele Bestellungen warten gleichzeitig auf einen Code (über dem Limit deines Kontos für gleichzeitige waiting_code-Bestellungen) – warte, bis einige abgeschlossen sind, und miete dann erneut.
ORDER_NOT_CANCELLABLE409false · nullDie Bestellung kann nicht storniert werden – sie hat möglicherweise schon einen Code oder ist nicht mehr im Status waiting_code. next-code liefert diesen Code auch, wenn die Bestellung ihren ersten Code noch nicht erhalten hat.
RERENT_NOT_AVAILABLE409false · nullDie Bestellung kann nicht erneut gemietet werden. Lager 3: Sie ist nicht die neueste Bestellung des Postfachs, das Postfach ist deinem Konto nicht mehr zugewiesen oder das vorherige Empfangsfenster ist noch offen. Lager 2: Die Bestellung ist noch nicht geschlossen (sie kann noch Codes empfangen – nutze next-code), sie ist zu alt zum erneuten Mieten, die E-Mail hat eine andere aktive Bestellung oder der Dienst ist pausiert. Die Lager 1 und 4 haben kein erneutes Mieten.
RERENT_EMAIL_BANNED409false · nullErneutes Mieten in Lager 2: Der Anbieter meldet, dass die E-Mail vom Dienst gesperrt wurde (email banned) – sie kann nicht erneut gemietet werden; dir wird nichts berechnet.
RERENT_MAIL_GONE409false · nullErneutes Mieten in Lager 2: Der Anbieter kann diese E-Mail nicht mehr öffnen – miete eine neue; dir wird nichts berechnet.
NEXT_CODE_FEE_CONFIRM409false · nullDer kostenlose Zeitraum der Bestellung aus Lager 2 ist beendet – rufe next-code erneut mit confirmFee: true auf, um die Gebühr (nextCodeFee.feePriceVnd) zu zahlen. Es wurde nichts berechnet.
EMAIL_BANNED409false · nullDie E-Mail wurde vom Dienst gesperrt (der Anbieter meldet email banned) – sie kann keine weiteren Codes empfangen; miete eine neue. Seit 2026-10-02 (zuvor ORDER_EXPIRED, gleiches HTTP 409).
ORDER_EXPIRED409false · nullDie Bestellung kann keine weiteren Codes empfangen – ihr Zeitlimit ist abgelaufen (rentExpiresAt / nextCodeFee.hardUntil), der Anbieter hat das Postfach geschlossen, das Code-Limit (maxCodes) wurde erreicht oder der Dienst hat keinen „nächsten Code“. Der Grund steht in order.nextCodeBlockReason.
RATE_LIMITED429true · = Retry-AfterMehr als 100 Anfragen/Sekunde oder 3000 Anfragen/Minute mit deinem API-Schlüssel (ein Konto kann eigene Limits haben). Der Header Retry-After sagt dir, wie viele Sekunden du warten musst. In Lager 2 liefern POST /v1/orders und /rerent diesen Code auch, wenn das Konto in den letzten 60 Minuten viele Bestellungen aus Lager 2 ohne Code storniert hat und nur sehr wenige einen Code bekamen – das Mieten aus Lager 2 pausiert, bis die Stornoquote sinkt ("warehouse": 99 hält mit diesem Fehler ebenfalls dort an – wähle so lange ein anderes Lager). Facebook / Instagram (fb, fbb, ig) sind ausgenommen.
DUPLICATE_REQUEST409true · 2 / falseDer Idempotency-Key wurde bereits mit einem anderen Payload oder Endpunkt verwendet, oder die erste Anfrage mit diesem Schlüssel läuft noch (versuche es gleich noch einmal). Erzeuge einen neuen Schlüssel, um eine weitere Bestellung anzulegen.
PROVIDER_ERROR502true · 5Fehler beim Postfachanbieter (vorübergehend – versuche es nach ein paar Sekunden erneut).
MAINTENANCE503false · nullDas System wird gewartet – das Anlegen und Stornieren von Bestellungen ist pausiert.
MAILSHOP_DISABLED503false · nullDer Kauf kurzlebiger Gmail-Konten ist pausiert oder dieses Produkt ist ausgeschaltet – versuche es später erneut.
MAILSHOP_PRODUCT_NOT_FOUND404false · nullKein Produkt mit diesem Code – nimm den genauen Code aus GET /v1/mail-shop/products.
MAILSHOP_OUT_OF_STOCK409false · nullNicht genug Bestand für diese Menge zum aktuellen Preis – error.details.stock = wie viele Konten du jetzt kaufen kannst.
MAILSHOP_QTY_LIMIT400false · nullquantity liegt über dem Maximum pro Bestellung (derzeit 500) – error.details.max.
MAILSHOP_SUPPLIER_ERROR502false · nullDie Quelle hat den Kauf sofort abgelehnt: Eine fehlgeschlagene Bestellung wurde erfasst und das Geld erstattet (error.details.orderId). Versuche es später mit einem neuen Idempotency-Key erneut.
INTERNAL500false · nullUnerwarteter Serverfehler – versuche es später erneut; wende dich an den Support, wenn er weiter auftritt.

Webhooks (Push-Benachrichtigungen)

Registrieren Sie eine URL pro Konto, und wir senden per POST ein signiertes JSON-Ereignis dorthin, sobald eine Bestellung einen neuen Code erhält (order.code) oder storniert / erstattet wird (order.cancelled). Webhooks sind optional und gelten für alle Bestellungen Ihres Kontos (Web und API). Die Zustellung erfolgt nach bestem Bemühen — fragen Sie als Rückfallebene weiterhin in großen Abständen ab (siehe unten).
Einrichtung in 3 Schritten
  1. PUT /v1/webhook mit Ihrer URL (optional mit events) — speichern Sie data.secret (64 Hex-Zeichen): Sie brauchen es, um jede Zustellung zu prüfen. Ein erneuter PUT-Aufruf behält dasselbe Secret.
  2. POST /v1/webhook/test — wir senden sofort ein ping-Ereignis und liefern zurück, was Ihr Server geantwortet hat (ok, httpStatus, error).
  3. Ihr Endpunkt muss innerhalb von 5 Sekunden mit einem beliebigen 2xx antworten — zuerst antworten, dann das Ereignis verarbeiten. Alles andere (3xx, 4xx, 5xx, Zeitüberschreitung, Verbindungsfehler) gilt als Fehlschlag und wird wiederholt.
Die URL muss mit http:// oder https:// beginnen (gültiges Zertifikat) und aus dem Internet erreichbar sein: localhost, private, Link-Local-, Multicast- und reservierte Adressen (10.x, 172.16–31.x, 192.168.x, 169.254.x, fc00::/7, fe80::/10…) sowie die eigenen Server von OTPGmail werden mit VALIDATION_ERROR abgelehnt. Der Hostname wird vor jeder Zustellung neu aufgelöst. Weiterleitungen werden nicht verfolgt — registrieren Sie die endgültige URL.
Format der Anfrage
Jede Zustellung ist ein HTTP-POST an Ihre URL mit JSON-Body (UTF-8) und diesen Headern:
HeaderWert
Content-Typeapplication/json
User-AgentOTPGmail-Webhook/1
X-OTPGmail-EventName des Ereignisses: order.code, order.cancelled oder ping
X-OTPGmail-DeliveryEindeutige ID des Ereignisses (UUID) — gleich deliveryId im Body und bei jeder Wiederholung unverändert
X-OTPGmail-TimestampUnix-Zeit (Sekunden), zu der dieser Versuch signiert wurde — gleich sentAt im Body
X-OTPGmail-Signaturesha256= + Hex in Kleinbuchstaben des HMAC-SHA256 über timestamp + "." + Roh-Body, mit Ihrem Secret als Schlüssel
Signatur prüfen
Berechnen Sie HMAC-SHA256 mit Ihrem Secret als Schlüssel über X-OTPGmail-Timestamp + . + den rohen Request-Body als Hex in Kleinbuchstaben und vergleichen Sie das Ergebnis in konstanter Zeit mit dem Wert nach sha256=. Der Schlüssel ist die 64-stellige Secret-Zeichenkette selbst (ihre UTF-8-Bytes — nicht hex-dekodieren). Verwenden Sie die rohen Bytes: Parsen und erneutes Serialisieren des JSON verändert sie. Lehnen Sie Zeitstempel ab, die mehr als 5 Minuten von Ihrer Uhr abweichen (Schutz vor Replays).
Empfänger-Beispiele (Python und Node nutzen nur die Standardbibliothek; der curl-Befehl sendet eine korrekt signierte Testzustellung an Ihren eigenen Empfänger)
# Lokaler Test: Senden Sie Ihrem Empfänger eine Zustellung, die genau wie unsere signiert ist
SECRET="9f2c6d1e8b7a4f3c5e0d2b1a6c9e8f7d3b5a1c2e4f6a8b0d9c7e5f3a1b2c4d6e"
TS=$(date +%s)
BODY='{"event":"ping","deliveryId":"6f1d2c3b-8a9e-4f70-b1c2-d3e4f5a6b7c8","sentAt":'$TS'}'
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -X POST "http://localhost:8080/" \
  -H "Content-Type: application/json" \
  -H "X-OTPGmail-Event: ping" \
  -H "X-OTPGmail-Delivery: 6f1d2c3b-8a9e-4f70-b1c2-d3e4f5a6b7c8" \
  -H "X-OTPGmail-Timestamp: $TS" \
  -H "X-OTPGmail-Signature: sha256=$SIG" \
  -d "$BODY"
Ereignisse
EreignisGesendet, wenn
order.codeEine Bestellung hat einen neuen Code erhalten — ein Ereignis pro Code, auch für jeden weiteren Code auf demselben Postfach. code ist der neue Code; otp listet alle bisherigen Codes der Bestellung, der älteste zuerst (wie in GET /v1/orders/{id}; nur der neue Code, wenn der Body 64 KB überschreiten würde).
order.cancelledEine Bestellung wurde storniert oder erstattet — einmal pro Bestellung: kein Code vor Ablauf der Frist (timeout), von Ihnen (user) oder einem Administrator (admin) storniert, oder erstattet, weil das Lager ausgefallen ist (provider). refundVnd = VND, die Ihrem Guthaben gutgeschrieben wurden (0, wenn die Regeln des Dienstes keine Erstattung vorsehen).
pingNur durch POST /v1/webhook/test.
Felder des Ereignisses
FeldTypBeschreibung
eventstringName des Ereignisses (auch im Header X-OTPGmail-Event)
deliveryIdstringEindeutige ID des Ereignisses (UUID, = X-OTPGmail-Delivery) — speichern, um Duplikate zu überspringen
sentAtnumberUnix-Zeit dieses Versuchs (= X-OTPGmail-Timestamp)
orderIdstringBestell-ID — dieselbe wie in GET /v1/orders/{id}
servicestringDienstcode
warehouse1 | 2 | 3 | 4 | nullLager 1–4, das das Postfach geliefert hat; null, wenn die Bestellung nie eines bekommen hat
domainstringDomain des Postfachs: gmail.com, icloud.com oder gmx.com
emailstring | nullAdresse des gemieteten Postfachs
codestringNur order.code: der neue Code — kann auch ein Link oder eine vollständige E-Mail sein, wie otp[].code
receivedAtnumberNur order.code: Unix-Zeit, zu der der Code ankam
statusstringStatus der Bestellung in diesem Moment: completed bei order.code, cancelled bei order.cancelled (erstattet ein Administrator eine Bestellung, die schon Codes hatte, bleibt sie completed)
otparrayNur order.code: alle bisherigen Codes, [{code, receivedAt}], der älteste zuerst
reasonstringNur order.cancelled: timeout | user | admin | provider
refundVndnumberNur order.cancelled: VND, die Ihrem Guthaben erstattet wurden (0 = keine Erstattung)
Beispiel-Body — order.code
{
  "event": "order.code",
  "deliveryId": "6f1d2c3b-8a9e-4f70-b1c2-d3e4f5a6b7c8",
  "sentAt": 1760700125,
  "orderId": "k3m9x2p7q1w8e5r4",
  "service": "fb",
  "warehouse": 1,
  "domain": "gmail.com",
  "email": "[email protected]",
  "code": "482913",
  "receivedAt": 1760700124,
  "status": "completed",
  "otp": [
    {
      "code": "482913",
      "receivedAt": 1760700124
    }
  ]
}
Beispiel-Body — order.cancelled
{
  "event": "order.cancelled",
  "deliveryId": "0b7e4c2a-5d1f-4e8b-9a3c-2f6d8e1b4a7c",
  "sentAt": 1760701630,
  "orderId": "p8v2n5c1x7z3m9k4",
  "service": "ig",
  "warehouse": 2,
  "domain": "gmail.com",
  "email": "[email protected]",
  "reason": "timeout",
  "refundVnd": 1500,
  "status": "cancelled"
}
Beispiel-Body — ping
{
  "event": "ping",
  "deliveryId": "3c9a1e7f-2b4d-4c6e-8f0a-5d7b9c1e3a2f",
  "sentAt": 1760700000
}
Wiederholungen, Pausieren und Verlauf
  • Eine fehlgeschlagene Zustellung wird nach 10 s, 1 Min., 5 Min., 30 Min. und 2 Std. wiederholt (5 Wiederholungen); schlägt auch die letzte fehl, wird das Ereignis verworfen (failed).
  • 50 Fehlversuche in Folge (über alle Ereignisse) pausieren den Webhook: GET /v1/webhook zeigt pausedAt, es wird nichts gesendet und neue Ereignisse werden nicht eingereiht.
  • Zum Fortsetzen: PUT /v1/webhook erneut aufrufen (dieselbe URL genügt) oder einen erfolgreichen POST /v1/webhook/test ausführen — danach werden die wartenden Ereignisse gesendet.
  • GET /v1/webhook/deliveries listet die letzten 100 Zustellungen mit Status, Versuchen und letztem Fehler; der Verlauf wird 7 Tage aufbewahrt.
Duplikate und Reihenfolge
Dasselbe Ereignis kann mehrfach ankommen (etwa wenn Ihre 2xx-Antwort verloren ging), und Ereignisse können außer der Reihe ankommen (mehrere werden parallel gesendet). Speichern Sie verarbeitete deliveryIds und überspringen Sie Wiederholungen; verlassen Sie sich bei einer Bestellung auf ihre neuesten Daten (otp, status) statt auf die Ankunftsreihenfolge.
Abfragen als Rückfallebene beibehalten
Webhooks können sich verzögern (Wiederholungen) oder ausbleiben (Webhook pausiert, Ihr Server nicht erreichbar). Fragen Sie weiterhin in großen Abständen ab — etwa GET /v1/orders/{id} alle 30–60 s für Bestellungen, die noch waiting_code sind — und betrachten Sie das Bestellobjekt der API als maßgeblich.
PUT/v1/webhook
Registriert oder aktualisiert den Webhook Ihres Kontos. Der erste Aufruf legt ihn an und liefert ein neues Secret; spätere Aufrufe behalten das Secret, schalten den Webhook wieder ein und setzen ihn fort, falls er pausiert war. Ohne events: beide Ereignisse für einen neuen Webhook, unverändert für einen bestehenden. Die URL muss eine öffentliche Adresse sein — sonst 400 VALIDATION_ERROR (den Grund nennt error.details.problem).
Parameter
NameTypErforderlichBeschreibung
urlstringJaIhr Endpunkt (http:// oder https://, öffentliche Adresse, höchstens 2048 Zeichen)
eventsstring[]NeinWelche Ereignisse gesendet werden: ["order.code", "order.cancelled"] (Standard: beide)
Beispiel
# data.secret speichern — Sie brauchen es, um jede Zustellung zu prüfen
curl -X PUT "https://otpgmail.net/v1/webhook" \
  -H "Authorization: Bearer og_xxx..." \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-server.example/otpgmail-webhook","events":["order.code","order.cancelled"]}'
Beispielantwort
{
  "success": true,
  "data": {
    "url": "https://your-server.example/otpgmail-webhook",
    "events": [
      "order.code",
      "order.cancelled"
    ],
    "enabled": true,
    "secret": "9f2c6d1e8b7a4f3c5e0d2b1a6c9e8f7d3b5a1c2e4f6a8b0d9c7e5f3a1b2c4d6e",
    "pausedAt": null,
    "consecutiveFailures": 0,
    "createdAt": 1760699000,
    "updatedAt": 1760699000
  }
}
Mögliche Fehler: UNAUTHORIZED, VALIDATION_ERROR
GET/v1/webhook
Der Webhook Ihres Kontos, einschließlich secret, pausedAt und consecutiveFailures. data ist null, wenn kein Webhook registriert ist.
Beispiel
curl "https://otpgmail.net/v1/webhook" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "url": "https://your-server.example/otpgmail-webhook",
    "events": [
      "order.code",
      "order.cancelled"
    ],
    "enabled": true,
    "secret": "9f2c6d1e8b7a4f3c5e0d2b1a6c9e8f7d3b5a1c2e4f6a8b0d9c7e5f3a1b2c4d6e",
    "pausedAt": 1760705400,
    "consecutiveFailures": 50,
    "createdAt": 1760699000,
    "updatedAt": 1760705400
  }
}
Mögliche Fehler: UNAUTHORIZED
DELETE/v1/webhook
Entfernt den Webhook; noch wartende Ereignisse werden verworfen. Immer 200 — data.deleted ist false, wenn es nichts zu entfernen gab.
Beispiel
curl -X DELETE "https://otpgmail.net/v1/webhook" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "deleted": true
  }
}
Mögliche Fehler: UNAUTHORIZED
POST/v1/webhook/test
Sendet sofort ein ping-Ereignis an Ihre URL und meldet das Ergebnis: ok (Ihr Server hat mit 2xx geantwortet), httpStatus, durationMs, error. Ein erfolgreicher Test setzt einen pausierten Webhook fort. Höchstens 10 Tests pro Minute; 503 MAINTENANCE, solange Webhooks systemweit abgeschaltet sind.
Beispiel
curl -X POST "https://otpgmail.net/v1/webhook/test" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": {
    "ok": true,
    "httpStatus": 200,
    "durationMs": 143,
    "error": null,
    "deliveryId": "3c9a1e7f-2b4d-4c6e-8f0a-5d7b9c1e3a2f"
  }
}
Antwort 200, wenn Ihr Server nicht mit 2xx antwortet
{
  "success": true,
  "data": {
    "ok": false,
    "httpStatus": 500,
    "durationMs": 88,
    "error": "HTTP 500",
    "deliveryId": "7d2b9f4e-1c6a-4e3b-8d5f-0a9c2e7b1f4d"
  }
}
Mögliche Fehler: UNAUTHORIZED, NOT_FOUND, RATE_LIMITED, MAINTENANCE
GET/v1/webhook/deliveries
Letzte Zustellungen, die neuesten zuerst (7 Tage aufbewahrt): status = queued (nächster Versuch zu nextAt) | delivered | failed | paused, mit attempts, lastHttpStatus und lastError. Die Bodys der Ereignisse werden nicht zurückgegeben.
Parameter
NameTypErforderlichBeschreibung
limitnumberNeinAnzahl der Zustellungen, 1–100 (Standard 100)
Beispiel
curl "https://otpgmail.net/v1/webhook/deliveries?limit=20" \
  -H "Authorization: Bearer og_xxx..."
Beispielantwort
{
  "success": true,
  "data": [
    {
      "id": "0b7e4c2a-5d1f-4e8b-9a3c-2f6d8e1b4a7c",
      "event": "order.cancelled",
      "orderId": "p8v2n5c1x7z3m9k4",
      "status": "queued",
      "attempts": 1,
      "lastHttpStatus": 503,
      "lastError": "HTTP 503",
      "nextAt": 1760701640,
      "createdAt": 1760701630,
      "deliveredAt": null
    },
    {
      "id": "6f1d2c3b-8a9e-4f70-b1c2-d3e4f5a6b7c8",
      "event": "order.code",
      "orderId": "k3m9x2p7q1w8e5r4",
      "status": "delivered",
      "attempts": 1,
      "lastHttpStatus": 200,
      "lastError": null,
      "nextAt": null,
      "createdAt": 1760700124,
      "deliveredAt": 1760700125
    }
  ]
}
Mögliche Fehler: UNAUTHORIZED