{"openapi":"3.1.0","info":{"title":"ping! Integrator API","version":"1.0.0","description":"Public API v1 untuk backend integrator, bukan cookie API internal. Scope wajib mengikuti x-required-scope setiap operasi. Key hanya mengakses akun pemiliknya. Tidak menyediakan akses admin, top-up, atau webhook pelanggan."},"servers":[{"url":"/","description":"Origin instalasi ping; gunakan HTTPS di produksi."}],"security":[{"bearerAuth":[]}],"paths":{"/api/v1/me":{"get":{"operationId":"integrator-me","tags":["akun"],"summary":"Identitas akun","description":"Profil pemilik API key, tanpa akses akun lain. Tidak menerima parameter query.","x-required-scope":"account:read","responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/ApiKeyIdentity"}}},"example":{"ok":true,"data":{"id":"user-example","name":"Developer","username":null,"email":"developer@example.com"}}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/wallet":{"get":{"operationId":"integrator-wallet","tags":["dompet"],"summary":"Saldo dompet","description":"balance, held, dan available milik akun sendiri dalam integer IDR. Tidak menerima parameter query.","x-required-scope":"wallet:read","responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/Wallet"}}},"example":{"ok":true,"data":{"balance":50000,"held":1500,"available":48500}}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/catalog/services":{"get":{"operationId":"integrator-services","tags":["katalog"],"summary":"Layanan gabungan","description":"Layanan dari server 1–5 yang aktif dan dikonfigurasi, dideduplikasi. logo_url adalah URL absolut atau null. Tidak perlu memilih server.","x-required-scope":"catalog:read","parameters":[{"name":"provider","in":"query","required":false,"schema":{"type":"string","enum":["all"],"default":"all"},"example":"all"}],"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/CatalogService"}}}},"example":{"ok":true,"data":[{"id":"service-example","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi.","logo_url":"https://pingotp.com/images/services/whatsapp-25a4d1bfbd6934df.svg"}]}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/catalog/countries":{"get":{"operationId":"integrator-countries","tags":["katalog"],"summary":"Negara gabungan","description":"Katalog negara gabungan dengan flag_url. Dengan serviceId, membaca penawaran, harga mulai sesuai akun, dan stok indikatif. Harga dan stok bukan reservasi.","x-required-scope":"catalog:read","parameters":[{"name":"provider","in":"query","required":false,"schema":{"type":"string","enum":["all"],"default":"all"},"example":"all"},{"name":"serviceId","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":128},"example":"service-example"}],"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/CatalogCountry"}}}},"example":{"ok":true,"data":[{"id":"country-example","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID","flag_url":"https://flagcdn.com/w80/id.png","price_idr":1500,"qualitative_stock":"available","stock_count":120,"unavailable":false}]}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/catalog/quotes":{"get":{"operationId":"integrator-quotes","tags":["katalog"],"summary":"Quote gabungan","description":"Pilihan harga terurut dari murah, dengan target sumber otomatis pada options[].selection. Saat membeli, salin kedua ID sumber dari selection opsi yang sama, bukan ID root quote. Harga mengikuti akun pemilik key.","x-required-scope":"catalog:read","parameters":[{"name":"provider","in":"query","required":false,"schema":{"type":"string","enum":["all"],"default":"all"},"example":"all"},{"name":"serviceId","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":128},"example":"service-example"},{"name":"countryId","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":128},"example":"country-example"}],"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/CatalogQuote"}}},"example":{"ok":true,"data":{"serviceId":"service-example","countryId":"country-example","price_idr":1500,"qualitative_stock":"available","stock_count":120,"options":[{"id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","price_idr":1500,"qualitative_stock":"available","stock_count":120,"selection":{"service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi.","logo_url":"https://pingotp.com/images/services/whatsapp-25a4d1bfbd6934df.svg"},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID","flag_url":"https://flagcdn.com/w80/id.png"}}}],"fetched_at":"2026-09-23T00:00:00.000Z","expires_at":"2026-09-23T00:02:00.000Z"}}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orders":{"get":{"operationId":"integrator-order-history","tags":["pesanan"],"summary":"Riwayat pesanan","description":"Hanya pesanan akun pemilik key, termasuk pesanan dari website dan key lain milik akun yang sama. Tidak memanggil provider.","x-required-scope":"orders:read","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"example":20},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":1000000,"default":0},"example":0},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["all","waiting","completed","cancelled","failed"],"default":"all"},"example":"all"},{"name":"search","in":"query","required":false,"schema":{"type":"string","maxLength":128,"default":""},"example":"Telegram"}],"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/OrderPage"}}},"example":{"ok":true,"data":{"orders":[{"provider":"v3","id":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19","serviceId":"service-example","countryId":"country-example","service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi."},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID"},"phone":"+6281200000000","price":1500,"status":"waiting","createdAt":1790121600000,"expiresAt":1790122800000,"canCancel":false,"canComplete":false,"isPreparing":false,"cancelAvailableAt":1790121780000,"message":null}],"counts":{"all":1,"waiting":1,"completed":0,"cancelled":0,"failed":0},"pagination":{"limit":20,"offset":0,"hasMore":false,"total":1}}}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"integrator-create-order","tags":["pesanan"],"summary":"Beli nomor","description":"Satu nomor per request. Target awal ditentukan server dari ID selection quote; jangan kirim provider. Memakai alur hold, validasi harga/rank, fallback aman, dan settlement aplikasi. UUID clientRequestId dan seluruh payload harus tetap untuk replay. maxPriceIdr opsional harus disetujui sebelumnya; tanpa itu fallback tidak melebihi expectedPriceIdr. HTTP 200 bukan jaminan nomor aktif: periksa isPreparing, status, dan message.","x-required-scope":"orders:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegratorCreateOrder"},"example":{"serviceId":"service-example","countryId":"country-example","clientRequestId":"f41c29d8-967d-469d-ae4f-0114c38bf715","priceOptionId":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","expectedPriceIdr":1500}}}},"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/Order"}}},"example":{"ok":true,"data":{"provider":"v3","id":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19","serviceId":"service-example","countryId":"country-example","service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi."},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID"},"phone":"+6281200000000","price":1500,"status":"waiting","createdAt":1790121600000,"expiresAt":1790122800000,"canCancel":false,"canComplete":false,"isPreparing":false,"cancelAvailableAt":1790121780000,"message":null}}}}},"400":{"description":"BAD_REQUEST: Body JSON atau Content-Type tidak valid.\nVALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"PRICE_CHANGED: Harga saat pembelian berbeda dari konfirmasi.\nPRICE_OPTION_UNAVAILABLE: Pilihan harga sudah tidak tersedia.\nPURCHASE_UNRESOLVED: Hasil pembelian sendiri belum pasti.\nIDEMPOTENCY_CONFLICT: Kunci idempotensi pesanan, top-up, atau ledger sudah dipakai untuk detail transaksi berbeda.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Body JSON melebihi 4096 byte.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"INSUFFICIENT_BALANCE: Saldo tersedia tidak cukup.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"ACTIVE_ORDER_LIMIT: Batas order aktif tercapai.\nORDER_RATE_LIMIT: Terlalu banyak percobaan pembelian.\nRATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"PROVIDER_ERROR: Provider belum dapat memproses permintaan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"ORDERING_PAUSED: Server sementara belum menerima pesanan baru; tidak ada alokasi saldo baru.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orders/active":{"get":{"operationId":"integrator-active-orders","tags":["pesanan"],"summary":"Pesanan aktif","description":"Pesanan terbuka akun sendiri. Tidak menerima query dan tidak memanggil provider. Periksa detail untuk pembaruan SMS.","x-required-scope":"orders:read","responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Order"}}}},"example":{"ok":true,"data":[{"provider":"v3","id":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19","serviceId":"service-example","countryId":"country-example","service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi."},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID"},"phone":"+6281200000000","price":1500,"status":"waiting","createdAt":1790121600000,"expiresAt":1790122800000,"canCancel":false,"canComplete":false,"isPreparing":false,"cancelAvailableAt":1790121780000,"message":null}]}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orders/{id}":{"get":{"operationId":"integrator-order-detail","tags":["pesanan"],"summary":"Status & OTP","description":"Membaca dan menyinkronkan pesanan akun sendiri. code dan smsText dikirim jika sudah ada. waiting dapat sudah memiliki SMS; ikuti canComplete. Poll minimal setiap 5 detik, berhenti saat terminal, dan patuhi rate limit.","x-required-scope":"orders:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128},"example":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19"}],"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/Order"}}},"example":{"ok":true,"data":{"provider":"v3","id":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19","serviceId":"service-example","countryId":"country-example","service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi."},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID"},"phone":"+6281200000000","price":1500,"status":"waiting","createdAt":1790121600000,"expiresAt":1790122800000,"canCancel":false,"canComplete":false,"isPreparing":false,"cancelAvailableAt":1790121780000,"message":null}}}}},"400":{"description":"VALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"NOT_FOUND: Resource tidak ditemukan atau bukan milik sesi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"PROVIDER_ERROR: Provider belum dapat memproses permintaan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orders/{id}/cancel":{"post":{"operationId":"integrator-cancel-order","tags":["pesanan"],"summary":"Batalkan pesanan","description":"Hanya sebelum SMS dan saat canCancel. Body {} wajib. Saldo dilepas setelah pembatalan terkonfirmasi, bukan saat request dikirim. Replay tidak menggandakan pelepasan dana.","x-required-scope":"orders:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128},"example":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmptyAction"},"example":{}}}},"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/Order"}}},"example":{"ok":true,"data":{"provider":"v3","id":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19","serviceId":"service-example","countryId":"country-example","service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi."},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID"},"phone":"+6281200000000","price":1500,"status":"cancelled","createdAt":1790121600000,"expiresAt":1790122800000,"canCancel":false,"canComplete":false,"isPreparing":false,"cancelAvailableAt":null,"message":null}}}}},"400":{"description":"BAD_REQUEST: Body JSON atau Content-Type tidak valid.\nVALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"NOT_FOUND: Resource tidak ditemukan atau bukan milik sesi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"EARLY_CANCEL_DENIED: Belum melewati batas pembatalan.\nINVALID_STATE: Aksi tidak cocok dengan status terkini.\nRECONCILIATION_REQUIRED: Hasil provider perlu dikonfirmasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Body JSON melebihi 4096 byte.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orders/{id}/complete":{"post":{"operationId":"integrator-complete-order","tags":["pesanan"],"summary":"Selesaikan pesanan","description":"Saat canComplete=true. Body {} wajib. Menggunakan settlement dan reward referral yang sama dengan aplikasi. Replay tidak membebankan biaya dua kali.","x-required-scope":"orders:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128},"example":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmptyAction"},"example":{}}}},"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/Order"}}},"example":{"ok":true,"data":{"provider":"v3","id":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19","serviceId":"service-example","countryId":"country-example","service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi."},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID"},"phone":"+6281200000000","price":1500,"status":"completed","createdAt":1790121600000,"expiresAt":1790122800000,"canCancel":false,"canComplete":false,"isPreparing":false,"cancelAvailableAt":null,"message":null}}}}},"400":{"description":"BAD_REQUEST: Body JSON atau Content-Type tidak valid.\nVALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"NOT_FOUND: Resource tidak ditemukan atau bukan milik sesi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"INVALID_STATE: Aksi tidak cocok dengan status terkini.\nRECONCILIATION_REQUIRED: Hasil provider perlu dikonfirmasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Body JSON melebihi 4096 byte.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orders/{id}/resend":{"post":{"operationId":"integrator-resend-order","tags":["pesanan"],"summary":"Minta SMS berikutnya","description":"Hanya bila canResend=true. Satu UUID requestId tetap untuk satu niat resend, termasuk respons hilang. Tidak membeli nomor atau membebankan biaya lagi; dukungan mengikuti provider pesanan.","x-required-scope":"orders:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128},"example":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendOrder"},"example":{"requestId":"f41c29d8-967d-469d-ae4f-0114c38bf715"}}}},"responses":{"200":{"description":"Envelope ok/data. Contoh ilustratif, bukan data live.","headers":{"X-Request-ID":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"additionalProperties":false,"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/Order"}}},"example":{"ok":true,"data":{"provider":"v3","id":"a4c4b37c-9b68-4b18-bd52-8e0ea3b08d19","serviceId":"service-example","countryId":"country-example","service":{"id":"service-example","provider":"v3","name":"WhatsApp","icon":"whatsapp","category":"Pesan","description":"Nomor untuk satu aktivasi."},"country":{"id":"country-example","provider":"v3","name":"Indonesia","flag":"ID","dial":"+62","iso2":"ID"},"phone":"+6281200000000","price":1500,"status":"waiting","createdAt":1790121600000,"expiresAt":1790122800000,"canCancel":false,"canComplete":false,"isPreparing":false,"cancelAvailableAt":1790121780000,"message":null}}}}},"400":{"description":"BAD_REQUEST: Body JSON atau Content-Type tidak valid.\nVALIDATION_ERROR: Input tidak sesuai schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"UNAUTHENTICATED: Sesi tidak tersedia atau sudah berakhir.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"FORBIDDEN: Origin atau izin akses ditolak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"NOT_FOUND: Resource tidak ditemukan atau bukan milik sesi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"INVALID_STATE: Aksi tidak cocok dengan status terkini.\nRECONCILIATION_REQUIRED: Hasil provider perlu dikonfirmasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Body JSON melebihi 4096 byte.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"RATE_LIMITED: Antrean server penuh atau cooldown provider sedang aktif.","headers":{"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Detik minimum sebelum mencoba lagi."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"INTERNAL_ERROR: Kegagalan internal yang disanitasi.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"default":{"description":"Error domain/provider disanitasi; gunakan error.code, bukan teks message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key akun, bukan cookie atau key provider. Buat melalui Pengaturan → Developer API. Jangan simpan di browser."}},"schemas":{"ApiError":{"type":"object","properties":{"ok":{"type":"boolean","const":false},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false},"Wallet":{"type":"object","properties":{"balance":{"type":"integer","minimum":0,"description":"Saldo tercatat, termasuk alokasi untuk pesanan aktif."},"held":{"type":"integer","minimum":0,"description":"Alokasi saldo untuk order, belum menjadi pembebanan akhir."},"available":{"type":"integer","minimum":0,"description":"Saldo yang dapat dipakai. Gunakan nilai server, bukan perhitungan client."}},"required":["balance","held","available"],"additionalProperties":false},"Service":{"type":"object","properties":{"provider":{"type":"string","enum":["v1","v2","v3","v4"],"description":"Identitas server publik v1–v4. Gunakan nilai dari katalog atau quote, bukan nama penyedia di belakang server. Setiap pesanan tetap terikat ke servernya. Fallback sebelum aktivasi memakai pesanan terpisah setelah penolakan terkonfirmasi, bukan memindahkan pesanan aktif."},"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan nama layanan atau kode provider."},"name":{"type":"string","description":"Nama layanan untuk ditampilkan."},"icon":{"type":"string","description":"Kunci ikon layanan, bukan URL gambar."},"category":{"type":"string","description":"Label kategori untuk tampilan."},"description":{"type":"string"},"reliable":{"type":"boolean","description":"Opsional pada katalog v1: true jika layanan pada server ini pernah menerima SMS minimal sekali dari riwayat nyata seluruh pengguna di ping. Tanpa batas usia atau syarat pesanan selesai. Tanpa bukti bernilai false; bukan jaminan OTP atau statistik upstream."}},"required":["provider","id","name","icon","category","description"],"additionalProperties":false},"Country":{"type":"object","properties":{"provider":{"type":"string","enum":["v1","v2","v3","v4"],"description":"Identitas server publik v1–v4. Gunakan nilai dari katalog atau quote, bukan nama penyedia di belakang server. Setiap pesanan tetap terikat ke servernya. Fallback sebelum aktivasi memakai pesanan terpisah setelah penolakan terkonfirmasi, bukan memindahkan pesanan aktif."},"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan kode ISO atau kode provider."},"name":{"type":"string"},"flag":{"type":"string","description":"Label bendera untuk tampilan; jangan dipakai sebagai identifier."},"dial":{"type":"string","description":"Kode panggilan negara untuk tampilan."},"iso2":{"type":"string","description":"Kode negara yang tersedia pada metadata."},"price_idr":{"type":"integer","minimum":0,"description":"Hanya pada daftar negara dengan serviceId: harga mulai, bukan reservasi."},"qualitative_stock":{"type":"string","enum":["available","limited"],"description":"Hanya pada daftar negara dengan serviceId."},"reliable":{"type":"boolean","description":"Hanya pada daftar negara dengan serviceId: true jika server, layanan, dan negara yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna, termasuk pesanan aktif dan riwayat lama. Tidak mengungkap identitas atau isi SMS; bukan jaminan OTP."},"otp_delivery_7d":{"type":"object","properties":{"received":{"type":"integer","minimum":0},"samples":{"type":"integer","minimum":1}},"required":["received","samples"],"additionalProperties":false,"description":"Opsional pada daftar negara dengan serviceId: jumlah nomor yang menerima CODE_RECEIVED kanonis dibanding sampel nomor yang dibuat dalam 7 hari, sudah terbit, dan sudah menerima OTP atau berstatus COMPLETED/CANCELLED/EXPIRED. Pesanan menunggu tanpa OTP dan gagal provisioning dikecualikan. Katalog gabungan hanya menjumlahkan sumber yang sedang visible dan eligible. Persentase baru ditampilkan mulai 10 sampel; tanpa identitas atau isi SMS, bukan peluang atau jaminan OTP berikutnya."},"stock_count":{"type":["integer","null"],"minimum":0,"description":"Hanya pada daftar negara dengan serviceId: total stok pilihan harga eligible dan visible menurut filter selector dengan biaya provider maksimal USD0.50 (inklusif); negara yang hanya punya stok di atas batas ini tidak ditampilkan, setelah deduplikasi bucket yang sama. Null jika ada jumlah tidak diketahui atau total tidak aman; laporan pool dapat overlap, bukan jumlah nomor unik atau reservasi."}},"required":["provider","id","name","flag","dial","iso2"],"additionalProperties":false},"OrderService":{"type":"object","properties":{"provider":{"type":"string","enum":["v1","v2","v3","v4","archived"],"description":"Server asal pesanan. archived hanya untuk riwayat server yang sudah dinonaktifkan; bukan pilihan katalog atau pembelian. Aksi provider tidak tersedia untuk arsip."},"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan nama layanan atau kode provider."},"name":{"type":"string","description":"Nama layanan untuk ditampilkan."},"icon":{"type":"string","description":"Kunci ikon layanan, bukan URL gambar."},"category":{"type":"string","description":"Label kategori untuk tampilan."},"description":{"type":"string"},"reliable":{"type":"boolean","description":"Opsional pada katalog v1: true jika layanan pada server ini pernah menerima SMS minimal sekali dari riwayat nyata seluruh pengguna di ping. Tanpa batas usia atau syarat pesanan selesai. Tanpa bukti bernilai false; bukan jaminan OTP atau statistik upstream."}},"required":["provider","id","name","icon","category","description"],"additionalProperties":false},"OrderCountry":{"type":"object","properties":{"provider":{"type":"string","enum":["v1","v2","v3","v4","archived"],"description":"Server asal pesanan. archived hanya untuk riwayat server yang sudah dinonaktifkan; bukan pilihan katalog atau pembelian. Aksi provider tidak tersedia untuk arsip."},"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan kode ISO atau kode provider."},"name":{"type":"string"},"flag":{"type":"string","description":"Label bendera untuk tampilan; jangan dipakai sebagai identifier."},"dial":{"type":"string","description":"Kode panggilan negara untuk tampilan."},"iso2":{"type":"string","description":"Kode negara yang tersedia pada metadata."},"price_idr":{"type":"integer","minimum":0,"description":"Hanya pada daftar negara dengan serviceId: harga mulai, bukan reservasi."},"qualitative_stock":{"type":"string","enum":["available","limited"],"description":"Hanya pada daftar negara dengan serviceId."},"reliable":{"type":"boolean","description":"Hanya pada daftar negara dengan serviceId: true jika server, layanan, dan negara yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna, termasuk pesanan aktif dan riwayat lama. Tidak mengungkap identitas atau isi SMS; bukan jaminan OTP."},"otp_delivery_7d":{"type":"object","properties":{"received":{"type":"integer","minimum":0},"samples":{"type":"integer","minimum":1}},"required":["received","samples"],"additionalProperties":false,"description":"Opsional pada daftar negara dengan serviceId: jumlah nomor yang menerima CODE_RECEIVED kanonis dibanding sampel nomor yang dibuat dalam 7 hari, sudah terbit, dan sudah menerima OTP atau berstatus COMPLETED/CANCELLED/EXPIRED. Pesanan menunggu tanpa OTP dan gagal provisioning dikecualikan. Katalog gabungan hanya menjumlahkan sumber yang sedang visible dan eligible. Persentase baru ditampilkan mulai 10 sampel; tanpa identitas atau isi SMS, bukan peluang atau jaminan OTP berikutnya."},"stock_count":{"type":["integer","null"],"minimum":0,"description":"Hanya pada daftar negara dengan serviceId: total stok pilihan harga eligible dan visible menurut filter selector dengan biaya provider maksimal USD0.50 (inklusif); negara yang hanya punya stok di atas batas ini tidak ditampilkan, setelah deduplikasi bucket yang sama. Null jika ada jumlah tidak diketahui atau total tidak aman; laporan pool dapat overlap, bukan jumlah nomor unik atau reservasi."}},"required":["provider","id","name","flag","dial","iso2"],"additionalProperties":false},"Order":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string","enum":["v1","v2","v3","v4","archived"],"description":"Server asal pesanan. archived hanya untuk riwayat server yang sudah dinonaktifkan; bukan pilihan katalog atau pembelian. Aksi provider tidak tersedia untuk arsip."},"countryId":{"type":"string"},"serviceId":{"type":"string"},"country":{"$ref":"#/components/schemas/OrderCountry"},"service":{"$ref":"#/components/schemas/OrderService"},"phone":{"type":"string","description":"Nomor, atau teks status ketika nomor belum/tidak diterbitkan. Periksa isPreparing."},"price":{"type":"integer","minimum":0,"description":"Harga IDR yang dibekukan saat order dibuat."},"status":{"type":"string","enum":["waiting","completed","cancelled","expired","failed"],"description":"Status DTO client; tidak sama dengan enum database."},"createdAt":{"type":"integer","description":"Unix timestamp dalam milidetik, bukan detik."},"expiresAt":{"type":["integer","null"],"description":"Unix timestamp dalam milidetik, bukan detik."},"code":{"type":"string","description":"Opsional; field tidak dikirim bila belum ada kode terklasifikasi. SMS dapat diterima tanpa field ini. Jangan log OTP produksi."},"smsText":{"type":"string","description":"Opsional; isi SMS, termasuk teks/tautan tanpa kode. Berbeda dari message yang berisi status aplikasi. Jangan log SMS produksi."},"canCancel":{"type":"boolean","description":"Izin pembatalan pada saat respons dibuat. Server memeriksa ulang provider ketika aksi dikirim."},"canComplete":{"type":"boolean","description":"True pada CODE_RECEIVED, termasuk SMS tanpa kode. Jangan mensyaratkan code untuk menawarkan penyelesaian."},"canResend":{"type":"boolean","description":"Sesuai dukungan provider, setelah SMS diterima, sebelum kedaluwarsa, dan saat provider mengizinkan. Diperiksa ulang pada aksi; resend tidak mengubah harga atau saldo."},"canReplace":{"type":"boolean","description":"Semua provider sebelum SMS, saat pembatalan diizinkan; provider berbasis capability juga memerlukan can_replace. UI mengonfirmasi harga terlebih dahulu, membatalkan lewat /cancel, lalu membeli pengganti lewat POST /api/orders hanya setelah pembatalan terkonfirmasi tanpa SMS. Bukan penggantian atomik; permintaan pembelian yang belum pasti diperiksa ulang dengan kunci idempotensi yang sama."},"canProlong":{"type":"boolean","description":"True pada order selesai yang nomornya boleh diaktifkan ulang (saat ini server v4). Perpanjangan adalah pembelian berbayar dengan harga order ini; provider tetap dapat menolaknya, dan itu melepas saldo."},"isProlongation":{"type":"boolean","description":"True pada order yang mengaktifkan ulang nomor lama, bukan membeli nomor baru. Order seperti ini tidak dapat diganti nomornya."},"resendAvailableAt":{"type":["integer","null"],"description":"Cooldown provider untuk resend. Timer habis bukan izin; ikuti canResend terbaru."},"smsRevision":{"type":"integer","minimum":0,"description":"Revisi pasangan agregat code/smsText dari provider berbasis revisi, atau revisi lokal kode baru dari polling handler API. SMS baru dapat mempertahankan kode sebelumnya; keduanya belum tentu berasal dari satu pesan."},"message":{"type":["string","null"],"description":"Pesan status/kendala yang aman untuk ditampilkan, bukan isi SMS."},"isPreparing":{"type":"boolean","description":"True saat order masih CREATED atau PURCHASING."},"cancelAvailableAt":{"type":["integer","null"],"description":"Waktu pembatalan paling cepat 3 menit setelah nomor aktif untuk semua server, atau lebih lama jika provider mensyaratkan. Null jika bukan ACTIVE atau izin eksplisit provider belum diketahui. Provider dapat menolak aksi; saldo hanya dilepas setelah pembatalan terkonfirmasi."}},"required":["id","provider","countryId","serviceId","country","service","phone","price","status","createdAt","expiresAt","canCancel","canComplete","message","isPreparing","cancelAvailableAt"],"additionalProperties":false},"OrderPage":{"type":"object","properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"counts":{"type":"object","properties":{"all":{"type":"integer","minimum":0},"waiting":{"type":"integer","minimum":0},"completed":{"type":"integer","minimum":0},"cancelled":{"type":"integer","minimum":0},"failed":{"type":"integer","minimum":0}},"required":["all","waiting","completed","cancelled","failed"],"additionalProperties":false,"description":"Jumlah seluruh hasil pencarian per status, sebelum filter status dan pagination. cancelled mencakup cancelled dan expired."},"pagination":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":100},"offset":{"type":"integer","minimum":0,"maximum":1000000},"hasMore":{"type":"boolean"},"total":{"type":"integer","minimum":0,"description":"Jumlah hasil yang cocok dengan pencarian dan filter status."}},"required":["limit","offset","hasMore","total"],"additionalProperties":false}},"required":["orders","counts","pagination"],"additionalProperties":false},"EmptyAction":{"type":"object","properties":{},"required":[],"additionalProperties":false,"description":"Kirim body JSON {}. Body kosong atau field tambahan ditolak."},"ResendOrder":{"type":"object","properties":{"requestId":{"type":"string","format":"uuid","description":"UUID satu permintaan resend. Simpan untuk retry respons hilang; kunci sama tidak mengirim resend kedua, termasuk setelah restart."}},"required":["requestId"],"additionalProperties":false},"ApiKeyIdentity":{"type":"object","additionalProperties":false,"required":["id","name","username","email"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"username":{"type":["string","null"]},"email":{"type":"string","format":"email"}}},"CatalogService":{"type":"object","properties":{"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan nama layanan atau kode provider."},"name":{"type":"string","description":"Nama layanan untuk ditampilkan."},"icon":{"type":"string","description":"Kunci ikon layanan, bukan URL gambar."},"category":{"type":"string","description":"Label kategori untuk tampilan."},"description":{"type":"string"},"reliable":{"type":"boolean","description":"Opsional pada katalog v1: true jika layanan pada server ini pernah menerima SMS minimal sekali dari riwayat nyata seluruh pengguna di ping. Tanpa batas usia atau syarat pesanan selesai. Tanpa bukti bernilai false; bukan jaminan OTP atau statistik upstream."},"source_ids":{"type":"array","items":{"type":"string"},"description":"ID sumber yang tergabung. Jangan memilih server dari array ini; gunakan selection pada opsi quote."},"logo_url":{"type":["string","null"],"format":"uri","description":"URL gambar siap pakai, atau null jika tidak tersedia. Sediakan fallback tampilan sendiri."}},"required":["id","name","icon","category","description","logo_url"],"additionalProperties":false},"CatalogCountry":{"type":"object","properties":{"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan kode ISO atau kode provider."},"name":{"type":"string"},"flag":{"type":"string","description":"Label bendera untuk tampilan; jangan dipakai sebagai identifier."},"dial":{"type":"string","description":"Kode panggilan negara untuk tampilan."},"iso2":{"type":"string","description":"Kode negara yang tersedia pada metadata."},"price_idr":{"type":["integer","null"],"minimum":0,"description":"Dengan serviceId: harga mulai sesuai akun, bukan reservasi. Null jika tidak tersedia."},"qualitative_stock":{"type":"string","enum":["available","limited"],"description":"Hanya pada daftar negara dengan serviceId."},"reliable":{"type":"boolean","description":"Hanya pada daftar negara dengan serviceId: true jika server, layanan, dan negara yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna, termasuk pesanan aktif dan riwayat lama. Tidak mengungkap identitas atau isi SMS; bukan jaminan OTP."},"otp_delivery_7d":{"type":"object","properties":{"received":{"type":"integer","minimum":0},"samples":{"type":"integer","minimum":1}},"required":["received","samples"],"additionalProperties":false,"description":"Opsional pada daftar negara dengan serviceId: jumlah nomor yang menerima CODE_RECEIVED kanonis dibanding sampel nomor yang dibuat dalam 7 hari, sudah terbit, dan sudah menerima OTP atau berstatus COMPLETED/CANCELLED/EXPIRED. Pesanan menunggu tanpa OTP dan gagal provisioning dikecualikan. Katalog gabungan hanya menjumlahkan sumber yang sedang visible dan eligible. Persentase baru ditampilkan mulai 10 sampel; tanpa identitas atau isi SMS, bukan peluang atau jaminan OTP berikutnya."},"stock_count":{"type":["integer","null"],"minimum":0,"description":"Hanya pada daftar negara dengan serviceId: total stok pilihan harga eligible dan visible menurut filter selector dengan biaya provider maksimal USD0.50 (inklusif); negara yang hanya punya stok di atas batas ini tidak ditampilkan, setelah deduplikasi bucket yang sama. Null jika ada jumlah tidak diketahui atau total tidak aman; laporan pool dapat overlap, bukan jumlah nomor unik atau reservasi."},"source_ids":{"type":"array","items":{"type":"string"},"description":"ID sumber yang tergabung. Jangan memilih server dari array ini; gunakan selection pada opsi quote."},"flag_url":{"type":["string","null"],"format":"uri","description":"URL gambar siap pakai, atau null jika tidak tersedia. Sediakan fallback tampilan sendiri."},"base_price_idr":{"type":"integer","minimum":0},"discount_idr":{"type":"integer","minimum":0},"unavailable":{"type":"boolean"}},"required":["id","name","flag","dial","iso2","flag_url"],"additionalProperties":false},"QuoteSourceService":{"type":"object","properties":{"provider":{"type":"string","enum":["v1","v2","v3","v4"],"description":"Identitas server publik v1–v4. Gunakan nilai dari katalog atau quote, bukan nama penyedia di belakang server. Setiap pesanan tetap terikat ke servernya. Fallback sebelum aktivasi memakai pesanan terpisah setelah penolakan terkonfirmasi, bukan memindahkan pesanan aktif."},"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan nama layanan atau kode provider."},"name":{"type":"string","description":"Nama layanan untuk ditampilkan."},"icon":{"type":"string","description":"Kunci ikon layanan, bukan URL gambar."},"category":{"type":"string","description":"Label kategori untuk tampilan."},"description":{"type":"string"},"reliable":{"type":"boolean","description":"Opsional pada katalog v1: true jika layanan pada server ini pernah menerima SMS minimal sekali dari riwayat nyata seluruh pengguna di ping. Tanpa batas usia atau syarat pesanan selesai. Tanpa bukti bernilai false; bukan jaminan OTP atau statistik upstream."},"logo_url":{"type":["string","null"],"format":"uri","description":"URL gambar siap pakai, atau null jika tidak tersedia. Sediakan fallback tampilan sendiri."}},"required":["provider","id","name","icon","category","description","logo_url"],"additionalProperties":false},"QuoteSourceCountry":{"type":"object","properties":{"provider":{"type":"string","enum":["v1","v2","v3","v4"],"description":"Identitas server publik v1–v4. Gunakan nilai dari katalog atau quote, bukan nama penyedia di belakang server. Setiap pesanan tetap terikat ke servernya. Fallback sebelum aktivasi memakai pesanan terpisah setelah penolakan terkonfirmasi, bukan memindahkan pesanan aktif."},"id":{"type":"string","description":"ID internal dari katalog server yang dipilih. Bukan kode ISO atau kode provider."},"name":{"type":"string"},"flag":{"type":"string","description":"Label bendera untuk tampilan; jangan dipakai sebagai identifier."},"dial":{"type":"string","description":"Kode panggilan negara untuk tampilan."},"iso2":{"type":"string","description":"Kode negara yang tersedia pada metadata."},"price_idr":{"type":"integer","minimum":0,"description":"Hanya pada daftar negara dengan serviceId: harga mulai, bukan reservasi."},"qualitative_stock":{"type":"string","enum":["available","limited"],"description":"Hanya pada daftar negara dengan serviceId."},"reliable":{"type":"boolean","description":"Hanya pada daftar negara dengan serviceId: true jika server, layanan, dan negara yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna, termasuk pesanan aktif dan riwayat lama. Tidak mengungkap identitas atau isi SMS; bukan jaminan OTP."},"otp_delivery_7d":{"type":"object","properties":{"received":{"type":"integer","minimum":0},"samples":{"type":"integer","minimum":1}},"required":["received","samples"],"additionalProperties":false,"description":"Opsional pada daftar negara dengan serviceId: jumlah nomor yang menerima CODE_RECEIVED kanonis dibanding sampel nomor yang dibuat dalam 7 hari, sudah terbit, dan sudah menerima OTP atau berstatus COMPLETED/CANCELLED/EXPIRED. Pesanan menunggu tanpa OTP dan gagal provisioning dikecualikan. Katalog gabungan hanya menjumlahkan sumber yang sedang visible dan eligible. Persentase baru ditampilkan mulai 10 sampel; tanpa identitas atau isi SMS, bukan peluang atau jaminan OTP berikutnya."},"stock_count":{"type":["integer","null"],"minimum":0,"description":"Hanya pada daftar negara dengan serviceId: total stok pilihan harga eligible dan visible menurut filter selector dengan biaya provider maksimal USD0.50 (inklusif); negara yang hanya punya stok di atas batas ini tidak ditampilkan, setelah deduplikasi bucket yang sama. Null jika ada jumlah tidak diketahui atau total tidak aman; laporan pool dapat overlap, bukan jumlah nomor unik atau reservasi."},"flag_url":{"type":["string","null"],"format":"uri","description":"URL gambar siap pakai, atau null jika tidak tersedia. Sediakan fallback tampilan sendiri."}},"required":["provider","id","name","flag","dial","iso2","flag_url"],"additionalProperties":false},"CatalogQuoteOption":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"ID pilihan harga; teruskan sebagai priceOptionId, jangan buat sendiri."},"price_idr":{"type":"integer","minimum":0,"description":"Rupiah utuh, bukan sen atau string berformat."},"qualitative_stock":{"type":"string","enum":["available","limited"],"description":"Ketersediaan kualitatif, bukan jumlah stok yang dijamin."},"stock_count":{"type":["integer","null"],"minimum":0,"description":"Jumlah yang dilaporkan provider untuk pilihan harga ini, bukan jaminan atau reservasi stok. Null jika snapshot cache lama belum menyimpan jumlah."},"operator":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-f0-9]{64}$"},"name":{"type":"string","minLength":1,"maxLength":80}},"required":["key","name"],"additionalProperties":false,"description":"Opsional: operator seluler yang dipin pilihan ini ketika didukung provider. Key stabil dan opaque, bukan ID upstream atau nama provider. Kelompokkan menurut key, lalu kirim id opsi sebagai priceOptionId; fallback tetap operator yang sama. Tanpa field ini tidak ada batasan operator."},"selection":{"type":"object","additionalProperties":false,"required":["service","country"],"properties":{"service":{"$ref":"#/components/schemas/QuoteSourceService"},"country":{"$ref":"#/components/schemas/QuoteSourceCountry"}}},"recommended":{"type":"boolean","description":"Opsional pada katalog gabungan: pilihan awal, tetap memakai harga opsi ini. Jika ada riwayat tier valid minimal 10 sampel dan pernah OTP, acuannya batas bawah Wilson 95%; tier lain yang tidak terbukti lebih buruk (batas atas Wilson-nya mencapai acuan itu) dan paling mahal 10% di atasnya bergiliran menjadi pilihan awal per akun, layanan, negara, dan jam. Tanpa bukti cukup, tidak ada opsi yang diberi recommended. Bukan peluang, klaim kecepatan, atau jaminan OTP. Opsi termurah tetap diurutkan lebih dulu dan dapat dipilih."},"otp_delivery_7d":{"type":"object","properties":{"received":{"type":"integer","minimum":0},"samples":{"type":"integer","minimum":1}},"required":["received","samples"],"additionalProperties":false,"description":"Opsional: received dan samples dari nomor yang dibuat dalam 7 hari pada exact tier upstream pilihan awal. Hanya nomor terbit yang sudah menerima OTP atau berstatus COMPLETED/CANCELLED/EXPIRED; menunggu tanpa OTP dan gagal provisioning dikecualikan. Menggunakan CODE_RECEIVED kanonis, tidak mencampur provider/produk/operator/tier atau opsi berharga sama. Persentase tampil mulai 10 sampel; bukan peluang atau jaminan OTP berikutnya."},"fallback_price_min_idr":{"type":"integer","minimum":0,"description":"Pada katalog gabungan: harga minimum pilihan awal dan alternatif eligible. Jika opsi memiliki operator, hanya alternatif dengan operator.key yang sama."},"fallback_price_max_idr":{"type":"integer","minimum":0,"description":"Pada katalog gabungan: harga maksimum pilihan awal dan alternatif eligible untuk operator yang sama jika dipin, bukan persetujuan otomatis. Harga di atas pilihan awal memerlukan maxPriceIdr eksplisit."},"otp_eta_seconds":{"type":"integer","minimum":1,"description":"Opsional: median detik ACTIVE ke CODE_RECEIVED pertama pada exact tier upstream. Mengutamakan 6 jam terakhir, lalu histori hingga 7 hari, minimal 1 sampel dengan identity dan latency terverifikasi. Tidak menunggu aktivasi kedaluwarsa atau mensyaratkan persentase keberhasilan; hanya mengukur waktu pada OTP yang masuk, bukan peluang OTP. Pada katalog gabungan mengikuti tier pilihan awal, tidak mencampur histori opsi berharga sama atau provider fallback. Sampel bisa terbatas; bukan jaminan OTP."},"last_otp_received_at":{"type":"string","format":"date-time","description":"Opsional: waktu CODE_RECEIVED kanonis terakhir yang valid dalam histori 7 hari pada exact tier upstream pilihan awal. Tidak mencampur provider, produk, operator, atau tier harga; tidak berisi kode OTP atau identitas pengguna. Bukan jaminan OTP berikutnya."},"reliable":{"type":"boolean","description":"Opsional untuk v1: true jika server, layanan, negara, produk, operator, dan bucket harga yang sama pernah menerima SMS minimal sekali dari riwayat seluruh pengguna. Termasuk pesanan aktif dan riwayat lama. Tidak memakai stok atau status selesai sebagai bukti, dan bukan jaminan OTP."},"base_price_idr":{"type":"integer","minimum":0},"discount_idr":{"type":"integer","minimum":0},"rank_name":{"type":"string"},"rank_discount_bps":{"type":"integer","minimum":0,"maximum":10000}},"required":["id","price_idr","qualitative_stock","stock_count","selection"],"additionalProperties":false},"CatalogQuote":{"type":"object","properties":{"serviceId":{"type":"string"},"countryId":{"type":"string"},"price_idr":{"type":"integer","minimum":0,"description":"Harga opsi pertama. Konfirmasi memakai harga opsi yang dipilih."},"price_min_idr":{"type":"integer","minimum":0,"description":"Pada katalog gabungan: harga minimum dari pilihan visible dan eligible."},"price_max_idr":{"type":"integer","minimum":0,"description":"Pada katalog gabungan: harga maksimum dari pilihan visible dan eligible; bukan batas yang otomatis disetujui."},"qualitative_stock":{"type":"string","enum":["available","limited"],"description":"Ketersediaan kualitatif, bukan jumlah stok yang dijamin."},"stock_count":{"type":["integer","null"],"minimum":0,"description":"Jumlah yang dilaporkan provider untuk pilihan harga ini, bukan jaminan atau reservasi stok. Null jika snapshot cache lama belum menyimpan jumlah."},"options":{"type":"array","items":{"$ref":"#/components/schemas/CatalogQuoteOption"}},"fetched_at":{"type":"string","format":"date-time","description":"ISO 8601 dengan zona waktu."},"expires_at":{"type":"string","format":"date-time","description":"Masa berlaku terpendek kandidat quote; bukan reservasi harga atau nomor."}},"required":["serviceId","countryId","price_idr","qualitative_stock","stock_count","options","fetched_at","expires_at"],"additionalProperties":false},"IntegratorCreateOrder":{"type":"object","properties":{"serviceId":{"type":"string","minLength":1,"maxLength":128,"description":"Salin options[].selection.service.id dari opsi quote yang disetujui; bukan ID representatif pada root quote."},"countryId":{"type":"string","minLength":1,"maxLength":128,"description":"Salin options[].selection.country.id dari opsi yang sama. Provider ditentukan server dari ID sumber."},"clientRequestId":{"type":"string","format":"uuid","description":"UUID per niat pembelian. Pertahankan UUID yang sama saat recovery request yang sama."},"priceOptionId":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Wajib: Quote.options[].id yang dipilih pengguna. Jangan membuat ID sendiri."},"expectedPriceIdr":{"type":"integer","minimum":1,"maximum":2147483647,"description":"Wajib: Quote.options[].price_idr untuk kandidat awal. Harga awal berubah meminta konfirmasi baru."},"maxPriceIdr":{"type":"integer","minimum":1,"maximum":2147483647,"description":"Opsional: batas harga per nomor yang disetujui secara eksplisit, minimal expectedPriceIdr. Tanpa field ini, fallback tidak boleh lebih mahal dari harga awal. Simpan nilai yang sama pada intent dan setiap replay."}},"required":["serviceId","countryId","clientRequestId","priceOptionId","expectedPriceIdr"],"additionalProperties":false}}}}