Dokumentasi API
API publik /v1 — autentikasi dengan API key dan otomatiskan penyewaan kotak masuk dari perangkat lunak Anda sendiri
API key Anda
Anda belum masuk — masuk untuk mendapatkan API key Anda sendiri.
Kirim key ini di header Authorization: Bearer <key> atau parameter query ?api_key=<key>. Klik ikon mata untuk menampilkan key lengkap; klik Buat key baru bila Anda perlu menggantinya (key lama langsung berhenti berfungsi).Ringkasan
Base URL
https://otpgmail.netAutentikasi
Sertakan API key Anda di setiap permintaan, baik di header
Authorization: Bearer og_xxx... (disarankan) maupun di query string ?api_key=og_xxx....Idempotency-Key (mencegah pesanan ganda)
Setiap permintaan
POST /v1/orders sebaiknya menyertakan header Idempotency-Key: <UUID baru per pesanan>. Jika permintaan timeout atau koneksi terputus, kirim ulang dengan key yang sama — server mengembalikan respons aslinya dan tidak membuat pesanan lagi. Buat UUID baru untuk setiap pesanan baru dan pakai ulang UUID hanya saat mengulang permintaan.Banyak pesanan sekaligus: status dalam satu panggilan
Menyewa puluhan atau ratusan kotak masuk? Cek statusnya sekaligus:
POST /v1/orders/status mengembalikan hingga 100 pesanan per panggilan (dihitung satu permintaan pada rate limit Anda), dan GET /v1/orders menampilkan daftar pesanan Anda — setelah restart, ?status=waiting_code mengembalikan semua pesanan yang masih menunggu kode. Setiap error juga memberi tahu apakah perlu dicoba lagi (error.retryable, error.retryAfterSec). Detail: bagian Status banyak pesanan.Jenis kotak masuk: Gmail atau iCloud
Secara default Anda mendapat kotak masuk
@gmail.com. Untuk menyewa kotak masuk @icloud.com, tambahkan "domain": "icloud.com" ke body POST /v1/orders. Jika domain dihilangkan, artinya gmail.com — integrasi yang sudah ada tetap berjalan tanpa perubahan. iCloud hanya tersedia untuk layanan yang field icloud-nya bukan null di GET /v1/services; harga iCloud bisa berbeda dari harga Gmail (lihat icloud.price). Setiap respons pesanan menyertakan field domain yang menunjukkan jenis kotak masuk yang dipakai pesanan tersebut. Beberapa layanan juga menawarkan kotak masuk @gmx.com (saat ini hanya Pokemon, kode pkm, gudang 2): kirim "domain": "gmx.com" — sebuah layanan menawarkannya bila offers[]-nya di GET /v1/services memiliki entri dengan domain = "gmx.com"; layanan lain ⇒ DOMAIN_UNAVAILABLE. gmx.com tidak tersedia untuk paket.Gudang: satu layanan, beberapa sumber kotak masuk
Sebuah layanan dapat dijual dari beberapa gudang, masing-masing dengan harga, stok, dan aturan kodenya sendiri (array
offers[] di GET /v1/services). Kirim "warehouse": 2 saat membuat pesanan untuk memilih salah satunya; "warehouse": 99 ⇒ sistem memilih gudang termurah yang masih ada stok dan berpindah ke gudang berikutnya jika stoknya habis (ideal untuk layanan yang sering kehabisan stok, seperti Facebook dan Instagram); 0 ⇒ gudang termurah yang masih ada stok. Klien yang sudah ada tidak perlu perubahan: tanpa warehouse ⇒ gudang 1 seperti sebelumnya, dan price, stock, serta icloud menggambarkan gudang 1 (layanan tanpa gudang 1 ⇒ gudang termurah yang masih ada stok). Detail: lihat Gudang.Sewa per paket (batch)
Paket dibeli di web (halaman Sewa per paket); lewat API Anda dapat melihat daftar paket dan membaca kode. Gmail (Gudang 1): setiap kotak masuk punya
proxyUrl — link kode yang tidak memerlukan API key. iCloud (Gudang 3): kotak masuk pribadi; baca kode dengan GET /v1/batches/{id}/mails/{mailId}/codes atau buka readUrl di web. Detail: bagian Sewa per paket.Beli akun Gmail masa aktif singkat (mail shop)
Selain menyewa kotak masuk, Anda juga bisa membeli akun Gmail secara utuh dari 2 gudang — gudang 1: email, kata sandi, cookies + link kotak masuk (juga dalam JSON); gudang 2: email, kata sandi + link login cepat; gudang 99 memilih yang termurah dulu (halaman Beli Gmail masa aktif singkat di web). API bisa melakukan semua yang dilakukan halaman itu: melihat gudang, harga, dan stok, membeli (
Idempotency-Key wajib), memantau pengiriman, mengunduh akun, dan membaca email-nya. Detail: bagian Beli Gmail masa aktif singkat.Biarkan pelanggan Anda melihat kode sendiri (halaman /otp)
Menjual ulang kotak masuk yang Anda sewa (lewat web atau API)? Aktifkan kode bagikan di halaman Akun, lalu kirim tautan
https://otpgmail.net/id/otp#k7m2q9:[email protected] ke pelanggan (k7m2q9 = kode bagikan Anda) — mereka melihat kode terbaru email itu tanpa login dan tidak pernah melihat harga atau saldo Anda; halaman ini hanya melakukan tindakan gratis (tidak pernah memotong saldo Anda).Link JSON untuk alat / bot (milik Anda atau pelanggan): pakai link berbentuk path
https://otpgmail.net/id/otp/k7m2q9:[email protected] (membuka halaman /otp sama seperti link #) lalu tambahkan ?json ⇒ server mengembalikan JSON berisi data yang sama persis dengan yang ditampilkan halaman: cards[] — satu kartu untuk setiap layanan yang disewa pada email itu (email, serviceName, state, items[] = maksimal 5 kode / link terbaru, yang terbaru dulu) — serta serverTime. state: waiting menunggu kode pertama · listening menunggu kode baru · received sudah ada kode · needs_owner penjual harus memperpanjang · expired kedaluwarsa · banned kotak masuk dikunci. Beberapa email: pisahkan dengan koma (maksimal 50). Tanpa kunci API dan cookie, CORS terbuka; hanya baca (“Muat OTP baru” / “Sewa email ini lagi” tetap di halaman /otp). Batas anti-tebak sama seperti halaman: kode salah / email tanpa pesanan yang bisa ditampilkan ⇒ 404 NOT_FOUND; salah berulang kali atau lebih dari 30 kali baca per menit untuk satu email ⇒ 429 RATE_LIMITED beserta retryAfter (detik). Menambahkan ?json ke link /otp#… TIDAK mengembalikan JSON: browser tidak pernah mengirim bagian setelah # ke server.curl "https://otpgmail.net/id/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
}
}Kode contoh lengkap di GitHub
Klien siap jalan untuk Python, Node.js, PHP, C# (.NET), dan curl — menyewa kotak masuk, menunggu kode, membatalkan otomatis untuk refund, dan berpindah antara iCloud dan Gmail saat stok habis: github.com/letoan0902/otpgmail-email-otp-api (Inggris) · thue-otp-gmail (Vietnam). Catatan Python: gunakan
requests, bukan urllib polos (User-Agent bawaannya mendapat 403 dari CDN).Webhook
Daripada terus menanyakan setiap pesanan, daftarkan satu URL dan terima POST bertanda tangan begitu pesanan mendapat kode baru atau dibatalkan / dikembalikan dananya — lihat Webhook.
Endpoint
GET/v1/balance
Mengembalikan saldo akun (dalam VND) untuk API key yang dipakai pada permintaan.
Contohcurl
Python
Node.js
curl "https://otpgmail.net/v1/balance" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"balance": 25000
}
}Kemungkinan error: UNAUTHORIZEDGET/v1/services
Mendaftar layanan yang aktif — kode, nama, harga Gmail (dalam VND) dan stok Gmail saat ini, blok icloud berisi harga dan stok kotak masuk @icloud.com, serta array offers[]: setiap gudang yang menjual layanan itu, satu entri per gudang × jenis kotak masuk (harga, stok, jumlah kode, aturan). Data di-cache: stok diperbarui sekitar setiap 30–60 detik, harga lebih jarang.
icloud = null berarti layanan belum mendukung iCloud (penyedia tidak menawarkannya, atau dinonaktifkan sementara) — hanya Gmail yang bisa disewa. Untuk layanan dengan blok icloud, kirim "domain": "icloud.com" saat membuat pesanan untuk mendapatkan kotak masuk @icloud.com dengan harga icloud.price. price / stock / icloud.price menggambarkan gudang 1 — tempat pesanan tanpa warehouse diarahkan (layanan tanpa gudang 1 ⇒ gudang termurah yang ada stok); harga dan stok per gudang ada di offers[] (lihat Gudang). stock adalah angka yang diumumkan sumber (gudang 1: diperbarui kira-kira setiap 30 detik), bukan jaminan bahwa satu panggilan bisa menyewa sebanyak itu: kotak masuk diberikan satu per satu dan sumber bisa habis di tengah jalan — saat itu POST /v1/orders mengembalikan lebih sedikit pesanan disertai field partial. Gudang 2 dan 4 juga menampilkan angka dari sumbernya masing-masing.
Contohcurl
Python
Node.js
curl "https://otpgmail.net/v1/services" \
-H "Authorization: Bearer og_xxx..."{
"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
}
]
}Kemungkinan error: UNAUTHORIZEDPOST/v1/orders
Membuat pesanan untuk kotak masuk yang menerima kode verifikasi — Gmail secara default, atau iCloud bila Anda mengirim domain: "icloud.com". Saldo Anda dipotong begitu penyedia memberikan kotak masuk. Mengembalikan daftar pesanan (satu, atau beberapa bila quantity > 1).
Pengiriman sebagian: quantity adalah batas maksimum
- Kotak masuk diberikan satu per satu. Jika penyedia kehabisan di tengah jalan (misalnya gudang 1 saat itu tidak punya kotak masuk lagi untuk layanan tersebut), panggilan berhenti dan mengembalikan HTTP 200 beserta pesanan yang sudah dibuat —
databisa lebih sedikit dari quantity, dan Anda hanya dikenakan biaya untuk pesanan didata. NilaistockdiGET /v1/servicestidak menjamin berapa yang didapat satu panggilan. - Dalam kasus itu respons juga membawa field tingkat atas
partialdi sampingdata(tidak ada jika semuanya terkirim — bot yang hanya membacadatatetap berjalan tanpa perubahan):requested,delivered,missing, danreason={code, message, retryable, retryAfterSec}, dengancodeberupa error yang menghentikan panggilan (NO_MAILS_AVAILABLE,OUT_OF_STOCK,ALL_WAREHOUSES_EXHAUSTED,INSUFFICIENT_BALANCE,WAITING_LIMIT_REACHED…). - Kehabisan biasanya hanya sementara — di gudang 1 penyedia sering sudah punya kotak masuk lagi beberapa detik kemudian. Jika
reason.retryable= true, tunggureason.retryAfterSecdetik (3–5 detik), lalu minta lagi kotak masuk yang kurang (missing) denganIdempotency-KeyBARU (key lama hanya mengulang respons yang sama ini); ulangi sampai semuanya didapat. - Tepat setelah pengiriman sebagian, panggilan berikutnya bisa mengembalikan 409
WAREHOUSE_UNAVAILABLE/OUT_OF_STOCKatau 503NO_MAILS_AVAILABLE— itu normal: tetap beri jeda 3–5 detik antarpanggilan dan baru berhenti jika banyak panggilan berturut-turut gagal (misalnya 10 percobaan, sekitar satu menit); lalu coba lagi nanti, atau kirim"warehouse": 99agar sistem pindah ke gudang lain.retryable= false (INSUFFICIENT_BALANCE,WAITING_LIMIT_REACHED…) ⇒ atasi dulu penyebabnya (isi saldo, atau tunggu pesanan yang masih terbuka selesai). - Respons error membawa dua field yang sama di dalam
error:error.retryabledanerror.retryAfterSec(misalnya 409OUT_OF_STOCK⇒true, 5; 402INSUFFICIENT_BALANCE⇒false,null) — lihat Kode error.
Header Idempotency-Key (sebuah UUID) wajib. Buat UUID baru untuk setiap sewa baru; saat mengulang setelah timeout, pakai ulang key yang SAMA agar tidak membuat pesanan ganda. Panggilan ini menunggu penyedia memberikan kotak masuk, sehingga biasanya memakan waktu sekitar 0,3 detik tetapi bisa sampai 10 detik pada jam sibuk (jarang) — atur timeout klien Anda sekitar 15 detik, jangan menyerah setelah 5 detik lalu mengulang terlalu cepat.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
service | string | Ya | Kode layanan (dari GET /v1/services, mis. "ig", "tg", "fb") |
quantity | number | Tidak | Jumlah MAKSIMUM kotak masuk yang disewa (default 1; paling banyak 200 per permintaan) — jika gudang habis di tengah jalan, Anda mendapat lebih sedikit: hitung data[] dan baca partial |
domain | string | Tidak | Jenis kotak masuk: "gmail.com" (default), "icloud.com", atau "gmx.com". Dihilangkan = gmail.com — integrasi yang sudah ada tidak perlu perubahan. iCloud hanya bisa disewa untuk layanan yang field icloud-nya bukan null di GET /v1/services; "gmx.com" hanya untuk layanan dengan offers[].domain = "gmx.com" (saat ini hanya Pokemon, kode pkm) — selain itu panggilan gagal dengan DOMAIN_UNAVAILABLE. Nilai lain ⇒ VALIDATION_ERROR. |
warehouse | number | Tidak | Gudang tempat menyewa. Dihilangkan = GUDANG 1, persis seperti sebelum ada multi-gudang (layanan tanpa gudang 1 beralih ke yang termurah) — klien yang sudah ada tidak perlu perubahan. 0 = gudang termurah yang ada stok (hanya satu gudang yang dicoba). 1–4 = persis gudang tersebut (dari offers[].warehouse di GET /v1/services); jika gudang itu tidak menjual layanan/jenis kotak masuk ini, dijeda, atau kehabisan stok ⇒ WAREHOUSE_UNAVAILABLE. 99 = OTOMATIS: gudang yang ada stok dicoba dari yang termurah hingga termahal, berpindah saat satu gudang kehabisan stok atau gagal; tidak ada yang tersisa ⇒ ALL_WAREHOUSES_EXHAUSTED — disarankan untuk layanan yang sering kehabisan stok (Facebook, Instagram…). Lihat Gudang. |
curl
Python
Node.js
# Buat Idempotency-Key acak untuk setiap pesanan BARU
# PAKAI ULANG key yang sama saat mengulang setelah timeout (mencegah pesanan ganda)
# domain: "gmail.com" (default, opsional) | "icloud.com" (sewa kotak masuk @icloud.com) | "gmx.com" (hanya layanan yang menawarkannya, mis. Pokemon)
# warehouse: 0 | 1–4 | 99 (opsional — hilangkan untuk tetap memakai gudang 1 seperti sebelumnya; 0 = gudang termurah yang ada stok; 99 = otomatis, berpindah saat gudang kehabisan stok; lihat offers[] di 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"}'{
"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
}
]
}Contoh respons — pengiriman sebagian (quantity 5, 2 kotak masuk terkirim)
Kemungkinan error: {
"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
}
}
}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, MAINTENANCEGET/v1/orders/{id}
Mengembalikan pesanan menurut ID, termasuk kode yang sudah diterima sejauh ini (otp[]). Lakukan polling endpoint ini setiap 3–5 detik untuk mengambil kode selama pesanan berstatus waiting_code.
Array otp[] diurutkan menurut kedatangan: yang terlama dulu, TERBARU TERAKHIR — baca kode terbaru dengan otp[otp.length - 1]. Pada setiap panggilan, sistem memeriksa penyedia untuk pesanan yang masih terbuka dengan ritme masing-masing gudang (setiap beberapa detik) — memanggil lebih sering hanya mengembalikan data yang tersimpan. Setelah kode pertama, terus lakukan polling: kode berikutnya (jika layanan mengirim lebih dari satu) ditambahkan di akhir otp[] tanpa biaya tambahan selama masa pesanan — batas dan jendela waktunya ditampilkan sebelum Anda menyewa (maxCodes dan fee di offers[]); untuk memastikan sistem sedang menunggu kode baru, panggil POST /v1/orders/{id}/next-code (semua gudang; hanya gudang 2 di luar jendela gratisnya yang mengenakan biaya). Jika layanan hanya mengizinkan satu kode per kotak masuk, otp[] berhenti pada kode itu — respons tetap normal, tanpa error. Field warehouse memberi tahu gudang mana yang memasok kotak masuk (1–4); nextCodeFee adalah biaya untuk kode tambahan (pesanan gudang 2 dan 3; null untuk gudang 1 dan 4) — lihat Gudang. Sejak 2026-10-02 pesanan juga menyertakan nextCodeBlocked (true = kotak masuk tidak bisa menerima kode lagi) dan nextCodeBlockReason — alasannya: "email_banned" (email dikunci oleh layanan — sewa email baru), "activation_gone" (penyedia menutup kotak masuk), "hard_expired" (jendela kode telah berakhir), "max_codes" (jumlah kode maksimum telah diterima), null (tidak diblokir / pesanan lama tanpa alasan tercatat). Ini field tambahan — klien yang sudah ada tidak perlu perubahan. Melakukan polling banyak pesanan? Kirim hingga 100 ID per panggilan ke POST /v1/orders/status, bukan satu GET per pesanan — objek pesanan yang sama, satu permintaan pada rate limit Anda.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | ID pesanan (orderId yang dikembalikan saat pesanan dibuat) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/orders/ORDER_ID" \
-H "Authorization: Bearer og_xxx..."{
"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
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUNDPOST/v1/orders/{id}/cancel
Membatalkan pesanan dan me-refund 100% harga. Hanya diizinkan selama pesanan berstatus waiting_code DAN belum ada kode yang diterima.
Pesanan yang sudah menerima kode (meski hanya satu) tidak dapat dibatalkan atau di-refund — berlaku untuk situs web maupun API. Pesanan dengan rules.cancellable = false (layanan stok sendiri seperti Cointiply) tidak dapat dibatalkan: begitu kotak masuk diberikan, tidak ada pembatalan atau refund — panggilan cancel mengembalikan ORDER_NOT_CANCELLABLE. Pesanan Gudang 3 dari layanan reguler (rules.cancellable = true) dapat dibatalkan seperti pesanan lain sebelum kode tiba.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | ID pesanan yang akan dibatalkan |
curl
Python
Node.js
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/cancel" \
-H "Authorization: Bearer og_xxx..." \
-H "Content-Type: application/json"{
"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
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, ORDER_NOT_CANCELLABLE, PROVIDER_ERRORPOST/v1/orders/{id}/rerent
Menyewa kotak masuk yang SAMA lagi untuk pesanan layanan stok sendiri (provider = "inventory", mis. Cointiply): membuat pesanan BARU pada kotak masuk itu dengan jendela penerimaan baru, dengan harga rerentPriceVnd milik pesanan lama.
Hanya berfungsi bila pesanan lama memiliki canRerent = true (pesanan terbaru dari kotak masuk itu, yang jendela sebelumnya telah berakhir atau sudah menerima semua emailnya). Biaya langsung dipotong begitu panggilan berhasil — kirim Idempotency-Key dan pakai ulang saat mengulang agar Anda tidak pernah dikenai biaya dua kali. Responsnya { order, message }; order memiliki bentuk yang sama dengan GET /v1/orders/{id}. Gudang 2 (sejak 2026-10-09): pesanan yang dibatalkan, habis waktu, atau kedaluwarsa (kotak masuk tertutup) — panggil saat
ParametercanRerent = true (pesanan terbaru dari email itu dan email tidak punya pesanan aktif lain). Sistem membuka kembali email YANG SAMA sebagai pesanan BARU (status = waiting_code) dengan harga rerentPriceVnd (saat ini 100% harga Anda) dan menunggu email selama 20 menit seperti pesanan Gudang 2 biasa — tanpa email, pesanan dibatalkan dan dikembalikan 100%. Email dikunci layanan ⇒ RERENT_EMAIL_BANNED; penyedia tidak bisa membukanya lagi ⇒ RERENT_MAIL_GONE; Gudang 2 sedang terganggu ⇒ PROVIDER_ERROR — ketiganya tidak memotong saldo.| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | ID pesanan sebelumnya dari kotak masuk itu (yang memiliki canRerent = true) |
curl
Python
Node.js
# PAKAI ULANG key yang sama saat mengulang setelah timeout (mencegah pesanan ganda)
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/rerent" \
-H "Authorization: Bearer og_xxx..." \
-H "Idempotency-Key: $(uuidgen)"{
"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"
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, RERENT_NOT_AVAILABLE, RERENT_EMAIL_BANNED, RERENT_MAIL_GONE, INSUFFICIENT_BALANCE, WAITING_LIMIT_REACHED, RATE_LIMITED, DUPLICATE_REQUEST, PROVIDER_ERROR, MAINTENANCEPOST/v1/orders/{id}/next-code
Meminta kode berikutnya pada kotak masuk yang SAMA — berlaku untuk pesanan dari SEMUA gudang (warehouse 1–4) yang sudah menerima kode pertamanya (status = completed). Gratis, kecuali pesanan gudang 2 yang jendela gratisnya sudah berakhir (nextCodeFee.feePriceVnd > 0): kirim confirmFee: true untuk membayar biayanya dan membuka jendela kode lain (hingga batas keras nextCodeFee.hardUntil). Bot Anda tidak perlu tahu gudangnya: terus lakukan polling GET /v1/orders/{id} — kode baru ditambahkan di akhir otp[] — dan panggil endpoint ini bila Anda ingin sistem menunggu kode baru.
Apa yang dilakukan panggilan ini di tiap gudang
- Gudang 1: sistem menunggu kode berikutnya sendiri setelah setiap kode selama masa pesanan (
rentExpiresAt) — panggilan mengembalikan 200 dan tidak mengubah apa pun; bila kotak masuk tidak bisa menerima kode lagi (batas kode tercapai, kotak ditutup, kedaluwarsa) ⇒ 409 dengannextCodeBlockReason. - Gudang 2: membuka kembali kotak masuk di penyedia (
reordered= true) lalu menunggu email selama 20 menit; gratis di dalam jendela gratis (nextCodeFee.feePriceVnd= 0), di luar jendela Anda harus mengirimconfirmFee= true (tanpa itu ⇒ 409NEXT_CODE_FEE_CONFIRM, tidak ada biaya ditarik). Biaya hanya ditahan bila kode baru tiba — tidak ada kode dalam 20 menit ⇒ dikembalikan otomatis. Setelah setiap kode, sistem terus menunggu kode berikutnya 25 menit lagi (listening= true,listenUntil). - Gudang 3 (layanan biasa): membuka kembali jendela mendengarkan singkat, gratis di dalam jendela gratis (
nextCodeFee.freeUntil); setelah itu ⇒ 409 — gunakanPOST /v1/orders/{id}/rerent. Layanan inventaris sendiri seperti Cointiply (rules.cancellable= false) tidak punya panggilan ini (409): setiap email muncul sendiri selama jendela. - Gudang 4: membaca ulang pesanan di penyedia dan menunggu kode baru, gratis hingga
rentExpiresAt.
200 = sistem sedang menunggu kode baru untuk pesanan ini (listening = true, listenUntil = kapan penantian berhenti; reordered = true bila kotak masuk baru dibuka kembali; chargedVnd bila ada biaya ditarik), atau kode baru tiba tepat saat Anda memanggil (sudah ada di order.otp). 409 = penantian baru tidak bisa dibuka — baca error.code; alasan kotak masuk tidak menerima kode lagi ada di order.nextCodeBlockReason (GET /v1/orders/{id}). Pesanan yang masih menunggu kode pertama (waiting_code) ⇒ 409, belum perlu dipanggil. Kirim Idempotency-Key pada panggilan berbayar (gunakan lagi saat mencoba ulang agar tidak terpotong dua kali). Penyedia tidak dapat dihubungi ⇒ 502 PROVIDER_ERROR, coba lagi nanti. Kode error 409 untuk panggilan ini: NEXT_CODE_FEE_CONFIRM (gudang 2 memerlukan biaya — panggil lagi dengan confirmFee: true), EMAIL_BANNED (email dikunci oleh layanan — sewa yang baru; error.details.blockReason = "email_banned"), ORDER_EXPIRED (kedaluwarsa, kotak masuk ditutup, batas kode tercapai, atau layanan tidak punya "kode berikutnya" — alasannya ada di order.nextCodeBlockReason), ORDER_NOT_CANCELLABLE (pesanan belum menerima kode pertamanya). Bot yang menganggap setiap 409 dari panggilan ini sebagai "tidak ada kode lagi" tetap berjalan tanpa perubahan. refresh: true (gudang 2): pertama memeriksa email baru (yang baru tiba langsung dikirim, tidak dibuka ulang), lalu membuka kembali kotak masuk di penyedia — di dalam jendela gratis penantian dimulai lagi selama 20 menit; di luar jendela batas penantian (listenUntil) tetap dan tidak ada biaya. Respons menambahkan refreshed (true = baru dibuka ulang) dan refreshAvailableAt (detik unix; sebelum waktu itu penyegaran berikutnya hanya memeriksa email; null = boleh sekarang). Gudang lain: tidak berpengaruh (refreshed = false). Tanpa refresh panggilan berjalan persis seperti sebelumnya.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | ID pesanan (orderId) — gudang mana pun |
confirmFee | boolean | Tidak | true = Anda setuju membayar nextCodeFee.feePriceVnd untuk membuka jendela kode lain. Tidak diperlukan selama feePriceVnd = 0. |
refresh | boolean | Tidak | true = gudang 2, pesanan menunggu kode baru (listening = true) tetapi kode tidak kunjung datang: buka kembali kotak masuk di penyedia sekarang juga (segarkan penantian) — penantian lama mungkin sudah berhenti menerima email di sisi penyedia. Gratis; paling sering sekali per 5 dtk per pesanan. Pesanan tidak sedang menunggu ⇒ sama seperti panggilan biasa. |
curl
Python
Node.js
# Gratis (nextCodeFee null atau feePriceVnd = 0 — semua gudang kecuali gudang 2 setelah jendela gratisnya): tidak perlu confirmFee
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/next-code" \
-H "Authorization: Bearer og_xxx..."
# Gudang 2 setelah jendela gratisnya: kirim confirmFee = true untuk membayar nextCodeFee.feePriceVnd
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}'{
"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
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, NEXT_CODE_FEE_CONFIRM, INSUFFICIENT_BALANCE, EMAIL_BANNED, ORDER_EXPIRED, ORDER_NOT_CANCELLABLE, PROVIDER_ERROR, DUPLICATE_REQUEST, MAINTENANCEStatus banyak pesanan dalam satu panggilan
Reseller dan bot yang menyewa banyak kotak masuk sebaiknya tidak mengirim satu
GET /v1/orders/{id} per pesanan: POST /v1/orders/status membaca hingga 100 pesanan per panggilan dengan objek pesanan yang sama, dan GET /v1/orders menampilkan daftar pesanan Anda dari data tersimpan.- Lakukan polling setiap 3–5 detik dengan
POST /v1/orders/status, hingga 100 ID pesanan per panggilan — setiap panggilan dihitung satu permintaan pada rate limit Anda. - Setiap panggilan, sistem memeriksa penyedia untuk paling banyak 20 pesanan terbuka dalam daftar (yang menunggu kode pertama lebih dulu, lalu yang paling lama tidak diperiksa) dan mengembalikan sisanya dari data tersimpan; sisanya diperiksa pada panggilan berikutnya. Satu panggilan menjawab dalam sekitar 3 detik.
- Keluarkan pesanan dari daftar Anda begitu sudah final: cancelled (dana dikembalikan), atau completed jika Anda tidak butuh kode lagi.
- ID yang tidak ada atau milik akun lain dikembalikan di
missing(tanpa 404) — keluarkan juga. - Setelah restart, ambil kembali semua pesanan terbuka dengan
GET /v1/orders?status=waiting_code(ikutinextCursor), lalu lanjutkan polling denganPOST /v1/orders/status.
POST/v1/orders/status
Mengembalikan hingga 100 pesanan Anda dalam satu panggilan. Setiap elemen sama persis dengan yang dikembalikan GET /v1/orders/{id} untuk pesanan itu (status, otp[] berisi semua kode yang diterima, warehouse…). Gunakan untuk polling banyak pesanan terbuka sekaligus.
data mengikuti urutan ids; ID duplikat diabaikan. ID yang tidak ada atau milik akun lain dicantumkan di missing (tanpa 404). Untuk pesanan terbuka, sistem memeriksa penyedia seperti GET /v1/orders/{id}, tetapi paling banyak untuk 20 pesanan per panggilan — yang menunggu kode pertama lebih dulu, lalu yang paling lama tidak diperiksa — dan mengembalikan sisanya dari data tersimpan (diperiksa pada panggilan berikutnya); satu panggilan menjawab dalam sekitar 3 detik. Satu panggilan dihitung satu permintaan pada rate limit Anda, berapa pun jumlah ID-nya. Lebih dari 100 ID, daftar kosong, atau ID yang bukan string ⇒ 400 VALIDATION_ERROR.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
ids | string[] | Ya | ID pesanan (orderId) — 1 sampai 100 per panggilan |
curl
Python
Node.js
# ID pesanan yang sedang Anda tunggu (orderId dari POST /v1/orders) — hingga 100 per panggilan
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"]}'{
"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"
]
}Kemungkinan error: UNAUTHORIZED, VALIDATION_ERROR, RATE_LIMITEDGET/v1/orders
Menampilkan daftar pesanan Anda, yang terbaru lebih dulu, hanya dari data tersimpan — tidak meminta kode baru ke penyedia (itu tugas POST /v1/orders/status). Filter menurut status dan waktu dibuat; pindah halaman dengan nextCursor.
Kirim nextCursor sebagai cursor untuk mendapatkan halaman berikutnya; nextCursor = null ⇒ halaman terakhir. Halaman tidak pernah mengulang atau melewatkan pesanan, bahkan jika banyak pesanan dibuat pada detik yang sama. Status internal tidak pernah ditampilkan (hanya waiting_code, completed, cancelled, dan queued). Setelah bot Anda restart: GET /v1/orders?status=waiting_code&limit=200, ikuti nextCursor, lalu lakukan polling pesanan-pesanan itu dengan POST /v1/orders/status.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
status | string | Tidak | Hanya status ini: waiting_code | completed | cancelled | queued (kosong ⇒ semuanya) |
since | number | Tidak | Hanya pesanan yang dibuat pada atau setelah waktu unix ini (detik) |
limit | number | Tidak | Pesanan per halaman: 1–200 (default 50) |
cursor | string | Tidak | nextCursor dari halaman sebelumnya, apa adanya |
curl
Python
Node.js
# Setelah restart: kumpulkan semua pesanan yang masih menunggu kode, halaman demi halaman
curl "https://otpgmail.net/v1/orders?status=waiting_code&limit=200" \
-H "Authorization: Bearer og_xxx..."{
"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"
}Kemungkinan error: UNAUTHORIZED, VALIDATION_ERROR, RATE_LIMITEDGudang
Setiap entri
offers[] adalah satu penawaran = (layanan, gudang, jenis kotak masuk). Hanya gudang yang sedang menjual yang dicantumkan; gudang yang stoknya sementara habis tetap ada di daftar dengan stock = 0. Nomor gudang bersifat tetap: 1, 2, 3, 4 — kirim nomor yang sama di parameter warehouse saat membuat pesanan.Klien yang sudah ada tidak perlu perubahan. Tanpa
warehouse ⇒ perilaku sama seperti sebelumnya (gudang 1: menunggu 25 menit, kode tambahan masuk otomatis selama masa pesanan 24 jam bila layanan mengirim lebih dari satu kode); kirim 0 ⇒ gudang termurah yang ada stok; kirim 99 ⇒ gudang otomatis, berpindah saat satu gudang habis (lihat tepat di bawah); setiap field yang sudah ada tetap seperti semula — hanya ditambah field baru (offers[], warehouse, nextCodeFee, listening, nextCodeBlocked…).Gudang otomatis: warehouse = 99 (disarankan untuk layanan yang sering kehabisan stok)
Field offers[]Kirim
"warehouse": 99 dan sistem menyewa dari gudang termurah yang ada stok; jika gudang itu kehabisan stok atau gagal, sistem berpindah ke gudang berikutnya dalam panggilan yang sama. Ideal untuk layanan yang sering kehabisan stok, seperti Facebook dan Instagram — bot Anda tidak perlu lagi membaca offers[] dan mengulang gudang demi gudang.- Urutan yang dicoba: harga dari rendah ke tinggi (mis. 1.500 → 1.600 → 1.700 VND); harga sama ⇒ nomor gudang yang lebih kecil lebih dulu.
- Gudang yang melaporkan stok nol (
stock= 0 dioffers[]) tidak pernah dipanggil — tidak ada waktu yang terbuang. - Berpindah saat gudang saat ini kehabisan stok atau gagal (
OUT_OF_STOCK,NO_MAILS_AVAILABLE,WAREHOUSE_UNAVAILABLE,PROVIDER_ERROR). - Error lain langsung dikembalikan, tanpa mencoba gudang berikutnya:
INSUFFICIENT_BALANCE,WAITING_LIMIT_REACHED,QUANTITY_EXCEEDED,MAINTENANCE,SERVICE_DISABLED,DOMAIN_UNAVAILABLE… quantity> 1: jika gudang pertama kekurangan stok, sisanya diambil dari gudang berikutnya — pesanan bisa mencakup beberapa gudang; setiap pesanan memuatwarehousedanpricedari gudang yang menerbitkannya (dan mengikuti aturan kode gudang itu).- Jika semua gudang sudah dicoba dan tidak ada kotak masuk yang bisa disewa ⇒
ALL_WAREHOUSES_EXHAUSTED(HTTP 409, tidak ada yang dikenakan) — coba lagi nanti. Jika hanya sebagian kotak masuk yang berhasil disewa, Anda tetap mendapat 200 dengan pesanan-pesanan tersebut.
curl
Python
Node.js
# warehouse: 99 — gudang termurah yang ada stok; stok habis atau gagal ⇒ otomatis ke gudang berikutnya
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}'¹ Layanan yang hanya dijual dari stok sendiri, seperti Cointiply (
nextCodeFee — biaya untuk kode tambahan (pesanan gudang 2 dan 3)provider = "inventory", rules.cancellable = false), mengikuti rules: hingga rules.maxCodes email dalam rules.windowMinutes menit, tanpa pembatalan, dan tanpa refund jika waktu habis tanpa ada email.Disertakan di
GET /v1/orders/{id} dan setiap respons pesanan; null untuk pesanan gudang 1 dan 4 (kode tambahan gratis). Gudang 2: panggil POST /v1/orders/{id}/next-code saat Anda butuh kode lain — jika feePriceVnd di atas 0 Anda harus mengirim confirmFee = true. Gudang 3 (layanan biasa): setelah jendela gratis berakhir, feePriceVnd adalah harga menyewa ulang kotak masuk yang sama (rerent).curl
Python
Node.js
# warehouse: 0 | 1–4 | 99 (opsional — hilangkan untuk tetap memakai gudang 1 seperti sebelumnya; 0 = gudang termurah yang ada stok; 99 = otomatis, berpindah saat gudang kehabisan stok; lihat offers[] di 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}'{
"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
}
}Sewa per paket (batch)
Paket dibeli di web (halaman Sewa per paket) — lewat API Anda dapat melihat daftar paket dan membaca kode. Domain email menentukan gudang: Gmail = Gudang 1, iCloud = Gudang 3 (kotak masuk pribadi).
- Sebuah paket disimpan selama 24 jam sejak pembelian — baca kode secara gratis selama waktu itu, tanpa pembatasan jumlah kode.
- Kotak masuk yang tidak menerima kode tidak di-refund; hanya kekurangan jumlah yang di-refund (otomatis).
- Gudang 1 (Gmail): setiap kotak masuk punya
proxyUrl— sebuahGETmengembalikan{"status":1,"code":"482913","all_codes":[...]}tanpa API key (link itulah rahasianya — jaga kerahasiaannya). Endpoint kode di bawah juga berfungsi. - Gudang 3 (iCloud):
proxyUrl=null— baca kode dengan endpoint di bawah atau bukareadUrl(halaman web). Setiap kotak masuk terikat ke akun Anda untuk layanan itu; setelah kedaluwarsa Anda masih bisa menyewanya lagi di web. - Email tanpa kode yang bisa diekstrak (email selamat datang, newsletter…) dikembalikan apa adanya di
codedenganisRaw: true(HTML atau teks, hingga 20.000 karakter).
GET/v1/batches
Paket Anda, yang terbaru lebih dulu (dipaginasi dengan page / pageSize, hingga 100). Setiap paket memiliki warehouse (1 = Gmail, 3 = iCloud), status (active | expired | failed), expiresAt, codeCount, dan readUrl.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
page | number | Tidak | Halaman (default 1) |
pageSize | number | Tidak | Jumlah paket per halaman (default 20, maks 100) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/batches?page=1&pageSize=20" \
-H "Authorization: Bearer og_xxx..."{
"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
}
}Kemungkinan error: UNAUTHORIZEDGET/v1/batches/{id}
Satu paket beserta mails[]-nya — ambil id setiap kotak masuk untuk membaca kodenya.
Paket Gmail (gudang 1): proxyUrl setiap kotak masuk berfungsi di skrip Anda persis seperti sebelumnya. Paket iCloud (gudang 3): proxyUrl = null, baca kode dengan endpoint di bawah. Paket iCloud yang sudah kedaluwarsa menyertakan rerentPriceVnd — harga untuk menyewa satu kotak masuk lagi secara satuan (dilakukan di web).
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | ID paket (tampil di halaman Sewa per paket dan di readUrl) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/batches/BATCH_ID" \
-H "Authorization: Bearer og_xxx..."{
"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
}
]
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUNDGET/v1/batches/{id}/mails/{mailId}/codes
Baca kode dari satu kotak masuk dalam paket: sistem memeriksa penyedia jika kotak masuk sudah waktunya, lalu mengembalikan SETIAP kode yang diterima, yang terlama dulu — kode terbaru ada di urutan terakhir codes[].
Lakukan polling setiap 5–10 detik selagi menunggu (memanggil lebih sering hanya mengembalikan data tersimpan — polledAt tidak berubah). Email tanpa kode yang bisa diekstrak dikembalikan apa adanya di code dengan isRaw: true. Paket yang sudah kedaluwarsa tetap mengembalikan kode tersimpannya tetapi tidak diperiksa lagi.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | ID paket |
mailId | number | Ya | ID kotak masuk (mails[].id) |
poll | 0 | 1 | Tidak | 0 = hanya baca kode tersimpan, jangan periksa penyedia (default 1) |
curl
Python
Node.js
# Panggil lagi setiap 5–10 detik selagi menunggu; kode terbaru ada di urutan terakhir codes[]
curl "https://otpgmail.net/v1/batches/BATCH_ID/mails/MAIL_ID/codes" \
-H "Authorization: Bearer og_xxx..."{
"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
}
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, RATE_LIMITEDBuy 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- Akun gudang 1 (
gm_cookie) datang tanpa email pemulihan — Anda menerima email, kata sandi, cookies, dan link kotak masuk (inboxUrl/inboxJsonUrl), tidak lebih. - Cara masuk yang disarankan: impor cookies (
cookies, JSON) ke browser — dengan ekstensi impor cookie atau fitur impor cookie di browser antidetect Anda — lalu buka Gmail: sesi sudah dalam keadaan masuk, biasanya tanpa diminta kata sandi atau verifikasi. - Hanya butuh kode atau email? Baca lewat
inboxUrl(halaman web) atauinboxJsonUrl(JSON) — tanpa API key dan tanpa masuk ke Google. - Masuk dengan kata sandi di perangkat atau IP baru bisa membuat Google meminta verifikasi (email pemulihan, telepon, atau kode) — akun ini tidak punya apa pun untuk melewatinya, jadi gunakan cookies atau link kotak masuk.
- Menjual kembali? Berikan kepada pelanggan Anda cookies dan link kotak masuk — unduhan .txt (
GET /v1/mail-shop/orders/{id}/download) memuatemail|password|cookies|inboxUrldalam satu baris — dan minta mereka masuk dengan cookies, bukan kata sandi. - Akun gudang 2 (
gm_login) sebagai gantinya dilengkapi link login cepat (loginUrl) — buka di browser komputer untuk masuk.
- 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, callGET /v1/mail-shop/quote. - Buy:
POST /v1/mail-shop/orderswith{"warehouse": 1 | 2 | 99, "quantity": N}+ headerIdempotency-Key(a new UUID per purchase). The response is the order, usually alreadycompletedwithitems[]. - Get the accounts: read
items[](table below) or download the TXTGET /v1/mail-shop/orders/{id}/download.status=pending⇒ pollGET /v1/mail-shop/orders/{id}every 5 seconds untilcompleted/partial/failed.
curl
Python
Node.js
# 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..."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"
# 424819Order statuses- Money:
chargedVnd= Σroute[].quantity×unitPriceVnd(a 99 order split across warehouses ⇒ two legs inroute). Shortfalls are refunded automatically (refundVnd). Delivered accounts are not refunded — the only warranty is the warehouse'sterms(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 ⇒ 429RATE_LIMITED. - Common errors:
MAILSHOP_OUT_OF_STOCK409 (details.stock= how many you can buy now) ·MAILSHOP_QTY_LIMIT400 (details.max) ·INSUFFICIENT_BALANCE402 ·MAILSHOP_DISABLED503 (warehouse / section paused) ·MAILSHOP_SUPPLIER_ERROR502 (the source refused at once — a failed order was recorded and refunded,details.orderId) ·VALIDATION_ERROR400 (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.
Contohcurl
Python
Node.js
curl "https://otpgmail.net/v1/mail-shop/products" \
-H "Authorization: Bearer og_xxx..."{
"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
]
}
}
}Kemungkinan error: UNAUTHORIZEDGET/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
warehouse | 1 | 2 | 99 | Tidak | Mail-shop warehouse: 1 = email + password + cookies + inbox link · 2 = email + password + login link · 99 = automatic, cheapest first. Send warehouse OR product |
product | string | Tidak | Instead of warehouse: gm_cookie (= warehouse 1) | gm_login (= warehouse 2) |
quantity | number | Tidak | Number of accounts (default 1) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/mail-shop/quote?warehouse=99&quantity=10" \
-H "Authorization: Bearer og_xxx..."{
"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
}
]
}
}Kemungkinan error: UNAUTHORIZED, VALIDATION_ERROR, MAILSHOP_PRODUCT_NOT_FOUNDPOST/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
warehouse | 1 | 2 | 99 | Tidak | Mail-shop warehouse: 1 = email + password + cookies + inbox link · 2 = email + password + login link · 99 = automatic, cheapest first. Send warehouse OR product |
product | string | Tidak | Instead of warehouse: gm_cookie (= warehouse 1) | gm_login (= warehouse 2) |
quantity | number | Tidak | Number of accounts (default 1) |
Idempotency-Key | string | Ya | Header: a new UUID for every new purchase — reuse it only to retry that same purchase |
curl
Python
Node.js
# Idempotency-Key wajib: UUID baru untuk setiap pembelian — pakai ulang key yang sama hanya saat mengulang setelah 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}'{
"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"
}
]
}
}Kemungkinan error: 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, MAINTENANCEGET/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
page | number | Tidak | Page (default 1) |
pageSize | number | Tidak | Orders per page (default 20, max 100) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/mail-shop/orders?page=1&pageSize=20" \
-H "Authorization: Bearer og_xxx..."{
"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
}
}Kemungkinan error: UNAUTHORIZEDGET/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | Order ID (data.id returned by the purchase) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID" \
-H "Authorization: Bearer og_xxx..."{
"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"
}
]
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUNDGET/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | Order ID (data.id returned by the purchase) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/download" \
-H "Authorization: Bearer og_xxx..." \
-o mail-ORDER_ID.txtemail|password|cookies|inboxUrl
[email protected]|Pw7Xq!k3d|[{"domain":".google.com","name":"SID","value":"g.a000…"}]|https://otpgmail.net/mail/Q7xK2mP9vL4tR8wN3cB6yH1dKemungkinan error: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERRORGET/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | Order ID (data.id returned by the purchase) |
lineNo | number | Ya | Account number within the order (items[].lineNo) |
max | number | Tidak | Number of latest emails: 10 (default), 20, 50, 100 or 200 |
curl
Python
Node.js
# 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"{
"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
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, PROVIDER_ERRORPOST/v1/mail-shop/orders/{id}/items/{lineNo}/inbox-link
Manage the inbox link (inboxUrl + inboxJsonUrl + twoFaUrl — one shared code) of a warehouse-1 account: action "regenerate" = NEW link (the old one stops working immediately; also re-enables a link you turned off), "off" = turn it off (inboxUrl / inboxJsonUrl / twoFaUrl become null). Returns the account (an element of items[]).
At most 60 changes per 10 minutes for your account.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | Order ID (data.id returned by the purchase) |
lineNo | number | Ya | Account number within the order (items[].lineNo) |
action | string | Ya | "regenerate" = new link (the old one stops working immediately) · "off" = turn the link off |
curl
Python
Node.js
# Link baru (link lama langsung berhenti berfungsi); kirim {"action":"off"} untuk mematikannya
curl -X POST "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/items/1/inbox-link" \
-H "Authorization: Bearer og_xxx..." \
-H "Content-Type: application/json" \
-d '{"action":"regenerate"}'{
"success": true,
"data": {
"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/Hn4Vw8Zr2Lk6Tq9Pm3Xs7Bd1",
"inboxJsonUrl": "https://otpgmail.net/mail/Hn4Vw8Zr2Lk6Tq9Pm3Xs7Bd1?json",
"twoFaUrl": "https://otpgmail.net/2fa/Hn4Vw8Zr2Lk6Tq9Pm3Xs7Bd1",
"inboxLinkExpiresAt": 1762592400
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITEDGET/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | Order ID (data.id returned by the purchase) |
lineNo | number | Ya | Account number within the order (items[].lineNo) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/items/1/status" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"email": "[email protected]",
"live": true,
"reason": null,
"checkedAt": 1760000600
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, PROVIDER_ERRORGET/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| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
id | string | Ya | Order ID (data.id returned by the purchase) |
lineNo | number | Ya | Account number within the order (items[].lineNo) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/mail-shop/orders/ORDER_ID/items/1/2fa" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"none": false,
"code": "424819",
"expiresIn": 24
}
}Kemungkinan error: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, PROVIDER_ERRORSiklus pesanan
Catatan penting:
- Lakukan polling
GET /v1/orders/{id}setiap 3–5 detik selama pesanan berstatus waiting_code agar Anda segera menerima kodenya. - Begitu kode pertama tiba, status berubah menjadi completed seketika.
- Tidak ada kode hingga
codeDeadlineAt(paling lama 25 menit; gudang 2: 20 menit; beberapa sumber gudang 4 lebih singkat) ⇒ sistem membatalkan pesanan dan mengembalikan 100% (status = cancelled). - Array
otp[]diurutkan terlama dulu, terbaru terakhir. Baca kode terbaru denganotp[otp.length - 1](Python:otp[-1]). - Pesanan completed terus menerima kode secara otomatis: terus lakukan polling
GET /v1/orders/{id}dan setiap kode baru muncul di akhirotp[]. Kode tambahan tidak dikenai biaya selama masa pesanan bila layanan mengirim lebih dari satu; batas dan jendela waktunya ditampilkan sebelum Anda menyewa (maxCodesdanfeedioffers[]; di gudang 1 kotak masuk biasanya tetap terbuka sekitar 25 menit setelah kode terakhir). Untuk meminta kode lain secara eksplisit (mis. login setelah mendaftar):POST /v1/orders/{id}/next-code— berlaku di semua gudang, dan hanya gudang 2 di luar jendela gratisnya yang mengenakan biaya. - Jika layanan hanya mengizinkan satu kode per kotak masuk,
otp[]berhenti pada kode itu. Respons tetap normal, tanpa error — Anda yang menentukan kapan berhenti melakukan polling. otp[].codebiasanya berupa kode pendek (angka / huruf) atau sebuah tautan. Email yang kodenya tidak bisa diambil (email sambutan, buletin, format kode yang tidak biasa…) dikirim apa adanya (HTML atau teks) diotp[].code— anggap sebagai email utuh bila lebih dari 80 karakter, berisi baris baru, atau berupa HTML, lalu cari sendiri kode atau tautannya.POST /v1/ordersmenunggu penyedia memberikan kotak masuk, sehingga biasanya memakan waktu ~0,3 detik tetapi bisa sampai 10 detik pada jam sibuk (jarang). Atur timeout klien ≈ 15 detik, jangan menyerah setelah 5 detik lalu mengulang terlalu cepat — dan saat Anda mengulang, pakai ulangIdempotency-Keyyang sama.- Pesanan yang sudah menerima kode tidak dapat dibatalkan atau di-refund — panggilan cancel mengembalikan
ORDER_NOT_CANCELLABLE. - Untuk menyewa kotak masuk @icloud.com, kirim
"domain": "icloud.com"saat membuat pesanan (hilangkan untuk Gmail; bot yang sudah ada tidak perlu perubahan). Jika layanan tidak mendukung iCloud, atau iCloud dinonaktifkan sementara, Anda mendapatDOMAIN_UNAVAILABLE— beralih ke Gmail dan penyewaan langsung berhasil. Siklus pesanan, aturan pembatalan dan refund, serta cara Anda membaca kode persis sama dengan Gmail. - Layanan stok sendiri (
provider="inventory", mis. Cointiply — lihatGET /v1/services) hanya iCloud; setiap email tiba sebagai link diotp[].code, bersamakind,label, dan teks lengkap email ditext. Sebuah pesanan menerima hinggarules.maxCodesemail dalamrules.windowMinutesmenit (hinggawindowEndsAt), tidak dapat dibatalkan (ORDER_NOT_CANCELLABLE), dan jika waktu habis tanpa email, statusnya menjadi cancelled tanpa refund. Untuk terus menerima di kotak masuk yang sama, panggilPOST /v1/orders/{id}/rerentselamacanRerentbernilai true (harga:rerentPriceVnd). Semua layanan / pesanan lain memilikiprovider="standard"— sejak 2026-10-24providerhanya punya dua nilai,"standard"|"inventory"(gudang sebenarnya ada diwarehouse); bandingkan hanya dengan"inventory". - Pesanan gudang 2 (
warehouse= 2): kode gratis selamafee.freeHoursjam; begitu jendela gratis tertutup, kode baru berhenti datang — panggilPOST /v1/orders/{id}/next-codedenganconfirmFee= true (membayarnextCodeFee.feePriceVnd) untuk membuka jendela lain, hingganextCodeFee.hardUntil. Pesanan gudang 3 untuk layanan biasa: kode gratis selama 24 jam, bisa dibatalkan sebelum kode tiba, setelah itu gunakanrerent. Pesanan gudang 4: kode baru masuk keotp[]saat Anda membaca pesanan, gratis hinggarentExpiresAt; pada sumber satu kode, setelah kode pertamanextCodeBlockReason="max_codes". - Banyak pesanan sekaligus? Lakukan polling hingga 100 per panggilan dengan
POST /v1/orders/status— lihat bagian Status banyak pesanan dalam satu panggilan.
Rate limit
Batas berlaku per API key (akun bisa punya batas sendiri; jika tidak, berlaku batas bawaan situs):
- 100 permintaan/detik — lonjakan singkat selama beberapa detik
- 3000 permintaan/menit — total lalu lintas per menit
Melebihi batas mengembalikan HTTP 429 dengan header
Retry-After: <seconds> dan body {"success":false,"error":{"code":"RATE_LIMITED",...}}. Tunggu sejumlah detik yang tertera di header sebelum mencoba lagi. Body error membawa jumlah detik yang sama di error.retryAfterSec (dengan error.retryable = true).Kode error
Setiap error memiliki bentuk yang sama:
{"success":false,"error":{"code":"...","message":"...","retryable":false,"retryAfterSec":null}}Sejak 2026-10-17 setiap error /v1 juga memiliki
Contoh respons errorerror.retryable dan error.retryAfterSec (field tambahan — klien lama tidak perlu diubah; details tidak berubah). retryable = true ⇒ panggilan yang sama bisa berhasil nanti: tunggu retryAfterSec detik (bilangan bulat ≥ 1) lalu kirim lagi — stok habis / tidak ada kotak masuk / gudang dijeda / error penyedia ⇒ 5 detik; 429 RATE_LIMITED ⇒ header Retry-After; 409 DUPLICATE_REQUEST selama permintaan pertama dengan Idempotency-Key yang sama masih berjalan ⇒ 2 detik. retryable = false (retryAfterSec = null) ⇒ memanggil lagi tidak akan membantu sampai Anda memperbaiki penyebabnya (saldo, batas pesanan menunggu, parameter, API key…). partial.reason dari POST /v1/orders mengikuti aturan yang sama. Saat mendapat jawaban 409 stok habis (OUT_OF_STOCK, WAREHOUSE_UNAVAILABLE, ALL_WAREHOUSES_EXHAUSTED), tunggu retryAfterSec detik penuh sebelum mencoba lagi — mencoba lebih cepat biasanya hanya mengembalikan jawaban yang sama — dan periksa stock / offers[].stock di GET /v1/services sebelum menyewa lagi; pantau pesanan yang sudah Anda sewa dengan webhook alih-alih polling terus-menerus.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
}
}Webhook (notifikasi push)
Daftarkan satu URL per akun dan kami akan mengirim (POST) event JSON bertanda tangan ke URL itu begitu pesanan menerima kode baru (
Siapkan dalam 3 langkahorder.code) atau dibatalkan / dikembalikan dananya (order.cancelled). Webhook bersifat opsional dan mencakup semua pesanan akun Anda (web dan API). Pengiriman bersifat upaya terbaik — tetap lakukan polling jarang sebagai cadangan (lihat di bawah).PUT /v1/webhookdengan URL Anda (dan opsionalevents) — simpandata.secret(64 karakter hex): diperlukan untuk memverifikasi setiap pengiriman. Memanggil PUT lagi tetap memakai secret yang sama.POST /v1/webhook/test— kami langsung mengirim eventpingdan mengembalikan jawaban server Anda (ok,httpStatus,error).- Endpoint Anda harus menjawab 2xx apa pun dalam 5 detik — jawab dulu, proses event-nya kemudian. Selain itu (3xx, 4xx, 5xx, waktu habis, galat koneksi) dihitung gagal dan dikirim ulang.
URL harus diawali
Format permintaanhttp:// atau https:// (dengan sertifikat valid) dan dapat dijangkau dari internet: localhost, alamat privat, link-local, multicast dan alamat yang dicadangkan (10.x, 172.16–31.x, 192.168.x, 169.254.x, fc00::/7, fe80::/10…) serta server milik OTPGmail sendiri ditolak dengan VALIDATION_ERROR. Nama host di-resolve ulang sebelum setiap pengiriman. Pengalihan (redirect) tidak diikuti — daftarkan URL akhirnya.Setiap pengiriman adalah HTTP POST ke URL Anda dengan body JSON (UTF-8) dan header berikut:
Hitung HMAC-SHA256 dengan secret Anda sebagai kunci atas
Contoh penerima (Python dan Node hanya memakai pustaka standar; perintah curl mengirim pengiriman uji bertanda tangan benar ke penerima Anda sendiri)X-OTPGmail-Timestamp + . + body permintaan mentah, dalam hex huruf kecil, lalu bandingkan dengan nilai setelah sha256= memakai perbandingan waktu-konstan. Kuncinya adalah string secret 64 karakter itu sendiri (byte UTF-8-nya — jangan didekode sebagai hex). Gunakan byte mentah: mem-parse lalu menyerialisasi ulang JSON akan mengubahnya. Tolak timestamp yang selisihnya lebih dari 5 menit dari jam Anda (perlindungan replay).curl
Python
Node.js
# Uji lokal: kirim ke penerima Anda pengiriman yang ditandatangani persis seperti milik kami
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"{
"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
}
]
}Contoh 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"
}Contoh body — ping{
"event": "ping",
"deliveryId": "3c9a1e7f-2b4d-4c6e-8f0a-5d7b9c1e3a2f",
"sentAt": 1760700000
}Pengiriman ulang, jeda, dan riwayat- Pengiriman yang gagal dikirim ulang setelah 10 dtk, 1 mnt, 5 mnt, 30 mnt dan 2 jam (5 kali); jika yang terakhir juga gagal, event dibuang (
failed). - 50 percobaan gagal berturut-turut (semua event) menjeda webhook:
GET /v1/webhookmenampilkanpausedAt, tidak ada yang dikirim dan event baru tidak dimasukkan ke antrean. - Untuk melanjutkan: panggil lagi
PUT /v1/webhook(URL yang sama boleh) atau dapatkanPOST /v1/webhook/testyang berhasil — event yang menunggu di antrean lalu dikirim. GET /v1/webhook/deliveriesmenampilkan 100 pengiriman terakhir beserta status, jumlah percobaan, dan galat terakhir; riwayat disimpan 7 hari.
Event yang sama bisa datang lebih dari sekali (misalnya saat jawaban 2xx Anda hilang) dan event bisa datang tidak berurutan (beberapa dikirim paralel). Simpan
Tetap lakukan polling sebagai cadangandeliveryId yang sudah diproses dan lewati pengulangan; untuk satu pesanan, andalkan data terbarunya (otp, status), bukan urutan kedatangan.Webhook bisa terlambat (pengiriman ulang) atau terlewat (webhook dijeda, server Anda mati). Tetap lakukan polling jarang — misalnya
GET /v1/orders/{id} setiap 30–60 detik untuk pesanan yang masih waiting_code — dan anggap objek pesanan dari API sebagai sumber kebenaran.PUT/v1/webhook
Mendaftarkan atau memperbarui webhook akun Anda. Panggilan pertama membuatnya dan mengembalikan secret baru; panggilan berikutnya mempertahankan secret, menyalakan kembali webhook, dan melanjutkannya jika sedang dijeda. Tanpa events: kedua event untuk webhook baru, tidak berubah untuk webhook yang sudah ada. URL harus alamat publik — jika tidak, 400 VALIDATION_ERROR (alasannya di error.details.problem).
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
url | string | Ya | Endpoint Anda (http:// atau https://, alamat publik, maksimal 2048 karakter) |
events | string[] | Tidak | Event yang dikirim: ["order.code", "order.cancelled"] (bawaan: keduanya) |
curl
Python
Node.js
# Simpan data.secret — diperlukan untuk memverifikasi setiap pengiriman
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"]}'{
"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
}
}Kemungkinan error: UNAUTHORIZED, VALIDATION_ERRORGET/v1/webhook
Webhook akun Anda, termasuk secret, pausedAt, dan consecutiveFailures. data bernilai null jika belum ada webhook terdaftar.
Contohcurl
Python
Node.js
curl "https://otpgmail.net/v1/webhook" \
-H "Authorization: Bearer og_xxx..."{
"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
}
}Kemungkinan error: UNAUTHORIZEDDELETE/v1/webhook
Menghapus webhook; event yang masih menunggu di antrean dibuang. Selalu 200 — data.deleted bernilai false jika tidak ada yang dihapus.
Contohcurl
Python
Node.js
curl -X DELETE "https://otpgmail.net/v1/webhook" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"deleted": true
}
}Kemungkinan error: UNAUTHORIZEDPOST/v1/webhook/test
Mengirim event ping ke URL Anda saat itu juga dan melaporkan hasilnya: ok (server Anda menjawab 2xx), httpStatus, durationMs, error. Tes yang berhasil melanjutkan webhook yang dijeda. Maksimal 10 tes per menit; 503 MAINTENANCE selama webhook dimatikan di seluruh sistem.
Contohcurl
Python
Node.js
curl -X POST "https://otpgmail.net/v1/webhook/test" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"ok": true,
"httpStatus": 200,
"durationMs": 143,
"error": null,
"deliveryId": "3c9a1e7f-2b4d-4c6e-8f0a-5d7b9c1e3a2f"
}
}Respons 200 saat server Anda tidak menjawab 2xx
Kemungkinan error: {
"success": true,
"data": {
"ok": false,
"httpStatus": 500,
"durationMs": 88,
"error": "HTTP 500",
"deliveryId": "7d2b9f4e-1c6a-4e3b-8d5f-0a9c2e7b1f4d"
}
}UNAUTHORIZED, NOT_FOUND, RATE_LIMITED, MAINTENANCEGET/v1/webhook/deliveries
Pengiriman terbaru, yang terbaru dulu (disimpan 7 hari): status = queued (percobaan berikutnya pada nextAt) | delivered | failed | paused, beserta attempts, lastHttpStatus, dan lastError. Body event tidak dikembalikan.
Parameter| Nama | Tipe | Wajib | Deskripsi |
|---|---|---|---|
limit | number | Tidak | Jumlah pengiriman, 1–100 (bawaan 100) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/webhook/deliveries?limit=20" \
-H "Authorization: Bearer og_xxx..."{
"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
}
]
}Kemungkinan error: UNAUTHORIZED