/api/v1/GET /api/v1/health//api/v1/auth//api/v1/license//api/v1/portal/
تمام endpointهای جدید JSON با این envelope پاسخ میدهند. برای پیگیری خطا، مقدار هدر X-Request-ID را ذخیره کنید.
// موفق
{"ok":true,"data":{...}}
// خطا
{"ok":false,"error":{"code":"ERROR_CODE","message":"...","details":{}}}
GET /api/v1/health/ برای readiness است و وضعیت اتصال دیتابیس را در checks اعلام میکند.
POST /api/v1/auth/login/ با ورودی {"email":"...","password":"..."}؛ توکن را با Authorization: Bearer <access_token> ارسال کنید.
GET /api/v1/portal/catalog/plans/ فهرست پلنهای فعال را برمیگرداند.
POST /api/v1/portal/purchase/wallet/
Authorization: Bearer <access_token>
Idempotency-Key: purchase-unique-key
{"plan_id":123,"purchase_mode":"new"}
// تمدید: {"plan_id":123,"purchase_mode":"renew","renew_license_ids":[45]}
کلید Idempotency تا ۲۴ ساعت پاسخ موفق را replay میکند؛ همان کلید با بدنه متفاوت، خطای HTTP 409 میدهد.
ثبتنام دومرحلهای: ابتدا POST /api/v1/auth/signup/otp/start/ با ایمیل، موبایل و رمز؛ سپس POST /api/v1/auth/signup/otp/verify/ با challenge و کد پیامک. در production ورود نیز پس از رمز عبور، OTP دوم را مطالبه میکند.
POST /api/v1/license/emergency-login/
ورودی:
{
"app": "hunterezdevaj",
"username": "....",
"password": "....",
"hwid": "S-1-5-21-....",
"device": {"os":"windows","ver":"10","model":"..."},
"client_version":"1.2.3"
}
خروجی (نمونه):
{
"ok": true,
"token": "....",
"expires_at": "2026-02-14T12:00:00Z",
"bind_mode": "PENDING|AUTO",
"license_status": "ACTIVE|PENDING|LOCKED|DISABLED|EXPIRED"
}
POST /api/v1/license/session/validate/
{
"app":"hunterezdevaj",
"token":"....",
"hwid":"S-1-5-21-....",
"client_version":"1.2.3"
}
خروجی:
{
"ok": true,
"expires_at": "...",
"revoked": false,
"reason": ""
}
AUTH_UNAUTHORIZED - نیاز به ورودPROFILE_INCOMPLETE - تکمیل پروفایل/تأیید تلفن پیش از خریدWALLET_INSUFFICIENT - موجودی ناکافیidempotency_conflict - استفاده از کلید تکراری با درخواست متفاوتrate_limited، invalid_credentials، license_locked - خطاهای لایسنساین صفحه عمداً ساده نگه داشته شده تا قرارداد API در تیم ثابت باشد.