{"components":{"schemas":{"AcceptOrderItemsDto":{"properties":{"itemIds":{"description":"Defaults to every PENDING line of this vendor","items":{"format":"uuid","type":"string"},"maxItems":50,"type":"array"}},"type":"object"},"AddressDto":{"properties":{"area":{"example":"Nərimanov","type":"string"},"details":{"example":"3-cü blok, 5-ci mərtəbə","nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"isDefault":{"example":true,"type":"boolean"},"label":{"example":"Ev","type":"string"},"lat":{"example":40.4041,"type":"number"},"lng":{"example":49.8702,"type":"number"},"street":{"example":"Ağa Nemətulla küçəsi 42, mənzil 7","type":"string"},"zone":{"allOf":[{"$ref":"#/components/schemas/AddressZoneDto"}],"description":"Delivery zone containing this point (point-in-polygon); null = out of zone","nullable":true,"type":"object"}},"required":["id","label","area","street","details","lat","lng","isDefault","zone"],"type":"object"},"AddressListDto":{"properties":{"items":{"description":"Default address first, then newest first","items":{"$ref":"#/components/schemas/AddressDto"},"type":"array"}},"required":["items"],"type":"object"},"AddressZoneDto":{"properties":{"feeQepik":{"description":"Delivery fee for this zone, qəpik","example":300,"type":"number"},"id":{"format":"uuid","type":"string"},"name":{"$ref":"#/components/schemas/LocalizedText"}},"required":["id","name","feeQepik"],"type":"object"},"AdjustStockDto":{"properties":{"delta":{"description":"Relative change (exactly one of delta / stockQty). Never below 0 → ERR_VALIDATION","example":-2,"type":"number"},"stockQty":{"description":"Absolute value","example":40,"maximum":1000000,"minimum":0,"type":"number"}},"type":"object"},"AdminCategory":{"properties":{"activeProductCount":{"description":"Active products assigned directly","example":9,"type":"number"},"children":{"items":{"$ref":"#/components/schemas/AdminCategory"},"type":"array"},"id":{"format":"uuid","type":"string"},"name":{"$ref":"#/components/schemas/LocalizedText"},"parentId":{"format":"uuid","nullable":true,"type":"string"},"productCount":{"description":"Products assigned directly (active and inactive)","example":12,"type":"number"},"slug":{"example":"kisi-geyimi","type":"string"},"sortOrder":{"description":"Ascending; ties break on the Azerbaijani name","example":3,"type":"number"},"subtreeProductCount":{"description":"Active products in this category AND every descendant (what the catalog shows)","example":148,"type":"number"}},"required":["id","parentId","slug","name","sortOrder","productCount","activeProductCount","subtreeProductCount","children"],"type":"object"},"AdminCategoryTree":{"properties":{"items":{"description":"Roots in sortOrder","items":{"$ref":"#/components/schemas/AdminCategory"},"type":"array"}},"required":["items"],"type":"object"},"AdminCreatableRole":{"enum":["ADMIN","SUPPORT","DRIVER","RUNNER"],"type":"string"},"AdminDriverProfileInput":{"properties":{"capacityM3":{"example":12,"maximum":100,"minimum":0.01,"type":"number"},"vehicleType":{"allOf":[{"$ref":"#/components/schemas/VehicleType"}],"example":"VAN"}},"required":["vehicleType","capacityM3"],"type":"object"},"AdminOrderBatch":{"properties":{"code":{"example":"B-2081","type":"string"},"driver":{"allOf":[{"$ref":"#/components/schemas/AdminUserRef"}],"nullable":true,"type":"object"},"id":{"format":"uuid","type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]},"stopIndex":{"description":"0-based route stop","example":3,"nullable":true,"type":"number"}},"required":["id","code","status","driver","stopIndex"],"type":"object"},"AdminOrderCancelRequest":{"properties":{"reason":{"maxLength":500,"type":"string"}},"required":["reason"],"type":"object"},"AdminOrderDetail":{"properties":{"address":{"allOf":[{"$ref":"#/components/schemas/OrderAddressDto"}],"description":"Immutable checkout snapshot"},"allowedTransitions":{"description":"Statuses an ADMIN may move this order to right now (CANCELLED goes through /cancel)","items":{"$ref":"#/components/schemas/OrderStatus"},"type":"array"},"audit":{"items":{"$ref":"#/components/schemas/AuditEntry"},"type":"array"},"batch":{"allOf":[{"$ref":"#/components/schemas/AdminOrderBatch"}],"nullable":true,"type":"object"},"cancelReason":{"nullable":true,"type":"string"},"codAmountQepik":{"example":12700,"type":"number"},"code":{"example":"ES-1042","type":"string"},"createdAt":{"format":"date-time","type":"string"},"customer":{"$ref":"#/components/schemas/AdminUserRef"},"deliveredAt":{"format":"date-time","nullable":true,"type":"string"},"deliveryFeeQepik":{"example":300,"type":"number"},"events":{"description":"Oldest first, with the acting user","items":{"$ref":"#/components/schemas/AdminOrderEvent"},"type":"array"},"id":{"format":"uuid","type":"string"},"items":{"items":{"$ref":"#/components/schemas/AdminOrderItem"},"type":"array"},"note":{"nullable":true,"type":"string"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethod"}]},"runner":{"allOf":[{"$ref":"#/components/schemas/AdminUserRef"}],"nullable":true,"type":"object"},"runnerClaimedAt":{"format":"date-time","nullable":true,"type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}]},"subtotalQepik":{"example":12400,"type":"number"},"timeWindowEnd":{"format":"date-time","nullable":true,"type":"string"},"timeWindowStart":{"format":"date-time","nullable":true,"type":"string"},"totalQepik":{"example":12700,"type":"number"},"zone":{"allOf":[{"$ref":"#/components/schemas/LocalizedText"}],"nullable":true,"type":"object"}},"required":["id","code","status","customer","paymentMethod","subtotalQepik","deliveryFeeQepik","totalQepik","codAmountQepik","address","zone","items","events","batch","runner","runnerClaimedAt","timeWindowStart","timeWindowEnd","note","cancelReason","allowedTransitions","audit","createdAt","deliveredAt"],"type":"object"},"AdminOrderEvent":{"properties":{"actor":{"allOf":[{"$ref":"#/components/schemas/AdminUserRef"}],"description":"null for SYSTEM transitions (the dispatch engine)","nullable":true,"type":"object"},"actorRole":{"enum":["CUSTOMER","SELLER","DRIVER","RUNNER","ADMIN","SUPPORT"],"nullable":true,"type":"string"},"at":{"format":"date-time","type":"string"},"fromStatus":{"enum":["PLACED","VENDOR_ACCEPTED","RUNNER_PICKUP","AT_HUB","SCANNED_TO_VAN","EN_ROUTE","DELIVERED","FAILED","RETURNED","CANCELLED"],"nullable":true,"type":"string"},"note":{"nullable":true,"type":"string"},"toStatus":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}]}},"required":["fromStatus","toStatus","actor","actorRole","note","at"],"type":"object"},"AdminOrderItem":{"properties":{"acceptance":{"allOf":[{"$ref":"#/components/schemas/ItemAcceptance"}]},"atHubAt":{"format":"date-time","nullable":true,"type":"string"},"barcode":{"description":"Printed hub label","example":"ES-1042-1","type":"string"},"id":{"format":"uuid","type":"string"},"lineTotalQepik":{"description":"qəpik","example":4980,"type":"number"},"name":{"$ref":"#/components/schemas/LocalizedText"},"pickedAt":{"format":"date-time","nullable":true,"type":"string"},"pickupStatus":{"allOf":[{"$ref":"#/components/schemas/PickupStatus"}]},"productId":{"format":"uuid","type":"string"},"qty":{"example":2,"type":"number"},"rejectReason":{"nullable":true,"type":"string"},"sku":{"example":"A-114-0007","type":"string"},"unitPriceQepik":{"description":"qəpik","example":2490,"type":"number"},"vendor":{"$ref":"#/components/schemas/AdminOrderItemVendor"}},"required":["id","productId","name","sku","barcode","vendor","qty","unitPriceQepik","lineTotalQepik","acceptance","rejectReason","pickupStatus","pickedAt","atHubAt"],"type":"object"},"AdminOrderItemVendor":{"properties":{"displayName":{"type":"string"},"id":{"format":"uuid","type":"string"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber"],"type":"object"},"AdminOrderSort":{"enum":["newest","oldest","total_desc","total_asc"],"type":"string"},"AdminOrderSummary":{"properties":{"area":{"description":"Delivery area from the address snapshot","example":"20 Yanvar","type":"string"},"batchCode":{"example":"B-2081","nullable":true,"type":"string"},"codAmountQepik":{"description":"Cash the driver must collect, qəpik","example":15900,"type":"number"},"code":{"example":"ES-1042","type":"string"},"createdAt":{"format":"date-time","type":"string"},"customer":{"$ref":"#/components/schemas/AdminUserRef"},"deliveredAt":{"format":"date-time","nullable":true,"type":"string"},"driverName":{"nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"itemCount":{"example":3,"type":"number"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethod"}]},"runnerName":{"nullable":true,"type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}]},"totalQepik":{"description":"qəpik","example":15900,"type":"number"},"vendorsCount":{"description":"Distinct vendors in the order","example":2,"type":"number"},"zone":{"allOf":[{"$ref":"#/components/schemas/LocalizedText"}],"description":"Delivery zone name in all three languages — the list renders in the operator’s own locale, exactly like the detail payload. Null when the order has no zone.","nullable":true,"type":"object"}},"required":["id","code","status","customer","paymentMethod","totalQepik","codAmountQepik","itemCount","vendorsCount","area","zone","batchCode","runnerName","driverName","createdAt","deliveredAt"],"type":"object"},"AdminOrderTransitionRequest":{"properties":{"note":{"description":"Why — stored on the OrderEvent","maxLength":500,"type":"string"},"to":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}],"description":"Must be an edge the OrderStateMachine allows; CANCELLED → use /cancel"}},"required":["to","note"],"type":"object"},"AdminPingResponseDto":{"properties":{"message":{"example":"pong","type":"string"},"role":{"allOf":[{"$ref":"#/components/schemas/UserRole"}],"example":"ADMIN"},"userId":{"description":"Id of the authenticated admin","format":"uuid","type":"string"}},"required":["message","userId","role"],"type":"object"},"AdminProduct":{"properties":{"barcode":{"example":"4761234567890","nullable":true,"type":"string"},"category":{"$ref":"#/components/schemas/VendorProductCategory"},"categoryId":{"format":"uuid","type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"isActive":{"description":"false = hidden from the catalog (soft-deleted or paused)","type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedText"},"photoKeys":{"description":"Storage keys — send these back in photoKeys when editing","example":["products/a114-01-1.jpg"],"items":{"type":"string"},"type":"array"},"photos":{"description":"Absolute URLs (S3_PUBLIC_URL + key)","example":["http://localhost:9000/esadarak-media/products/a114-01-1.jpg"],"items":{"type":"string"},"type":"array"},"priceQepik":{"description":"Unit price, integer qəpik","example":2490,"type":"number"},"ratingAvg":{"description":"Not money","example":4.5,"nullable":true,"type":"number"},"ratingCount":{"example":3,"type":"number"},"sku":{"description":"Generated: <stallNumber>-<4-digit sequence>","example":"A-114-0007","type":"string"},"stockQty":{"example":12,"type":"number"},"updatedAt":{"format":"date-time","type":"string"},"vendor":{"$ref":"#/components/schemas/AdminProductVendor"},"volumeCm3":{"description":"Packed volume for the dispatch capacity check","example":1500,"type":"number"}},"required":["id","sku","name","priceQepik","stockQty","categoryId","category","photos","photoKeys","volumeCm3","barcode","isActive","ratingAvg","ratingCount","createdAt","updatedAt","vendor"],"type":"object"},"AdminProductVendor":{"properties":{"displayName":{"example":"Elşən Ticarət","type":"string"},"id":{"format":"uuid","type":"string"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber"],"type":"object"},"AdminSetting":{"properties":{"description":{"example":"Batching tick interval, minutes","type":"string"},"group":{"allOf":[{"$ref":"#/components/schemas/AdminSettingGroup"}],"example":"dispatch"},"key":{"enum":["delivery.defaultFeeQepik","delivery.allowOutOfZone","vendor.defaultCommissionBps","driver.payPerDeliveryQepik","driver.payPerKmQepik","dispatch.batchIntervalMin","dispatch.hubBacklogThreshold","dispatch.acceptTimeoutSec","dispatch.avgVanCapacity","dispatch.sameStopRadiusM","catalog.promos"],"example":"dispatch.batchIntervalMin","type":"string"},"type":{"allOf":[{"$ref":"#/components/schemas/AdminSettingType"}],"example":"int"},"value":{"description":"Current value, typed by `type` (int → number, bool → boolean, json → any)","example":20,"oneOf":[{"type":"number"},{"type":"boolean"},{"type":"string"},{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"}]}},"required":["key","value","type","description","group"],"type":"object"},"AdminSettingGroup":{"enum":["delivery","vendor","driver","dispatch","catalog"],"type":"string"},"AdminSettingList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminSetting"},"type":"array"}},"required":["items"],"type":"object"},"AdminSettingType":{"enum":["int","bool","string","json"],"type":"string"},"AdminSettlement":{"properties":{"approvedAt":{"format":"date-time","nullable":true,"type":"string"},"approvedBy":{"allOf":[{"$ref":"#/components/schemas/SettlementActorRef"}],"nullable":true,"type":"object"},"commissionBps":{"description":"Vendor.commissionBps captured at generation","example":1250,"type":"number"},"commissionQepik":{"description":"round-half-up(gross × commissionBps / 10 000)","example":23063,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"grossSalesQepik":{"description":"Σ settled line totals, qəpik","example":184500,"type":"number"},"id":{"format":"uuid","type":"string"},"linesCount":{"example":27,"type":"number"},"ordersCount":{"description":"Distinct orders behind the settled lines","example":12,"type":"number"},"paidAt":{"format":"date-time","nullable":true,"type":"string"},"paymentReference":{"example":"KAPBANK-2026-08-24-118","nullable":true,"type":"string"},"payoutAmountQepik":{"description":"gross − commission","example":161437,"type":"number"},"periodEnd":{"description":"Next Monday 00:00 Asia/Baku (exclusive)","format":"date-time","type":"string"},"periodStart":{"description":"Monday 00:00 Asia/Baku (inclusive)","format":"date-time","type":"string"},"rejectReason":{"example":"Qaytarılmış sifariş daxil edilib","nullable":true,"type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/SettlementStatus"}]},"vendor":{"$ref":"#/components/schemas/SettlementVendorRef"}},"required":["id","vendor","periodStart","periodEnd","grossSalesQepik","commissionQepik","commissionBps","payoutAmountQepik","ordersCount","linesCount","status","approvedBy","approvedAt","paidAt","paymentReference","rejectReason","createdAt"],"type":"object"},"AdminSettlementDetail":{"properties":{"approvedAt":{"format":"date-time","nullable":true,"type":"string"},"approvedBy":{"allOf":[{"$ref":"#/components/schemas/SettlementActorRef"}],"nullable":true,"type":"object"},"commissionBps":{"description":"Vendor.commissionBps captured at generation","example":1250,"type":"number"},"commissionQepik":{"description":"round-half-up(gross × commissionBps / 10 000)","example":23063,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"generatedBy":{"allOf":[{"$ref":"#/components/schemas/SettlementActorRef"}],"nullable":true,"type":"object"},"grossSalesQepik":{"description":"Σ settled line totals, qəpik","example":184500,"type":"number"},"id":{"format":"uuid","type":"string"},"lines":{"description":"Settled lines, oldest delivery first","items":{"$ref":"#/components/schemas/AdminSettlementLine"},"type":"array"},"linesCount":{"example":27,"type":"number"},"ordersCount":{"description":"Distinct orders behind the settled lines","example":12,"type":"number"},"paidAt":{"format":"date-time","nullable":true,"type":"string"},"paymentReference":{"example":"KAPBANK-2026-08-24-118","nullable":true,"type":"string"},"payoutAmountQepik":{"description":"gross − commission","example":161437,"type":"number"},"periodEnd":{"description":"Next Monday 00:00 Asia/Baku (exclusive)","format":"date-time","type":"string"},"periodStart":{"description":"Monday 00:00 Asia/Baku (inclusive)","format":"date-time","type":"string"},"rejectReason":{"example":"Qaytarılmış sifariş daxil edilib","nullable":true,"type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/SettlementStatus"}]},"vendor":{"$ref":"#/components/schemas/SettlementVendorRef"}},"required":["id","vendor","periodStart","periodEnd","grossSalesQepik","commissionQepik","commissionBps","payoutAmountQepik","ordersCount","linesCount","status","approvedBy","approvedAt","paidAt","paymentReference","rejectReason","createdAt","lines","generatedBy"],"type":"object"},"AdminSettlementLine":{"properties":{"deliveredAt":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"lineTotalQepik":{"description":"qty × unit price, qəpik","example":3600,"type":"number"},"orderCode":{"example":"ES-1042","type":"string"},"orderId":{"format":"uuid","type":"string"},"orderItemId":{"format":"uuid","type":"string"},"productName":{"$ref":"#/components/schemas/LocalizedText"},"qty":{"example":3,"type":"number"},"unitPriceQepik":{"description":"Unit price snapshot, qəpik","example":1200,"type":"number"}},"required":["id","orderId","orderCode","orderItemId","productName","qty","unitPriceQepik","lineTotalQepik","deliveredAt"],"type":"object"},"AdminSettlementsSummary":{"properties":{"byStatus":{"description":"One row per status","items":{"$ref":"#/components/schemas/AdminSettlementsSummaryRow"},"type":"array"},"periodEnd":{"format":"date-time","nullable":true,"type":"string"},"periodStart":{"description":"The period the totals are scoped to; null = every period","format":"date-time","nullable":true,"type":"string"},"totalCommissionQepik":{"example":651250,"type":"number"},"totalCount":{"example":41,"type":"number"},"totalGrossSalesQepik":{"example":5210000,"type":"number"},"totalPayoutAmountQepik":{"example":4558750,"type":"number"},"unsettledGrossQepik":{"description":"Σ of those lines, integer qəpik","example":0,"type":"number"},"unsettledLines":{"description":"Accepted, delivered order lines in scope that no SettlementLine covers — money that fell outside every statement because it arrived after its vendor's week was signed. Recovering it needs a reject + regenerate.","example":0,"type":"number"}},"required":["periodStart","periodEnd","byStatus","totalCount","totalGrossSalesQepik","totalCommissionQepik","totalPayoutAmountQepik","unsettledLines","unsettledGrossQepik"],"type":"object"},"AdminSettlementsSummaryRow":{"properties":{"commissionQepik":{"example":230625,"type":"number"},"count":{"example":14,"type":"number"},"grossSalesQepik":{"example":1845000,"type":"number"},"payoutAmountQepik":{"example":1614375,"type":"number"},"status":{"allOf":[{"$ref":"#/components/schemas/SettlementStatus"}]}},"required":["status","count","grossSalesQepik","commissionQepik","payoutAmountQepik"],"type":"object"},"AdminUser":{"properties":{"blockedAt":{"format":"date-time","nullable":true,"type":"string"},"blockedReason":{"nullable":true,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"driver":{"allOf":[{"$ref":"#/components/schemas/AdminUserDriverRef"}],"nullable":true,"type":"object"},"id":{"format":"uuid","type":"string"},"isBlocked":{"description":"Blocked users cannot log in, refresh or use the realtime channel","type":"boolean"},"lastSeenAt":{"description":"Driver heartbeat for DRIVERs, otherwise the newest session's creation time","format":"date-time","nullable":true,"type":"string"},"locale":{"allOf":[{"$ref":"#/components/schemas/Locale"}]},"name":{"nullable":true,"type":"string"},"phone":{"example":"+994501234567","type":"string"},"role":{"allOf":[{"$ref":"#/components/schemas/UserRole"}]},"vendor":{"allOf":[{"$ref":"#/components/schemas/AdminUserVendorRef"}],"nullable":true,"type":"object"}},"required":["id","phone","name","role","locale","isBlocked","blockedAt","blockedReason","lastSeenAt","vendor","driver","createdAt"],"type":"object"},"AdminUserDriverRef":{"properties":{"capacityM3":{"description":"Cargo capacity in m³ (not money)","example":12,"type":"number"},"isOnline":{"type":"boolean"},"vehicleType":{"allOf":[{"$ref":"#/components/schemas/VehicleType"}],"example":"VAN"}},"required":["vehicleType","capacityM3","isOnline"],"type":"object"},"AdminUserRef":{"properties":{"id":{"format":"uuid","type":"string"},"name":{"example":"Aysel Məmmədova","nullable":true,"type":"string"},"phone":{"example":"+994501234567","type":"string"}},"required":["id","name","phone"],"type":"object"},"AdminUserVendorRef":{"properties":{"displayName":{"example":"Elşən Ticarət","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","displayName"],"type":"object"},"AdminVendor":{"properties":{"activeProductCount":{"example":44,"type":"number"},"commissionBps":{"description":"Platform commission, basis points (1250 = 12.50 %)","example":1250,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"displayName":{"example":"Elşən Ticarət","type":"string"},"gmv30dQepik":{"description":"Σ accepted line totals of orders DELIVERED in the last 30 days, qəpik","example":184500,"type":"number"},"id":{"format":"uuid","type":"string"},"isActive":{"description":"false hides every product from the catalog and refuses new orders","type":"boolean"},"isVerified":{"type":"boolean"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"ordersCount30d":{"description":"Distinct delivered orders in the same window","example":27,"type":"number"},"owner":{"$ref":"#/components/schemas/AdminUserRef"},"productCount":{"example":48,"type":"number"},"rating":{"description":"Average of the order-level reviews (not money)","example":4.6,"type":"number"},"ratingCount":{"example":12,"type":"number"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber","marketZone","isVerified","isActive","commissionBps","rating","ratingCount","productCount","activeProductCount","owner","gmv30dQepik","ordersCount30d","createdAt"],"type":"object"},"AdminVendorDetail":{"properties":{"activeProductCount":{"example":44,"type":"number"},"audit":{"description":"Ten most recent audit entries","items":{"$ref":"#/components/schemas/AuditEntry"},"type":"array"},"commissionBps":{"description":"Platform commission, basis points (1250 = 12.50 %)","example":1250,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"displayName":{"example":"Elşən Ticarət","type":"string"},"gmv30dQepik":{"description":"Σ accepted line totals of orders DELIVERED in the last 30 days, qəpik","example":184500,"type":"number"},"id":{"format":"uuid","type":"string"},"isActive":{"description":"false hides every product from the catalog and refuses new orders","type":"boolean"},"isVerified":{"type":"boolean"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"ordersCount30d":{"description":"Distinct delivered orders in the same window","example":27,"type":"number"},"owner":{"$ref":"#/components/schemas/AdminUserRef"},"productCount":{"example":48,"type":"number"},"rating":{"description":"Average of the order-level reviews (not money)","example":4.6,"type":"number"},"ratingCount":{"example":12,"type":"number"},"recentOrders":{"description":"Ten most recent orders","items":{"$ref":"#/components/schemas/AdminVendorOrder"},"type":"array"},"settlements":{"$ref":"#/components/schemas/AdminVendorSettlements"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber","marketZone","isVerified","isActive","commissionBps","rating","ratingCount","productCount","activeProductCount","owner","gmv30dQepik","ordersCount30d","createdAt","recentOrders","settlements","audit"],"type":"object"},"AdminVendorOrder":{"properties":{"code":{"example":"ES-1042","type":"string"},"createdAt":{"format":"date-time","type":"string"},"deliveredAt":{"format":"date-time","nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"itemCount":{"description":"Lines of THIS vendor","example":3,"type":"number"},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}],"example":"DELIVERED"},"totalQepik":{"description":"Order total, qəpik","example":15900,"type":"number"},"vendorSubtotalQepik":{"description":"Σ of THIS vendor's accepted lines, qəpik","example":12400,"type":"number"}},"required":["id","code","status","itemCount","vendorSubtotalQepik","totalQepik","createdAt","deliveredAt"],"type":"object"},"AdminVendorSettlementBucket":{"properties":{"count":{"example":3,"type":"number"},"payoutAmountQepik":{"description":"Σ payoutAmountQepik in this state","example":412300,"type":"number"},"status":{"allOf":[{"$ref":"#/components/schemas/SettlementStatus"}]}},"required":["status","count","payoutAmountQepik"],"type":"object"},"AdminVendorSettlements":{"properties":{"byStatus":{"items":{"$ref":"#/components/schemas/AdminVendorSettlementBucket"},"type":"array"},"lastPaidAt":{"format":"date-time","nullable":true,"type":"string"},"total":{"example":4,"type":"number"},"unpaidPayoutQepik":{"description":"Σ payout still owed (PENDING + APPROVED), qəpik","example":82600,"type":"number"}},"required":["total","byStatus","unpaidPayoutQepik","lastPaidAt"],"type":"object"},"AdminVendorSort":{"enum":["newest","name","rating","gmv"],"type":"string"},"AdminZone":{"properties":{"addressCount":{"description":"Saved customer addresses inside the polygon","example":128,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"feeQepik":{"description":"Delivery fee inside this zone, qəpik","example":300,"type":"number"},"id":{"format":"uuid","type":"string"},"isActive":{"description":"Inactive zones stop matching addresses; orders keep their zone id","type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedText"},"ordersCount30d":{"description":"Orders placed to this zone in the last 30 days","example":412,"type":"number"},"polygon":{"$ref":"#/components/schemas/GeoJsonPolygon"}},"required":["id","name","polygon","feeQepik","isActive","addressCount","ordersCount30d","createdAt"],"type":"object"},"AdminZoneList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminZone"},"type":"array"}},"required":["items"],"type":"object"},"AdminZoneResolution":{"properties":{"zone":{"allOf":[{"$ref":"#/components/schemas/AdminZone"}],"description":"The cheapest ACTIVE zone containing the point, or null","nullable":true,"type":"object"}},"required":["zone"],"type":"object"},"AdvanceOrderDto":{"properties":{"runnerId":{"description":"Runner (User.role RUNNER) to book the pickup to when advancing to RUNNER_PICKUP / AT_HUB. Omitted → the order’s current runner, else the first seeded RUNNER (by phone).","format":"uuid","type":"string"},"to":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}],"description":"Target status. Omitted → next happy-path status (PLACED → … → DELIVERED)."}},"type":"object"},"AnalyticsCodVariance":{"properties":{"batchesWithVariance":{"description":"Batches completed in the range with a non-zero variance","example":3,"type":"number"},"totalVarianceQepik":{"description":"Σ of those variances, qəpik","example":-1500,"type":"number"},"worst":{"description":"The 5 largest absolute variances, worst first","items":{"$ref":"#/components/schemas/AnalyticsCodVarianceBatch"},"type":"array"}},"required":["batchesWithVariance","totalVarianceQepik","worst"],"type":"object"},"AnalyticsCodVarianceBatch":{"properties":{"batchCode":{"example":"B-2083","type":"string"},"batchId":{"format":"uuid","type":"string"},"codVarianceQepik":{"description":"collected − expected, qəpik (can be negative)","example":-500,"type":"number"},"completedAt":{"format":"date-time","nullable":true,"type":"string"},"driverName":{"example":"Vaqif Həsənov","nullable":true,"type":"string"}},"required":["batchId","batchCode","driverName","codVarianceQepik","completedAt"],"type":"object"},"AnalyticsDayPoint":{"properties":{"cancelled":{"description":"Orders that entered CANCELLED that day","example":2,"type":"number"},"date":{"description":"Baku calendar day","example":"2026-08-27","type":"string"},"delivered":{"description":"Orders delivered that day (by deliveredAt)","example":37,"type":"number"},"gmvQepik":{"description":"Σ totalQepik of the orders delivered that day","example":1284500,"type":"number"},"placed":{"description":"Orders created that day","example":42,"type":"number"}},"required":["date","placed","delivered","cancelled","gmvQepik"],"type":"object"},"AnalyticsDeliverySla":{"properties":{"avgPlacedToDeliveredMin":{"description":"Mean minutes from PLACED to DELIVERED","example":312,"type":"number"},"onTimeOrders":{"description":"Of those, delivered at or before timeWindowEnd","example":80,"type":"number"},"onTimeRate":{"description":"onTimeOrders / ordersWithWindow, 4 decimals. 0 when no delivered order had a time window.","example":0.8421,"type":"number"},"ordersWithWindow":{"description":"Delivered orders that carried a time window","example":95,"type":"number"},"p50Min":{"description":"Median minutes (percentile_cont 0.5)","example":284,"type":"number"},"p90Min":{"description":"90th percentile minutes (percentile_cont 0.9)","example":561,"type":"number"}},"required":["avgPlacedToDeliveredMin","p50Min","p90Min","onTimeRate","ordersWithWindow","onTimeOrders"],"type":"object"},"AnalyticsOrdersPerDay":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AnalyticsDayPoint"},"type":"array"},"range":{"$ref":"#/components/schemas/AnalyticsRange"}},"required":["range","items"],"type":"object"},"AnalyticsOverview":{"properties":{"activeCustomers":{"description":"Distinct customers who placed an order in the range","example":264,"type":"number"},"aovQepik":{"description":"gmvQepik / ordersDelivered, rounded half up","example":34071,"type":"number"},"codVariance":{"$ref":"#/components/schemas/AnalyticsCodVariance"},"deliverySla":{"$ref":"#/components/schemas/AnalyticsDeliverySla"},"gmvQepik":{"description":"Σ totalQepik of DELIVERED orders with deliveredAt in the range","example":12845000,"type":"number"},"newCustomers":{"description":"CUSTOMER accounts created in the range","example":31,"type":"number"},"ordersCancelled":{"description":"Orders that entered CANCELLED in the range","example":18,"type":"number"},"ordersDelivered":{"description":"Orders delivered in the range (by deliveredAt)","example":377,"type":"number"},"ordersFailed":{"description":"Orders that entered FAILED in the range","example":9,"type":"number"},"ordersPerDay":{"description":"One point per Baku day, oldest first","items":{"$ref":"#/components/schemas/AnalyticsDayPoint"},"type":"array"},"ordersPlaced":{"description":"Orders created in the range","example":412,"type":"number"},"range":{"$ref":"#/components/schemas/AnalyticsRange"},"statusBreakdown":{"allOf":[{"$ref":"#/components/schemas/AnalyticsStatusBreakdown"}],"description":"Current status of the orders CREATED in the range; every status key is present"},"topAreas":{"description":"Top 10 delivery areas by GMV","items":{"$ref":"#/components/schemas/AnalyticsTopArea"},"type":"array"},"topCategories":{"description":"Top 10 by GMV","items":{"$ref":"#/components/schemas/AnalyticsTopCategory"},"type":"array"},"topVendors":{"description":"Top 10 by GMV","items":{"$ref":"#/components/schemas/AnalyticsTopVendor"},"type":"array"}},"required":["range","gmvQepik","ordersPlaced","ordersDelivered","ordersCancelled","ordersFailed","aovQepik","activeCustomers","newCustomers","ordersPerDay","statusBreakdown","deliverySla","codVariance","topVendors","topCategories","topAreas"],"type":"object"},"AnalyticsRange":{"properties":{"days":{"description":"Number of Baku days in the range","example":30,"type":"number"},"from":{"description":"00:00 Asia/Baku of fromDate, as UTC","format":"date-time","type":"string"},"fromDate":{"description":"First Baku day, inclusive","example":"2026-07-29","type":"string"},"to":{"description":"00:00 Asia/Baku of the day after toDate (exclusive), as UTC","format":"date-time","type":"string"},"toDate":{"description":"Last Baku day, inclusive","example":"2026-08-27","type":"string"}},"required":["fromDate","toDate","from","to","days"],"type":"object"},"AnalyticsStatusBreakdown":{"properties":{"AT_HUB":{"example":22,"type":"number"},"CANCELLED":{"example":18,"type":"number"},"DELIVERED":{"example":377,"type":"number"},"EN_ROUTE":{"example":9,"type":"number"},"FAILED":{"example":9,"type":"number"},"PLACED":{"example":12,"type":"number"},"RETURNED":{"example":3,"type":"number"},"RUNNER_PICKUP":{"example":4,"type":"number"},"SCANNED_TO_VAN":{"example":6,"type":"number"},"VENDOR_ACCEPTED":{"example":9,"type":"number"}},"required":["PLACED","VENDOR_ACCEPTED","RUNNER_PICKUP","AT_HUB","SCANNED_TO_VAN","EN_ROUTE","DELIVERED","FAILED","RETURNED","CANCELLED"],"type":"object"},"AnalyticsTopArea":{"properties":{"area":{"example":"20 Yanvar","type":"string"},"gmvQepik":{"example":984500,"type":"number"},"orders":{"example":41,"type":"number"}},"required":["area","orders","gmvQepik"],"type":"object"},"AnalyticsTopCategory":{"properties":{"categoryId":{"format":"uuid","type":"string"},"gmvQepik":{"example":284500,"type":"number"},"name":{"$ref":"#/components/schemas/LocalizedText"},"qty":{"description":"Σ qty of the accepted lines","example":156,"type":"number"}},"required":["categoryId","name","gmvQepik","qty"],"type":"object"},"AnalyticsTopVendor":{"properties":{"displayName":{"example":"Şirin Meyvə","type":"string"},"gmvQepik":{"description":"Σ accepted line totals of delivered orders","example":184500,"type":"number"},"orders":{"description":"Distinct delivered orders containing the vendor","example":24,"type":"number"},"stallNumber":{"example":"A-114","type":"string"},"vendorId":{"format":"uuid","type":"string"}},"required":["vendorId","displayName","stallNumber","gmvQepik","orders"],"type":"object"},"AuditEntry":{"properties":{"action":{"description":"<targetType>.<verb>","example":"vendor.verify","type":"string"},"actor":{"allOf":[{"$ref":"#/components/schemas/AdminUserRef"}],"description":"The admin who performed the write (null when the account was deleted)","nullable":true,"type":"object"},"after":{"additionalProperties":true,"description":"Snapshot AFTER the write (null on delete)","nullable":true,"type":"object"},"before":{"additionalProperties":true,"description":"Snapshot of the changed fields BEFORE the write (null on create)","nullable":true,"type":"object"},"createdAt":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"ip":{"example":"10.0.0.7","nullable":true,"type":"string"},"targetId":{"example":"9f1c0b6a-4c2e-4b9d-8a7f-3e2d1c0b9a8f","type":"string"},"targetType":{"allOf":[{"$ref":"#/components/schemas/AuditTargetType"}],"example":"vendor"}},"required":["id","actor","action","targetType","targetId","before","after","ip","createdAt"],"type":"object"},"AuditTargetType":{"enum":["vendor","product","category","user","zone","setting","order","settlement"],"type":"string"},"AuthSessionDto":{"properties":{"accessToken":{"description":"JWT, 15 min. Send as `Authorization: Bearer <token>`","type":"string"},"refreshToken":{"description":"Rotating refresh token, 30 days, single-use","type":"string"},"user":{"$ref":"#/components/schemas/UserDto"}},"required":["accessToken","refreshToken","user"],"type":"object"},"AuthTokensDto":{"properties":{"accessToken":{"description":"JWT, 15 min. Send as `Authorization: Bearer <token>`","type":"string"},"refreshToken":{"description":"Rotating refresh token, 30 days, single-use","type":"string"}},"required":["accessToken","refreshToken"],"type":"object"},"BatchAdminActionRequest":{"properties":{"note":{"example":"Sürücü xəstələnib","maxLength":500,"nullable":true,"type":"string"}},"type":"object"},"BatchDetail":{"properties":{"assignedAt":{"format":"date-time","nullable":true,"type":"string"},"clusterJson":{"additionalProperties":true,"description":"k-means diagnostics { k, centroid, iterations, seed }","nullable":true,"type":"object"},"codVarianceQepik":{"description":"collected − expected","nullable":true,"type":"number"},"code":{"example":"B-3001","type":"string"},"collectedCodQepik":{"nullable":true,"type":"number"},"completedAt":{"format":"date-time","nullable":true,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"departedAt":{"format":"date-time","nullable":true,"type":"string"},"distanceKm":{"example":31.2,"nullable":true,"type":"number"},"driverId":{"format":"uuid","nullable":true,"type":"string"},"driverName":{"nullable":true,"type":"string"},"events":{"description":"Chronological","items":{"$ref":"#/components/schemas/BatchEventEntry"},"type":"array"},"expectedCodQepik":{"example":45600,"type":"number"},"id":{"format":"uuid","type":"string"},"offerAttempts":{"example":1,"type":"number"},"offerExpiresAt":{"format":"date-time","nullable":true,"type":"string"},"offeredDriverId":{"format":"uuid","nullable":true,"type":"string"},"offeredDriverName":{"nullable":true,"type":"string"},"orders":{"description":"In sequence order","items":{"$ref":"#/components/schemas/BatchDetailOrder"},"type":"array"},"ordersCount":{"example":12,"type":"number"},"route":{"allOf":[{"$ref":"#/components/schemas/RouteJson"}],"description":"null until the route is computed","nullable":true,"type":"object"},"scanProgress":{"$ref":"#/components/schemas/ScanProgress"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]},"stopsDone":{"example":4,"type":"number"},"updatedAt":{"format":"date-time","type":"string"},"varianceNote":{"nullable":true,"type":"string"},"volumeCm3":{"description":"cm³ (volume, not money)","example":48000,"type":"number"}},"required":["id","code","status","driverId","driverName","ordersCount","stopsDone","expectedCodQepik","distanceKm","createdAt","updatedAt","offeredDriverId","offeredDriverName","offerExpiresAt","offerAttempts","volumeCm3","collectedCodQepik","codVarianceQepik","varianceNote","assignedAt","departedAt","completedAt","clusterJson","orders","events","route","scanProgress"],"type":"object"},"BatchDetailOrder":{"properties":{"area":{"example":"Nərimanov","type":"string"},"codAmountQepik":{"example":5280,"type":"number"},"codCollectedQepik":{"example":5280,"type":"number"},"code":{"example":"ES-1042","type":"string"},"deliveredAt":{"format":"date-time","nullable":true,"type":"string"},"deliveryStatus":{"allOf":[{"$ref":"#/components/schemas/DeliveryStatus"}]},"driverPayQepik":{"example":378,"type":"number"},"failReason":{"allOf":[{"$ref":"#/components/schemas/DeliveryFailReason"}],"nullable":true},"orderId":{"format":"uuid","type":"string"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethod"}]},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}]},"stopIndex":{"example":0,"nullable":true,"type":"number"},"street":{"example":"Ağa Nemətulla küçəsi 42, mənzil 7","type":"string"}},"required":["orderId","code","status","area","street","deliveryStatus","paymentMethod","codAmountQepik","codCollectedQepik","driverPayQepik","stopIndex","failReason","deliveredAt"],"type":"object"},"BatchEventEntry":{"properties":{"actorRole":{"allOf":[{"$ref":"#/components/schemas/UserRole"}],"description":"null = the dispatcher itself (SYSTEM)","nullable":true},"at":{"format":"date-time","type":"string"},"note":{"nullable":true,"type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]}},"required":["status","at","actorRole","note"],"type":"object"},"BatchForDriver":{"properties":{"assignedAt":{"format":"date-time","nullable":true,"type":"string"},"code":{"example":"B-3001","type":"string"},"collectedCodQepik":{"description":"Set at completion","nullable":true,"type":"number"},"completedAt":{"format":"date-time","nullable":true,"type":"string"},"departedAt":{"format":"date-time","nullable":true,"type":"string"},"distanceKm":{"description":"Set at start-route (km)","nullable":true,"type":"number"},"events":{"items":{"$ref":"#/components/schemas/BatchTimelineEntry"},"type":"array"},"expectedCodQepik":{"example":45600,"type":"number"},"id":{"format":"uuid","type":"string"},"ordersCount":{"example":12,"type":"number"},"scan":{"$ref":"#/components/schemas/ScanProgress"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]},"stops":{"description":"Routed stops (EN_ROUTE and later) or the unrouted grouping (stopIndex null) before","items":{"$ref":"#/components/schemas/DriverStop"},"type":"array"},"volumeCm3":{"description":"cm³","example":48000,"type":"number"}},"required":["id","code","status","ordersCount","volumeCm3","expectedCodQepik","collectedCodQepik","distanceKm","scan","stops","events","assignedAt","departedAt","completedAt"],"type":"object"},"BatchOffer":{"properties":{"areas":{"example":["20 Yanvar","Gənclik"],"items":{"type":"string"},"type":"array"},"batchId":{"format":"uuid","type":"string"},"code":{"example":"B-3001","type":"string"},"estimatedKm":{"description":"Haversine nearest-neighbour estimate, km (round trip)","example":31.2,"type":"number"},"expectedCodQepik":{"example":45600,"type":"number"},"expiresAt":{"format":"date-time","type":"string"},"ordersCount":{"example":12,"type":"number"},"secondsLeft":{"description":"Whole seconds until the offer expires","example":72,"type":"number"},"stopsPreview":{"description":"Stops in estimated visiting order","items":{"$ref":"#/components/schemas/BatchOfferStop"},"type":"array"},"volumeCm3":{"description":"cm³","example":48000,"type":"number"}},"required":["batchId","code","expiresAt","secondsLeft","ordersCount","areas","estimatedKm","expectedCodQepik","volumeCm3","stopsPreview"],"type":"object"},"BatchOfferStop":{"properties":{"area":{"example":"20 Yanvar","type":"string"},"street":{"example":"Zahid Xəlilov küçəsi 42","type":"string"}},"required":["area","street"],"type":"object"},"BatchRef":{"properties":{"code":{"example":"B-3001","type":"string"},"id":{"format":"uuid","type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]}},"required":["id","code","status"],"type":"object"},"BatchStatus":{"enum":["PENDING","OFFERED","ASSIGNED","LOADING","EN_ROUTE","COMPLETED","CANCELLED"],"type":"string"},"BatchSummary":{"properties":{"code":{"example":"B-3001","type":"string"},"createdAt":{"format":"date-time","type":"string"},"distanceKm":{"example":31.2,"nullable":true,"type":"number"},"driverId":{"format":"uuid","nullable":true,"type":"string"},"driverName":{"nullable":true,"type":"string"},"expectedCodQepik":{"example":45600,"type":"number"},"id":{"format":"uuid","type":"string"},"ordersCount":{"example":12,"type":"number"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]},"stopsDone":{"example":4,"type":"number"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","code","status","driverId","driverName","ordersCount","stopsDone","expectedCodQepik","distanceKm","createdAt","updatedAt"],"type":"object"},"BatchSummaryForDriver":{"properties":{"code":{"example":"B-3001","type":"string"},"completedAt":{"format":"date-time","nullable":true,"type":"string"},"deliveredCount":{"example":11,"type":"number"},"distanceKm":{"nullable":true,"type":"number"},"failedCount":{"description":"FAILED + RETURNED","example":1,"type":"number"},"id":{"format":"uuid","type":"string"},"ordersCount":{"example":12,"type":"number"},"payQepik":{"description":"Σ driver pay of the delivered stops, qəpik","example":4180,"type":"number"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]}},"required":["id","code","status","ordersCount","deliveredCount","failedCount","distanceKm","payQepik","completedAt"],"type":"object"},"BatchTimelineEntry":{"properties":{"at":{"format":"date-time","type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]}},"required":["status","at"],"type":"object"},"CancelOrderDto":{"properties":{"reason":{"example":"Səhv ünvan seçmişəm","maxLength":500,"nullable":true,"type":"string"}},"type":"object"},"CategoriesResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Category"},"type":"array"}},"required":["items"],"type":"object"},"Category":{"properties":{"children":{"description":"Nested children — present only with ?tree=true","items":{"$ref":"#/components/schemas/Category"},"type":"array"},"id":{"format":"uuid","type":"string"},"name":{"$ref":"#/components/schemas/LocalizedText"},"parentId":{"description":"null for a root","format":"uuid","nullable":true,"type":"string"},"productCount":{"description":"Active products in this category and all of its descendants","example":42,"type":"number"},"slug":{"example":"kisi-geyimi","type":"string"}},"required":["id","parentId","slug","name","productCount"],"type":"object"},"CategoryReorderItem":{"properties":{"id":{"format":"uuid","type":"string"},"sortOrder":{"maximum":100000,"minimum":0,"type":"number"}},"required":["id","sortOrder"],"type":"object"},"CompleteBatchRequest":{"properties":{"varianceNote":{"description":"Required when collected cash ≠ expected COD total","example":"Müştəri 5 ₼ az ödədi, qəbz əlavə olunub","maxLength":500,"nullable":true,"type":"string"}},"type":"object"},"CreateAddressDto":{"properties":{"area":{"description":"District / area name","example":"Nərimanov","maxLength":80,"type":"string"},"details":{"description":"Entrance / floor / door hints for the driver. Send \"\" to clear.","example":"3-cü blok, 5-ci mərtəbə","maxLength":200,"nullable":true,"type":"string"},"isDefault":{"description":"Make this the default address. The first address is always the default.","example":true,"type":"boolean"},"label":{"description":"Free label chosen by the customer","example":"Ev","maxLength":40,"type":"string"},"lat":{"example":40.4041,"maximum":90,"minimum":-90,"type":"number"},"lng":{"example":49.8702,"maximum":180,"minimum":-180,"type":"number"},"street":{"example":"Ağa Nemətulla küçəsi 42, mənzil 7","maxLength":200,"type":"string"}},"required":["label","area","street","lat","lng"],"type":"object"},"CreateAdminProductRequest":{"properties":{"barcode":{"example":"4761234567890","maxLength":64,"nullable":true,"type":"string"},"categoryId":{"description":"Must exist → otherwise ERR_CATEGORY_INVALID","format":"uuid","type":"string"},"isActive":{"description":"Defaults to true when omitted","type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedTextInput"},"photoKeys":{"description":"Keys from POST /v1/media/uploads (this vendor's own directory) or seed keys; anything else → ERR_MEDIA_INVALID","example":["products/6f1c0b6a-4c2e-4b9d-8a7f-3e2d1c0b9a8f/0b8c7d6e-….jpg"],"items":{"type":"string"},"maxItems":8,"type":"array"},"priceQepik":{"description":"qəpik","example":2490,"maximum":100000000,"minimum":1,"type":"number"},"stockQty":{"example":12,"maximum":1000000,"minimum":0,"type":"number"},"vendorId":{"description":"Vendor the product belongs to","format":"uuid","type":"string"},"volumeCm3":{"description":"Defaults to 1000 cm³ when omitted","example":1000,"maximum":10000000,"minimum":1,"type":"number"}},"required":["name","priceQepik","stockQty","categoryId","vendorId"],"type":"object"},"CreateAdminUserDto":{"properties":{"driver":{"allOf":[{"$ref":"#/components/schemas/AdminDriverProfileInput"}],"description":"Required in practice for role DRIVER — creates the DriverPresence row"},"locale":{"allOf":[{"$ref":"#/components/schemas/Locale"}],"description":"Defaults to 'az' when omitted"},"name":{"example":"Günel Rəhimova","maxLength":120,"minLength":2,"type":"string"},"phone":{"example":"+994501234567","type":"string"},"role":{"allOf":[{"$ref":"#/components/schemas/AdminCreatableRole"}],"example":"SUPPORT"}},"required":["phone","name","role"],"type":"object"},"CreateAdminVendorDto":{"properties":{"commissionBps":{"description":"Defaults to the vendor.defaultCommissionBps setting","maximum":10000,"minimum":0,"type":"number"},"displayName":{"example":"Elşən Ticarət","maxLength":120,"minLength":2,"type":"string"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"ownerName":{"description":"Set only when the user is created","maxLength":120,"type":"string"},"ownerPhone":{"description":"E.164 or a local Azerbaijani format","example":"+994501234567","type":"string"},"stallNumber":{"example":"A-114","type":"string"}},"required":["ownerPhone","displayName","stallNumber","marketZone"],"type":"object"},"CreateCategoryDto":{"properties":{"name":{"allOf":[{"$ref":"#/components/schemas/LocalizedTextInput"}],"description":"ru / en fall back to az"},"parentId":{"format":"uuid","nullable":true,"type":"string"},"slug":{"description":"Derived from the Azerbaijani name when omitted; must be unique","maxLength":60,"type":"string"},"sortOrder":{"description":"Defaults to 0 when omitted","maximum":100000,"minimum":0,"type":"number"}},"required":["name"],"type":"object"},"CreateOrderDto":{"properties":{"addressId":{"description":"Delivery address; the default address is used when omitted","format":"uuid","type":"string"},"items":{"items":{"$ref":"#/components/schemas/OrderItemInputDto"},"maxItems":50,"minItems":1,"type":"array"},"note":{"example":"Qapının zəngi işləmir, zəng edin","maxLength":500,"nullable":true,"type":"string"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethod"}],"example":"COD"},"timeWindowEnd":{"format":"date-time","type":"string"},"timeWindowStart":{"description":"One of the quote timeWindows","format":"date-time","type":"string"}},"required":["items","paymentMethod"],"type":"object"},"CreateUploadDto":{"properties":{"contentType":{"allOf":[{"$ref":"#/components/schemas/MediaContentType"}],"example":"image/jpeg"},"kind":{"allOf":[{"$ref":"#/components/schemas/MediaKind"}],"example":"product"},"sizeBytes":{"description":"Bytes","example":245312,"maximum":8000000,"minimum":1,"type":"number"}},"required":["kind","contentType","sizeBytes"],"type":"object"},"CreateVendorProductDto":{"properties":{"barcode":{"example":"4761234567890","maxLength":64,"nullable":true,"type":"string"},"categoryId":{"description":"Must exist → otherwise ERR_CATEGORY_INVALID","format":"uuid","type":"string"},"isActive":{"description":"Defaults to true when omitted","type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedTextInput"},"photoKeys":{"description":"Keys from POST /v1/media/uploads (this vendor's own directory) or seed keys; anything else → ERR_MEDIA_INVALID","example":["products/6f1c0b6a-4c2e-4b9d-8a7f-3e2d1c0b9a8f/0b8c7d6e-….jpg"],"items":{"type":"string"},"maxItems":8,"type":"array"},"priceQepik":{"description":"qəpik","example":2490,"maximum":100000000,"minimum":1,"type":"number"},"stockQty":{"example":12,"maximum":1000000,"minimum":0,"type":"number"},"volumeCm3":{"description":"Defaults to 1000 cm³ when omitted","example":1000,"maximum":10000000,"minimum":1,"type":"number"}},"required":["name","priceQepik","stockQty","categoryId"],"type":"object"},"CreateZoneDto":{"properties":{"feeQepik":{"example":300,"maximum":1000000,"minimum":0,"type":"number"},"isActive":{"description":"Defaults to true when omitted","type":"boolean"},"name":{"allOf":[{"$ref":"#/components/schemas/LocalizedTextInput"}],"description":"ru / en fall back to az"},"polygon":{"allOf":[{"$ref":"#/components/schemas/GeoJsonPolygon"}],"description":"Validated server-side → ERR_ZONE_INVALID_POLYGON with the list of problems"}},"required":["name","polygon","feeQepik"],"type":"object"},"DeclineOfferRequest":{"properties":{"reason":{"example":"Yanacaq azdır","maxLength":300,"nullable":true,"type":"string"}},"type":"object"},"DeliveredStopRequest":{"properties":{"codCollectedQepik":{"description":"Cash collected at the door, qəpik (0 for CARD orders)","example":5280,"minimum":0,"type":"number"},"note":{"maxLength":500,"nullable":true,"type":"string"},"proofPhotoKey":{"description":"Key from POST /v1/media/uploads (kind proof)","maxLength":300,"nullable":true,"type":"string"}},"required":["codCollectedQepik"],"type":"object"},"DeliveryFailReason":{"enum":["NOT_HOME","REFUSED","WRONG_ADDRESS","UNREACHABLE","OTHER"],"type":"string"},"DeliveryStatus":{"enum":["PENDING","DELIVERED","FAILED","RETURNED"],"type":"string"},"DependencyState":{"enum":["up","down"],"type":"string"},"DispatchActiveBatch":{"properties":{"code":{"example":"B-3001","type":"string"},"distanceKm":{"example":31.2,"nullable":true,"type":"number"},"driverName":{"nullable":true,"type":"string"},"expectedCodQepik":{"example":45600,"type":"number"},"id":{"format":"uuid","type":"string"},"ordersCount":{"example":12,"type":"number"},"status":{"allOf":[{"$ref":"#/components/schemas/BatchStatus"}]},"stopsDone":{"description":"Stops delivered / failed / returned","example":4,"type":"number"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","code","status","driverName","ordersCount","stopsDone","expectedCodQepik","distanceKm","updatedAt"],"type":"object"},"DispatchBatchCounters":{"properties":{"assigned":{"example":2,"type":"number"},"completedToday":{"description":"Completed since 00:00 Asia/Baku","example":4,"type":"number"},"enRoute":{"example":3,"type":"number"},"loading":{"example":1,"type":"number"},"offered":{"example":1,"type":"number"},"pending":{"example":1,"type":"number"}},"required":["pending","offered","assigned","loading","enRoute","completedToday"],"type":"object"},"DispatchDriverCounters":{"properties":{"busy":{"description":"Online and working a batch","example":2,"type":"number"},"offline":{"description":"Offline or stale heartbeat","example":1,"type":"number"},"online":{"description":"Online (heartbeat within 60 s) and free","example":3,"type":"number"}},"required":["online","busy","offline"],"type":"object"},"DispatchOnlineDriver":{"properties":{"capacityM3":{"description":"Cubic metres (float, not money)","example":12,"type":"number"},"currentBatchCode":{"example":"B-3001","nullable":true,"type":"string"},"driverId":{"format":"uuid","type":"string"},"lastSeenAt":{"format":"date-time","type":"string"},"lat":{"example":40.3452,"type":"number"},"lng":{"example":49.7441,"type":"number"},"name":{"example":"Tural Quliyev","nullable":true,"type":"string"},"vehicleType":{"allOf":[{"$ref":"#/components/schemas/VehicleType"}]}},"required":["driverId","name","lat","lng","vehicleType","capacityM3","currentBatchCode","lastSeenAt"],"type":"object"},"DispatchOverview":{"properties":{"activeBatches":{"description":"PENDING … EN_ROUTE, oldest first","items":{"$ref":"#/components/schemas/DispatchActiveBatch"},"type":"array"},"batches":{"$ref":"#/components/schemas/DispatchBatchCounters"},"drivers":{"$ref":"#/components/schemas/DispatchDriverCounters"},"hubBacklog":{"description":"AT_HUB orders not in an active batch","example":7,"type":"number"},"onlineDrivers":{"items":{"$ref":"#/components/schemas/DispatchOnlineDriver"},"type":"array"},"settings":{"$ref":"#/components/schemas/DispatchSettings"}},"required":["hubBacklog","batches","drivers","onlineDrivers","activeBatches","settings"],"type":"object"},"DispatchRunResult":{"properties":{"batchesCreated":{"example":2,"type":"number"},"offersMade":{"description":"Offers pushed to drivers in this tick","example":2,"type":"number"}},"required":["batchesCreated","offersMade"],"type":"object"},"DispatchSettings":{"properties":{"acceptTimeoutSec":{"description":"Seconds a driver has to accept an offer","example":90,"type":"number"},"avgVanCapacity":{"description":"Orders per van for the cluster count","example":25,"type":"number"},"batchIntervalMin":{"description":"Minutes between scheduler ticks","example":20,"type":"number"},"hubBacklogThreshold":{"description":"Unbatched AT_HUB orders that trigger an immediate tick","example":15,"type":"number"},"payPerDeliveryQepik":{"description":"Driver pay per delivery, qəpik","example":250,"type":"number"},"payPerKmQepik":{"description":"Driver pay per km of the delivery leg, qəpik","example":15,"type":"number"},"sameStopRadiusM":{"description":"Orders within this many metres collapse into one stop","example":25,"type":"number"}},"required":["batchIntervalMin","hubBacklogThreshold","acceptTimeoutSec","avgVanCapacity","sameStopRadiusM","payPerDeliveryQepik","payPerKmQepik"],"type":"object"},"DriverEarnings":{"properties":{"byDay":{"description":"One bucket per Asia/Baku day, oldest first","items":{"$ref":"#/components/schemas/DriverEarningsDay"},"type":"array"},"deliveries":{"example":7,"type":"number"},"failed":{"description":"Failed / returned attempts in the range","example":1,"type":"number"},"payQepik":{"example":2660,"type":"number"},"perDelivery":{"description":"Newest first, at most 100","items":{"$ref":"#/components/schemas/DriverEarningsDelivery"},"type":"array"},"range":{"allOf":[{"$ref":"#/components/schemas/DriverEarningsRange"}],"example":"today"}},"required":["range","deliveries","failed","payQepik","byDay","perDelivery"],"type":"object"},"DriverEarningsDay":{"properties":{"date":{"description":"Asia/Baku calendar day","example":"2026-08-27","type":"string"},"deliveries":{"example":7,"type":"number"},"payQepik":{"example":2660,"type":"number"}},"required":["date","deliveries","payQepik"],"type":"object"},"DriverEarningsDelivery":{"properties":{"batchCode":{"example":"B-3001","type":"string"},"deliveredAt":{"format":"date-time","type":"string"},"legKm":{"description":"Leg km of the stop (null if unrouted)","example":8.488,"nullable":true,"type":"number"},"orderCode":{"example":"ES-1042","type":"string"},"payQepik":{"example":378,"type":"number"}},"required":["orderCode","batchCode","deliveredAt","legKm","payQepik"],"type":"object"},"DriverEarningsRange":{"enum":["today","7d","30d"],"type":"string"},"DriverPresenceRequest":{"properties":{"capacityM3":{"description":"Cubic metres","example":12,"maximum":100,"minimum":0.01,"type":"number"},"isOnline":{"description":"false = going offline (removed from the locator)","example":true,"type":"boolean"},"lat":{"example":40.3452,"type":"number"},"lng":{"example":49.7441,"type":"number"},"vehicleType":{"allOf":[{"$ref":"#/components/schemas/VehicleType"}],"example":"VAN"}},"required":["lat","lng","isOnline"],"type":"object"},"DriverPresenceResponse":{"properties":{"at":{"description":"Server time of the heartbeat","format":"date-time","type":"string"},"currentBatch":{"allOf":[{"$ref":"#/components/schemas/BatchRef"}],"nullable":true,"type":"object"},"ok":{"example":true,"type":"boolean"},"pendingOffer":{"allOf":[{"$ref":"#/components/schemas/OfferRef"}],"nullable":true,"type":"object"}},"required":["ok","at","currentBatch","pendingOffer"],"type":"object"},"DriverProfile":{"properties":{"capacityM3":{"description":"Cubic metres","example":12,"type":"number"},"currentBatch":{"allOf":[{"$ref":"#/components/schemas/BatchForDriver"}],"nullable":true,"type":"object"},"id":{"format":"uuid","type":"string"},"isOnline":{"example":true,"type":"boolean"},"lastSeenAt":{"description":"null before the first heartbeat","format":"date-time","nullable":true,"type":"string"},"name":{"example":"Tural Quliyev","nullable":true,"type":"string"},"pendingOffer":{"allOf":[{"$ref":"#/components/schemas/BatchOffer"}],"nullable":true,"type":"object"},"phone":{"example":"+994500000020","type":"string"},"todayDeliveries":{"description":"Delivered since 00:00 Asia/Baku","example":7,"type":"number"},"todayPayQepik":{"description":"Pay earned since 00:00 Asia/Baku, qəpik","example":2660,"type":"number"},"vehicleType":{"allOf":[{"$ref":"#/components/schemas/VehicleType"}]}},"required":["id","name","phone","vehicleType","capacityM3","isOnline","lastSeenAt","currentBatch","pendingOffer","todayDeliveries","todayPayQepik"],"type":"object"},"DriverScanRequest":{"properties":{"barcode":{"description":"Printed line label; trimmed and upper-cased","example":"ES-1042-3","maxLength":40,"minLength":3,"type":"string"}},"required":["barcode"],"type":"object"},"DriverStop":{"properties":{"area":{"example":"20 Yanvar","type":"string"},"cumulativeKm":{"example":8.488,"nullable":true,"type":"number"},"lat":{"example":40.404,"type":"number"},"legKm":{"example":8.488,"nullable":true,"type":"number"},"lng":{"example":49.808,"type":"number"},"orders":{"description":"Orders delivered at this stop (same building)","items":{"$ref":"#/components/schemas/DriverStopOrder"},"type":"array"},"stopIndex":{"description":"null until the route is computed (EN_ROUTE)","nullable":true,"type":"number"},"street":{"example":"Zahid Xəlilov küçəsi 42, mənzil 7","type":"string"}},"required":["stopIndex","lat","lng","area","street","legKm","cumulativeKm","orders"],"type":"object"},"DriverStopOrder":{"properties":{"addressDetails":{"example":"2-ci blok, 4-cü mərtəbə","nullable":true,"type":"string"},"codAmountQepik":{"description":"0 for CARD orders","example":5280,"type":"number"},"codCollectedQepik":{"description":"null until delivered","nullable":true,"type":"number"},"code":{"example":"ES-1042","type":"string"},"customerName":{"example":"Aysel Məmmədova","nullable":true,"type":"string"},"customerPhone":{"description":"Full number — drivers must be able to call","example":"+994501234567","type":"string"},"deliveryStatus":{"allOf":[{"$ref":"#/components/schemas/DeliveryStatus"}]},"failReason":{"allOf":[{"$ref":"#/components/schemas/DeliveryFailReason"}],"nullable":true},"itemCount":{"description":"Accepted lines","example":3,"type":"number"},"navigateUrl":{"example":"https://www.google.com/maps/dir/?api=1&destination=40.404,49.808&travelmode=driving","type":"string"},"note":{"description":"Customer note","nullable":true,"type":"string"},"orderId":{"format":"uuid","type":"string"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethod"}]}},"required":["orderId","code","customerName","customerPhone","addressDetails","note","itemCount","codAmountQepik","paymentMethod","deliveryStatus","codCollectedQepik","failReason","navigateUrl"],"type":"object"},"ErrorCode":{"description":"Machine-readable error code. Clients map it to a localised string.","enum":["ERR_VALIDATION","ERR_BAD_REQUEST","ERR_UNAUTHORIZED","ERR_FORBIDDEN","ERR_NOT_FOUND","ERR_CONFLICT","ERR_RATE_LIMITED","ERR_INTERNAL","ERR_OTP_INVALID","ERR_OTP_EXPIRED","ERR_OTP_RATE_LIMITED","ERR_OTP_TOO_MANY_ATTEMPTS","ERR_NO_DEFAULT_ADDRESS","ERR_OUT_OF_STOCK","ERR_PRODUCT_UNAVAILABLE","ERR_OUT_OF_ZONE","ERR_PAYMENT_METHOD_UNAVAILABLE","ERR_ORDER_NOT_CANCELLABLE","ERR_ORDER_NOT_RATABLE","ERR_ALREADY_RATED","ERR_INVALID_TRANSITION","ERR_IDEMPOTENCY_KEY_REQUIRED","ERR_IDEMPOTENCY_CONFLICT","ERR_VENDOR_NOT_FOUND","ERR_ORDER_NOT_ACCEPTABLE","ERR_ITEMS_ALREADY_DECIDED","ERR_PRODUCT_NOT_OWNED","ERR_CATEGORY_INVALID","ERR_SKU_CONFLICT","ERR_MEDIA_UNAVAILABLE","ERR_MEDIA_INVALID","ERR_ORDER_ALREADY_CLAIMED","ERR_ORDER_NOT_CLAIMED","ERR_PICKUP_INCOMPLETE","ERR_ITEM_NOT_PICKABLE","ERR_DRIVER_NOT_FOUND","ERR_DRIVER_OFFLINE","ERR_DRIVER_BUSY","ERR_BATCH_NOT_OFFERED","ERR_OFFER_EXPIRED","ERR_BATCH_NOT_ASSIGNED","ERR_SCAN_INCOMPLETE","ERR_SCAN_WRONG_BATCH","ERR_STOP_ALREADY_RESOLVED","ERR_BATCH_INCOMPLETE","ERR_COD_VARIANCE_NOTE_REQUIRED","ERR_BATCH_INVALID_TRANSITION","ERR_ROUTE_UNAVAILABLE","ERR_DELIVERY_RETRY_LIMIT","ERR_USER_BLOCKED","ERR_CATEGORY_NOT_EMPTY","ERR_CATEGORY_CYCLE","ERR_ZONE_INVALID_POLYGON","ERR_SETTLEMENT_EXISTS","ERR_SETTLEMENT_INVALID_STATE","ERR_SETTLEMENT_PERIOD_OPEN","ERR_STAFF_PHONE_EXISTS","ERR_SETTING_UNKNOWN","ERR_SETTING_INVALID"],"type":"string"},"ErrorResponseDto":{"properties":{"code":{"allOf":[{"$ref":"#/components/schemas/ErrorCode"}],"description":"Machine-readable error code. Clients map it to a localised string.","example":"ERR_VALIDATION"},"details":{"description":"Extra context: an object (e.g. { retryAfterSec } for ERR_OTP_RATE_LIMITED) or an array (e.g. [{ field, messages[] }] for ERR_VALIDATION). Shape depends on `code`.","example":[{"field":"phone","messages":["phone must be an Azerbaijani number"]}],"oneOf":[{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"}]},"message":{"description":"Developer-facing English message. Never show it to end users.","example":"Validation failed","type":"string"},"statusCode":{"description":"HTTP status code (duplicated in the body)","example":400,"type":"number"}},"required":["statusCode","code","message"],"type":"object"},"FailedStopRequest":{"properties":{"note":{"maxLength":500,"nullable":true,"type":"string"},"reason":{"allOf":[{"$ref":"#/components/schemas/DeliveryFailReason"}],"example":"NOT_HOME"}},"required":["reason"],"type":"object"},"GenerateSettlementsRequest":{"properties":{"periodEnd":{"description":"Only used when periodStart is absent — the week containing (periodEnd − 1 ms)","format":"date-time","type":"string"},"periodStart":{"description":"Any instant inside the target ISO week (Asia/Baku); default = the most recent COMPLETED week","format":"date-time","type":"string"},"vendorId":{"description":"Single-vendor regenerate: an existing PENDING/APPROVED/PAID row is then ERR_SETTLEMENT_EXISTS instead of a skip","format":"uuid","type":"string"}},"type":"object"},"GenerateSettlementsResult":{"properties":{"created":{"description":"Settlements created (or refilled after a rejection)","example":12,"type":"number"},"periodEnd":{"format":"date-time","type":"string"},"periodStart":{"format":"date-time","type":"string"},"skipped":{"description":"Vendors that already had a settlement for the period","example":3,"type":"number"},"unsettledGrossQepik":{"description":"Σ of those lines, integer qəpik","example":0,"type":"number"},"unsettledLines":{"description":"Lines of this period still unsettled after the run — their vendor already had a signed statement, so a later generate skips them. Reject + regenerate is the only recovery.","example":0,"type":"number"},"vendors":{"items":{"$ref":"#/components/schemas/GeneratedSettlementVendor"},"type":"array"}},"required":["periodStart","periodEnd","created","skipped","unsettledLines","unsettledGrossQepik","vendors"],"type":"object"},"GeneratedSettlementVendor":{"properties":{"commissionQepik":{"example":23063,"type":"number"},"grossSalesQepik":{"example":184500,"type":"number"},"payoutAmountQepik":{"example":161437,"type":"number"},"settlementId":{"format":"uuid","type":"string"},"vendorId":{"format":"uuid","type":"string"}},"required":["vendorId","settlementId","grossSalesQepik","commissionQepik","payoutAmountQepik"],"type":"object"},"GeoJsonPolygon":{"properties":{"coordinates":{"description":"One closed outer ring of [lng, lat] positions (no holes, ≤ 500 points, inside the Baku box lat 39.5..41 / lng 48.5..51)","example":[[[49.73,40.34],[49.76,40.34],[49.76,40.36],[49.73,40.34]]],"items":{"items":{"items":{"type":"number"},"type":"array"},"type":"array"},"type":"array"},"type":{"enum":["Polygon"],"example":"Polygon","type":"string"}},"required":["type","coordinates"],"type":"object"},"HealthResponseDto":{"properties":{"db":{"allOf":[{"$ref":"#/components/schemas/DependencyState"}],"example":"up"},"redis":{"allOf":[{"$ref":"#/components/schemas/DependencyState"}],"example":"up"},"status":{"allOf":[{"$ref":"#/components/schemas/HealthState"}],"example":"ok"}},"required":["status","db","redis"],"type":"object"},"HealthState":{"enum":["ok","degraded"],"type":"string"},"ItemAcceptance":{"enum":["PENDING","ACCEPTED","REJECTED"],"type":"string"},"Locale":{"enum":["az","ru","en"],"type":"string"},"LocalizedText":{"properties":{"az":{"example":"Kişi köynəyi — Qara","type":"string"},"en":{"example":"Men's shirt — Black","type":"string"},"ru":{"example":"Мужская рубашка — Чёрный","type":"string"}},"required":["az","ru","en"],"type":"object"},"LocalizedTextInput":{"properties":{"az":{"example":"Kişi köynəyi — Qara","maxLength":120,"minLength":2,"type":"string"},"en":{"example":"Men's shirt — Black","maxLength":120,"type":"string"},"ru":{"example":"Мужская рубашка — Чёрный","maxLength":120,"type":"string"}},"required":["az"],"type":"object"},"MarkSettlementPaidRequest":{"properties":{"paidAt":{"description":"Defaults to now","format":"date-time","type":"string"},"paymentReference":{"example":"KAPBANK-2026-08-24-118","maxLength":120,"minLength":1,"type":"string"}},"required":["paymentReference"],"type":"object"},"MarketZone":{"enum":["A","B","C","D","E","F"],"type":"string"},"MediaContentType":{"enum":["image/jpeg","image/png","image/webp"],"type":"string"},"MediaKind":{"enum":["product","proof"],"type":"string"},"MediaUploadHeaders":{"properties":{"Content-Type":{"example":"image/jpeg","type":"string"}},"required":["Content-Type"],"type":"object"},"MediaUploadTicket":{"properties":{"expiresInSec":{"example":600,"type":"number"},"headers":{"allOf":[{"$ref":"#/components/schemas/MediaUploadHeaders"}],"description":"Send exactly these headers with the PUT"},"key":{"description":"Storage key — reference it in photoKeys after the PUT succeeded","example":"products/6f1c0b6a-4c2e-4b9d-8a7f-3e2d1c0b9a8f/0b8c7d6e-5f4a-4d1e-9a2f-5f1c9e2a7c3b.jpg","type":"string"},"method":{"enum":["PUT"],"example":"PUT","type":"string"},"publicUrl":{"description":"Where the object is readable after upload","type":"string"},"uploadUrl":{"description":"Presigned PUT URL (10 minutes)","type":"string"}},"required":["key","uploadUrl","method","headers","publicUrl","expiresInSec"],"type":"object"},"MissingScan":{"properties":{"barcode":{"example":"ES-1042-3","type":"string"},"name":{"$ref":"#/components/schemas/LocalizedText"},"orderCode":{"example":"ES-1042","type":"string"}},"required":["orderCode","barcode","name"],"type":"object"},"OfferRef":{"properties":{"batchId":{"format":"uuid","type":"string"},"code":{"example":"B-3001","type":"string"},"expiresAt":{"format":"date-time","type":"string"}},"required":["batchId","code","expiresAt"],"type":"object"},"OrderAddressDto":{"properties":{"area":{"example":"Nərimanov","type":"string"},"details":{"nullable":true,"type":"string"},"label":{"example":"Ev","type":"string"},"lat":{"example":40.4041,"type":"number"},"lng":{"example":49.8702,"type":"number"},"street":{"example":"Ağa Nemətulla küçəsi 42, mənzil 7","type":"string"}},"required":["label","area","street","details","lat","lng"],"type":"object"},"OrderBatchDto":{"properties":{"code":{"example":"B-2081","type":"string"},"driverName":{"example":"Tural Quliyev","nullable":true,"type":"string"},"driverPhoneMasked":{"example":"+99450*****20","nullable":true,"type":"string"}},"required":["code","driverName","driverPhoneMasked"],"type":"object"},"OrderDto":{"properties":{"address":{"allOf":[{"$ref":"#/components/schemas/OrderAddressDto"}],"description":"Snapshot taken at checkout"},"batch":{"allOf":[{"$ref":"#/components/schemas/OrderBatchDto"}],"description":"Set once dispatched to a van","nullable":true,"type":"object"},"canCancel":{"description":"PLACED or VENDOR_ACCEPTED","example":true,"type":"boolean"},"canRate":{"description":"DELIVERED and not yet rated","example":false,"type":"boolean"},"cancelReason":{"nullable":true,"type":"string"},"codAmountQepik":{"example":5280,"type":"number"},"code":{"example":"ES-1042","type":"string"},"createdAt":{"format":"date-time","type":"string"},"deliveryFeeQepik":{"example":300,"type":"number"},"id":{"format":"uuid","type":"string"},"items":{"items":{"$ref":"#/components/schemas/OrderItemDto"},"type":"array"},"note":{"nullable":true,"type":"string"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethod"}]},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}]},"subtotalQepik":{"example":4980,"type":"number"},"timeWindowEnd":{"format":"date-time","nullable":true,"type":"string"},"timeWindowStart":{"format":"date-time","nullable":true,"type":"string"},"timeline":{"description":"Chronological; first entry is PLACED","items":{"$ref":"#/components/schemas/OrderTimelineEntryDto"},"type":"array"},"totalQepik":{"example":5280,"type":"number"}},"required":["id","code","status","paymentMethod","subtotalQepik","deliveryFeeQepik","totalQepik","codAmountQepik","address","items","timeline","timeWindowStart","timeWindowEnd","note","cancelReason","canCancel","canRate","batch","createdAt"],"type":"object"},"OrderItemDto":{"properties":{"acceptance":{"allOf":[{"$ref":"#/components/schemas/ItemAcceptance"}],"description":"Vendor decision for this line. REJECTED lines stay visible but are excluded from the order totals.","example":"PENDING"},"barcode":{"description":"Printed label scanned at the hub","example":"ES-1042-1","type":"string"},"id":{"format":"uuid","type":"string"},"lineTotalQepik":{"example":4980,"type":"number"},"name":{"$ref":"#/components/schemas/LocalizedText"},"pickupStatus":{"allOf":[{"$ref":"#/components/schemas/PickupStatus"}],"example":"PENDING"},"productId":{"format":"uuid","type":"string"},"qty":{"example":2,"type":"number"},"rejectReason":{"description":"OUT_OF_STOCK | PRICE_CHANGED | CLOSED | OTHER once the vendor rejected the line","example":"OUT_OF_STOCK","nullable":true,"type":"string"},"unitPriceQepik":{"example":2490,"type":"number"},"vendor":{"$ref":"#/components/schemas/OrderItemVendorDto"}},"required":["id","productId","name","vendor","qty","unitPriceQepik","lineTotalQepik","pickupStatus","acceptance","rejectReason","barcode"],"type":"object"},"OrderItemInputDto":{"properties":{"productId":{"format":"uuid","type":"string"},"qty":{"description":"Units of this product. At most 99 per line (retail limit, independent of stock); stock shortages surface as QTY_REDUCED on a quote and ERR_OUT_OF_STOCK on create.","example":2,"maximum":99,"minimum":1,"type":"number"}},"required":["productId","qty"],"type":"object"},"OrderItemVendorDto":{"properties":{"displayName":{"example":"Elşən Ticarət","type":"string"},"id":{"format":"uuid","type":"string"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber"],"type":"object"},"OrderStatus":{"enum":["PLACED","VENDOR_ACCEPTED","RUNNER_PICKUP","AT_HUB","SCANNED_TO_VAN","EN_ROUTE","DELIVERED","FAILED","RETURNED","CANCELLED"],"type":"string"},"OrderSummaryDto":{"properties":{"code":{"example":"ES-1042","type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"itemCount":{"description":"Number of lines","example":3,"type":"number"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethod"}],"example":"COD"},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}],"example":"PLACED"},"timeWindowEnd":{"format":"date-time","nullable":true,"type":"string"},"timeWindowStart":{"format":"date-time","nullable":true,"type":"string"},"totalQepik":{"example":5280,"type":"number"}},"required":["id","code","status","paymentMethod","totalQepik","itemCount","timeWindowStart","timeWindowEnd","createdAt"],"type":"object"},"OrderTimelineEntryDto":{"properties":{"at":{"format":"date-time","type":"string"},"note":{"nullable":true,"type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}]}},"required":["status","at","note"],"type":"object"},"OtpRequestResponseDto":{"properties":{"devCode":{"description":"The code itself — ONLY present when OTP_PROVIDER=mock (dev/test). Never in production.","example":"483920","type":"string"},"expiresInSec":{"description":"Seconds until the code expires","example":120,"type":"number"},"requestId":{"description":"Identifies this code; informational for clients","format":"uuid","type":"string"}},"required":["requestId","expiresInSec"],"type":"object"},"PageOfOrderSummaryDto":{"properties":{"items":{"items":{"$ref":"#/components/schemas/OrderSummaryDto"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedAdminOrders":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminOrderSummary"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedAdminProducts":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminProduct"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedAdminSettlements":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminSettlement"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedAdminUsers":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminUser"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedAdminVendors":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminVendor"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedAuditEntries":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AuditEntry"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedBatches":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BatchSummary"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedDriverBatches":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BatchSummaryForDriver"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedPickupOrders":{"properties":{"items":{"items":{"$ref":"#/components/schemas/PickupOrder"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedProducts":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ProductSummary"},"type":"array"},"page":{"example":1,"type":"number"},"pageSize":{"example":20,"type":"number"},"total":{"description":"Total matches across all pages","example":137,"type":"number"}},"required":["items","page","pageSize","total"],"type":"object"},"PagedReviews":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Review"},"type":"array"},"page":{"example":1,"type":"number"},"pageSize":{"example":20,"type":"number"},"total":{"example":7,"type":"number"}},"required":["items","page","pageSize","total"],"type":"object"},"PagedVendorOrders":{"properties":{"items":{"items":{"$ref":"#/components/schemas/VendorOrder"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedVendorProducts":{"properties":{"items":{"items":{"$ref":"#/components/schemas/VendorProduct"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PagedVendorSettlements":{"properties":{"items":{"items":{"$ref":"#/components/schemas/VendorSettlement"},"type":"array"},"page":{"example":1,"maximum":10000,"minimum":1,"type":"number"},"pageSize":{"example":20,"maximum":50,"minimum":1,"type":"number"},"total":{"description":"Total matching rows","example":137,"type":"number"}},"required":["page","pageSize","total","items"],"type":"object"},"PaymentMethod":{"enum":["COD","CARD"],"type":"string"},"PickupItem":{"properties":{"barcode":{"description":"Printed label the runner scans","example":"ES-1042-3","type":"string"},"id":{"description":"OrderItem id","format":"uuid","type":"string"},"name":{"$ref":"#/components/schemas/LocalizedText"},"pickedAt":{"format":"date-time","nullable":true,"type":"string"},"pickupStatus":{"allOf":[{"$ref":"#/components/schemas/PickupStatus"}],"example":"PENDING"},"productId":{"format":"uuid","type":"string"},"qty":{"example":2,"type":"number"}},"required":["id","productId","name","qty","barcode","pickupStatus","pickedAt"],"type":"object"},"PickupOrder":{"properties":{"acceptedAt":{"description":"When the order became VENDOR_ACCEPTED (its OrderEvent; falls back to the latest line acceptedAt). Null only for orders that never went through vendor acceptance.","format":"date-time","nullable":true,"type":"string"},"claimedByMe":{"example":false,"type":"boolean"},"code":{"example":"ES-1042","type":"string"},"customerArea":{"description":"Delivery area from the address snapshot","example":"Nərimanov","type":"string"},"itemCount":{"description":"Accepted lines (rejected lines are never listed)","example":3,"type":"number"},"orderId":{"format":"uuid","type":"string"},"pickedCount":{"description":"Accepted lines already PICKED or AT_HUB","example":1,"type":"number"},"placedAt":{"description":"Order.createdAt","format":"date-time","type":"string"},"runner":{"allOf":[{"$ref":"#/components/schemas/PickupRunner"}],"description":"Who holds the claim","nullable":true,"type":"object"},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}],"example":"VENDOR_ACCEPTED"},"stops":{"description":"One stop per vendor, sorted by market zone then stall number (natural order)","items":{"$ref":"#/components/schemas/PickupStop"},"type":"array"},"timeWindowEnd":{"format":"date-time","nullable":true,"type":"string"},"timeWindowStart":{"format":"date-time","nullable":true,"type":"string"},"zones":{"description":"Distinct zones of the stops, sorted","example":["A","B"],"items":{"$ref":"#/components/schemas/MarketZone"},"type":"array"}},"required":["orderId","code","status","placedAt","acceptedAt","timeWindowStart","timeWindowEnd","runner","claimedByMe","itemCount","pickedCount","zones","stops","customerArea"],"type":"object"},"PickupRunner":{"properties":{"id":{"format":"uuid","type":"string"},"name":{"example":"Rəşad Əliyev","nullable":true,"type":"string"}},"required":["id","name"],"type":"object"},"PickupStatus":{"enum":["PENDING","PICKED","AT_HUB"],"type":"string"},"PickupStop":{"properties":{"displayName":{"example":"Elşən Ticarət","type":"string"},"items":{"description":"Accepted lines of this vendor, barcode order","items":{"$ref":"#/components/schemas/PickupItem"},"type":"array"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"stallNumber":{"example":"A-114","type":"string"},"vendorId":{"format":"uuid","type":"string"}},"required":["vendorId","displayName","stallNumber","marketZone","items"],"type":"object"},"ProductCategoryRef":{"properties":{"id":{"format":"uuid","type":"string"},"name":{"$ref":"#/components/schemas/LocalizedText"},"slug":{"example":"kisi-geyimi","type":"string"}},"required":["id","slug","name"],"type":"object"},"ProductDetail":{"properties":{"barcode":{"example":"4761234567890","nullable":true,"type":"string"},"category":{"$ref":"#/components/schemas/ProductCategoryRef"},"categoryId":{"format":"uuid","type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"isActive":{"example":true,"type":"boolean"},"isFavorite":{"type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedText"},"photos":{"description":"Absolute photo URLs (object keys rendered under S3_PUBLIC_URL; may be empty)","example":["http://localhost:9000/esadarak-media/products/a114-01-1.jpg"],"items":{"type":"string"},"type":"array"},"priceQepik":{"description":"Unit price, integer qəpik","example":2490,"type":"number"},"ratingAvg":{"example":4.6,"nullable":true,"type":"number"},"ratingCount":{"example":12,"type":"number"},"sku":{"example":"A114-01","type":"string"},"stockQty":{"example":12,"type":"number"},"vendor":{"$ref":"#/components/schemas/ProductDetailVendor"},"volumeCm3":{"description":"Packed volume for the van capacity check","example":1500,"type":"number"}},"required":["id","sku","name","priceQepik","stockQty","photos","categoryId","vendor","ratingAvg","ratingCount","isFavorite","volumeCm3","barcode","isActive","category","createdAt"],"type":"object"},"ProductDetailVendor":{"properties":{"displayName":{"example":"Elşən Ticarət","type":"string"},"id":{"format":"uuid","type":"string"},"isVerified":{"type":"boolean"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"rating":{"description":"Vendor average rating 0–5 over order-level ratings (0 until the first rating)","example":4.4,"type":"number"},"ratingCount":{"description":"Number of rated orders (one order-level rating per order; item ratings are not counted)","example":87,"type":"number"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber","marketZone","isVerified","rating","ratingCount"],"type":"object"},"ProductSort":{"enum":["relevance","price_asc","price_desc","newest","rating"],"type":"string"},"ProductSummary":{"properties":{"categoryId":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"isFavorite":{"description":"true only when a valid bearer token is sent and the product is a favourite","type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedText"},"photos":{"description":"Absolute photo URLs (object keys rendered under S3_PUBLIC_URL; may be empty). Clients show a placeholder when the list is empty or an image fails to load.","example":["http://localhost:9000/esadarak-media/products/a114-01-1.jpg"],"items":{"type":"string"},"type":"array"},"priceQepik":{"description":"Unit price, integer qəpik","example":2490,"type":"number"},"ratingAvg":{"description":"Average rating 1–5, null until the first review. Not money.","example":4.6,"nullable":true,"type":"number"},"ratingCount":{"example":12,"type":"number"},"sku":{"example":"A114-01","type":"string"},"stockQty":{"description":"0 = out of stock","example":12,"type":"number"},"vendor":{"$ref":"#/components/schemas/ProductVendor"}},"required":["id","sku","name","priceQepik","stockQty","photos","categoryId","vendor","ratingAvg","ratingCount","isFavorite"],"type":"object"},"ProductVendor":{"properties":{"displayName":{"example":"Elşən Ticarət","type":"string"},"id":{"format":"uuid","type":"string"},"isVerified":{"type":"boolean"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber","marketZone","isVerified"],"type":"object"},"Promo":{"properties":{"id":{"example":"autumn-sale","type":"string"},"imageUrl":{"description":"Absolute image URL (a stored object key is rendered under S3_PUBLIC_URL)","example":"http://localhost:9000/esadarak-media/promos/autumn.jpg","nullable":true,"type":"string"},"subtitle":{"allOf":[{"$ref":"#/components/schemas/LocalizedText"}],"nullable":true,"type":"object"},"target":{"$ref":"#/components/schemas/PromoTarget"},"title":{"$ref":"#/components/schemas/LocalizedText"}},"required":["id","title","subtitle","imageUrl","target"],"type":"object"},"PromoTarget":{"properties":{"id":{"description":"For category / product / vendor targets","format":"uuid","type":"string"},"type":{"allOf":[{"$ref":"#/components/schemas/PromoTargetType"}],"example":"category"},"url":{"description":"For url targets","example":"https://esadarak.az/sale","type":"string"}},"required":["type"],"type":"object"},"PromoTargetType":{"enum":["category","product","vendor","url"],"type":"string"},"PromosResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Promo"},"type":"array"}},"required":["items"],"type":"object"},"QuoteDto":{"properties":{"address":{"allOf":[{"$ref":"#/components/schemas/AddressDto"}],"nullable":true,"type":"object"},"codAmountQepik":{"description":"Cash to collect at the door (COD)","example":5280,"type":"number"},"deliverable":{"description":"false when there is no address or it is out of zone","example":true,"type":"boolean"},"deliveryFeeQepik":{"example":300,"type":"number"},"items":{"items":{"$ref":"#/components/schemas/QuoteItemDto"},"type":"array"},"subtotalQepik":{"example":4980,"type":"number"},"timeWindows":{"description":"Next 3 delivery slots (Asia/Baku)","items":{"$ref":"#/components/schemas/TimeWindowDto"},"type":"array"},"totalQepik":{"example":5280,"type":"number"},"warnings":{"items":{"$ref":"#/components/schemas/QuoteWarning"},"type":"array"}},"required":["items","subtotalQepik","deliveryFeeQepik","totalQepik","codAmountQepik","address","deliverable","timeWindows","warnings"],"type":"object"},"QuoteItemDto":{"properties":{"available":{"description":"false when nothing of this product can be ordered","example":true,"type":"boolean"},"availableQty":{"description":"Units in stock right now (0 when inactive)","example":12,"type":"number"},"lineTotalQepik":{"description":"qty × unit price, qəpik","example":4980,"type":"number"},"name":{"$ref":"#/components/schemas/LocalizedText"},"productId":{"format":"uuid","type":"string"},"qty":{"description":"Quantity that CAN be ordered (reduced when stock is short)","example":2,"type":"number"},"unitPriceQepik":{"description":"qəpik","example":2490,"type":"number"}},"required":["productId","name","qty","unitPriceQepik","lineTotalQepik","available","availableQty"],"type":"object"},"QuoteRequestDto":{"properties":{"addressId":{"description":"Delivery address; the default address is used when omitted","format":"uuid","type":"string"},"items":{"items":{"$ref":"#/components/schemas/OrderItemInputDto"},"maxItems":50,"minItems":1,"type":"array"}},"required":["items"],"type":"object"},"QuoteWarning":{"enum":["NO_DEFAULT_ADDRESS","OUT_OF_ZONE","ITEM_UNAVAILABLE","QTY_REDUCED"],"type":"string"},"RateOrderDto":{"properties":{"comment":{"example":"Çox tez çatdırıldı, təşəkkürlər","maxLength":1000,"nullable":true,"type":"string"},"items":{"description":"Optional per-item ratings","items":{"$ref":"#/components/schemas/RateOrderItemDto"},"type":"array"},"rating":{"description":"Overall order rating","example":5,"maximum":5,"minimum":1,"type":"number"}},"required":["rating"],"type":"object"},"RateOrderItemDto":{"properties":{"orderItemId":{"description":"OrderItem id from GET /v1/orders/:id","format":"uuid","type":"string"},"rating":{"example":5,"maximum":5,"minimum":1,"type":"number"}},"required":["orderItemId","rating"],"type":"object"},"RatingResponseDto":{"properties":{"ok":{"example":true,"type":"boolean"}},"required":["ok"],"type":"object"},"RefreshTokenDto":{"properties":{"refreshToken":{"description":"The refresh token from verify/refresh","type":"string"}},"required":["refreshToken"],"type":"object"},"RejectOrderItemsDto":{"properties":{"itemIds":{"description":"Defaults to every PENDING line of this vendor","items":{"format":"uuid","type":"string"},"maxItems":50,"type":"array"},"note":{"description":"Free text, recorded on the order event when the rejection cancels the order","maxLength":500,"nullable":true,"type":"string"},"reason":{"allOf":[{"$ref":"#/components/schemas/RejectReason"}],"example":"OUT_OF_STOCK"}},"required":["reason"],"type":"object"},"RejectReason":{"enum":["OUT_OF_STOCK","PRICE_CHANGED","CLOSED","OTHER"],"type":"string"},"RejectSettlementRequest":{"properties":{"reason":{"example":"Qaytarılmış sifariş daxil edilib","maxLength":500,"minLength":3,"type":"string"}},"required":["reason"],"type":"object"},"ReorderCategoriesRequest":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CategoryReorderItem"},"maxItems":500,"type":"array"}},"required":["items"],"type":"object"},"RequestOtpDto":{"properties":{"phone":{"description":"Azerbaijani phone. 0501234567, 994501234567 and \"+994 50 123 45 67\" are normalised to E.164.","example":"+994501234567","type":"string"}},"required":["phone"],"type":"object"},"Review":{"properties":{"comment":{"example":"Çox keyfiyyətli məhsul, tez çatdırıldı.","nullable":true,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"customerName":{"description":"First name + last initial. Empty string when the customer has no name on file.","example":"Aysel M.","type":"string"},"id":{"format":"uuid","type":"string"},"rating":{"example":5,"maximum":5,"minimum":1,"type":"number"}},"required":["id","rating","comment","customerName","createdAt"],"type":"object"},"RouteJson":{"properties":{"polyline":{"description":"Encoded overview polyline (Google provider only)","type":"string"},"provider":{"enum":["google","haversine"],"example":"haversine","type":"string"},"stops":{"description":"Collapsed stops in visiting order","items":{"$ref":"#/components/schemas/RouteStop"},"type":"array"},"totalKm":{"description":"Round trip hub → stops → hub, km (float, not money)","example":31.2,"type":"number"}},"required":["provider","stops","totalKm"],"type":"object"},"RouteStop":{"properties":{"area":{"example":"20 Yanvar","type":"string"},"cumulativeKm":{"description":"Km from the hub up to this stop","example":8.488,"type":"number"},"lat":{"example":40.404,"type":"number"},"legKm":{"description":"Km from the previous stop (the hub for the first)","example":8.488,"type":"number"},"lng":{"example":49.808,"type":"number"},"orderCodes":{"example":["ES-1042","ES-1043"],"items":{"type":"string"},"type":"array"},"orderIds":{"items":{"format":"uuid","type":"string"},"type":"array"},"stopIndex":{"description":"0-based position along the route","example":0,"type":"number"},"street":{"example":"Zahid Xəlilov küçəsi 42, mənzil 7","type":"string"}},"required":["stopIndex","lat","lng","area","street","orderIds","orderCodes","legKm","cumulativeKm"],"type":"object"},"RunnerStats":{"properties":{"activeCount":{"description":"Orders I currently hold in VENDOR_ACCEPTED / RUNNER_PICKUP (not windowed)","example":1,"type":"number"},"claimedCount":{"description":"Orders claimed in the window (still held by me)","example":4,"type":"number"},"completedCount":{"description":"My orders that reached AT_HUB in the window","example":3,"type":"number"},"from":{"description":"Window start (00:00 Asia/Baku)","format":"date-time","type":"string"},"itemsPicked":{"description":"Lines I marked PICKED in the window","example":11,"type":"number"},"range":{"allOf":[{"$ref":"#/components/schemas/RunnerStatsRange"}],"example":"today"},"to":{"description":"Window end (now)","format":"date-time","type":"string"}},"required":["range","from","to","claimedCount","completedCount","itemsPicked","activeCount"],"type":"object"},"RunnerStatsRange":{"enum":["today","7d"],"type":"string"},"ScanProgress":{"properties":{"missing":{"description":"Sorted by order code, then barcode","items":{"$ref":"#/components/schemas/MissingScan"},"type":"array"},"scanned":{"example":5,"type":"number"},"total":{"description":"Accepted items of the batch","example":8,"type":"number"}},"required":["scanned","total","missing"],"type":"object"},"ScanRequest":{"properties":{"barcode":{"description":"Printed line label (order code + line number). Trimmed and upper-cased.","example":"ES-1042-3","maxLength":40,"minLength":3,"type":"string"}},"required":["barcode"],"type":"object"},"SettlementActorRef":{"properties":{"id":{"format":"uuid","type":"string"},"name":{"example":"Rəşad Quliyev","type":"string"}},"required":["id","name"],"type":"object"},"SettlementStatus":{"enum":["PENDING","APPROVED","REJECTED","PAID"],"type":"string"},"SettlementVendorRef":{"properties":{"displayName":{"example":"Şirin Meyvə","type":"string"},"id":{"format":"uuid","type":"string"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber"],"type":"object"},"StatsRange":{"enum":["today","7d","30d"],"type":"string"},"SuggestItem":{"properties":{"id":{"format":"uuid","type":"string"},"label":{"$ref":"#/components/schemas/LocalizedText"},"type":{"allOf":[{"$ref":"#/components/schemas/SuggestItemType"}],"example":"product"}},"required":["type","id","label"],"type":"object"},"SuggestItemType":{"enum":["product","category","vendor"],"type":"string"},"SuggestResponse":{"properties":{"items":{"description":"At most 10 items","items":{"$ref":"#/components/schemas/SuggestItem"},"type":"array"}},"required":["items"],"type":"object"},"TeleportDriverDto":{"properties":{"isOnline":{"example":true,"type":"boolean"},"lat":{"example":40.3452,"type":"number"},"lng":{"example":49.7441,"type":"number"}},"required":["lat","lng","isOnline"],"type":"object"},"TeleportResultDto":{"properties":{"driverId":{"format":"uuid","type":"string"},"isOnline":{"example":true,"type":"boolean"},"lastSeenAt":{"format":"date-time","type":"string"},"lat":{"example":40.3452,"type":"number"},"lng":{"example":49.7441,"type":"number"}},"required":["driverId","lat","lng","isOnline","lastSeenAt"],"type":"object"},"TimeWindowDto":{"properties":{"end":{"example":"2026-08-28T10:00:00.000Z","format":"date-time","type":"string"},"start":{"example":"2026-08-28T06:00:00.000Z","format":"date-time","type":"string"}},"required":["start","end"],"type":"object"},"UpdateAddressDto":{"properties":{"area":{"description":"District / area name","example":"Nərimanov","maxLength":80,"type":"string"},"details":{"description":"Entrance / floor / door hints for the driver. Send \"\" to clear.","example":"3-cü blok, 5-ci mərtəbə","maxLength":200,"nullable":true,"type":"string"},"isDefault":{"description":"Make this the default address. The first address is always the default.","example":true,"type":"boolean"},"label":{"description":"Free label chosen by the customer","example":"Ev","maxLength":40,"type":"string"},"lat":{"example":40.4041,"maximum":90,"minimum":-90,"type":"number"},"lng":{"example":49.8702,"maximum":180,"minimum":-180,"type":"number"},"street":{"example":"Ağa Nemətulla küçəsi 42, mənzil 7","maxLength":200,"type":"string"}},"type":"object"},"UpdateAdminProductRequest":{"properties":{"barcode":{"maxLength":64,"nullable":true,"type":"string"},"categoryId":{"format":"uuid","type":"string"},"isActive":{"type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedTextInput"},"photoKeys":{"items":{"type":"string"},"maxItems":8,"type":"array"},"priceQepik":{"example":2490,"maximum":100000000,"minimum":1,"type":"number"},"stockQty":{"example":12,"maximum":1000000,"minimum":0,"type":"number"},"volumeCm3":{"example":1000,"maximum":10000000,"minimum":1,"type":"number"}},"type":"object"},"UpdateAdminSettingRequest":{"properties":{"key":{"description":"Only whitelisted keys. An unknown key is answered by the service with ERR_SETTING_UNKNOWN (not ERR_VALIDATION), so the portal can say \"this build does not know that setting\".","enum":["delivery.defaultFeeQepik","delivery.allowOutOfZone","vendor.defaultCommissionBps","driver.payPerDeliveryQepik","driver.payPerKmQepik","dispatch.batchIntervalMin","dispatch.hubBacklogThreshold","dispatch.acceptTimeoutSec","dispatch.avgVanCapacity","dispatch.sameStopRadiusM","catalog.promos"],"type":"string"},"value":{"description":"Validated per key → ERR_SETTING_INVALID { key, expected }","example":20,"oneOf":[{"type":"number"},{"type":"boolean"},{"type":"string"},{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"}]}},"required":["key","value"],"type":"object"},"UpdateAdminUserDto":{"properties":{"blockedReason":{"description":"Stored with the block","maxLength":500,"type":"string"},"driver":{"$ref":"#/components/schemas/AdminDriverProfileInput"},"isBlocked":{"description":"true revokes every session immediately and refuses new logins","type":"boolean"},"locale":{"allOf":[{"$ref":"#/components/schemas/Locale"}]},"name":{"maxLength":120,"minLength":2,"type":"string"},"role":{"allOf":[{"$ref":"#/components/schemas/UserRole"}]}},"type":"object"},"UpdateAdminVendorDto":{"properties":{"commissionBps":{"example":1250,"maximum":10000,"minimum":0,"type":"number"},"displayName":{"maxLength":120,"minLength":2,"type":"string"},"isActive":{"type":"boolean"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}]},"stallNumber":{"example":"A-114","type":"string"}},"type":"object"},"UpdateCategoryDto":{"properties":{"name":{"$ref":"#/components/schemas/LocalizedTextInput"},"parentId":{"description":"null makes it a root; a descendant of itself → ERR_CATEGORY_CYCLE","format":"uuid","nullable":true,"type":"string"},"slug":{"maxLength":60,"type":"string"},"sortOrder":{"maximum":100000,"minimum":0,"type":"number"}},"type":"object"},"UpdateDispatchSettingsRequest":{"properties":{"acceptTimeoutSec":{"example":90,"maximum":3600,"minimum":1,"type":"number"},"avgVanCapacity":{"example":25,"maximum":500,"minimum":1,"type":"number"},"batchIntervalMin":{"example":20,"maximum":1440,"minimum":1,"type":"number"},"hubBacklogThreshold":{"example":15,"maximum":10000,"minimum":1,"type":"number"},"sameStopRadiusM":{"description":"Metres","example":25,"maximum":1000,"minimum":0,"type":"number"}},"type":"object"},"UpdateMeDto":{"properties":{"locale":{"allOf":[{"$ref":"#/components/schemas/Locale"}],"example":"ru"},"name":{"example":"Aysel Məmmədova","maxLength":80,"nullable":true,"type":"string"}},"type":"object"},"UpdateVendorProductDto":{"properties":{"barcode":{"maxLength":64,"nullable":true,"type":"string"},"categoryId":{"format":"uuid","type":"string"},"isActive":{"type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedTextInput"},"photoKeys":{"items":{"type":"string"},"maxItems":8,"type":"array"},"priceQepik":{"example":2490,"maximum":100000000,"minimum":1,"type":"number"},"stockQty":{"example":12,"maximum":1000000,"minimum":0,"type":"number"},"volumeCm3":{"example":1000,"maximum":10000000,"minimum":1,"type":"number"}},"type":"object"},"UpdateVendorProfileDto":{"properties":{"displayName":{"example":"Elşən Ticarət","maxLength":80,"minLength":2,"type":"string"}},"type":"object"},"UpdateZoneDto":{"properties":{"feeQepik":{"maximum":1000000,"minimum":0,"type":"number"},"isActive":{"type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedTextInput"},"polygon":{"$ref":"#/components/schemas/GeoJsonPolygon"}},"type":"object"},"UserDto":{"properties":{"id":{"example":"5f1c9e2a-7c3b-4d1e-9a2f-0b8c7d6e5f4a","format":"uuid","type":"string"},"locale":{"allOf":[{"$ref":"#/components/schemas/Locale"}],"example":"az"},"name":{"example":"Aysel Məmmədova","nullable":true,"type":"string"},"phone":{"description":"E.164","example":"+994501234567","type":"string"},"role":{"allOf":[{"$ref":"#/components/schemas/UserRole"}],"example":"CUSTOMER"}},"required":["id","phone","name","role","locale"],"type":"object"},"UserRole":{"enum":["CUSTOMER","SELLER","DRIVER","RUNNER","ADMIN","SUPPORT"],"type":"string"},"VehicleType":{"enum":["VAN","BIKE"],"type":"string"},"VendorAcceptance":{"enum":["PENDING","ACCEPTED","REJECTED","PARTIAL"],"type":"string"},"VendorOrder":{"properties":{"code":{"example":"ES-1042","type":"string"},"customerName":{"description":"Masked (first name + initial)","example":"Aysel M.","type":"string"},"items":{"description":"ONLY this vendor’s lines","items":{"$ref":"#/components/schemas/VendorOrderItem"},"type":"array"},"note":{"description":"Customer note","nullable":true,"type":"string"},"orderId":{"format":"uuid","type":"string"},"otherVendorsCount":{"description":"Other vendors with lines in the same order","example":1,"type":"number"},"placedAt":{"format":"date-time","type":"string"},"runner":{"allOf":[{"$ref":"#/components/schemas/VendorOrderRunner"}],"description":"Set once a runner claimed the pickup; null again on a CANCELLED order (a claim the customer cancelled is not a pickup)","nullable":true,"type":"object"},"status":{"allOf":[{"$ref":"#/components/schemas/OrderStatus"}],"description":"Order.status (whole order)"},"timeWindowEnd":{"format":"date-time","nullable":true,"type":"string"},"timeWindowStart":{"format":"date-time","nullable":true,"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"vendorAcceptance":{"allOf":[{"$ref":"#/components/schemas/VendorAcceptance"}],"example":"PENDING"},"vendorSubtotalQepik":{"description":"Sum of this vendor’s accepted + pending lines","example":4980,"type":"number"}},"required":["orderId","code","status","placedAt","customerName","items","vendorSubtotalQepik","vendorAcceptance","otherVendorsCount","timeWindowStart","timeWindowEnd","note","runner","updatedAt"],"type":"object"},"VendorOrderItem":{"properties":{"acceptance":{"allOf":[{"$ref":"#/components/schemas/ItemAcceptance"}]},"barcode":{"description":"Printed label","example":"ES-1042-1","type":"string"},"id":{"description":"OrderItem id — use in accept / reject","format":"uuid","type":"string"},"lineTotalQepik":{"example":4980,"type":"number"},"name":{"$ref":"#/components/schemas/LocalizedText"},"pickupStatus":{"allOf":[{"$ref":"#/components/schemas/PickupStatus"}]},"productId":{"format":"uuid","type":"string"},"qty":{"example":2,"type":"number"},"rejectReason":{"enum":["OUT_OF_STOCK","PRICE_CHANGED","CLOSED","OTHER",null],"example":null,"nullable":true,"type":"string"},"unitPriceQepik":{"example":2490,"type":"number"}},"required":["id","productId","name","qty","unitPriceQepik","lineTotalQepik","acceptance","rejectReason","pickupStatus","barcode"],"type":"object"},"VendorOrderRunner":{"properties":{"name":{"example":"Tural Quliyev","nullable":true,"type":"string"},"phoneMasked":{"example":"+99450*****30","type":"string"}},"required":["name","phoneMasked"],"type":"object"},"VendorOrderView":{"enum":["NEW","ACCEPTED","IN_PICKUP","COMPLETED","REJECTED","ALL"],"type":"string"},"VendorProduct":{"properties":{"barcode":{"example":"4761234567890","nullable":true,"type":"string"},"category":{"$ref":"#/components/schemas/VendorProductCategory"},"categoryId":{"format":"uuid","type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"isActive":{"description":"false = hidden from the catalog (soft-deleted or paused)","type":"boolean"},"name":{"$ref":"#/components/schemas/LocalizedText"},"photoKeys":{"description":"Storage keys — send these back in photoKeys when editing","example":["products/a114-01-1.jpg"],"items":{"type":"string"},"type":"array"},"photos":{"description":"Absolute URLs (S3_PUBLIC_URL + key)","example":["http://localhost:9000/esadarak-media/products/a114-01-1.jpg"],"items":{"type":"string"},"type":"array"},"priceQepik":{"description":"Unit price, integer qəpik","example":2490,"type":"number"},"ratingAvg":{"description":"Not money","example":4.5,"nullable":true,"type":"number"},"ratingCount":{"example":3,"type":"number"},"sku":{"description":"Generated: <stallNumber>-<4-digit sequence>","example":"A-114-0007","type":"string"},"stockQty":{"example":12,"type":"number"},"updatedAt":{"format":"date-time","type":"string"},"volumeCm3":{"description":"Packed volume for the dispatch capacity check","example":1500,"type":"number"}},"required":["id","sku","name","priceQepik","stockQty","categoryId","category","photos","photoKeys","volumeCm3","barcode","isActive","ratingAvg","ratingCount","createdAt","updatedAt"],"type":"object"},"VendorProductCategory":{"properties":{"id":{"format":"uuid","type":"string"},"name":{"$ref":"#/components/schemas/LocalizedText"},"slug":{"example":"kisi-geyimi","type":"string"}},"required":["id","slug","name"],"type":"object"},"VendorProductSort":{"enum":["newest","name","price_asc","price_desc","stock"],"type":"string"},"VendorProfile":{"properties":{"activeProductCount":{"example":12,"type":"number"},"commissionBps":{"description":"Platform commission in basis points (1250 = 12.50 %)","example":1250,"type":"number"},"displayName":{"example":"Elşən Ticarət","type":"string"},"id":{"format":"uuid","type":"string"},"isVerified":{"type":"boolean"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"productCount":{"description":"All products incl. inactive ones","example":14,"type":"number"},"rating":{"description":"Average of the order-level reviews (0 until rated)","example":4.6,"type":"number"},"ratingCount":{"description":"Number of rated orders","example":12,"type":"number"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber","marketZone","rating","ratingCount","isVerified","commissionBps","productCount","activeProductCount"],"type":"object"},"VendorPublic":{"properties":{"displayName":{"example":"Elşən Ticarət","type":"string"},"id":{"format":"uuid","type":"string"},"isVerified":{"type":"boolean"},"marketZone":{"allOf":[{"$ref":"#/components/schemas/MarketZone"}],"example":"A"},"productCount":{"description":"Active products","example":10,"type":"number"},"rating":{"description":"Average rating 0–5 over order-level ratings (0 until the first rating)","example":4.4,"type":"number"},"ratingCount":{"description":"Number of rated orders (one order-level rating per order; item ratings are not counted)","example":87,"type":"number"},"stallNumber":{"example":"A-114","type":"string"}},"required":["id","displayName","stallNumber","marketZone","rating","ratingCount","isVerified","productCount"],"type":"object"},"VendorSettlement":{"properties":{"commissionBps":{"description":"Commission rate captured when the row was generated","example":1250,"type":"number"},"commissionQepik":{"example":23063,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"grossSalesQepik":{"example":184500,"type":"number"},"id":{"format":"uuid","type":"string"},"linesCount":{"description":"Settled order lines","example":27,"type":"number"},"ordersCount":{"description":"Distinct orders behind the settled lines","example":12,"type":"number"},"paidAt":{"description":"Set when the payout was made","format":"date-time","nullable":true,"type":"string"},"paymentReference":{"description":"Bank transfer reference recorded with the payout","example":"KAPBANK-2026-08-24-118","nullable":true,"type":"string"},"payoutAmountQepik":{"example":161437,"type":"number"},"periodEnd":{"description":"Next Monday 00:00 Asia/Baku (exclusive)","format":"date-time","type":"string"},"periodStart":{"description":"Monday 00:00 Asia/Baku","format":"date-time","type":"string"},"rejectReason":{"description":"Why the settlement was sent back (REJECTED only)","nullable":true,"type":"string"},"status":{"allOf":[{"$ref":"#/components/schemas/SettlementStatus"}],"description":"PENDING → APPROVED → PAID; REJECTED sends the period back for regeneration"}},"required":["id","periodStart","periodEnd","grossSalesQepik","commissionQepik","payoutAmountQepik","commissionBps","ordersCount","linesCount","status","paidAt","paymentReference","rejectReason","createdAt"],"type":"object"},"VendorSettlementPreview":{"properties":{"commissionBps":{"example":1250,"type":"number"},"commissionQepik":{"description":"gross × bps / 10 000, rounded half up","example":5350,"type":"number"},"grossSalesQepik":{"description":"Accepted lines of orders DELIVERED in the period","example":42800,"type":"number"},"ordersCount":{"example":4,"type":"number"},"payoutAmountQepik":{"example":37450,"type":"number"},"periodEnd":{"description":"Next Monday 00:00 Asia/Baku (exclusive)","format":"date-time","type":"string"},"periodStart":{"description":"Monday 00:00 Asia/Baku of the current ISO week","format":"date-time","type":"string"}},"required":["periodStart","periodEnd","grossSalesQepik","commissionBps","commissionQepik","payoutAmountQepik","ordersCount"],"type":"object"},"VendorStats":{"properties":{"acceptedCount":{"description":"Non-cancelled orders with ≥1 accepted line of mine","example":6,"type":"number"},"avgLineValueQepik":{"description":"revenue / accepted lines (0 when none)","example":4361,"type":"number"},"byDay":{"description":"One entry per day of the range, oldest first","items":{"$ref":"#/components/schemas/VendorStatsDay"},"type":"array"},"deliveredRevenueQepik":{"description":"Accepted lines of DELIVERED orders","example":31200,"type":"number"},"from":{"description":"00:00 Asia/Baku of the first day","format":"date-time","type":"string"},"ordersCount":{"description":"Orders (placed in range) containing my lines","example":9,"type":"number"},"pendingCount":{"description":"PLACED orders where I still have PENDING lines","example":2,"type":"number"},"range":{"allOf":[{"$ref":"#/components/schemas/StatsRange"}]},"rejectedCount":{"description":"Orders where every line of mine was rejected, or CANCELLED","example":1,"type":"number"},"revenueQepik":{"description":"Accepted lines of non-cancelled orders","example":52340,"type":"number"},"to":{"description":"now","format":"date-time","type":"string"},"topItems":{"description":"Top 5 by revenue","items":{"$ref":"#/components/schemas/VendorStatsTopItem"},"type":"array"}},"required":["range","from","to","ordersCount","pendingCount","acceptedCount","rejectedCount","revenueQepik","deliveredRevenueQepik","avgLineValueQepik","topItems","byDay"],"type":"object"},"VendorStatsDay":{"properties":{"date":{"description":"Asia/Baku calendar day","example":"2026-08-27","type":"string"},"ordersCount":{"example":3,"type":"number"},"revenueQepik":{"example":12480,"type":"number"}},"required":["date","ordersCount","revenueQepik"],"type":"object"},"VendorStatsTopItem":{"properties":{"name":{"$ref":"#/components/schemas/LocalizedText"},"productId":{"format":"uuid","type":"string"},"qty":{"description":"Units in accepted lines","example":7,"type":"number"},"revenueQepik":{"example":17430,"type":"number"}},"required":["productId","name","qty","revenueQepik"],"type":"object"},"VerifyOtpDto":{"properties":{"code":{"description":"6-digit code from the SMS","example":"483920","maxLength":6,"minLength":6,"type":"string"},"phone":{"description":"Azerbaijani phone. 0501234567, 994501234567 and \"+994 50 123 45 67\" are normalised to E.164.","example":"+994501234567","type":"string"}},"required":["phone","code"],"type":"object"},"VerifyVendorDto":{"properties":{"isVerified":{"description":"true = verified, false = revoke the verification","type":"boolean"},"note":{"description":"Recorded in the audit entry","maxLength":500,"type":"string"}},"required":["isVerified"],"type":"object"}},"securitySchemes":{"bearer":{"bearerFormat":"JWT","description":"Access token from /v1/auth/otp/verify or /v1/auth/refresh","scheme":"bearer","type":"http"}}},"info":{"contact":{},"description":"Marketplace + logistics platform for Sadarak Trade Center, Baku.\n\n- All money fields are **integers in qəpik** and end in `Qepik` (1 ₼ = 100 qəpik).\n- Every error is `{ statusCode, code, message, details? }` — switch on `code` (`ERR_*`); `message` is developer-only English.\n- Phones are E.164 (`+994XXXXXXXXX`).\n- Realtime: Socket.IO namespace `/realtime`, handshake `auth: { token: <accessToken> }`.","title":"E-Sadarak API","version":"0.1.0"},"openapi":"3.0.0","paths":{"/health":{"get":{"operationId":"getHealth","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponseDto"}}},"description":""},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponseDto"}}},"description":"A dependency is down"}},"summary":"Liveness + dependency check (Postgres, Redis)","tags":["health"]}},"/media/{key}":{"get":{"description":"Streams a public object (products/…) from storage through the API. This is what a photo URL of the form `/media/<key>` points at when the API runs without S3_PUBLIC_URL; with S3_PUBLIC_URL set photos are absolute bucket URLs and this route is not linked. Honours If-None-Match (304), sends ETag / Last-Modified / Cache-Control. Not under /v1.","operationId":"getMediaObject","parameters":[{"description":"Storage key. `products/…` is public; `proofs/<driverId>/<file>` needs a bearer token (ADMIN / SUPPORT, or the DRIVER who uploaded it). Anything else is 404.","in":"path","name":"key","required":true,"schema":{"example":"products/a101-03-1.jpg"}}],"responses":{"200":{"content":{"image/jpeg":{"schema":{"format":"binary","type":"string"}},"image/png":{"schema":{"format":"binary","type":"string"}},"image/webp":{"schema":{"format":"binary","type":"string"}}},"description":"The object"},"304":{"description":"Not modified (If-None-Match matched the ETag)"},"401":{"content":{"image/jpeg":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/png":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/webp":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"Delivery proof photos (`proofs/…`) are private: 401 ERR_UNAUTHORIZED without a bearer token, 403 ERR_FORBIDDEN for a driver who did not take the photo (ADMIN and SUPPORT read every proof). Product keys stay public and ignore the header."},"403":{"content":{"image/jpeg":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/png":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/webp":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"Delivery proof photos (`proofs/…`) are private: 401 ERR_UNAUTHORIZED without a bearer token, 403 ERR_FORBIDDEN for a driver who did not take the photo (ADMIN and SUPPORT read every proof). Product keys stay public and ignore the header."},"404":{"content":{"image/jpeg":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/png":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/webp":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown object, or a key outside the served prefixes"},"503":{"content":{"image/jpeg":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/png":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}},"image/webp":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_MEDIA_UNAVAILABLE — storage not configured or unreachable"}},"summary":"Photo bytes (relative media mode)","tags":["media"]},"head":{"operationId":"headMediaObject","parameters":[{"description":"Storage key. `products/…` is public; `proofs/<driverId>/<file>` needs a bearer token (ADMIN / SUPPORT, or the DRIVER who uploaded it). Anything else is 404.","in":"path","name":"key","required":true,"schema":{"example":"products/a101-03-1.jpg"}}],"responses":{"200":{"description":"Headers of the object (no body)"},"304":{"description":"Not modified"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"Delivery proof photos (`proofs/…`) are private: 401 ERR_UNAUTHORIZED without a bearer token, 403 ERR_FORBIDDEN for a driver who did not take the photo (ADMIN and SUPPORT read every proof). Product keys stay public and ignore the header."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"Delivery proof photos (`proofs/…`) are private: 401 ERR_UNAUTHORIZED without a bearer token, 403 ERR_FORBIDDEN for a driver who did not take the photo (ADMIN and SUPPORT read every proof). Product keys stay public and ignore the header."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_MEDIA_UNAVAILABLE"}},"summary":"Photo headers only (relative media mode)","tags":["media"]}},"/v1/admin/analytics/orders-per-day":{"get":{"operationId":"getAnalyticsOrdersPerDay","parameters":[{"description":"First Baku calendar day (inclusive). Default: 29 days before `to`.","in":"query","name":"from","required":false,"schema":{"example":"2026-07-29","type":"string"}},{"description":"Last Baku calendar day (inclusive). Default: today in Asia/Baku.","in":"query","name":"to","required":false,"schema":{"example":"2026-08-27","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsOrdersPerDay"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN or SUPPORT"}},"security":[{"bearer":[]}],"summary":"The per-day series alone (one point per Baku day, gaps filled with zeros)","tags":["admin-analytics"]}},"/v1/admin/analytics/overview":{"get":{"description":"Default range: the last 30 Asia/Baku calendar days ending today. GMV = Σ totalQepik of DELIVERED orders by deliveredAt; ordersPlaced counts by createdAt; ordersCancelled / ordersFailed count the distinct orders that entered that status inside the range; statusBreakdown is the current status of the orders created in the range. Everything is computed in SQL with Asia/Baku day bucketing.","operationId":"getAnalyticsOverview","parameters":[{"description":"First Baku calendar day (inclusive). Default: 29 days before `to`.","in":"query","name":"from","required":false,"schema":{"example":"2026-07-29","type":"string"}},{"description":"Last Baku calendar day (inclusive). Default: today in Asia/Baku.","in":"query","name":"to","required":false,"schema":{"example":"2026-08-27","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsOverview"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN or SUPPORT"}},"security":[{"bearer":[]}],"summary":"GMV, order counts, per-day series, delivery SLA, COD variance and the top lists","tags":["admin-analytics"]}},"/v1/admin/audit":{"get":{"description":"Every admin mutation, newest first. Each row carries the actor, the action, the target and the before/after snapshots the write recorded in its own transaction.","operationId":"listAdminAudit","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"description":"Filter by the acting admin","in":"query","name":"actorId","required":false,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"targetType","required":false,"schema":{"$ref":"#/components/schemas/AuditTargetType"}},{"description":"Exact id of the audited row","in":"query","name":"targetId","required":false,"schema":{"type":"string"}},{"description":"Exact action","in":"query","name":"action","required":false,"schema":{"example":"vendor.verify","type":"string"}},{"description":"createdAt ≥ from (inclusive)","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"createdAt < to (exclusive)","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAuditEntries"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Admin audit trail","tags":["admin"]}},"/v1/admin/categories":{"get":{"description":"Roots in sortOrder with their children, direct product counts and the subtree count the catalog shows.","operationId":"listAdminCategories","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCategoryTree"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Category tree","tags":["admin"]},"post":{"description":"The slug is derived from the Azerbaijani name when omitted (unique).","operationId":"createCategory","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCategory"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_CATEGORY_INVALID — unknown parentId"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_CONFLICT { reason: slug_taken }"}},"security":[{"bearer":[]}],"summary":"Create a category","tags":["admin"]}},"/v1/admin/categories/reorder":{"post":{"description":"Bulk sortOrder write in one transaction; the public catalog cache is invalidated so the new order is live immediately.","operationId":"reorderCategories","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderCategoriesRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCategoryTree"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_CATEGORY_INVALID — unknown id"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Reorder categories","tags":["admin"]}},"/v1/admin/categories/{id}":{"delete":{"description":"Refused with ERR_CATEGORY_NOT_EMPTY while it has products or children.","operationId":"deleteCategory","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_CATEGORY_NOT_EMPTY { productCount, childCount }"}},"security":[{"bearer":[]}],"summary":"Delete an empty category","tags":["admin"]},"patch":{"description":"Names, slug, sortOrder and the parent (a descendant of itself → ERR_CATEGORY_CYCLE).","operationId":"updateCategory","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCategoryDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCategory"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_CATEGORY_CYCLE · ERR_CATEGORY_INVALID"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_CONFLICT { reason: slug_taken }"}},"security":[{"bearer":[]}],"summary":"Update a category","tags":["admin"]}},"/v1/admin/orders":{"get":{"description":"Free text matches the order code or a customer phone fragment; plus status, vendor, zone and a createdAt range. SUPPORT may read.","operationId":"listAdminOrders","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"in":"query","name":"status","required":false,"schema":{"$ref":"#/components/schemas/OrderStatus"}},{"description":"Order code (ES-1042, case-insensitive) or a customer phone fragment","in":"query","name":"q","required":false,"schema":{"maxLength":40,"type":"string"}},{"in":"query","name":"vendorId","required":false,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"zoneId","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"createdAt ≥ from","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"createdAt < to","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":"string"}},{"in":"query","name":"sort","required":false,"schema":{"$ref":"#/components/schemas/AdminOrderSort"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAdminOrders"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"}},"security":[{"bearer":[]}],"summary":"Search orders","tags":["admin"]}},"/v1/admin/orders/{id}":{"get":{"description":"Totals, the address snapshot, lines with vendor / acceptance / pickup, the event timeline with its actors, the batch and driver, the runner and the audit tail. SUPPORT may read.","operationId":"getAdminOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrderDetail"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Order detail","tags":["admin"]}},"/v1/admin/orders/{id}/cancel":{"post":{"description":"Restores the stock of every non-rejected line, frees the runner claim and moves the order to CANCELLED. An order already in an active batch answers ERR_INVALID_TRANSITION { reason: in_active_batch, batchCode } — cancel the batch first.","operationId":"cancelAdminOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrderCancelRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrderDetail"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_INVALID_TRANSITION { from, to, reason, batchCode? }"}},"security":[{"bearer":[]}],"summary":"Cancel an order","tags":["admin"]}},"/v1/admin/orders/{id}/transition":{"post":{"description":"Runs through OrderStateMachine with actor ADMIN — only edges the graph allows. Cancelling is a separate endpoint because it also restores stock.","operationId":"transitionAdminOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrderTransitionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrderDetail"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_INVALID_TRANSITION { from, to }"}},"security":[{"bearer":[]}],"summary":"Move an order (ADMIN actor)","tags":["admin"]}},"/v1/admin/ping":{"get":{"operationId":"adminPing","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminPingResponseDto"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":""},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":""}},"security":[{"bearer":[]}],"summary":"Role-guard probe: requires an ADMIN access token","tags":["admin"]}},"/v1/admin/products":{"get":{"description":"Free text on names / SKU plus vendor, category, price and isActive filters. SUPPORT may read.","operationId":"listAdminProducts","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"description":"Any name or the SKU, case-insensitive","in":"query","name":"q","required":false,"schema":{"maxLength":80,"type":"string"}},{"in":"query","name":"vendorId","required":false,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"categoryId","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"Omit for active and inactive products","in":"query","name":"isActive","required":false,"schema":{"type":"boolean"}},{"in":"query","name":"minPriceQepik","required":false,"schema":{"maximum":100000000,"minimum":0,"type":"number"}},{"in":"query","name":"maxPriceQepik","required":false,"schema":{"maximum":100000000,"minimum":0,"type":"number"}},{"in":"query","name":"sort","required":false,"schema":{"$ref":"#/components/schemas/VendorProductSort"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAdminProducts"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"}},"security":[{"bearer":[]}],"summary":"Search products across every vendor","tags":["admin"]},"post":{"description":"The SKU is generated under the vendor's advisory lock exactly as the seller app does. photoKeys may come from `products/admin/…` or the vendor's own prefix.","operationId":"createAdminProduct","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdminProductRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProduct"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_CATEGORY_INVALID · ERR_MEDIA_INVALID"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown vendor"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SKU_CONFLICT"}},"security":[{"bearer":[]}],"summary":"Create a product on behalf of a vendor","tags":["admin"]}},"/v1/admin/products/{id}":{"delete":{"description":"Sets isActive=false — order history keeps the row, the catalog stops showing it.","operationId":"deleteAdminProduct","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProduct"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Soft-delete a product","tags":["admin"]},"get":{"operationId":"getAdminProduct","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProduct"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Product detail","tags":["admin"]},"patch":{"description":"Names, price, stock, category, photos, volume, barcode and isActive.","operationId":"updateAdminProduct","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminProductRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProduct"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_CATEGORY_INVALID · ERR_MEDIA_INVALID"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Update any product","tags":["admin"]}},"/v1/admin/settings":{"get":{"description":"The whitelisted keys with their current value (or the default when the row is absent), type, group and description.","operationId":"listAdminSettings","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSettingList"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Editable platform settings","tags":["admin"]},"patch":{"description":"Per-key validation. `dispatch.*` writes go through the dispatch settings service, so a changed batchIntervalMin re-registers the repeatable batching job exactly like PATCH /v1/dispatch/settings.","operationId":"updateAdminSetting","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminSettingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSetting"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SETTING_UNKNOWN { key } · ERR_SETTING_INVALID { key, expected } · ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Update one setting","tags":["admin"]}},"/v1/admin/settlements":{"get":{"description":"`periodStart` accepts any instant inside an ISO week and matches that week exactly.","operationId":"listSettlements","parameters":[{"in":"query","name":"status","required":false,"schema":{"$ref":"#/components/schemas/SettlementStatus"}},{"in":"query","name":"vendorId","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"Any instant inside an ISO week — matches that week exactly","in":"query","name":"periodStart","required":false,"schema":{"format":"date-time","type":"string"}},{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAdminSettlements"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"List settlements, newest period first","tags":["admin-settlements"]}},"/v1/admin/settlements/generate":{"post":{"description":"Period = an ISO week in Asia/Baku (Monday 00:00 → next Monday 00:00); the default is the most recent COMPLETED week. Per vendor the lines are the ACCEPTED order items of orders DELIVERED inside the period that are not already settled (SettlementLine.orderItemId is UNIQUE, so a line can never be settled twice); gross = Σ line totals, commission = round-half-up(gross × Vendor.commissionBps / 10 000), payout = gross − commission. Vendors with no lines get no row, an existing (vendor, period) row is skipped, and a REJECTED row is refilled with the freed lines. Passing `vendorId` makes it an explicit single-vendor regenerate, where a PENDING/APPROVED/PAID row in the way is ERR_SETTLEMENT_EXISTS. Also runs every Monday 03:00 Asia/Baku as the BullMQ repeatable job `settlements:weekly`.","operationId":"generateSettlements","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSettlementsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSettlementsResult"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SETTLEMENT_PERIOD_OPEN · ERR_SETTLEMENT_EXISTS · ERR_CONFLICT"}},"security":[{"bearer":[]}],"summary":"Generate the settlements of one ISO week","tags":["admin-settlements"]}},"/v1/admin/settlements/summary":{"get":{"description":"Scoped to one ISO week when `periodStart` is given, otherwise all periods.","operationId":"getSettlementsSummary","parameters":[{"description":"Any instant inside an ISO week; absent = totals over every period","in":"query","name":"periodStart","required":false,"schema":{"format":"date-time","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSettlementsSummary"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Settlement totals by status","tags":["admin-settlements"]}},"/v1/admin/settlements/{id}":{"get":{"operationId":"getSettlement","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSettlementDetail"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Settlement detail with its settled lines","tags":["admin-settlements"]}},"/v1/admin/settlements/{id}/approve":{"post":{"operationId":"approveSettlement","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSettlementDetail"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SETTLEMENT_INVALID_STATE"}},"security":[{"bearer":[]}],"summary":"Approve a settlement for payout (PENDING → APPROVED)","tags":["admin-settlements"]}},"/v1/admin/settlements/{id}/mark-paid":{"post":{"operationId":"markSettlementPaid","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkSettlementPaidRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSettlementDetail"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SETTLEMENT_INVALID_STATE"}},"security":[{"bearer":[]}],"summary":"Record the payout transfer (APPROVED → PAID)","tags":["admin-settlements"]}},"/v1/admin/settlements/{id}/reject":{"post":{"description":"Deletes the settlement lines so the order items become unsettled again; the next generate for that period refills this same row (the (vendor, period) pair is unique).","operationId":"rejectSettlement","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectSettlementRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSettlementDetail"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SETTLEMENT_INVALID_STATE"}},"security":[{"bearer":[]}],"summary":"Reject a settlement (PENDING | APPROVED → REJECTED)","tags":["admin-settlements"]}},"/v1/admin/users":{"get":{"description":"Filters: role, free text on phone / name, isBlocked. SUPPORT may read.","operationId":"listAdminUsers","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"in":"query","name":"role","required":false,"schema":{"$ref":"#/components/schemas/UserRole"}},{"description":"Phone or name, case-insensitive","in":"query","name":"q","required":false,"schema":{"maxLength":80,"type":"string"}},{"in":"query","name":"isBlocked","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAdminUsers"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"}},"security":[{"bearer":[]}],"summary":"List users and staff","tags":["admin"]},"post":{"description":"ADMIN / SUPPORT / DRIVER / RUNNER. A DRIVER also gets a DriverPresence row (offline, parked at the hub). A phone that already exists → ERR_STAFF_PHONE_EXISTS (use PATCH).","operationId":"createAdminUser","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdminUserDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUser"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_STAFF_PHONE_EXISTS { phone, userId, role }"}},"security":[{"bearer":[]}],"summary":"Create a staff account","tags":["admin"]}},"/v1/admin/users/{id}":{"get":{"operationId":"getAdminUser","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUser"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"User detail","tags":["admin"]},"patch":{"description":"Name, role, locale, the driver profile and the block flag. Blocking revokes every session immediately — the user is then refused with ERR_USER_BLOCKED on verify / refresh / me / realtime. An admin cannot block or demote their own account.","operationId":"updateAdminUser","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminUserDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUser"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_CONFLICT { reason: self_block | self_role }"}},"security":[{"bearer":[]}],"summary":"Update a user","tags":["admin"]}},"/v1/admin/vendors":{"get":{"description":"Filters: free text (display name / stall / owner phone / owner name), isVerified, isActive, market zone. `sort=gmv` ranks by the 30-day delivered GMV. SUPPORT may read.","operationId":"listAdminVendors","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"description":"Case-insensitive match on display name, stall number or the owner phone / name","in":"query","name":"q","required":false,"schema":{"maxLength":80,"type":"string"}},{"in":"query","name":"isVerified","required":false,"schema":{"type":"boolean"}},{"in":"query","name":"isActive","required":false,"schema":{"type":"boolean"}},{"in":"query","name":"zone","required":false,"schema":{"$ref":"#/components/schemas/MarketZone"}},{"in":"query","name":"sort","required":false,"schema":{"$ref":"#/components/schemas/AdminVendorSort"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAdminVendors"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"}},"security":[{"bearer":[]}],"summary":"List vendors","tags":["admin"]},"post":{"description":"Creates the owner as a SELLER when the phone is unknown, upgrades a CUSTOMER when it is. A phone that already owns a vendor → ERR_CONFLICT; a staff / driver / runner phone → ERR_STAFF_PHONE_EXISTS.","operationId":"createAdminVendor","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdminVendorDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminVendor"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_CONFLICT (vendor_exists / stall_number_taken) · ERR_STAFF_PHONE_EXISTS"}},"security":[{"bearer":[]}],"summary":"Onboard a vendor","tags":["admin"]}},"/v1/admin/vendors/verification-queue":{"get":{"description":"Unverified vendors, oldest first. SUPPORT may read.","operationId":"listVendorVerificationQueue","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedAdminVendors"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"}},"security":[{"bearer":[]}],"summary":"Vendor verification queue","tags":["admin"]}},"/v1/admin/vendors/{id}":{"get":{"description":"The list row plus the ten most recent orders, the settlement rollup and the audit tail. SUPPORT may read.","operationId":"getAdminVendor","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminVendorDetail"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Vendor detail","tags":["admin"]},"patch":{"description":"Display name, stall number, market zone, commission (basis points) and the isActive kill-switch. An inactive vendor is hidden from the catalog and refuses new orders.","operationId":"updateAdminVendor","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminVendorDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminVendor"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_CONFLICT { reason: stall_number_taken }"}},"security":[{"bearer":[]}],"summary":"Update a vendor","tags":["admin"]}},"/v1/admin/vendors/{id}/verify":{"post":{"description":"Writes an AdminAuditLog row and pushes `vendor.verified { vendorId, isVerified, at }` to the vendor's realtime room.","operationId":"verifyVendor","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyVendorDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminVendor"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Verify or un-verify a vendor","tags":["admin"]}},"/v1/admin/zones":{"get":{"description":"Active zones first, cheapest first; with the address and 30-day order counts.","operationId":"listAdminZones","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminZoneList"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"List delivery zones","tags":["admin"]},"post":{"description":"The polygon must be one closed ring of [lng, lat] positions inside the Baku bounding box and free of self-intersections.","operationId":"createZone","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateZoneDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminZone"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_ZONE_INVALID_POLYGON { problems }"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Create a delivery zone","tags":["admin"]}},"/v1/admin/zones/resolve":{"get":{"description":"The cheapest ACTIVE zone containing (lat, lng) — the exact rule order creation uses; null when the point is outside every zone.","operationId":"resolveAdminZone","parameters":[{"in":"query","name":"lat","required":true,"schema":{"example":40.3452,"maximum":90,"minimum":-90,"type":"number"}},{"in":"query","name":"lng","required":true,"schema":{"example":49.7441,"maximum":180,"minimum":-180,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminZoneResolution"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Which zone covers this point?","tags":["admin"]}},"/v1/admin/zones/{id}":{"delete":{"description":"Soft delete (isActive=false) — existing orders keep their deliveryZoneId.","operationId":"deleteZone","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminZone"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Deactivate a delivery zone","tags":["admin"]},"patch":{"operationId":"updateZone","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateZoneDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminZone"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_ZONE_INVALID_POLYGON"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Update a delivery zone","tags":["admin"]}},"/v1/auth/logout":{"post":{"description":"Revokes the access token and its refresh token.","operationId":"logout","parameters":[],"responses":{"204":{"description":"Logged out"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"End this session","tags":["auth"]}},"/v1/auth/me":{"get":{"operationId":"getMe","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"Current user","tags":["auth"]},"patch":{"operationId":"updateMe","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"Update name / locale","tags":["auth"]}},"/v1/auth/otp/request":{"post":{"description":"Limits (sliding windows): 5 per phone / 10 min, OTP_IP_LIMIT (default 20) per client address / 10 min, OTP_GLOBAL_LIMIT (default 500) platform-wide / hour → 429 ERR_OTP_RATE_LIMITED with `details.retryAfterSec` and `details.scope` (phone | ip | global). With OTP_PROVIDER=mock the code is returned as `devCode`.","operationId":"requestOtp","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestOtpDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OtpRequestResponseDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_OTP_RATE_LIMITED"}},"summary":"Send a one-time code by SMS","tags":["auth"]}},"/v1/auth/otp/verify":{"post":{"description":"An unknown phone becomes a CUSTOMER. Codes are single-use, 5 attempts max.","operationId":"verifyOtp","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyOtpDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_OTP_INVALID · ERR_OTP_EXPIRED"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_OTP_TOO_MANY_ATTEMPTS"}},"summary":"Verify the code and open a session","tags":["auth"]}},"/v1/auth/refresh":{"post":{"description":"The presented refresh token is revoked and a new pair issued. Presenting it again within 30 s (a second tab racing the first) returns another fresh pair for the same lineage; after that, reuse revokes every session of the user and deny-lists their access tokens.","operationId":"refreshTokens","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthTokensDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"summary":"Rotate tokens","tags":["auth"]}},"/v1/catalog/categories":{"get":{"description":"productCount counts active products in the category and all descendants. Cached 60 s.","operationId":"listCategories","parameters":[{"description":"true → nested roots with `children`; false → flat pre-order list","in":"query","name":"tree","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoriesResponse"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"}},"security":[{},{"bearer":[]}],"summary":"Category list (flat) or tree","tags":["catalog"]}},"/v1/catalog/products":{"get":{"description":"Accent-insensitive trigram search over az/ru/en names (unaccent + pg_trgm; Nərimanov = Narimanov), category filter includes descendants, pageSize capped at 50. Public. When a valid bearer token is sent, `isFavorite` is filled; an invalid token is rejected with ERR_UNAUTHORIZED.","operationId":"searchProducts","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"description":"Values above 50 are capped","in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"in":"query","name":"sort","required":false,"schema":{"$ref":"#/components/schemas/ProductSort"}},{"description":"Accent-insensitive across az/ru/en names (Nərimanov = Narimanov, Kişi = Kisi) with typo tolerance","in":"query","name":"q","required":false,"schema":{"example":"kisi koyneyi","maxLength":100,"type":"string"}},{"description":"Includes all descendant categories","in":"query","name":"categoryId","required":false,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"vendorId","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"Inclusive, qəpik","in":"query","name":"minPriceQepik","required":false,"schema":{"example":500,"minimum":0,"type":"number"}},{"description":"Inclusive, qəpik","in":"query","name":"maxPriceQepik","required":false,"schema":{"example":5000,"minimum":0,"type":"number"}},{"description":"true → only stockQty > 0","in":"query","name":"inStock","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedProducts"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{},{"bearer":[]}],"summary":"Search / browse products","tags":["catalog"]}},"/v1/catalog/products/{id}":{"get":{"description":"Public. When a valid bearer token is sent, `isFavorite` is filled; an invalid token is rejected with ERR_UNAUTHORIZED.","operationId":"getProduct","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductDetail"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND (unknown or inactive)"}},"security":[{},{"bearer":[]}],"summary":"Product detail","tags":["catalog"]}},"/v1/catalog/products/{id}/reviews":{"get":{"operationId":"listReviews","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"description":"Values above 50 are capped","in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedReviews"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND (unknown or inactive)"}},"security":[{},{"bearer":[]}],"summary":"Product reviews, newest first (customer names masked)","tags":["catalog"]}},"/v1/catalog/promos":{"get":{"description":"From Setting 'catalog.promos'; empty list when the setting is absent.","operationId":"listPromos","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromosResponse"}}},"description":""}},"security":[{},{"bearer":[]}],"summary":"Home-screen promo cards","tags":["catalog"]}},"/v1/catalog/suggest":{"get":{"description":"Products, categories and vendors; at most 10 items; q shorter than 2 characters → empty list.","operationId":"suggest","parameters":[{"description":"At least 2 characters; shorter input returns an empty list","in":"query","name":"q","required":false,"schema":{"example":"kis","maxLength":100,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestResponse"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"}},"security":[{},{"bearer":[]}],"summary":"Search-as-you-type suggestions","tags":["catalog"]}},"/v1/catalog/vendors/{id}":{"get":{"operationId":"getVendor","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorPublic"}}},"description":""},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{},{"bearer":[]}],"summary":"Public vendor profile","tags":["catalog"]}},"/v1/catalog/vendors/{id}/products":{"get":{"description":"Public. When a valid bearer token is sent, `isFavorite` is filled; an invalid token is rejected with ERR_UNAUTHORIZED.","operationId":"listVendorProducts","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"description":"Values above 50 are capped","in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"in":"query","name":"sort","required":false,"schema":{"$ref":"#/components/schemas/ProductSort"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedProducts"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{},{"bearer":[]}],"summary":"A vendor's active products","tags":["catalog"]}},"/v1/dev/dispatch/tick":{"post":{"description":"Any authenticated user (the ADMIN route is POST /v1/dispatch/run). Not registered in production.","operationId":"devDispatchTick","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispatchRunResult"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"[dev] Run one dispatch tick","tags":["dev"]}},"/v1/dev/drivers/{driverId}/teleport":{"post":{"description":"Any authenticated user. Upserts the DRIVER’s presence (lastSeenAt = now, vehicle defaults kept) and the GEO locator exactly like a heartbeat would. Not registered in production.","operationId":"devTeleportDriver","parameters":[{"in":"path","name":"driverId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeleportDriverDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeleportResultDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_DRIVER_NOT_FOUND — not a DRIVER user"}},"security":[{"bearer":[]}],"summary":"[dev] Place a driver at a coordinate (simulations)","tags":["dev"]}},"/v1/dev/orders/{id}/advance":{"post":{"description":"Any authenticated user. Goes through OrderStateMachine with actor role ADMIN and note \"dev-advance\", and does the bookkeeping the real modules would have done so the order keeps flowing through the runner and dispatch flows: → VENDOR_ACCEPTED marks every PENDING line ACCEPTED (vendors module); → RUNNER_PICKUP assigns a runner (`runnerId`, else the order’s current runner, else the first seeded RUNNER user by phone) and marks every accepted line PICKED; → AT_HUB marks them AT_HUB (runner module). `runner.queue.updated` (available / picked / at_hub) is emitted like the real flow. Later statuses only transition (dispatch bookkeeping — batches, scans — belongs to the dispatch / drivers endpoints). Not registered in production.","operationId":"devAdvanceOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvanceOrderDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION — `runnerId` is not a RUNNER / ADMIN user"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_INVALID_TRANSITION {from, to}"}},"security":[{"bearer":[]}],"summary":"[dev] Move an order along its lifecycle","tags":["dev"]}},"/v1/dispatch/batches":{"get":{"description":"Newest first; optional status filter.","operationId":"listDispatchBatches","parameters":[{"in":"query","name":"status","required":false,"schema":{"$ref":"#/components/schemas/BatchStatus"}},{"in":"query","name":"page","required":false,"schema":{"default":1,"example":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"example":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedBatches"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"List batches","tags":["dispatch"]}},"/v1/dispatch/batches/{id}":{"get":{"description":"Orders (sequence order), status events, the stored route and the scan progress.","operationId":"getDispatchBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchDetail"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Batch detail","tags":["dispatch"]}},"/v1/dispatch/batches/{id}/cancel":{"post":{"description":"PENDING / OFFERED / ASSIGNED / **LOADING** / **EN_ROUTE** → CANCELLED (actor ADMIN) — the last two are the \"abort batch\" recovery path. In one transaction: every order that is not DELIVERED goes back to AT_HUB (a FAILED one → RETURNED) with the OrderEvent note `batch_aborted:<code>`, its BatchOrder is reset to PENDING / RETURNED, ScanEvents are deleted, the driver is freed and the pending offer-timeout job is dropped. DELIVERED stops keep their COD and driver pay. The returned orders are batching candidates again on the next tick.","operationId":"cancelDispatchBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchAdminActionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchDetail"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_BATCH_INVALID_TRANSITION (details { from, to }) — the batch is already COMPLETED or CANCELLED"}},"security":[{"bearer":[]}],"summary":"Cancel (abort) a batch","tags":["dispatch"]}},"/v1/dispatch/batches/{id}/unassign":{"post":{"description":"OFFERED / ASSIGNED / **LOADING** → PENDING (actor ADMIN); the driver is freed, the pending offer-timeout job is dropped, every ScanEvent is deleted and any order already SCANNED_TO_VAN goes back to AT_HUB (OrderEvent note `batch_aborted:<code>`). The route / distance bookkeeping is cleared and the next tick re-offers the same batch.","operationId":"unassignDispatchBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchAdminActionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchDetail"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_BATCH_INVALID_TRANSITION (details { from, to }) — not OFFERED / ASSIGNED / LOADING (an EN_ROUTE batch can only be aborted with /cancel)"}},"security":[{"bearer":[]}],"summary":"Take the batch away from its driver","tags":["dispatch"]}},"/v1/dispatch/overview":{"get":{"description":"Hub backlog (AT_HUB orders not in an active batch), batch counters, driver counters (online = heartbeat within 60 s), online driver positions, active batches and the dispatch settings. Realtime: ADMIN sockets join `role:ADMIN` and receive `batch.status`, `batch.assigned` and `admin.cod.variance`.","operationId":"getDispatchOverview","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispatchOverview"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Live ops overview","tags":["dispatch"]}},"/v1/dispatch/run":{"post":{"description":"Batching pass (k-means++ over the hub backlog, k = ceil(n / avgVanCapacity)) followed by offers for every PENDING batch to the nearest eligible driver. The same work the scheduler does every batchIntervalMin minutes and the backlog trigger does at hubBacklogThreshold. Ticks are serialised — a concurrent tick answers zeros.","operationId":"runDispatchTick","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispatchRunResult"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Run one dispatch tick now","tags":["dispatch"]}},"/v1/dispatch/settings":{"patch":{"description":"Partial update of dispatch.* (batchIntervalMin re-registers the repeatable tick). Driver pay rates are returned but edited through the Phase 4 admin settings.","operationId":"updateDispatchSettings","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDispatchSettingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispatchSettings"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — ADMIN only"}},"security":[{"bearer":[]}],"summary":"Update dispatch settings","tags":["dispatch"]}},"/v1/drivers/batches":{"get":{"description":"Newest first; optional status filter (e.g. COMPLETED for history).","operationId":"listDriverBatches","parameters":[{"in":"query","name":"status","required":false,"schema":{"$ref":"#/components/schemas/BatchStatus"}},{"in":"query","name":"page","required":false,"schema":{"default":1,"example":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"example":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedDriverBatches"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"}},"security":[{"bearer":[]}],"summary":"My batches","tags":["drivers"]}},"/v1/drivers/batches/current":{"get":{"description":"JSON `null` when I have none.","operationId":"getDriverCurrentBatch","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"}},"security":[{"bearer":[]}],"summary":"My current batch (ASSIGNED / LOADING / EN_ROUTE)","tags":["drivers"]}},"/v1/drivers/batches/offer":{"get":{"description":"JSON `null` when there is none (or it expired).","operationId":"getDriverOffer","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchOffer"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"}},"security":[{"bearer":[]}],"summary":"The offer currently held by me","tags":["drivers"]}},"/v1/drivers/batches/{id}":{"get":{"description":"Another driver’s batch is 403 ERR_FORBIDDEN — the same answer the scan / stop / complete routes give (a batch id is not a secret, and a driver who mistypes an id must not be able to probe which ids exist). An unknown id is 404.","operationId":"getDriverBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown batch"}},"security":[{"bearer":[]}],"summary":"One of my batches","tags":["drivers"]}},"/v1/drivers/batches/{id}/accept":{"post":{"description":"OFFERED → ASSIGNED (actor DRIVER); must be the offered driver and within acceptTimeoutSec. Sets DriverPresence.currentBatchId. Realtime `batch.assigned` to me + admins.","operationId":"acceptDriverBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND · ERR_DRIVER_NOT_FOUND (no presence yet)"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_BATCH_NOT_OFFERED (details { status }) · ERR_OFFER_EXPIRED (details { expiredAt }) · ERR_DRIVER_OFFLINE · ERR_DRIVER_BUSY (details { currentBatchId }) — defence in depth: the assignment path never offers a batch to a driver who already has one (the offer transaction re-checks it under a row lock on DriverPresence), so ERR_DRIVER_BUSY only fires if that invariant is ever broken from outside the API."}},"security":[{"bearer":[]}],"summary":"Accept the offered batch","tags":["drivers"]}},"/v1/drivers/batches/{id}/complete":{"post":{"description":"Every stop must be DELIVERED or FAILED. expected = Σ COD of delivered COD orders, collected = Σ collected; a non-zero variance REQUIRES varianceNote and is flagged to admins (`admin.cod.variance`). FAILED orders → RETURNED; EN_ROUTE → COMPLETED; I am free for the next offer.","operationId":"completeDriverBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteBatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_BATCH_INCOMPLETE (details { pending: orderCodes[] }) · ERR_COD_VARIANCE_NOTE_REQUIRED (details { expectedCodQepik, collectedCodQepik, codVarianceQepik }) · ERR_BATCH_NOT_ASSIGNED"}},"security":[{"bearer":[]}],"summary":"Complete the batch (COD reconciliation)","tags":["drivers"]}},"/v1/drivers/batches/{id}/decline":{"post":{"description":"OFFERED → PENDING (actor DRIVER); I am excluded from this batch for 1 h and the next-nearest driver is offered immediately.","operationId":"declineDriverBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclineOfferRequest"}}},"required":true},"responses":{"204":{"description":"Declined"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_BATCH_NOT_OFFERED (details { status })"}},"security":[{"bearer":[]}],"summary":"Decline the offered batch","tags":["drivers"]}},"/v1/drivers/batches/{id}/scan":{"post":{"description":"Idempotent. The barcode must belong to an accepted line of an order in THIS batch. The first scan moves ASSIGNED → LOADING; once every accepted item of an order is scanned that order becomes SCANNED_TO_VAN.","operationId":"scanDriverBatchItem","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriverScanRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SCAN_WRONG_BATCH (details { barcode, batchCode }) · ERR_BATCH_NOT_ASSIGNED (details { status }) — not ASSIGNED / LOADING"}},"security":[{"bearer":[]}],"summary":"Scan one item barcode at the hub","tags":["drivers"]}},"/v1/drivers/batches/{id}/scan-progress":{"get":{"operationId":"getDriverScanProgress","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanProgress"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Scan progress of my batch","tags":["drivers"]}},"/v1/drivers/batches/{id}/start-route":{"post":{"description":"HARD server-side gate: count(ScanEvent) must equal count(accepted items) — checked in SQL inside the same transaction as LOADING → EN_ROUTE. Route = Google Directions (optimize:true, ≤ 25 stops, 5 s timeout) or the haversine nearest-neighbour fallback; same-building orders collapse into one stop. Every order → EN_ROUTE.","operationId":"startDriverRoute","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SCAN_INCOMPLETE (details { scanned, total, missing[] }) · ERR_BATCH_NOT_ASSIGNED (details { status }) · ERR_ROUTE_UNAVAILABLE (details { orderIds }) — an order without coordinates"}},"security":[{"bearer":[]}],"summary":"Depart: compute the route and go EN_ROUTE","tags":["drivers"]}},"/v1/drivers/batches/{id}/stops/{orderId}/delivered":{"post":{"description":"Records the cash collected (must be 0 for CARD orders), the optional proof photo and the driver pay (payPerDeliveryQepik + round(legKm × payPerKmQepik), the leg shared across the stop’s orders). Order → DELIVERED.","operationId":"markDriverStopDelivered","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveredStopRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION (codCollectedQepik: card_must_be_zero)"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — batch, or the order is not in it"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_STOP_ALREADY_RESOLVED (details { orderId, deliveryStatus }) · ERR_BATCH_NOT_ASSIGNED (details { status }) — not EN_ROUTE"}},"security":[{"bearer":[]}],"summary":"Mark a stop delivered","tags":["drivers"]}},"/v1/drivers/batches/{id}/stops/{orderId}/failed":{"post":{"description":"Records the reason; order → FAILED. Retry once with /retry; otherwise it returns to the hub at completion.","operationId":"markDriverStopFailed","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailedStopRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_STOP_ALREADY_RESOLVED · ERR_BATCH_NOT_ASSIGNED"}},"security":[{"bearer":[]}],"summary":"Mark a stop failed","tags":["drivers"]}},"/v1/drivers/batches/{id}/stops/{orderId}/retry":{"post":{"description":"FAILED → EN_ROUTE (actor DRIVER); the stop is PENDING again. One retry per order: a second call answers 409 ERR_DELIVERY_RETRY_LIMIT (details { orderId, retries, limit }).","operationId":"retryDriverStop","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchForDriver"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — the batch is assigned to another driver"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_STOP_ALREADY_RESOLVED (not FAILED) · ERR_DELIVERY_RETRY_LIMIT (details { orderId, retries, limit }) · ERR_BATCH_NOT_ASSIGNED"}},"security":[{"bearer":[]}],"summary":"Retry a failed stop (once)","tags":["drivers"]}},"/v1/drivers/me":{"get":{"description":"Presence, current batch, pending offer, today’s deliveries and pay.","operationId":"getDriverProfile","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriverProfile"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"}},"security":[{"bearer":[]}],"summary":"My driver profile","tags":["drivers"]}},"/v1/drivers/me/earnings":{"get":{"description":"Delivered stops and pay over Asia/Baku days (today / 7d / 30d); perDelivery newest first, at most 100.","operationId":"getDriverEarnings","parameters":[{"in":"query","name":"range","required":false,"schema":{"$ref":"#/components/schemas/DriverEarningsRange"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriverEarnings"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"}},"security":[{"bearer":[]}],"summary":"My earnings","tags":["drivers"]}},"/v1/drivers/presence":{"post":{"description":"Upserts DriverPresence (lastSeenAt = now), updates the Redis GEO locator (removed when isOnline=false) and pushes `driver.location` to the customers of the EN_ROUTE orders of the current batch (at most once per 15 s). Returns the current batch and any pending offer. Realtime to `user:<driverId>`: `batch.offer`, `batch.offer.expired`, `batch.assigned`, `batch.status`.","operationId":"driverPresence","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriverPresenceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriverPresenceResponse"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — DRIVER only"}},"security":[{"bearer":[]}],"summary":"Presence heartbeat (every 15 s while online)","tags":["drivers"]}},"/v1/me/addresses":{"get":{"operationId":"listAddresses","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddressListDto"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"My saved addresses (default first)","tags":["me"]},"post":{"description":"The first address becomes the default automatically.","operationId":"createAddress","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAddressDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddressDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"Add an address","tags":["me"]}},"/v1/me/addresses/{id}":{"delete":{"description":"Deleting the default promotes the most recent remaining address.","operationId":"deleteAddress","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Delete an address","tags":["me"]},"patch":{"operationId":"updateAddress","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAddressDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddressDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Edit an address","tags":["me"]}},"/v1/me/addresses/{id}/default":{"post":{"operationId":"setDefaultAddress","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddressDto"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Make this the default address","tags":["me"]}},"/v1/me/favorites":{"get":{"operationId":"listFavorites","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"example":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"example":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedProducts"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"My favourite products (newest first)","tags":["me"]}},"/v1/me/favorites/{productId}":{"delete":{"operationId":"removeFavorite","parameters":[{"in":"path","name":"productId","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Favourite removed (or was not present)"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"}},"security":[{"bearer":[]}],"summary":"Remove a favourite (idempotent)","tags":["me"]},"put":{"operationId":"addFavorite","parameters":[{"in":"path","name":"productId","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Favourite stored"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown or inactive product"}},"security":[{"bearer":[]}],"summary":"Add a favourite (idempotent)","tags":["me"]}},"/v1/media/uploads":{"post":{"description":"Returns a 10-minute presigned PUT. Upload the bytes with exactly the returned headers, then reference `key` (a product’s photoKeys, or a delivered stop’s proofPhotoKey). kind product: sellers get products/<vendorId>/…, admins products/admin/…. kind proof (Phase 3): drivers get proofs/<driverId>/…, admins proofs/admin/….","operationId":"createUpload","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUploadTicket"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_MEDIA_INVALID"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not SELLER/ADMIN/DRIVER, or a kind the role may not use) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_MEDIA_UNAVAILABLE — object storage is not configured"}},"security":[{"bearer":[]}],"summary":"Presigned direct upload for a product or proof-of-delivery photo","tags":["media"]}},"/v1/orders":{"get":{"operationId":"listOrders","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"example":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"example":20,"maximum":50,"minimum":1,"type":"number"}},{"in":"query","name":"status","required":false,"schema":{"$ref":"#/components/schemas/OrderStatus"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageOfOrderSummaryDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — customer routes"}},"security":[{"bearer":[]}],"summary":"My orders, newest first","tags":["orders"]},"post":{"description":"Decrements stock atomically, snapshots the address, assigns ES-<n>. Replay with the same Idempotency-Key returns 200 and the same order; a different payload under the same key is 409.","operationId":"createOrder","parameters":[{"description":"Client-generated UUID (RFC 4122 text form, case-insensitive). Same key + same payload → 200 with the existing order. Missing → ERR_IDEMPOTENCY_KEY_REQUIRED; not a uuid → ERR_VALIDATION (field Idempotency-Key).","in":"header","name":"Idempotency-Key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDto"}}},"description":"Idempotent replay"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION (incl. a non-uuid Idempotency-Key or a timeWindowStart/End pair that is not one of the quote timeWindows) · ERR_IDEMPOTENCY_KEY_REQUIRED · ERR_NO_DEFAULT_ADDRESS · ERR_OUT_OF_ZONE · ERR_PAYMENT_METHOD_UNAVAILABLE"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — customer routes"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_OUT_OF_STOCK (details [{productId, requested, available}]) · ERR_PRODUCT_UNAVAILABLE · ERR_IDEMPOTENCY_CONFLICT"}},"security":[{"bearer":[]}],"summary":"One-click order (COD)","tags":["orders"]}},"/v1/orders/quote":{"post":{"description":"No writes. Availability, delivery fee (zone or default), warnings and the next 3 delivery windows in Asia/Baku.","operationId":"quoteOrder","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequestDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — customer routes"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown product or address"}},"security":[{"bearer":[]}],"summary":"Price a basket before ordering","tags":["orders"]}},"/v1/orders/{id}":{"get":{"description":"Customers see their own orders; ADMIN and SUPPORT may read any order.","operationId":"getOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDto"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — customer routes"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND (also for orders of other customers)"}},"security":[{"bearer":[]}],"summary":"Order detail with timeline","tags":["orders"]}},"/v1/orders/{id}/cancel":{"post":{"operationId":"cancelOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — customer routes"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_CANCELLABLE"}},"security":[{"bearer":[]}],"summary":"Cancel (PLACED or VENDOR_ACCEPTED only); stock is restored","tags":["orders"]}},"/v1/orders/{id}/rating":{"post":{"description":"Creates one order-level review per vendor in the order plus optional per-item reviews. Vendor.rating/ratingCount aggregate the order-level reviews (one per rated order); Product.ratingAvg/ratingCount aggregate the per-item reviews.","operationId":"rateOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateOrderDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatingResponseDto"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — customer routes"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_RATABLE · ERR_ALREADY_RATED"}},"security":[{"bearer":[]}],"summary":"Rate a delivered order once","tags":["orders"]}},"/v1/orders/{id}/reorder":{"post":{"operationId":"reorderOrder","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteDto"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — customer routes"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"}},"security":[{"bearer":[]}],"summary":"Quote the same items again at current prices and availability","tags":["orders"]}},"/v1/runner/items/{orderItemId}/picked":{"post":{"description":"The order must be claimed by me and the line accepted + PENDING. The first picked line moves the order VENDOR_ACCEPTED → RUNNER_PICKUP (actor RUNNER) in the same transaction. Picking an already-picked line is a 200 no-op.","operationId":"markRunnerItemPicked","parameters":[{"in":"path","name":"orderItemId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickupOrder"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown line"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_CLAIMED (details { status }) · ERR_ITEM_NOT_PICKABLE (details { orderItemId, acceptance, pickupStatus }) — rejected / undecided line, or already at the hub"}},"security":[{"bearer":[]}],"summary":"Mark one line as picked at the stall","tags":["runner"]}},"/v1/runner/me/stats":{"get":{"description":"Counts over Asia/Baku calendar days (today or the last 7 days). activeCount is not windowed.","operationId":"getRunnerStats","parameters":[{"description":"Asia/Baku calendar days: today, or the last 7 days including today","in":"query","name":"range","required":false,"schema":{"$ref":"#/components/schemas/RunnerStatsRange"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerStats"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"}},"security":[{"bearer":[]}],"summary":"My pickup statistics","tags":["runner"]}},"/v1/runner/orders/{orderId}":{"get":{"description":"A RUNNER sees orders in VENDOR_ACCEPTED / RUNNER_PICKUP / AT_HUB and any order they claimed (also after a cancel); other orders are 404. ADMIN reads any order.","operationId":"getRunnerOrder","parameters":[{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickupOrder"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown id, or an order outside the pickup flow not held by me"}},"security":[{"bearer":[]}],"summary":"One order as a pickup sheet (accepted lines grouped by stall)","tags":["runner"]}},"/v1/runner/orders/{orderId}/at-hub":{"post":{"description":"Every accepted line must be PICKED; all lines become AT_HUB and the order moves RUNNER_PICKUP → AT_HUB (actor RUNNER). Already AT_HUB → 200 no-op.","operationId":"markRunnerOrderAtHub","parameters":[{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickupOrder"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_CLAIMED (details { status }) · ERR_PICKUP_INCOMPLETE (details { missing: [{ orderItemId, barcode }] }; { missing: [], reason: \"no_accepted_lines\" } when nothing is accepted) · ERR_INVALID_TRANSITION (details { from, to }) once the order is past the hub"}},"security":[{"bearer":[]}],"summary":"Hand the order over at the Sadarak hub","tags":["runner"]}},"/v1/runner/orders/{orderId}/claim":{"post":{"description":"Atomic (UPDATE … WHERE runnerId IS NULL AND status = VENDOR_ACCEPTED AND the order has ≥ 1 accepted line — the queue rule): of two simultaneous claims exactly one succeeds. Re-claiming my own order is a 200 no-op.","operationId":"claimRunnerOrder","parameters":[{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickupOrder"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_ALREADY_CLAIMED (details { runnerId }) · ERR_CONFLICT (details { status }) when the order is not VENDOR_ACCEPTED, or { status, reason: \"no_accepted_lines\" } when it has nothing to pick up"}},"security":[{"bearer":[]}],"summary":"Claim a VENDOR_ACCEPTED order","tags":["runner"]}},"/v1/runner/orders/{orderId}/release":{"post":{"description":"Only my own claim and only before the first line is picked.","operationId":"releaseRunnerOrder","parameters":[{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickupOrder"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_CLAIMED (details { status }) — not my claim · ERR_PICKUP_INCOMPLETE (details { missing[], reason: \"pickup_started\", pickedCount }) — pickup started and lines are still to collect, finish it · ERR_CONFLICT (details { status, reason: \"pickup_complete\", pickedCount, totalCount }) — every line is picked, hand it over at the hub · ERR_CONFLICT (details { status }) — already at the hub or later"}},"security":[{"bearer":[]}],"summary":"Give my claim back to the queue","tags":["runner"]}},"/v1/runner/queue":{"get":{"description":"VENDOR_ACCEPTED orders that are unclaimed or claimed by me, plus my RUNNER_PICKUP orders. Only orders with at least one accepted line; `zone` keeps orders with a stop in that market zone. Sorted by delivery window (orders without one last), then placedAt. Realtime: `runner.queue.updated` on room `role:RUNNER` `{ orderId, code, event: available|claimed|released|picked|at_hub|cancelled }` (`cancelled` = the customer cancelled a queued or claimed order).","operationId":"listRunnerQueue","parameters":[{"description":"Only orders with at least one stop in this market zone","in":"query","name":"zone","required":false,"schema":{"$ref":"#/components/schemas/MarketZone"}},{"in":"query","name":"page","required":false,"schema":{"default":1,"example":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"example":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedPickupOrders"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"}},"security":[{"bearer":[]}],"summary":"Pickup queue","tags":["runner"]}},"/v1/runner/scan":{"post":{"description":"Resolves the line by its printed barcode (ES-<n>-<line>) and marks it picked.","operationId":"scanRunnerItem","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickupOrder"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN — RUNNER or ADMIN only"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — unknown barcode"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_CLAIMED (details { status }) · ERR_ITEM_NOT_PICKABLE"}},"security":[{"bearer":[]}],"summary":"Scan a line barcode (same effect as /items/:id/picked)","tags":["runner"]}},"/v1/vendors/me":{"get":{"operationId":"getVendorProfile","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorProfile"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"}},"security":[{"bearer":[]}],"summary":"My vendor profile","tags":["vendors"]},"patch":{"operationId":"updateVendorProfile","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVendorProfileDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorProfile"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"}},"security":[{"bearer":[]}],"summary":"Edit my vendor profile (display name)","tags":["vendors"]}},"/v1/vendors/me/orders":{"get":{"operationId":"listVendorOrders","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"description":"NEW = PLACED with my PENDING lines · ACCEPTED = none of my lines pending and ≥1 accepted (PARTIAL counts), order PLACED/VENDOR_ACCEPTED · IN_PICKUP = RUNNER_PICKUP…EN_ROUTE with ≥1 accepted line of mine · COMPLETED = DELIVERED with ≥1 accepted line of mine · REJECTED = all my lines rejected or order CANCELLED (a fully rejected vendor sees the order only here) · ALL","in":"query","name":"view","required":false,"schema":{"$ref":"#/components/schemas/VendorOrderView"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedVendorOrders"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"}},"security":[{"bearer":[]}],"summary":"Orders containing my lines, by view, newest first","tags":["vendors"]}},"/v1/vendors/me/orders/{orderId}":{"get":{"operationId":"getVendorOrder","parameters":[{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorOrder"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND (no line of mine in it)"}},"security":[{"bearer":[]}],"summary":"One order — only my lines are shown","tags":["vendors"]}},"/v1/vendors/me/orders/{orderId}/accept":{"post":{"description":"Only while the order is PLACED. When every non-rejected line of the order is accepted the order moves to VENDOR_ACCEPTED (actor SELLER) in the same transaction and runners are notified.","operationId":"acceptVendorOrder","parameters":[{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptOrderItemsDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorOrder"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND (order or a line that is not mine)"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_ACCEPTABLE {status} · ERR_ITEMS_ALREADY_DECIDED {itemIds}"}},"security":[{"bearer":[]}],"summary":"Accept my lines (default: all my PENDING lines)","tags":["vendors"]}},"/v1/vendors/me/orders/{orderId}/reject":{"post":{"description":"Restores stock, recomputes the order totals without the rejected lines and tells the customer (order.updated). No line left → CANCELLED (cancelReason vendor_rejected:<reason>); every remaining line accepted → VENDOR_ACCEPTED.","operationId":"rejectVendorOrder","parameters":[{"in":"path","name":"orderId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectOrderItemsDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorOrder"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_ORDER_NOT_ACCEPTABLE {status} · ERR_ITEMS_ALREADY_DECIDED {itemIds}"}},"security":[{"bearer":[]}],"summary":"Reject my lines with a reason (default: all my PENDING lines)","tags":["vendors"]}},"/v1/vendors/me/products":{"get":{"operationId":"listMyVendorProducts","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}},{"description":"Case-insensitive match on any name or the SKU","in":"query","name":"q","required":false,"schema":{"maxLength":80,"type":"string"}},{"in":"query","name":"categoryId","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"Omit for every product (active and inactive)","in":"query","name":"isActive","required":false,"schema":{"type":"boolean"}},{"in":"query","name":"sort","required":false,"schema":{"$ref":"#/components/schemas/VendorProductSort"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedVendorProducts"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"}},"security":[{"bearer":[]}],"summary":"My products (active and inactive), filter + sort","tags":["vendors"]},"post":{"description":"SKU is generated (<stallNumber>-<4-digit sequence>). Missing ru/en names are copied from az. photoKeys come from POST /v1/media/uploads.","operationId":"createVendorProduct","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVendorProductDto"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorProduct"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_CATEGORY_INVALID · ERR_MEDIA_INVALID (details: [{ key, problem }])"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_SKU_CONFLICT (after one retry)"}},"security":[{"bearer":[]}],"summary":"Add a product","tags":["vendors"]}},"/v1/vendors/me/products/{id}":{"delete":{"operationId":"deleteVendorProduct","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Retired"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_PRODUCT_NOT_OWNED (details { productId }) — another vendor’s product"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — no such product"}},"security":[{"bearer":[]}],"summary":"Retire a product (soft: isActive=false; it stays in order history)","tags":["vendors"]},"get":{"operationId":"getVendorProduct","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorProduct"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_PRODUCT_NOT_OWNED (details { productId }) — another vendor’s product"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — no such product"}},"security":[{"bearer":[]}],"summary":"One of my products","tags":["vendors"]},"patch":{"operationId":"updateVendorProduct","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVendorProductDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorProduct"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION · ERR_CATEGORY_INVALID · ERR_MEDIA_INVALID"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_PRODUCT_NOT_OWNED (details { productId }) — another vendor’s product"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — no such product"}},"security":[{"bearer":[]}],"summary":"Edit a product (partial)","tags":["vendors"]}},"/v1/vendors/me/products/{id}/stock":{"post":{"description":"Exactly one of delta (relative) or stockQty (absolute). Stock never goes below 0.","operationId":"adjustVendorProductStock","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdjustStockDto"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorProduct"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION (both / neither given, or the delta would drop stock below 0)"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_PRODUCT_NOT_OWNED (details { productId }) — another vendor’s product"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_NOT_FOUND — no such product"}},"security":[{"bearer":[]}],"summary":"Adjust stock atomically","tags":["vendors"]}},"/v1/vendors/me/settlements":{"get":{"operationId":"listVendorSettlements","parameters":[{"in":"query","name":"page","required":false,"schema":{"default":1,"maximum":10000,"minimum":1,"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"default":20,"maximum":50,"minimum":1,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedVendorSettlements"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"}},"security":[{"bearer":[]}],"summary":"Weekly settlements, newest first","tags":["vendors"]}},"/v1/vendors/me/settlements/current":{"get":{"description":"Accepted lines of orders DELIVERED this week; commission in basis points, integer math, round half up.","operationId":"getCurrentVendorSettlement","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorSettlementPreview"}}},"description":""},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"}},"security":[{"bearer":[]}],"summary":"Running ISO week preview (Asia/Baku)","tags":["vendors"]}},"/v1/vendors/me/stats":{"get":{"operationId":"getVendorStats","parameters":[{"in":"query","name":"range","required":false,"schema":{"$ref":"#/components/schemas/StatsRange"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorStats"}}},"description":""},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_VALIDATION"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_UNAUTHORIZED"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"description":"ERR_FORBIDDEN (not a SELLER) · ERR_VENDOR_NOT_FOUND (SELLER without a vendor)"}},"security":[{"bearer":[]}],"summary":"Dashboard numbers for today / 7d / 30d (Asia/Baku days)","tags":["vendors"]}}},"servers":[{"description":"Local development","url":"http://localhost:3000"}],"tags":[{"description":"Liveness / readiness","name":"health"},{"description":"Phone OTP login, tokens, profile","name":"auth"}]}