API ডকুমেন্টেশন
পাবলিক API /v1 — API key দিয়ে অথেন্টিকেট করে নিজের সফটওয়্যার থেকে ইনবক্স ভাড়া অটোমেট করুন
আপনার API key
আপনি লগইন করেননি — নিজের API key পেতে লগইন করুন।
এই key-টি Authorization: Bearer <key> হেডারে বা ?api_key=<key> কোয়েরি প্যারামিটারে পাঠান। পুরো key দেখতে চোখের আইকনে ক্লিক করুন; বদলাতে হলে নতুন key তৈরি করুন-এ ক্লিক করুন (পুরোনো key সঙ্গে সঙ্গে কাজ করা বন্ধ করবে)।ওভারভিউ
Base URL
https://otpgmail.netঅথেন্টিকেশন
প্রতিটি অনুরোধে আপনার API key যোগ করুন —
Authorization: Bearer og_xxx... হেডারে (প্রস্তাবিত) অথবা ?api_key=og_xxx... কোয়েরি স্ট্রিংয়ে।Idempotency-Key (ডুপ্লিকেট অর্ডার ঠেকায়)
প্রতিটি
POST /v1/orders অনুরোধে Idempotency-Key: <প্রতিটি অর্ডারের জন্য নতুন UUID> হেডার থাকা উচিত। অনুরোধ টাইমআউট হলে বা সংযোগ কেটে গেলে একই key দিয়ে আবার পাঠান — সার্ভার নতুন অর্ডার না বানিয়ে আগের রেসপন্সটিই ফেরত দেবে। প্রতিটি নতুন অর্ডারের জন্য নতুন UUID বানান, আর শুধু রিট্রাইয়ের সময়ই আগের UUID ব্যবহার করুন।একসাথে অনেক অর্ডার: এক কলে স্ট্যাটাস
দশ বা শত শত ইনবক্স ভাড়া নিচ্ছেন? সেগুলোর স্ট্যাটাস একসাথে পড়ুন:
POST /v1/orders/status প্রতি কলে সর্বোচ্চ 100টি অর্ডার ফেরত দেয় (আপনার রেট লিমিটে একটিই অনুরোধ হিসেবে গোনা হয়), আর GET /v1/orders আপনার অর্ডারের তালিকা দেয় — রিস্টার্টের পর ?status=waiting_code সেই সব অর্ডার ফেরত দেয় যেগুলো এখনও কোডের অপেক্ষায়। প্রতিটি ত্রুটিও জানায় আবার চেষ্টা করা উচিত কি না (error.retryable, error.retryAfterSec)। বিস্তারিত: অনেক অর্ডারের স্ট্যাটাস অংশ।ইনবক্সের ধরন: Gmail বা iCloud
ডিফল্টে আপনি একটি
@gmail.com ইনবক্স পান। @icloud.com ইনবক্স ভাড়া নিতে POST /v1/orders-এর বডিতে "domain": "icloud.com" যোগ করুন। domain না দিলে ধরা হয় gmail.com — বিদ্যমান ইন্টিগ্রেশন কোনো পরিবর্তন ছাড়াই চলবে। iCloud শুধু সেসব সার্ভিসের জন্য পাওয়া যায়, GET /v1/services-এ যাদের icloud ফিল্ড null নয়; iCloud-এর দাম Gmail-এর দামের চেয়ে আলাদা হতে পারে (icloud.price দেখুন)। প্রতিটি অর্ডারের রেসপন্সে একটি domain ফিল্ড থাকে, যা জানায় অর্ডারটি কোন ধরনের ইনবক্স ব্যবহার করছে। কিছু সার্ভিস @gmx.com ইনবক্সও দেয় (এখন শুধু Pokemon, কোড pkm, গুদাম 2): "domain": "gmx.com" পাঠান — কোনো সার্ভিস এটি দেয় যদি GET /v1/services-এ তার offers[]-এ domain = "gmx.com" সহ একটি এন্ট্রি থাকে; অন্য যেকোনো সার্ভিসে ⇒ DOMAIN_UNAVAILABLE। ব্যাচের জন্য gmx.com পাওয়া যায় না।গুদাম: একটি সার্ভিস, ইনবক্সের একাধিক উৎস
একটি সার্ভিস একাধিক গুদাম থেকে বিক্রি হতে পারে; প্রতিটির নিজস্ব দাম, স্টক ও কোডের নিয়ম আছে (
GET /v1/services-এর offers[] অ্যারে)। অর্ডার তৈরির সময় "warehouse": 2 পাঠিয়ে একটি বেছে নিন; "warehouse": 99 ⇒ সিস্টেম স্টক আছে এমন সবচেয়ে কম দামের গুদামটি বেছে নেয়, শেষ হয়ে গেলে পরেরটিতে চলে যায় (Facebook ও Instagram-এর মতো যেসব সার্ভিসের স্টক প্রায়ই শেষ হয়, তাদের জন্য আদর্শ); 0 ⇒ স্টক আছে এমন সবচেয়ে কম দামের গুদাম। বিদ্যমান ক্লায়েন্টদের কিছু বদলাতে হবে না: warehouse না দিলে ⇒ আগের মতোই গুদাম 1, আর price, stock ও icloud গুদাম 1-এর তথ্য দেয় (যেসব সার্ভিসে গুদাম 1 নেই ⇒ স্টক আছে এমন সবচেয়ে কম দামের গুদাম)। বিস্তারিত: গুদাম অংশ দেখুন।বাল্ক ভাড়া (ব্যাচ)
ব্যাচ ওয়েবে কিনতে হয় (বাল্ক ভাড়া পেজ); API দিয়ে ব্যাচের তালিকা দেখা ও কোড পড়া যায়। Gmail (গুদাম 1): প্রতিটি ইনবক্সের একটি
proxyUrl আছে — API key ছাড়াই কাজ করে এমন কোড লিংক। iCloud (গুদাম 3): ব্যক্তিগত ইনবক্স; GET /v1/batches/{id}/mails/{mailId}/codes দিয়ে কোড পড়ুন বা ওয়েবে readUrl খুলুন। বিস্তারিত: বাল্ক ভাড়া অংশ।স্বল্পস্থায়ী Gmail অ্যাকাউন্ট কিনুন (mail shop)
ইনবক্স ভাড়ার পাশাপাশি আপনি 2টি গুদাম থেকে পুরো Gmail অ্যাকাউন্ট কিনে নিতে পারেন — গুদাম 1: ইমেইল, পাসওয়ার্ড, কুকিজ + ইনবক্স লিংক (JSON হিসেবেও); গুদাম 2: ইমেইল, পাসওয়ার্ড + কুইক লগইন লিংক; গুদাম 99 সবচেয়ে সস্তাটি আগে নেয় (ওয়েবের স্বল্পস্থায়ী Gmail কিনুন পেজ)। ওই পেজে যা কিছু করা যায়, API দিয়েও তার সবই করা যায়: গুদাম, দাম ও স্টক দেখা, কেনা (
Idempotency-Key বাধ্যতামূলক), ডেলিভারির অগ্রগতি দেখা, অ্যাকাউন্ট ডাউনলোড করা এবং সেগুলোর ইমেইল পড়া। বিস্তারিত: স্বল্পস্থায়ী Gmail কিনুন অংশ।আপনার গ্রাহকরা নিজেরাই কোড দেখুক (/otp পেজ)
ভাড়া নেওয়া ইনবক্স (ওয়েবে বা API দিয়ে) আবার বিক্রি করেন? অ্যাকাউন্ট পেজে একটি শেয়ার কোড চালু করুন, তারপর গ্রাহককে
https://otpgmail.net/bn/otp#k7m2q9:[email protected] লিংকটি পাঠান (k7m2q9 = আপনার শেয়ার কোড) — তারা লগইন না করেই ওই ইমেলের সর্বশেষ কোড দেখেন, দাম বা আপনার ব্যালান্স কখনো দেখেন না; পেজটি শুধু বিনামূল্যের কাজ করে (কখনো আপনার টাকা কাটে না)।টুল / বটের জন্য JSON লিংক (আপনার বা আপনার গ্রাহকের): পাথ-আকারের লিংক
https://otpgmail.net/bn/otp/k7m2q9:[email protected] নিন (এটি # লিংকের মতোই /otp পেজ খোলে) এবং শেষে ?json যোগ করুন ⇒ সার্ভার পেজে যা দেখায় ঠিক সেই ডেটাই JSON-এ ফেরত দেয়: cards[] — ওই ইমেইলে ভাড়া নেওয়া প্রতিটি সার্ভিসের জন্য একটি কার্ড (email, serviceName, state, items[] = নতুনটি আগে রেখে সর্বোচ্চ ৫টি সর্বশেষ কোড / লিংক) — আর serverTime। state: waiting প্রথম কোডের অপেক্ষা · listening নতুন কোডের অপেক্ষা · received কোড এসেছে · needs_owner বিক্রেতাকে মেয়াদ বাড়াতে হবে · expired মেয়াদ শেষ · banned ইনবক্স লক। একাধিক ইমেইল: কমা দিয়ে আলাদা করুন (সর্বোচ্চ ৫০)। API কি বা কুকি লাগে না, CORS খোলা; শুধু পড়ার জন্য (“নতুন OTP লোড করুন” / “এই ইমেইল আবার ভাড়া নিন” /otp পেজেই থাকে)। অনুমান ঠেকানোর সীমা পেজের মতোই: ভুল কোড / ইমেইলে দেখানোর মতো অর্ডার নেই ⇒ 404 NOT_FOUND; বারবার ভুল বা একটি ইমেইলের জন্য মিনিটে ৩০ বারের বেশি পড়া ⇒ 429 RATE_LIMITED সঙ্গে retryAfter (সেকেন্ড)। /otp#… লিংকে ?json যোগ করলে JSON আসে না: ব্রাউজার #-এর পরের অংশ কখনো সার্ভারে পাঠায় না।curl "https://otpgmail.net/bn/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
}
}GitHub-এ সম্পূর্ণ নমুনা কোড
চালানোর জন্য প্রস্তুত ক্লায়েন্ট Python, Node.js, PHP, C# (.NET) ও curl-এ — ইনবক্স ভাড়া, কোডের অপেক্ষা, রিফান্ডের জন্য স্বয়ংক্রিয় বাতিল, আর স্টক শেষ হলে iCloud ও Gmail-এর মধ্যে বদল: github.com/letoan0902/otpgmail-email-otp-api (ইংরেজি) · thue-otp-gmail (ভিয়েতনামী)। Python-এর নোট: খালি
urllib-এর বদলে requests ব্যবহার করুন (এর ডিফল্ট User-Agent CDN থেকে 403 পায়)।ওয়েবহুক
প্রতিটি অর্ডার বারবার জিজ্ঞেস করার বদলে একটি URL রেজিস্টার করুন এবং কোনো অর্ডারে নতুন কোড এলে বা সেটি বাতিল / রিফান্ড হলে সঙ্গে সঙ্গে স্বাক্ষরিত POST পান — দেখুন ওয়েবহুক।
এন্ডপয়েন্ট
GET/v1/balance
অনুরোধে ব্যবহৃত API key-এর অ্যাকাউন্টের ব্যালেন্স (VND-এ) ফেরত দেয়।
উদাহরণcurl
Python
Node.js
curl "https://otpgmail.net/v1/balance" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"balance": 25000
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZEDGET/v1/services
চালু সার্ভিসের তালিকা দেয় — কোড, নাম, Gmail-এর দাম (VND-এ) ও বর্তমান Gmail স্টক; @icloud.com ইনবক্সের দাম ও স্টকসহ একটি icloud ব্লক; এবং একটি offers[] অ্যারে: সার্ভিসটি যেসব গুদাম থেকে বিক্রি হয় সেগুলোর প্রতিটির জন্য গুদাম × ইনবক্সের ধরন অনুযায়ী একটি এন্ট্রি (দাম, স্টক, কোডের সংখ্যা, নিয়ম)। ডেটা ক্যাশ করা: স্টক প্রায় প্রতি 30–60 সেকেন্ডে রিফ্রেশ হয়, দাম আরও কম ঘন ঘন।
icloud = null মানে সার্ভিসটি এখনও iCloud সমর্থন করে না (সরবরাহকারী দেয় না, অথবা সাময়িকভাবে বন্ধ) — শুধু Gmail ভাড়া নেওয়া যায়। যেসব সার্ভিসে icloud ব্লক আছে, সেখানে অর্ডার তৈরির সময় "domain": "icloud.com" পাঠালে icloud.price-এ একটি @icloud.com ইনবক্স পাবেন। price / stock / icloud.price গুদাম 1-এর তথ্য দেয় — warehouse ছাড়া অর্ডার যেখানে যায় (যেসব সার্ভিসে গুদাম 1 নেই ⇒ স্টক আছে এমন সবচেয়ে কম দামের গুদাম); প্রতিটি গুদামের দাম ও স্টক offers[]-এ আছে (গুদাম অংশ দেখুন)। stock হলো উৎস যে সংখ্যা প্রকাশ করে (গুদাম 1: প্রায় প্রতি 30 সেকেন্ডে হালনাগাদ হয়), এক কলে ততগুলো ভাড়া পাওয়ার প্রতিশ্রুতি নয়: ইনবক্স একটি একটি করে বরাদ্দ হয় এবং উৎস মাঝপথে ফুরিয়ে যেতে পারে — তখন POST /v1/orders কম অর্ডার আর একটি partial ফিল্ড ফেরত দেয়। গুদাম 2 ও 4-ও নিজ নিজ উৎসের সংখ্যা দেখায়।
উদাহরণcurl
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
}
]
}সম্ভাব্য ত্রুটি: UNAUTHORIZEDPOST/v1/orders
ভেরিফিকেশন কোড পাওয়ার ইনবক্সের জন্য একটি অর্ডার তৈরি করে — ডিফল্টে Gmail, অথবা domain: "icloud.com" পাঠালে iCloud। সরবরাহকারী ইনবক্স বরাদ্দ করামাত্র আপনার ব্যালেন্স থেকে দাম কাটা হয়। অর্ডারের একটি তালিকা ফেরত দেয় (একটি, অথবা quantity > 1 হলে একাধিক)।
আংশিক ডেলিভারি: quantity হলো সর্বোচ্চ সীমা
- ইনবক্স একটি একটি করে বরাদ্দ হয়। সরবরাহকারীর ইনবক্স মাঝপথে ফুরিয়ে গেলে (যেমন সেই মুহূর্তে গুদাম 1-এ ওই সার্ভিসের জন্য আর ইনবক্স নেই), কল থেমে যায় এবং এ পর্যন্ত তৈরি অর্ডারগুলোসহ HTTP 200 ফেরত দেয় —
dataquantity-এর চেয়ে ছোট হতে পারে, আর টাকা কাটা হয় শুধুdata-র অর্ডারগুলোর জন্য।GET /v1/services-এরstockএক কলে কতগুলো মিলবে তার নিশ্চয়তা দেয় না। - তখন উত্তরে
data-র পাশে শীর্ষ স্তরের একটিpartialফিল্ডও থাকে (সব ডেলিভারি হলে থাকে না — যে বট শুধুdataপড়ে, সেগুলো কোনো পরিবর্তন ছাড়াই চলে):requested,delivered,missingএবংreason={code, message, retryable, retryAfterSec}, যেখানেcodeহলো সেই এরর যা কলটি থামিয়েছে (NO_MAILS_AVAILABLE,OUT_OF_STOCK,ALL_WAREHOUSES_EXHAUSTED,INSUFFICIENT_BALANCE,WAITING_LIMIT_REACHED…)। - ইনবক্স ফুরিয়ে যাওয়া সাধারণত সাময়িক — গুদাম 1-এ কয়েক সেকেন্ড পরে সরবরাহকারীর কাছে প্রায়ই আবার ইনবক্স চলে আসে।
reason.retryable= true হলেreason.retryAfterSecসেকেন্ড (3–5 সেকেন্ড) অপেক্ষা করুন, তারপর বাকি ইনবক্সগুলো (missing) নতুনIdempotency-Keyদিয়ে আবার চান (পুরোনো key শুধু এই একই উত্তর আবার ফেরত দেয়); সবগুলো না পাওয়া পর্যন্ত চালিয়ে যান। - আংশিক ডেলিভারির ঠিক পরের কলে 409
WAREHOUSE_UNAVAILABLE/OUT_OF_STOCKবা 503NO_MAILS_AVAILABLEআসতে পারে — এটা স্বাভাবিক: কলের মাঝে 3–5 সেকেন্ড বিরতি দিতে থাকুন এবং কেবল পরপর অনেকগুলো কল ব্যর্থ হলে থামুন (যেমন 10 বার চেষ্টা, প্রায় এক মিনিট); তারপর পরে আবার চেষ্টা করুন, অথবা"warehouse": 99পাঠান যাতে সিস্টেম অন্য গুদামে চলে যায়।retryable= false (INSUFFICIENT_BALANCE,WAITING_LIMIT_REACHED…) ⇒ আগে কারণটি ঠিক করুন (ব্যালান্স রিচার্জ করুন, অথবা খোলা অর্ডারগুলো শেষ হওয়া পর্যন্ত অপেক্ষা করুন)। - ত্রুটির উত্তরেও এই দুটি ফিল্ড
error-এর ভেতরে থাকে:error.retryableওerror.retryAfterSec(যেমন 409OUT_OF_STOCK⇒true, 5; 402INSUFFICIENT_BALANCE⇒false,null) — এরর কোড দেখুন।
Idempotency-Key হেডার (একটি UUID) বাধ্যতামূলক। প্রতিটি নতুন ভাড়ার জন্য নতুন UUID বানান; টাইমআউটের পর রিট্রাই করার সময় ডুপ্লিকেট অর্ডার এড়াতে একই key আবার ব্যবহার করুন। এই কল সরবরাহকারীর ইনবক্স বরাদ্দের জন্য অপেক্ষা করে, তাই সাধারণত প্রায় 0.3 সেকেন্ড লাগে, তবে ব্যস্ত সময়ে (বিরল) 10 সেকেন্ড পর্যন্ত লাগতে পারে — 5 সেকেন্ড পরে ছেড়ে দিয়ে আগেভাগে রিট্রাই করার বদলে আপনার ক্লায়েন্টের টাইমআউট প্রায় 15 সেকেন্ড রাখুন।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
service | string | হ্যাঁ | সার্ভিসের কোড (GET /v1/services থেকে, যেমন "ig", "tg", "fb") |
quantity | number | না | সর্বোচ্চ কতগুলো ইনবক্স ভাড়া নেবেন (ডিফল্ট 1; প্রতি অনুরোধে সর্বোচ্চ 200) — গুদাম মাঝপথে ফুরিয়ে গেলে কম পাবেন: data[] গুনুন এবং partial পড়ুন |
domain | string | না | ইনবক্সের ধরন: "gmail.com" (ডিফল্ট), "icloud.com" বা "gmx.com"। না দিলে gmail.com — বিদ্যমান ইন্টিগ্রেশনে কোনো পরিবর্তন লাগবে না। iCloud শুধু সেসব সার্ভিসে ভাড়া নেওয়া যায় যাদের icloud ফিল্ড GET /v1/services-এ null নয়; "gmx.com" শুধু offers[].domain = "gmx.com" আছে এমন সার্ভিসে (বর্তমানে শুধু Pokemon, কোড pkm) — নইলে কল DOMAIN_UNAVAILABLE দিয়ে ব্যর্থ হয়। অন্য যেকোনো মান ⇒ VALIDATION_ERROR। |
warehouse | number | না | যে গুদাম থেকে ভাড়া নেবেন। না দিলে = গুদাম 1, ঠিক বহু-গুদাম চালুর আগের মতো (যেসব সার্ভিসে গুদাম 1 নেই সেগুলো সবচেয়ে কম দামেরটিতে যায়) — বিদ্যমান ক্লায়েন্টদের কিছু বদলাতে হবে না। 0 = স্টক আছে এমন সবচেয়ে কম দামের গুদাম (শুধু একটি গুদাম চেষ্টা করা হয়)। 1–4 = ঠিক সেই গুদাম (GET /v1/services-এর offers[].warehouse থেকে); সেটি এই সার্ভিস/ইনবক্সের ধরন বিক্রি না করলে, বন্ধ থাকলে বা স্টক শেষ হলে ⇒ WAREHOUSE_UNAVAILABLE। 99 = স্বয়ংক্রিয়: স্টক আছে এমন গুদামগুলো সবচেয়ে কম থেকে সবচেয়ে বেশি দামের ক্রমে চেষ্টা করা হয়, একটি স্টক শেষ বা ব্যর্থ হলে পরেরটিতে যায়; একটিও না থাকলে ⇒ ALL_WAREHOUSES_EXHAUSTED — যেসব সার্ভিসের স্টক প্রায়ই শেষ হয় (Facebook, Instagram…) তাদের জন্য প্রস্তাবিত। গুদাম অংশ দেখুন। |
curl
Python
Node.js
# প্রতিটি নতুন অর্ডারের জন্য এলোমেলোভাবে তৈরি নতুন একটি Idempotency-Key ব্যবহার করুন
# টাইমআউটের পর রিট্রাই করার সময় একই key আবার ব্যবহার করুন (ডুপ্লিকেট অর্ডার ঠেকায়)
# domain: "gmail.com" (ডিফল্ট, ঐচ্ছিক) | "icloud.com" (@icloud.com ইনবক্স ভাড়া) | "gmx.com" (শুধু যেসব সার্ভিসে আছে, যেমন Pokemon)
# warehouse: 0 | 1–4 | 99 (ঐচ্ছিক — আগের মতো গুদাম 1 রাখতে এটি বাদ দিন; 0 = স্টক আছে এমন সবচেয়ে কম দামের গুদাম; 99 = স্বয়ংক্রিয়, একটি শেষ হলে পরেরটিতে যায়; GET /v1/services-এর offers[] দেখুন)
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
}
]
}নমুনা উত্তর — আংশিক ডেলিভারি (quantity 5, 2টি ইনবক্স ডেলিভারি হয়েছে)
সম্ভাব্য ত্রুটি: {
"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}
ID দিয়ে একটি অর্ডার ফেরত দেয়, এ পর্যন্ত পাওয়া কোডসহ (otp[])। অর্ডার waiting_code অবস্থায় থাকাকালে কোড ধরতে প্রতি 3–5 সেকেন্ডে এই এন্ডপয়েন্ট পোল করুন।
otp[] অ্যারে আসার ক্রমে সাজানো: সবচেয়ে পুরোনোটি আগে, সবচেয়ে নতুনটি শেষে — সর্বশেষ কোড পড়ুন otp[otp.length - 1] দিয়ে। প্রতিটি কলে সিস্টেম এখনও খোলা অর্ডারের জন্য প্রতিটি গুদামের নিজস্ব গতিতে (কয়েক সেকেন্ড পরপর) সরবরাহকারীর কাছে খোঁজ নেয় — এর চেয়ে ঘন ঘন কল করলে শুধু সংরক্ষিত ডেটাই ফেরত আসে। প্রথম কোডের পর পোল করতে থাকুন: পরের কোডগুলো (সার্ভিস একাধিক পাঠালে) অর্ডারের মেয়াদের মধ্যে বাড়তি খরচ ছাড়াই otp[]-এর শেষে যোগ হয় — সীমা ও সময় ভাড়ার আগেই দেখানো হয় (offers[]-এর maxCodes ও fee); সিস্টেম নতুন কোডের অপেক্ষা করছে কি না নিশ্চিত হতে POST /v1/orders/{id}/next-code কল করুন (যেকোনো গুদামে; ফি নেয় শুধু গুদাম 2, তার বিনামূল্যের সময়ের বাইরে)। সার্ভিস প্রতি ইনবক্সে একটিই কোড দিলে otp[] ওই কোডেই থেমে যায় — রেসপন্স স্বাভাবিক থাকে, কোনো ত্রুটি আসে না। warehouse ফিল্ড জানায় ইনবক্সটি কোন গুদাম দিয়েছে (1–4); nextCodeFee হলো আরও কোডের ফি (গুদাম 2 ও 3-এর অর্ডারে; গুদাম 1 ও 4-এ null) — গুদাম অংশ দেখুন। 2026-10-02 থেকে অর্ডারে nextCodeBlocked (true = ইনবক্সে আর কোনো কোড আসবে না) এবং nextCodeBlockReason-ও থাকে — কারণ: "email_banned" (সার্ভিস ইমেইলটি লক করেছে — নতুন একটি ভাড়া নিন), "activation_gone" (সরবরাহকারী ইনবক্স বন্ধ করেছে), "hard_expired" (কোড পাওয়ার সময় শেষ), "max_codes" (কোডের সর্বোচ্চ সংখ্যা এসে গেছে), null (ব্লক হয়নি / কারণ রেকর্ড করা নেই এমন পুরোনো অর্ডার)। এগুলো নতুন যোগ করা ফিল্ড — বিদ্যমান ক্লায়েন্টদের কিছু বদলাতে হবে না। অনেক অর্ডার পোল করছেন? প্রতিটি অর্ডারের জন্য আলাদা GET-এর বদলে প্রতি কলে সর্বোচ্চ 100টি ID POST /v1/orders/status-এ পাঠান — একই অর্ডার অবজেক্ট, রেট লিমিটে একটিই অনুরোধ।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | অর্ডার আইডি (অর্ডার তৈরির সময় পাওয়া orderId) |
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
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, NOT_FOUNDPOST/v1/orders/{id}/cancel
অর্ডার বাতিল করে দামের 100% ফেরত দেয়। শুধু তখনই করা যায় যখন অর্ডার waiting_code অবস্থায় আছে এবং কোনো কোড আসেনি।
যে অর্ডার একটি কোডও (একটিই হলেও) পেয়েছে তা বাতিল বা রিফান্ড করা যায় না — ওয়েবসাইট ও API দুটোর ক্ষেত্রেই এটি প্রযোজ্য। rules.cancellable = false থাকা অর্ডার (নিজস্ব ইনভেন্টরির সার্ভিস, যেমন Cointiply) বাতিল করা যায় না: ইনবক্স বরাদ্দ হয়ে গেলে বাতিল বা রিফান্ড নেই — বাতিলের কল ORDER_NOT_CANCELLABLE ফেরত দেয়। সাধারণ সার্ভিসের গুদাম-3 অর্ডার (rules.cancellable = true) কোড আসার আগে অন্য যেকোনো অর্ডারের মতোই বাতিল করা যায়।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | যে অর্ডার বাতিল করবেন তার ID |
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
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, NOT_FOUND, ORDER_NOT_CANCELLABLE, PROVIDER_ERRORPOST/v1/orders/{id}/rerent
নিজস্ব ইনভেন্টরির সার্ভিসের (provider = "inventory", যেমন Cointiply) অর্ডারের জন্য একই ইনবক্স আবার ভাড়া নেয়: ওই ইনবক্সে নতুন গ্রহণ-সময়সহ একটি নতুন অর্ডার তৈরি করে, দাম পুরোনো অর্ডারের rerentPriceVnd।
শুধু তখনই কাজ করে যখন পুরোনো অর্ডারের canRerent = true (ওই ইনবক্সের সর্বশেষ অর্ডার, যার আগের সময় শেষ হয়েছে বা সব ইমেইল এসে গেছে)। সফল হওয়ামাত্র দাম কাটা হয় — Idempotency-Key পাঠান এবং রিট্রাইয়ের সময় সেটি আবার ব্যবহার করুন, যাতে দুবার কখনো না কাটে। রেসপন্স হলো { order, message }; order-এর গঠন GET /v1/orders/{id}-এর মতোই। গুদাম 2 (2026-10-09 থেকে): যে অর্ডার বাতিল হয়েছে, সময় শেষ হয়েছে বা মেয়াদ পেরিয়েছে (ইনবক্স বন্ধ) —
প্যারামিটারcanRerent = true হলে কল করুন (ওই ইমেইলের সর্বশেষ অর্ডার, এবং ইমেইলে আর কোনো চালু অর্ডার নেই)। সিস্টেম ঠিক সেই ইমেইলটিকে একটি নতুন অর্ডার হিসেবে আবার খোলে (status = waiting_code), দাম rerentPriceVnd (এখন আপনার দামের 100%), এবং যেকোনো গুদাম 2 অর্ডারের মতো ইমেইলের জন্য 20 মিনিট অপেক্ষা করে — ইমেইল না এলে অর্ডার বাতিল হয়ে 100% ফেরত। সার্ভিস ইমেইল লক করলে ⇒ RERENT_EMAIL_BANNED; সরবরাহকারী আর খুলতে না পারলে ⇒ RERENT_MAIL_GONE; গুদাম 2 সাময়িকভাবে বন্ধ ⇒ PROVIDER_ERROR — তিনটির কোনোটিতেই টাকা কাটা হয় না।| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | ইনবক্সের আগের অর্ডারের ID (যেটির canRerent = true) |
curl
Python
Node.js
# টাইমআউটের পর রিট্রাই করার সময় একই key আবার ব্যবহার করুন (ডুপ্লিকেট অর্ডার ঠেকায়)
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"
}
}সম্ভাব্য ত্রুটি: 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
একই ইনবক্সে পরের কোড চায় — যেকোনো গুদামের (warehouse 1–4) সেই অর্ডারগুলোর জন্য কাজ করে যেগুলোতে প্রথম কোড এসে গেছে (status = completed)। বিনামূল্যে, শুধু গুদাম-2-এর যে অর্ডারের বিনামূল্যের সময় শেষ হয়েছে সেগুলো বাদে (nextCodeFee.feePriceVnd > 0): তখন ফি দিতে ও আরেকটি কোড-সময় খুলতে confirmFee: true পাঠান (সর্বোচ্চ nextCodeFee.hardUntil পর্যন্ত)। আপনার বটকে গুদাম জানতে হবে না: GET /v1/orders/{id} পোল করতে থাকুন — নতুন কোড otp[]-এর শেষে যোগ হয় — আর সিস্টেম যেন নতুন কোডের অপেক্ষা করে এমন চাইলে এই কল করুন।
প্রতিটি গুদামে এই কল কী করে
- গুদাম 1: অর্ডারের মেয়াদের মধ্যে (
rentExpiresAt) প্রতিটি কোডের পর সিস্টেম নিজেই পরের কোডের অপেক্ষা করে — কলটি 200 ফেরত দেয় এবং কিছুই বদলায় না; ইনবক্সে আর কোড আসা সম্ভব না হলে (কোডের সীমা পূর্ণ, ইনবক্স বন্ধ, মেয়াদ শেষ) ⇒nextCodeBlockReasonসহ 409। - গুদাম 2: সরবরাহকারীর কাছে ইনবক্স আবার খোলে (
reordered= true) এবং ইমেলের জন্য 20 মিনিট অপেক্ষা করে; বিনামূল্যের সময়ের মধ্যে কোনো ফি নেই (nextCodeFee.feePriceVnd= 0), তার বাইরেconfirmFee= true পাঠাতে হবে (না পাঠালে ⇒ 409NEXT_CODE_FEE_CONFIRM, কিছুই কাটা হয় না)। নতুন কোড এলেই কেবল ফি রাখা হয় — 20 মিনিটে কোড না এলে ⇒ স্বয়ংক্রিয় ফেরত। প্রতিটি কোডের পর সিস্টেম আরও 25 মিনিট পরের কোডের অপেক্ষা করে (listening= true,listenUntil)। - গুদাম 3 (সাধারণ সার্ভিস): অল্প সময়ের অপেক্ষা আবার চালু করে, বিনামূল্যের সময়ের মধ্যে কোনো ফি নেই (
nextCodeFee.freeUntil); তারপর ⇒ 409 —POST /v1/orders/{id}/rerentব্যবহার করুন। Cointiply-এর মতো নিজস্ব-ইনভেন্টরি সার্ভিসে (rules.cancellable= false) এই কল নেই (409): সময়ের মধ্যে প্রতিটি ইমেল নিজে থেকেই দেখা যায়। - গুদাম 4: সরবরাহকারীর কাছে অর্ডারটি আবার পড়ে এবং নতুন কোডের অপেক্ষা করে,
rentExpiresAtপর্যন্ত বিনামূল্যে।
200 = সিস্টেম এই অর্ডারের জন্য নতুন কোডের অপেক্ষা করছে (listening = true, listenUntil = অপেক্ষা কখন শেষ হবে; ইনবক্স সবে আবার খোলা হলে reordered = true; ফি কাটা হলে chargedVnd), অথবা কল করার মুহূর্তেই নতুন কোড এসেছে (তা order.otp-এ আছে)। 409 = নতুন অপেক্ষা খোলা যায়নি — error.code পড়ুন; ইনবক্সে কেন আর কোড আসবে না তা order.nextCodeBlockReason-এ (GET /v1/orders/{id}) আছে। যে অর্ডার এখনও প্রথম কোডের অপেক্ষায় (waiting_code) ⇒ 409, এখনই কল করার দরকার নেই। ফি-যুক্ত কলে Idempotency-Key পাঠান (আবার চেষ্টা করলে একই key দিন, যাতে দুবার টাকা না কাটে)। সরবরাহকারীর সাথে যোগাযোগ না হলে ⇒ 502 PROVIDER_ERROR, পরে আবার চেষ্টা করুন। এই কলের 409 ত্রুটি কোড: NEXT_CODE_FEE_CONFIRM (গুদাম 2-এ ফি লাগবে — confirmFee: true দিয়ে আবার কল করুন), EMAIL_BANNED (সার্ভিস ইমেলটি লক করেছে — নতুন একটি ভাড়া নিন; error.details.blockReason = "email_banned"), ORDER_EXPIRED (মেয়াদ শেষ, ইনবক্স বন্ধ, কোডের সীমা পূর্ণ, অথবা সার্ভিসে "পরের কোড" নেই — কারণ order.nextCodeBlockReason-এ), ORDER_NOT_CANCELLABLE (অর্ডারে এখনও প্রথম কোড আসেনি)। যে বট এই কলের যেকোনো 409-কে “আর কোড নেই” ধরে, সেগুলো কোনো পরিবর্তন ছাড়াই চলবে। refresh: true (গুদাম 2): প্রথমে নতুন ইমেল দেখা হয় (এইমাত্র আসা ইমেল সঙ্গে সঙ্গে দেওয়া হয়, আবার খোলা হয় না), তারপর সরবরাহকারীর কাছে ইনবক্স আবার খোলা হয় — বিনামূল্যের সময়ের মধ্যে অপেক্ষা আবার 20 মিনিটের জন্য শুরু হয়; সময়ের বাইরে অপেক্ষার শেষ সময় (listenUntil) একই থাকে এবং কোনো টাকা কাটা হয় না। উত্তরে refreshed (true = এইমাত্র আবার খোলা হয়েছে) ও refreshAvailableAt (unix সেকেন্ড; এর আগে আবার রিফ্রেশ করলে শুধু ইমেল দেখা হয়; null = এখনই করা যাবে) যোগ হয়। অন্য গুদাম: কোনো প্রভাব নেই (refreshed = false)। refresh না পাঠালে কল ঠিক আগের মতোই কাজ করে।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | অর্ডার ID (orderId) — যেকোনো গুদাম |
confirmFee | boolean | না | true = আরেকটি কোড-সময় খুলতে nextCodeFee.feePriceVnd দিতে রাজি। feePriceVnd = 0 থাকলে লাগে না। |
refresh | boolean | না | true = গুদাম 2, অর্ডার নতুন কোডের অপেক্ষায় (listening = true) কিন্তু কোড আসছে না: সরবরাহকারীর কাছে এখনই ইনবক্সটি আবার খুলুন (অপেক্ষা রিফ্রেশ) — পুরোনো অপেক্ষাটি সরবরাহকারীর দিকে ইমেল নেওয়া বন্ধ করে দিয়ে থাকতে পারে। বিনামূল্যে; প্রতি অর্ডারে 5 সেকেন্ডে সর্বোচ্চ একবার। অর্ডার অপেক্ষায় না থাকলে ⇒ সাধারণ কলের মতোই। |
curl
Python
Node.js
# বিনামূল্যে (nextCodeFee null অথবা feePriceVnd = 0 — বিনামূল্যের সময় শেষ হওয়া গুদাম 2 ছাড়া সব গুদাম): confirmFee লাগবে না
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/next-code" \
-H "Authorization: Bearer og_xxx..."
# বিনামূল্যের সময় শেষ হওয়া গুদাম 2: nextCodeFee.feePriceVnd দিতে confirmFee = true পাঠান
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
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, NEXT_CODE_FEE_CONFIRM, INSUFFICIENT_BALANCE, EMAIL_BANNED, ORDER_EXPIRED, ORDER_NOT_CANCELLABLE, PROVIDER_ERROR, DUPLICATE_REQUEST, MAINTENANCEএক কলে অনেক অর্ডারের স্ট্যাটাস
যেসব রিসেলার ও বট অনেক ইনবক্স ভাড়া নেয়, তাদের প্রতিটি অর্ডারের জন্য আলাদা
GET /v1/orders/{id} পাঠানো উচিত নয়: POST /v1/orders/status একই অর্ডার অবজেক্টসহ প্রতি কলে সর্বোচ্চ 100টি অর্ডার পড়ে, আর GET /v1/orders সংরক্ষিত ডেটা থেকে আপনার অর্ডারের তালিকা দেয়।POST /v1/orders/statusদিয়ে প্রতি 3–5 সেকেন্ডে পোল করুন, প্রতি কলে সর্বোচ্চ 100টি অর্ডার ID — প্রতিটি কল রেট লিমিটে একটিই অনুরোধ।- প্রতিটি কলে সিস্টেম তালিকার সর্বোচ্চ 20টি খোলা অর্ডারের জন্য সরবরাহকারীর কাছে খোঁজ নেয় (আগে যেগুলো প্রথম কোডের অপেক্ষায়, তারপর যেগুলোর সবচেয়ে বেশি সময় খোঁজ নেওয়া হয়নি) আর বাকিগুলো সংরক্ষিত ডেটা থেকে ফেরত দেয়; সেগুলোর খোঁজ পরের কলগুলোতে নেওয়া হয়। একটি কল প্রায় 3 সেকেন্ডের মধ্যে উত্তর দেয়।
- অর্ডার চূড়ান্ত হলে তালিকা থেকে সরিয়ে দিন: cancelled (টাকা ফেরত হয়েছে), অথবা আর কোড না লাগলে completed।
- যেসব ID নেই বা অন্য অ্যাকাউন্টের, সেগুলো
missing-এ ফেরত আসে (404 নয়) — সেগুলোও সরিয়ে দিন। - রিস্টার্টের পর
GET /v1/orders?status=waiting_codeদিয়ে সব খোলা অর্ডার আবার নিন (nextCursorধরে এগোন), তারপরPOST /v1/orders/statusদিয়ে পোল চালিয়ে যান।
POST/v1/orders/status
এক কলে আপনার সর্বোচ্চ 100টি অর্ডার ফেরত দেয়। প্রতিটি উপাদান হুবহু সেটিই, যা ওই অর্ডারের জন্য GET /v1/orders/{id} ফেরত দেয় (status, পাওয়া সব কোডসহ otp[], warehouse…)। একসাথে অনেক খোলা অর্ডার পোল করতে এটি ব্যবহার করুন।
data, ids-এর ক্রম বজায় রাখে; পুনরাবৃত্ত ID উপেক্ষা করা হয়। যেসব ID নেই বা অন্য অ্যাকাউন্টের, সেগুলো missing-এ থাকে (404 নয়)। খোলা অর্ডারের জন্য সিস্টেম GET /v1/orders/{id}-এর মতোই সরবরাহকারীর কাছে খোঁজ নেয়, তবে প্রতি কলে সর্বোচ্চ 20টি অর্ডারের জন্য — আগে যেগুলো প্রথম কোডের অপেক্ষায়, তারপর যেগুলোর সবচেয়ে বেশি সময় খোঁজ নেওয়া হয়নি — বাকিগুলো সংরক্ষিত ডেটা থেকে ফেরত দেয় (সেগুলোর খোঁজ পরের কলগুলোতে নেওয়া হয়); একটি কল প্রায় 3 সেকেন্ডের মধ্যে উত্তর দেয়। যত ID-ই থাকুক, একটি কল রেট লিমিটে একটিই অনুরোধ। 100-এর বেশি ID, খালি তালিকা বা স্ট্রিং নয় এমন ID ⇒ 400 VALIDATION_ERROR।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
ids | string[] | হ্যাঁ | অর্ডার ID (orderId) — প্রতি কলে 1 থেকে 100টি |
curl
Python
Node.js
# যেসব অর্ডারের অপেক্ষায় আছেন সেগুলোর ID (POST /v1/orders থেকে পাওয়া orderId) — প্রতি কলে সর্বোচ্চ 100টি
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"
]
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, VALIDATION_ERROR, RATE_LIMITEDGET/v1/orders
আপনার অর্ডারের তালিকা, নতুনটি আগে, শুধু সংরক্ষিত ডেটা থেকে — নতুন কোডের জন্য সরবরাহকারীর কাছে জানতে চায় না (সেটি করে POST /v1/orders/status)। স্ট্যাটাস ও তৈরির সময় অনুযায়ী ফিল্টার করুন; nextCursor দিয়ে পেজ বদলান।
পরের পেজ পেতে nextCursor-কে cursor হিসেবে পাঠান; nextCursor = null ⇒ শেষ পেজ। একই সেকেন্ডে অনেক অর্ডার তৈরি হলেও পেজগুলো কখনও কোনো অর্ডার পুনরাবৃত্তি করে না বা বাদ দেয় না। অভ্যন্তরীণ স্ট্যাটাস কখনও তালিকায় আসে না (শুধু waiting_code, completed, cancelled ও queued)। বট রিস্টার্টের পর: GET /v1/orders?status=waiting_code&limit=200, nextCursor ধরে এগোন, তারপর সেই অর্ডারগুলো POST /v1/orders/status দিয়ে পোল করুন।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
status | string | না | শুধু এই স্ট্যাটাস: waiting_code | completed | cancelled | queued (না দিলে ⇒ সবগুলো) |
since | number | না | শুধু এই unix সময়ে (সেকেন্ড) বা তার পরে তৈরি অর্ডার |
limit | number | না | প্রতি পেজে অর্ডার: 1–200 (ডিফল্ট 50) |
cursor | string | না | আগের পেজের nextCursor, অপরিবর্তিত অবস্থায় |
curl
Python
Node.js
# রিস্টার্টের পর: এখনও কোডের অপেক্ষায় থাকা সব অর্ডার পেজ ধরে ধরে সংগ্রহ করুন
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"
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, VALIDATION_ERROR, RATE_LIMITEDগুদাম
প্রতিটি
offers[] এন্ট্রি একটি অফার = (সার্ভিস, গুদাম, ইনবক্সের ধরন)। শুধু বর্তমানে বিক্রি করছে এমন গুদামই তালিকায় থাকে; সাময়িকভাবে স্টক শেষ হওয়া গুদাম stock = 0 নিয়ে তালিকায় থেকে যায়। গুদামের নম্বর স্থির: 1, 2, 3, 4 — অর্ডার তৈরির সময় warehouse প্যারামিটারে একই নম্বর দিন।বিদ্যমান ক্লায়েন্টদের কিছু বদলাতে হবে না।
warehouse না দিলে ⇒ আগের মতোই আচরণ (গুদাম 1: 25 মিনিট অপেক্ষা, সার্ভিস একাধিক কোড পাঠালে 24 ঘণ্টার অর্ডার-মেয়াদের মধ্যে অতিরিক্ত কোড নিজে থেকেই আসে); 0 পাঠালে ⇒ স্টক আছে এমন সবচেয়ে কম দামের গুদাম; 99 পাঠালে ⇒ স্বয়ংক্রিয় গুদাম, একটি শেষ হলে পরেরটিতে যায় (ঠিক নিচে দেখুন); বিদ্যমান প্রতিটি ফিল্ড আগের মতোই থাকে — শুধু নতুন ফিল্ড যোগ হয় (offers[], warehouse, nextCodeFee, listening, nextCodeBlocked…)।স্বয়ংক্রিয় গুদাম: warehouse = 99 (যেসব সার্ভিসের স্টক প্রায়ই শেষ হয় তাদের জন্য প্রস্তাবিত)
offers[]-এর ফিল্ড"warehouse": 99 পাঠালে সিস্টেম স্টক আছে এমন সবচেয়ে কম দামের গুদাম থেকে ভাড়া নেয়; সেই গুদামের স্টক শেষ বা সেটি ব্যর্থ হলে একই কলের মধ্যেই পরেরটিতে চলে যায়। Facebook ও Instagram-এর মতো যেসব সার্ভিসের স্টক প্রায়ই শেষ হয়, তাদের জন্য আদর্শ — আপনার বটকে আর offers[] পড়ে গুদামে গুদামে রিট্রাই করতে হয় না।- চেষ্টার ক্রম: দাম কম থেকে বেশি (যেমন 1,500 → 1,600 → 1,700 VND); দাম সমান হলে ⇒ কম নম্বরের গুদাম আগে।
- যে গুদাম শূন্য স্টক জানায় (
offers[]-এstock= 0) তাতে কখনো কল করা হয় না — সময় নষ্ট হয় না। - বর্তমান গুদামের স্টক শেষ বা ব্যর্থ হলে পরেরটিতে যায় (
OUT_OF_STOCK,NO_MAILS_AVAILABLE,WAREHOUSE_UNAVAILABLE,PROVIDER_ERROR)। - অন্য ত্রুটিগুলো পরের গুদাম চেষ্টা না করে সঙ্গে সঙ্গে ফেরত দেওয়া হয়:
INSUFFICIENT_BALANCE,WAITING_LIMIT_REACHED,QUANTITY_EXCEEDED,MAINTENANCE,SERVICE_DISABLED,DOMAIN_UNAVAILABLE… quantity> 1: প্রথম গুদামে কম পড়লে বাকিটা পরের গুদাম থেকে আসে — অর্ডার একাধিক গুদামে ভাগ হতে পারে; প্রতিটি অর্ডারে যে গুদাম দিয়েছে তারwarehouseওpriceথাকে (এবং সেই গুদামের কোডের নিয়ম মেনে চলে)।- সব গুদাম চেষ্টা করেও কোনো ইনবক্স ভাড়া নেওয়া না গেলে ⇒
ALL_WAREHOUSES_EXHAUSTED(HTTP 409, কিছুই কাটা হয় না) — পরে আবার চেষ্টা করুন। শুধু কিছু ইনবক্স ভাড়া হলে সেই অর্ডারগুলোসহ 200-ই পাবেন।
curl
Python
Node.js
# warehouse: 99 — স্টক আছে এমন সবচেয়ে কম দামের গুদাম; স্টক শেষ বা ব্যর্থ হলে ⇒ নিজে থেকেই পরের গুদাম
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}'¹ শুধু নিজস্ব ইনভেন্টরি থেকে বিক্রি হওয়া সার্ভিস, যেমন Cointiply (
nextCodeFee — আরও কোডের ফি (গুদাম 2 ও 3-এর অর্ডার)provider = "inventory", rules.cancellable = false), rules মেনে চলে: rules.windowMinutes মিনিটের মধ্যে সর্বোচ্চ rules.maxCodesটি ইমেইল, বাতিল নেই, এবং ইমেইল ছাড়া সময় শেষ হলে রিফান্ড নেই।GET /v1/orders/{id} ও প্রতিটি অর্ডার রেসপন্সে থাকে; গুদাম 1 ও 4-এর অর্ডারে null (আরও কোড বিনামূল্যে)। গুদাম 2: আরেকটি কোড দরকার হলে POST /v1/orders/{id}/next-code কল করুন — feePriceVnd 0-এর বেশি হলে confirmFee = true পাঠাতেই হবে। গুদাম 3 (সাধারণ সার্ভিস): বিনামূল্যের সময় শেষ হলে feePriceVnd হলো একই ইনবক্স আবার ভাড়া নেওয়ার (rerent) দাম।curl
Python
Node.js
# warehouse: 0 | 1–4 | 99 (ঐচ্ছিক — আগের মতো গুদাম 1 রাখতে এটি বাদ দিন; 0 = স্টক আছে এমন সবচেয়ে কম দামের গুদাম; 99 = স্বয়ংক্রিয়, একটি শেষ হলে পরেরটিতে যায়; GET /v1/services-এর offers[] দেখুন)
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
}
}বাল্ক ভাড়া (ব্যাচ)
ব্যাচ ওয়েবে কেনা হয় (বাল্ক ভাড়া পেজ) — API দিয়ে ব্যাচের তালিকা দেখা ও কোড পড়া যায়। ইমেইলের ডোমেইনই গুদাম ঠিক করে: Gmail = গুদাম 1, iCloud = গুদাম 3 (ব্যক্তিগত ইনবক্স)।
- কেনার সময় থেকে একটি ব্যাচ 24 ঘণ্টা রাখা হয় — এই সময়ে বিনামূল্যে কোড পড়ুন, কোডের সংখ্যায় কোনো সীমা নেই।
- যে ইনবক্সে কোড আসে না তার রিফান্ড নেই; শুধু সংখ্যায় কম পড়লে সেটুকু ফেরত যায় (স্বয়ংক্রিয়ভাবে)।
- গুদাম 1 (Gmail): প্রতিটি ইনবক্সের একটি
proxyUrlআছে — API key ছাড়াইGETকরলে{"status":1,"code":"482913","all_codes":[...]}ফেরত আসে (লিংকটিই এখানে গোপন তথ্য — এটি আর কাউকে দেবেন না)। নিচের কোড এন্ডপয়েন্টও কাজ করে। - গুদাম 3 (iCloud):
proxyUrl=null— নিচের এন্ডপয়েন্ট দিয়ে কোড পড়ুন বাreadUrl(ওয়েব পেজ) খুলুন। প্রতিটি ইনবক্স ওই সার্ভিসের জন্য আপনার অ্যাকাউন্টে বাঁধা থাকে; মেয়াদ শেষের পরেও ওয়েবে আবার ভাড়া নিতে পারবেন। - যে ইমেইল থেকে কোড আলাদা করা যায় না (ওয়েলকাম ইমেইল, নিউজলেটার…) সেগুলো
isRaw: trueসহcode-এ হুবহু ফেরত আসে (HTML বা টেক্সট, সর্বোচ্চ 20,000 অক্ষর)।
GET/v1/batches
আপনার ব্যাচগুলো, নতুনটি আগে (page / pageSize দিয়ে পেজ করা, সর্বোচ্চ 100)। প্রতিটি ব্যাচে আছে warehouse (1 = Gmail, 3 = iCloud), status (active | expired | failed), expiresAt, codeCount ও readUrl।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
page | number | না | পেজ (ডিফল্ট 1) |
pageSize | number | না | প্রতি পেজে ব্যাচের সংখ্যা (ডিফল্ট 20, সর্বোচ্চ 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
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZEDGET/v1/batches/{id}
একটি ব্যাচ তার mails[] সহ — কোড পড়তে প্রতিটি ইনবক্সের id নিন।
Gmail ব্যাচ (গুদাম 1): প্রতিটি ইনবক্সের proxyUrl আপনার স্ক্রিপ্টে আগের মতোই কাজ করে। iCloud ব্যাচ (গুদাম 3): proxyUrl = null, নিচের এন্ডপয়েন্ট দিয়ে কোড পড়ুন। মেয়াদ শেষ হওয়া iCloud ব্যাচে rerentPriceVnd থাকে — একটি ইনবক্স আলাদাভাবে আবার ভাড়া নেওয়ার দাম (ওয়েবে করতে হয়)।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | ব্যাচ আইডি (বাল্ক ভাড়া পেজে ও 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
}
]
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, NOT_FOUNDGET/v1/batches/{id}/mails/{mailId}/codes
ব্যাচের একটি ইনবক্সের কোড পড়ুন: ইনবক্সের যাচাইয়ের সময় হলে সিস্টেম সরবরাহকারীর কাছে যাচাই করে, তারপর পাওয়া প্রতিটি কোড ফেরত দেয়, পুরোনোটি আগে — সবচেয়ে নতুন কোডটি codes[]-এর শেষে।
অপেক্ষার সময় প্রতি 5–10 সেকেন্ডে পোল করুন (এর চেয়ে ঘন ঘন কল করলে শুধু সংরক্ষিত ডেটা ফেরত আসে — polledAt বদলায় না)। যে ইমেইল থেকে কোড আলাদা করা যায় না সেগুলো isRaw: true-সহ code-এ হুবহু ফেরত আসে। মেয়াদ শেষ হওয়া ব্যাচও সংরক্ষিত কোড ফেরত দেয়, তবে আর যাচাই করা হয় না।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | ব্যাচ আইডি |
mailId | number | হ্যাঁ | ইনবক্স আইডি (mails[].id) |
poll | 0 | 1 | না | 0 = শুধু সংরক্ষিত কোড পড়ুন, সরবরাহকারীর কাছে যাচাই করবেন না (ডিফল্ট 1) |
curl
Python
Node.js
# অপেক্ষার সময় প্রতি 5–10 সেকেন্ডে আবার কল করুন; সবচেয়ে নতুন কোডটি 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
}
}
}সম্ভাব্য ত্রুটি: 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- গুদাম 1 (
gm_cookie)-এর অ্যাকাউন্ট আসে রিকভারি ইমেইল ছাড়া — আপনি পান ইমেইল, পাসওয়ার্ড, কুকিজ আর ইনবক্স লিংক (inboxUrl/inboxJsonUrl), এর বেশি কিছু নয়। - লগইনের প্রস্তাবিত উপায়: কুকিজ ইমপোর্ট করুন (
cookies, JSON) ব্রাউজারে — কুকি-ইমপোর্ট এক্সটেনশন দিয়ে বা আপনার antidetect ব্রাউজারের কুকি ইমপোর্ট দিয়ে — তারপর Gmail খুলুন: সেশন আগে থেকেই লগইন করা থাকে, সাধারণত পাসওয়ার্ড বা যাচাই চাওয়া হয় না। - শুধু কোড বা ইমেইল দরকার? সেগুলো
inboxUrl(ওয়েব পেজ) বাinboxJsonUrl(JSON) দিয়ে পড়ুন — API key বা Google লগইন লাগে না। - নতুন ডিভাইস বা IP থেকে পাসওয়ার্ড দিয়ে লগইন করলে Google যাচাই চাইতে পারে (রিকভারি ইমেইল, ফোন বা কোড) — এই অ্যাকাউন্টগুলোর কাছে তা পার হওয়ার কিছু নেই, তাই কুকিজ বা ইনবক্স লিংক ব্যবহার করুন।
- আবার বিক্রি করছেন? আপনার গ্রাহকদের কুকিজ এবং ইনবক্স লিংক দুটোই দিন — .txt ডাউনলোডে (
GET /v1/mail-shop/orders/{id}/download) এক লাইনেemail|password|cookies|inboxUrlথাকে — আর তাদের বলুন পাসওয়ার্ড নয়, কুকিজ দিয়ে লগইন করতে। - গুদাম 2 (
gm_login)-এর অ্যাকাউন্ট এর বদলে একটি কুইক লগইন লিংক (loginUrl) সহ আসে — লগইন করতে এটি কম্পিউটারের ব্রাউজারে খুলুন।
- 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.
উদাহরণcurl
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
]
}
}
}সম্ভাব্য ত্রুটি: 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.
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
warehouse | 1 | 2 | 99 | না | Mail-shop warehouse: 1 = email + password + cookies + inbox link · 2 = email + password + login link · 99 = automatic, cheapest first. Send warehouse OR product |
product | string | না | Instead of warehouse: gm_cookie (= warehouse 1) | gm_login (= warehouse 2) |
quantity | number | না | 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
}
]
}
}সম্ভাব্য ত্রুটি: 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.
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
warehouse | 1 | 2 | 99 | না | Mail-shop warehouse: 1 = email + password + cookies + inbox link · 2 = email + password + login link · 99 = automatic, cheapest first. Send warehouse OR product |
product | string | না | Instead of warehouse: gm_cookie (= warehouse 1) | gm_login (= warehouse 2) |
quantity | number | না | Number of accounts (default 1) |
Idempotency-Key | string | হ্যাঁ | Header: a new UUID for every new purchase — reuse it only to retry that same purchase |
curl
Python
Node.js
# Idempotency-Key বাধ্যতামূলক: প্রতিটি কেনার জন্য নতুন UUID — শুধু টাইমআউটের পর রিট্রাই করতে একই key আবার ব্যবহার করুন
# 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"
}
]
}
}সম্ভাব্য ত্রুটি: 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).
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
page | number | না | Page (default 1) |
pageSize | number | না | 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
}
}সম্ভাব্য ত্রুটি: 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.
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | 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"
}
]
}
}সম্ভাব্য ত্রুটি: 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.
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | 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/Q7xK2mP9vL4tR8wN3cB6yH1dসম্ভাব্য ত্রুটি: 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.
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | Order ID (data.id returned by the purchase) |
lineNo | number | হ্যাঁ | Account number within the order (items[].lineNo) |
max | number | না | 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
}
}সম্ভাব্য ত্রুটি: 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.
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | Order ID (data.id returned by the purchase) |
lineNo | number | হ্যাঁ | Account number within the order (items[].lineNo) |
action | string | হ্যাঁ | "regenerate" = new link (the old one stops working immediately) · "off" = turn the link off |
curl
Python
Node.js
# নতুন লিংক (পুরোনোটি সঙ্গে সঙ্গে অকেজো হয়ে যায়); বন্ধ করতে {"action":"off"} পাঠান
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
}
}সম্ভাব্য ত্রুটি: 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.
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | Order ID (data.id returned by the purchase) |
lineNo | number | হ্যাঁ | 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
}
}সম্ভাব্য ত্রুটি: 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).
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
id | string | হ্যাঁ | Order ID (data.id returned by the purchase) |
lineNo | number | হ্যাঁ | 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
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, PROVIDER_ERRORঅর্ডারের লাইফসাইকেল
গুরুত্বপূর্ণ নোট:
- অর্ডার waiting_code অবস্থায় থাকার সময় প্রতি 3–5 সেকেন্ডে
GET /v1/orders/{id}পোল করুন, যাতে কোড দ্রুত ধরতে পারেন। - প্রথম কোড আসামাত্র অবস্থা সঙ্গে সঙ্গে completed হয়ে যায়।
codeDeadlineAt-এর মধ্যে কোড না এলে (সর্বোচ্চ 25 মিনিট; গুদাম 2: 20 মিনিট; গুদাম 4-এর কিছু উৎসে কম) ⇒ সিস্টেম অর্ডার বাতিল করে 100% ফেরত দেয় (status = cancelled)।otp[]অ্যারে পুরোনোটি আগে, নতুনটি শেষে সাজানো। সর্বশেষ কোড পড়ুনotp[otp.length - 1]দিয়ে (Python:otp[-1])।- একটি completed অর্ডার নিজে থেকেই কোড পেতে থাকে:
GET /v1/orders/{id}পোল করতে থাকুন, প্রতিটি নতুন কোডotp[]-এর শেষে দেখা যাবে। সার্ভিস একাধিক কোড পাঠালে অর্ডারের মেয়াদের মধ্যে অতিরিক্ত কোডের জন্য কিছুই লাগে না; সীমা ও সময় ভাড়ার আগেই দেখানো হয় (offers[]-এরmaxCodesওfee; গুদাম 1-এ শেষ কোডের পর ইনবক্স সাধারণত প্রায় 25 মিনিট খোলা থাকে)। স্পষ্টভাবে আরেকটি কোড চাইতে (যেমন সাইন-আপের পর লগইন):POST /v1/orders/{id}/next-code— যেকোনো গুদামে কাজ করে, আর ফি নেয় শুধু গুদাম 2, তার বিনামূল্যের সময়ের বাইরে। - সার্ভিস প্রতি ইনবক্সে একটিই কোড দিলে
otp[]ওই কোডেই থেমে যায়। রেসপন্স স্বাভাবিক থাকে, কোনো ত্রুটি আসে না — কখন পোল বন্ধ করবেন তা আপনিই ঠিক করেন। otp[].codeসাধারণত একটি ছোট কোড (অঙ্ক / অক্ষর) বা একটি লিংক। যে ইমেল থেকে কোড বের করা যায়নি (স্বাগত ইমেল, নিউজলেটার, অস্বাভাবিক ফরম্যাটের কোড…) সেটিotp[].code-এ হুবহু (HTML বা টেক্সট) দেওয়া হয় — 80 অক্ষরের বেশি লম্বা হলে, লাইন-ব্রেক থাকলে বা HTML হলে পুরো ইমেল ধরে নিন এবং কোড বা লিংক নিজেই খুঁজে নিন।POST /v1/ordersসরবরাহকারীর ইনবক্স বরাদ্দের জন্য অপেক্ষা করে, তাই সাধারণত ~0.3 সেকেন্ড লাগে, তবে ব্যস্ত সময়ে (বিরল) 10 সেকেন্ড পর্যন্ত লাগতে পারে। 5 সেকেন্ড পরে ছেড়ে দিয়ে আগেভাগে রিট্রাই না করে ক্লায়েন্টের টাইমআউট ≈ 15 সেকেন্ড রাখুন — আর রিট্রাই করলে একইIdempotency-Keyব্যবহার করুন।- যে অর্ডার কোড পেয়ে গেছে তা বাতিল করা যায় না বা রিফান্ড হয় না — বাতিলের কল
ORDER_NOT_CANCELLABLEফেরত দেয়। - @icloud.com ইনবক্স ভাড়া নিতে অর্ডার তৈরির সময়
"domain": "icloud.com"পাঠান (Gmail-এর জন্য বাদ দিন; বিদ্যমান বটগুলোর কিছু বদলাতে হবে না)। সার্ভিসটি iCloud সমর্থন না করলে, বা iCloud সাময়িকভাবে বন্ধ থাকলেDOMAIN_UNAVAILABLEপাবেন — Gmail-এ বদলালে ভাড়া সঙ্গে সঙ্গে হয়ে যায়। লাইফসাইকেল, বাতিল ও রিফান্ডের নিয়ম এবং কোড পড়ার পদ্ধতি Gmail-এর সঙ্গে হুবহু এক। - নিজস্ব ইনভেন্টরির সার্ভিস (
provider="inventory", যেমন Cointiply —GET /v1/servicesদেখুন) শুধু iCloud-এ; প্রতিটি ইমেইলotp[].code-এ একটি লিংক হিসেবে আসে, সঙ্গেkind,labelএবংtext-এ ইমেইলের পূর্ণ লেখা। একটি অর্ডারrules.windowMinutesমিনিটের মধ্যে (windowEndsAtপর্যন্ত) সর্বোচ্চrules.maxCodesটি ইমেইল পায়, বাতিল করা যায় না (ORDER_NOT_CANCELLABLE), এবং ইমেইল ছাড়া সময় শেষ হলে রিফান্ড ছাড়াই cancelled হয়ে যায়। একই ইনবক্সে পেতে থাকতেcanRerenttrue থাকা অবস্থায়POST /v1/orders/{id}/rerentকল করুন (দাম:rerentPriceVnd)। অন্য সব সার্ভিস / অর্ডারেprovider="standard"— 2026-10-24 থেকেprovider-এর মাত্র দুটি মান:"standard"|"inventory"(আসল ওয়্যারহাউসwarehouse-এ); শুধু"inventory"-এর সাথে তুলনা করুন। - গুদাম 2-এর অর্ডার (
warehouse= 2):fee.freeHoursঘণ্টা কোড বিনামূল্যে; বিনামূল্যের সময় শেষ হলে নতুন কোড আসা বন্ধ হয় — আরেকটি সময় খুলতেconfirmFee= true দিয়েPOST /v1/orders/{id}/next-codeকল করুন (nextCodeFee.feePriceVndদিয়ে), সর্বোচ্চnextCodeFee.hardUntilপর্যন্ত। সাধারণ সার্ভিসের গুদাম 3 অর্ডার: 24 ঘণ্টা বিনামূল্যে কোড, কোড আসার আগে বাতিল করা যায়, তারপরrerentব্যবহার করুন। গুদাম 4-এর অর্ডার: অর্ডার পড়লেই নতুন কোডotp[]-এ চলে আসে,rentExpiresAtপর্যন্ত বিনামূল্যে; একটিমাত্র কোড দেওয়া উৎসে প্রথম কোডের পরnextCodeBlockReason="max_codes"হয়। - একসাথে অনেক অর্ডার?
POST /v1/orders/statusদিয়ে প্রতি কলে সর্বোচ্চ 100টি পোল করুন — এক কলে অনেক অর্ডারের স্ট্যাটাস দেখুন।
রেট লিমিট
সীমা প্রতিটি API key-এর জন্য প্রযোজ্য (কোনো অ্যাকাউন্টের নিজস্ব সীমা থাকতে পারে; না থাকলে সাইটের ডিফল্ট সীমা প্রযোজ্য):
- 100টি অনুরোধ/সেকেন্ড — কয়েক সেকেন্ডের ছোট বার্স্ট
- 3000টি অনুরোধ/মিনিট — প্রতি মিনিটের মোট ট্রাফিক
কোনো সীমা ছাড়ালে HTTP 429 ফেরত আসে, সঙ্গে
Retry-After: <seconds> হেডার এবং বডি {"success":false,"error":{"code":"RATE_LIMITED",...}}। আবার চেষ্টা করার আগে হেডারে দেওয়া সংখ্যক সেকেন্ড অপেক্ষা করুন। ত্রুটির বডিতে একই সংখ্যক সেকেন্ড error.retryAfterSec-এও থাকে (error.retryable = true সহ)।এরর কোড
প্রতিটি ত্রুটির গঠন একই:
{"success":false,"error":{"code":"...","message":"...","retryable":false,"retryAfterSec":null}}2026-10-17 থেকে /v1-এর প্রতিটি ত্রুটিতে
ত্রুটির উত্তরের নমুনাerror.retryable ও error.retryAfterSec-ও থাকে (যোগ করা ফিল্ড — বিদ্যমান ক্লায়েন্টে কিছু বদলাতে হবে না; details অপরিবর্তিত)। retryable = true ⇒ একই কল পরে সফল হতে পারে: retryAfterSec সেকেন্ড (≥ 1 পূর্ণসংখ্যা) অপেক্ষা করে আবার পাঠান — স্টক নেই / ইনবক্স নেই / গুদাম সাময়িক বন্ধ / সরবরাহকারীর ত্রুটি ⇒ 5 সেকেন্ড; 429 RATE_LIMITED ⇒ Retry-After হেডার; একই Idempotency-Key-এর প্রথম অনুরোধ এখনও চলাকালে 409 DUPLICATE_REQUEST ⇒ 2 সেকেন্ড। retryable = false (retryAfterSec = null) ⇒ কারণটি ঠিক না করা পর্যন্ত (ব্যালেন্স, অপেক্ষমাণ অর্ডারের সীমা, প্যারামিটার, API key…) আবার কল করে লাভ নেই। POST /v1/orders-এর partial.reason-ও একই নিয়ম মানে। স্টক শেষ বোঝানো 409 উত্তর (OUT_OF_STOCK, WAREHOUSE_UNAVAILABLE, ALL_WAREHOUSES_EXHAUSTED) পেলে আবার চেষ্টার আগে পুরো retryAfterSec সেকেন্ড অপেক্ষা করুন — আগে চেষ্টা করলে সাধারণত একই উত্তর আসে — এবং আবার ভাড়া নেওয়ার আগে GET /v1/services-এ stock / offers[].stock দেখুন; আগে থেকে নেওয়া অর্ডারগুলো বারবার জিজ্ঞেস না করে webhook দিয়ে অনুসরণ করুন।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
}
}ওয়েবহুক (পুশ নোটিফিকেশন)
প্রতিটি অ্যাকাউন্টে একটি URL রেজিস্টার করুন; কোনো অর্ডারে নতুন কোড এলে (
3 ধাপে সেটআপorder.code) বা সেটি বাতিল / রিফান্ড হলে (order.cancelled) আমরা সঙ্গে সঙ্গে সেই URL-এ একটি স্বাক্ষরিত JSON ইভেন্ট POST করি। ওয়েবহুক ঐচ্ছিক এবং আপনার অ্যাকাউন্টের সব অর্ডারে প্রযোজ্য (ওয়েব ও API)। ডেলিভারি সর্বোচ্চ চেষ্টার ভিত্তিতে — ব্যাকআপ হিসেবে ধীর পোলিং চালু রাখুন (নিচে দেখুন)।- আপনার URL (চাইলে
events-ও) দিয়েPUT /v1/webhook—data.secret(64টি হেক্স অক্ষর) সংরক্ষণ করুন: প্রতিটি ডেলিভারি যাচাইয়ের জন্য এটি দরকার। আবার PUT ডাকলেও একই secret থাকে। POST /v1/webhook/test— আমরা সঙ্গে সঙ্গে একটিpingইভেন্ট পাঠাই এবং আপনার সার্ভার কী উত্তর দিল তা ফেরত দিই (ok,httpStatus,error)।- আপনার এন্ডপয়েন্টকে 5 সেকেন্ডের মধ্যে যেকোনো 2xx দিয়ে উত্তর দিতে হবে — আগে উত্তর দিন, পরে ইভেন্ট প্রসেস করুন। অন্য সবকিছু (3xx, 4xx, 5xx, টাইমআউট, সংযোগ ত্রুটি) ব্যর্থ হিসেবে গণ্য হয় এবং আবার পাঠানো হয়।
URL অবশ্যই
অনুরোধের ফরম্যাটhttp:// বা https:// দিয়ে শুরু হবে (বৈধ সার্টিফিকেটসহ) এবং ইন্টারনেট থেকে পৌঁছানো যাবে: localhost, প্রাইভেট, link-local, multicast ও সংরক্ষিত ঠিকানা (10.x, 172.16–31.x, 192.168.x, 169.254.x, fc00::/7, fe80::/10…) এবং OTPGmail-এর নিজস্ব সার্ভার VALIDATION_ERROR দিয়ে প্রত্যাখ্যাত হয়। প্রতিটি ডেলিভারির আগে হোস্ট নাম আবার রিজলভ করা হয়। রিডাইরেক্ট অনুসরণ করা হয় না — চূড়ান্ত URL রেজিস্টার করুন।প্রতিটি ডেলিভারি হলো আপনার URL-এ JSON বডি (UTF-8) ও এই হেডারগুলোসহ একটি HTTP POST:
আপনার secret-কে কী ধরে
রিসিভারের উদাহরণ (Python ও Node কেবল স্ট্যান্ডার্ড লাইব্রেরি ব্যবহার করে; curl কমান্ড আপনার নিজের রিসিভারে সঠিকভাবে স্বাক্ষরিত একটি টেস্ট ডেলিভারি পাঠায়)X-OTPGmail-Timestamp + . + অনুরোধের কাঁচা বডির ওপর HMAC-SHA256 ছোট হাতের hex-এ হিসাব করুন এবং sha256=-এর পরের মানের সঙ্গে ধ্রুব-সময়ে তুলনা করুন। কী হলো 64 অক্ষরের secret স্ট্রিংটি নিজেই (এর UTF-8 বাইট — hex ডিকোড করবেন না)। কাঁচা বাইটই ব্যবহার করুন: JSON পার্স করে আবার সিরিয়ালাইজ করলে বাইট বদলে যায়। আপনার ঘড়ি থেকে 5 মিনিটের বেশি সরে থাকা timestamp প্রত্যাখ্যান করুন (রিপ্লে সুরক্ষা)।curl
Python
Node.js
# লোকাল টেস্ট: আপনার রিসিভারে হুবহু আমাদের মতো স্বাক্ষরিত একটি ডেলিভারি পাঠান
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
}
]
}উদাহরণ বডি — 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"
}উদাহরণ বডি — ping{
"event": "ping",
"deliveryId": "3c9a1e7f-2b4d-4c6e-8f0a-5d7b9c1e3a2f",
"sentAt": 1760700000
}পুনঃচেষ্টা, বিরতি ও ইতিহাস- ব্যর্থ ডেলিভারি 10 সেকেন্ড, 1 মিনিট, 5 মিনিট, 30 মিনিট ও 2 ঘণ্টা পরে আবার পাঠানো হয় (5টি পুনঃচেষ্টা); শেষটিও ব্যর্থ হলে ইভেন্ট বাদ দেওয়া হয় (
failed)। - পরপর 50টি ব্যর্থ চেষ্টা (সব ইভেন্ট মিলিয়ে) ওয়েবহুক থামিয়ে দেয়:
GET /v1/webhook-এpausedAtদেখা যায়, কিছুই পাঠানো হয় না এবং নতুন ইভেন্ট সারিতে যোগ হয় না। - আবার চালু করতে: আবার
PUT /v1/webhookডাকুন (একই URL চলবে) অথবা একটি সফলPOST /v1/webhook/testকরুন — এরপর সারিতে অপেক্ষমাণ ইভেন্টগুলো পাঠানো হয়। GET /v1/webhook/deliveriesসর্বশেষ 100টি ডেলিভারি তাদের অবস্থা, চেষ্টার সংখ্যা ও শেষ ত্রুটিসহ দেখায়; ইতিহাস 7 দিন রাখা হয়।
একই ইভেন্ট একাধিকবার আসতে পারে (যেমন আপনার 2xx উত্তর হারিয়ে গেলে) এবং ইভেন্ট ক্রমের বাইরে আসতে পারে (কয়েকটি সমান্তরালে পাঠানো হয়)। প্রসেস করা
ব্যাকআপ হিসেবে পোলিং চালু রাখুনdeliveryId সংরক্ষণ করুন এবং পুনরাবৃত্তি বাদ দিন; কোনো অর্ডারের ক্ষেত্রে আসার ক্রমের বদলে তার সবচেয়ে নতুন ডেটার (otp, status) ওপর ভরসা করুন।ওয়েবহুক দেরিতে আসতে পারে (পুনঃচেষ্টা) বা হারিয়ে যেতে পারে (ওয়েবহুক থামানো, আপনার সার্ভার বন্ধ)। ধীর পোলিং চালু রাখুন — যেমন যেসব অর্ডার এখনো
waiting_code-এ আছে সেগুলোর জন্য প্রতি 30–60 সেকেন্ডে GET /v1/orders/{id} — এবং API থেকে পাওয়া অর্ডার অবজেক্টকেই সঠিক উৎস ধরুন।PUT/v1/webhook
আপনার অ্যাকাউন্টের ওয়েবহুক রেজিস্টার বা আপডেট করে। প্রথম কল এটি তৈরি করে এবং নতুন secret ফেরত দেয়; পরের কলগুলো secret একই রাখে, ওয়েবহুক আবার চালু করে এবং থেমে থাকলে আবার শুরু করে। events না পাঠালে: নতুন ওয়েবহুকে দুটি ইভেন্টই, বিদ্যমানটিতে কোনো পরিবর্তন নেই। URL অবশ্যই পাবলিক ঠিকানা হতে হবে — না হলে 400 VALIDATION_ERROR (কারণ error.details.problem-এ)।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
url | string | হ্যাঁ | আপনার এন্ডপয়েন্ট (http:// বা https://, পাবলিক ঠিকানা, সর্বোচ্চ 2048 অক্ষর) |
events | string[] | না | কোন ইভেন্ট পাঠাবেন: ["order.code", "order.cancelled"] (ডিফল্ট দুটিই) |
curl
Python
Node.js
# data.secret সংরক্ষণ করুন — প্রতিটি ডেলিভারি যাচাইয়ে এটি দরকার
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
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZED, VALIDATION_ERRORGET/v1/webhook
আপনার অ্যাকাউন্টের ওয়েবহুক, secret, pausedAt ও consecutiveFailures সহ। কোনো ওয়েবহুক রেজিস্টার না থাকলে data হয় null।
উদাহরণcurl
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
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZEDDELETE/v1/webhook
ওয়েবহুক মুছে দেয়; সারিতে অপেক্ষমাণ ইভেন্টগুলো বাদ যায়। সবসময় 200 — মোছার কিছু না থাকলে data.deleted হয় false।
উদাহরণcurl
Python
Node.js
curl -X DELETE "https://otpgmail.net/v1/webhook" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"deleted": true
}
}সম্ভাব্য ত্রুটি: UNAUTHORIZEDPOST/v1/webhook/test
এখনই আপনার URL-এ একটি ping ইভেন্ট পাঠায় এবং ফলাফল জানায়: ok (আপনার সার্ভার 2xx দিয়েছে), httpStatus, durationMs, error। সফল টেস্ট থেমে থাকা ওয়েবহুক আবার চালু করে। প্রতি মিনিটে সর্বোচ্চ 10টি টেস্ট; পুরো সিস্টেমে ওয়েবহুক বন্ধ থাকলে 503 MAINTENANCE।
উদাহরণcurl
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"
}
}আপনার সার্ভার 2xx না দিলে 200 উত্তর
সম্ভাব্য ত্রুটি: {
"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
সর্বশেষ ডেলিভারি, নতুনগুলো আগে (7 দিন রাখা হয়): status = queued (পরের চেষ্টা nextAt-এ) | delivered | failed | paused, সঙ্গে attempts, lastHttpStatus ও lastError। ইভেন্টের বডি ফেরত দেওয়া হয় না।
প্যারামিটার| নাম | ধরন | আবশ্যক | বিবরণ |
|---|---|---|---|
limit | number | না | ডেলিভারির সংখ্যা, 1–100 (ডিফল্ট 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
}
]
}সম্ভাব্য ত্রুটি: UNAUTHORIZED