{"openapi":"3.1.0","info":{"title":"Premilo Reseller API","version":"1.0.0","summary":"API grosir prabayar untuk reseller Premilo.","description":"Ambil katalog, hitung harga, dan order produk digital Premilo. Pembayaran memotong saldo akun (IDR). POST /order bersifat idempotent per `ref`. Dokumentasi lengkap: https://api.premilo.id/docs"},"servers":[{"url":"https://api.premilo.id/v1"}],"security":[{"ApiKey":[]}],"tags":[{"name":"Account"},{"name":"Catalog"},{"name":"Orders"},{"name":"Webhooks"},{"name":"System"}],"paths":{"/health":{"get":{"tags":["System"],"summary":"Cek layanan hidup","security":[],"operationId":"getHealth","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/me":{"get":{"tags":["Account"],"summary":"Info akun, saldo, dan limit","operationId":"getMe","responses":{"200":{"description":"OK","headers":{"$ref":"#/components/headers/RateLimitHeaders"},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"401":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/products":{"get":{"tags":["Catalog"],"summary":"Katalog produk","operationId":"listProducts","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductsResponse"}}}},"401":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}},"/quote":{"get":{"tags":["Catalog"],"summary":"Hitung harga pasti tanpa membeli","operationId":"getQuote","parameters":[{"name":"product_id","in":"query","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},{"name":"qty","in":"query","required":true,"schema":{"type":"integer","minimum":1,"maximum":1000}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"400":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/order":{"post":{"tags":["Orders"],"summary":"Beli produk (idempotent per ref)","operationId":"createOrder","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}}},"responses":{"200":{"description":"Order sukses (atau replay order sukses dengan ref sama)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"202":{"description":"Order needs_review: berhenti di tengah proses, diperiksa manual","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"400":{"$ref":"#/components/responses/OrderError"},"402":{"$ref":"#/components/responses/OrderError"},"404":{"$ref":"#/components/responses/OrderError"},"409":{"$ref":"#/components/responses/OrderError"},"413":{"$ref":"#/components/responses/Error"},"415":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/OrderError"},"503":{"$ref":"#/components/responses/OrderError"}}}},"/order/{ref}":{"get":{"tags":["Orders"],"summary":"Detail order (ref atau order_id)","operationId":"getOrder","parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{6,64}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"202":{"description":"needs_review","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"404":{"$ref":"#/components/responses/Error"}}}},"/orders":{"get":{"tags":["Orders"],"summary":"Riwayat order (tanpa items)","operationId":"listOrders","parameters":[{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/OrderStatus"}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrdersResponse"}}}},"400":{"$ref":"#/components/responses/Error"}}}},"/stats":{"get":{"tags":["Account"],"summary":"Ringkasan hari ini (WIB) & 7 hari","operationId":"getStats","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsResponse"}}}}}}},"/keys/rotate":{"post":{"tags":["Account"],"summary":"Ganti API key (key lama langsung mati)","operationId":"rotateKey","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateResponse"}}}}}}},"/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhook","operationId":"listWebhooks","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhooksResponse"}}}}}},"post":{"tags":["Webhooks"],"summary":"Daftarkan webhook (maks 3)","operationId":"createWebhook","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}}},"responses":{"201":{"description":"Created (secret hanya tampil di sini)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreatedResponse"}}}},"400":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}}},"/webhooks/{id}":{"delete":{"tags":["Webhooks"],"summary":"Hapus webhook","operationId":"deleteWebhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^wh_[a-f0-9]{24}$"}}],"responses":{"200":{"description":"OK"},"404":{"$ref":"#/components/responses/Error"}}}},"/webhooks/{id}/test":{"post":{"tags":["Webhooks"],"summary":"Kirim event ping sekarang","operationId":"testWebhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^wh_[a-f0-9]{24}$"}}],"responses":{"200":{"description":"Hasil pengiriman"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}}}}},"webhooks":{"orderEvent":{"post":{"summary":"Kabar perubahan status order","description":"Header X-Premilo-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>. Balas 2xx dalam 5 detik. Dedupe pakai `id`.","parameters":[{"name":"X-Premilo-Event","in":"header","required":true,"schema":{"type":"string","enum":["order.success","order.failed","order.needs_review","order.refunded","ping"]}},{"name":"X-Premilo-Delivery","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Premilo-Signature","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Diterima"}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Format prm_live_ + 32 karakter. Authorization: Bearer <key> juga diterima."}},"headers":{"RateLimitHeaders":{"description":"X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (detik), Retry-After pada 429","schema":{"type":"string"}}},"responses":{"Error":{"description":"Galat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"OrderError":{"description":"Order gagal (saldo tidak terpotong atau sudah dikembalikan)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderErrorResponse"}}}}},"schemas":{"ErrorCode":{"type":"string","enum":["UNAUTHORIZED","RATE_LIMITED","INVALID_JSON","UNSUPPORTED_MEDIA_TYPE","PAYLOAD_TOO_LARGE","INVALID_REF","INVALID_QTY","INVALID_PARAMETER","PRODUCT_NOT_FOUND","ORDER_NOT_FOUND","WEBHOOK_NOT_FOUND","NOT_FOUND","METHOD_NOT_ALLOWED","INSUFFICIENT_BALANCE","OUT_OF_STOCK","PRODUCT_BUSY","REF_CONFLICT","ORDER_PROCESSING","WEBHOOK_LIMIT","DAILY_LIMIT_REACHED","INVALID_WEBHOOK_URL","PRODUCT_UNAVAILABLE","SERVICE_UNAVAILABLE"]},"Error":{"type":"object","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string"}}},"ErrorResponse":{"type":"object","required":["success","error"],"properties":{"success":{"const":false},"error":{"$ref":"#/components/schemas/Error"},"request_id":{"type":"string"}}},"OrderErrorResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Order"}}}]},"HealthResponse":{"type":"object","properties":{"success":{"const":true},"data":{"type":"object","properties":{"status":{"const":"ok"}}}}},"Me":{"type":"object","properties":{"name":{"type":"string"},"telegram_id":{"type":"string"},"balance":{"type":"integer"},"currency":{"const":"IDR"},"key_prefix":{"type":"string"},"key_created_at":{"type":["string","null"],"format":"date-time"},"rate_limit_per_minute":{"type":"integer"},"daily_unit_limit":{"type":"integer"},"daily_units_used":{"type":"integer"},"daily_units_remaining":{"type":"integer"},"allowed_products":{"type":["array","null"],"items":{"type":"string"}},"webhooks":{"type":"integer"}}},"MeResponse":{"type":"object","properties":{"success":{"const":true},"data":{"$ref":"#/components/schemas/Me"}}},"WholesaleTier":{"type":"object","properties":{"min_qty":{"type":"integer"},"price":{"type":"integer"}}},"Product":{"type":"object","properties":{"id":{"type":"string"},"product":{"type":"string"},"name":{"type":"string"},"price":{"type":"integer"},"wholesale_tiers":{"type":"array","items":{"$ref":"#/components/schemas/WholesaleTier"}},"stock":{"type":"integer"},"stock_ready":{"type":"integer"},"daily_limit_applies":{"type":"boolean"},"max_qty":{"type":"integer"}}},"ProductsResponse":{"type":"object","properties":{"success":{"const":true},"data":{"type":"object","properties":{"currency":{"const":"IDR"},"updated_at":{"type":"string","format":"date-time"},"products":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}}}},"QuoteResponse":{"type":"object","properties":{"success":{"const":true},"data":{"type":"object","properties":{"product_id":{"type":"string"},"qty":{"type":"integer"},"unit_price":{"type":"integer"},"total":{"type":"integer"},"wholesale_tier_min_qty":{"type":["integer","null"]},"stock":{"type":"integer"},"stock_enough":{"type":"boolean"},"balance":{"type":"integer"},"balance_enough":{"type":"boolean"},"daily_units_remaining":{"type":["integer","null"]},"can_order":{"type":"boolean"},"reasons":{"type":"array","items":{"$ref":"#/components/schemas/ErrorCode"}}}}}},"CreateOrderRequest":{"type":"object","required":["ref","product_id","qty"],"additionalProperties":false,"properties":{"ref":{"type":"string","pattern":"^[A-Za-z0-9._:-]{6,64}$","description":"ID order dari sistem kamu. Unik per akun. Retry dengan ref sama tidak pernah membeli dua kali."},"product_id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"qty":{"type":"integer","minimum":1,"maximum":1000}}},"OrderStatus":{"type":"string","enum":["success","failed","processing","needs_review"]},"Order":{"type":"object","properties":{"order_id":{"type":"string","pattern":"^API-[A-F0-9]{10}$"},"ref":{"type":"string"},"status":{"$ref":"#/components/schemas/OrderStatus"},"product_id":{"type":"string"},"product":{"type":["string","null"]},"name":{"type":["string","null"]},"qty_ordered":{"type":"integer"},"qty_delivered":{"type":"integer"},"unit_price":{"type":["integer","null"]},"total_charged":{"type":"integer"},"refunded_amount":{"type":"integer"},"attempts":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"items":{"type":"array","items":{"type":"string"},"description":"Hanya pada status success, di POST /order dan GET /order/{ref}"},"balance_after":{"type":["integer","null"]},"terms":{"type":"string"},"replay":{"type":"boolean"},"error":{"$ref":"#/components/schemas/Error"},"balance_refunded":{"type":"boolean"},"retryable":{"type":"boolean"},"message":{"type":"string"}}},"OrderResponse":{"type":"object","properties":{"success":{"const":true},"data":{"$ref":"#/components/schemas/Order"}}},"OrdersResponse":{"type":"object","properties":{"success":{"const":true},"data":{"type":"object","properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"has_more":{"type":"boolean"}}}}},"StatsBucket":{"type":"object","properties":{"orders_success":{"type":"integer"},"orders_failed":{"type":"integer"},"orders_needs_review":{"type":"integer"},"units_delivered":{"type":"integer"},"total_spent":{"type":"integer"}}},"StatsResponse":{"type":"object","properties":{"success":{"const":true},"data":{"type":"object","properties":{"currency":{"const":"IDR"},"timezone":{"const":"Asia/Jakarta"},"today":{"$ref":"#/components/schemas/StatsBucket"},"last_7_days":{"$ref":"#/components/schemas/StatsBucket"}}}}},"RotateResponse":{"type":"object","properties":{"success":{"const":true},"data":{"type":"object","properties":{"api_key":{"type":"string"},"key_prefix":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"message":{"type":"string"}}}}},"WebhookEventType":{"type":"string","enum":["order.success","order.failed","order.needs_review","order.refunded"]},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"active":{"type":"boolean"},"consecutive_failures":{"type":"integer"},"last_success_at":{"type":["string","null"]},"last_failure_at":{"type":["string","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":["string","null"]}}},"WebhooksResponse":{"type":"object","properties":{"success":{"const":true},"data":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"available_events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}}}}}},"CreateWebhookRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"https://, port 443, nama domain yang resolve ke IP publik"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1}}},"WebhookCreatedResponse":{"type":"object","properties":{"success":{"const":true},"data":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string"}}}]}}},"WebhookEvent":{"type":"object","required":["id","type","created_at","data"],"properties":{"id":{"type":"string"},"type":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"data":{"$ref":"#/components/schemas/Order"}}}}}}