{"components":{"schemas":{"Error":{"example":{"error":"bad_request","message":"product_id is required.","request_id":"3f6a9b1f4b2c41f6a8d5c0a3baf4f6aa"},"properties":{"error":{"description":"Error code identifier.","type":"string"},"message":{"description":"Human-readable error summary.","type":"string"},"request_id":{"description":"Request id for troubleshooting.","type":"string"}},"type":"object"},"TokenResponse":{"example":{"request_id":"3f6a9b1f4b2c41f6a8d5c0a3baf4f6aa","role":"reseller","token":"owr_live_9o2Hc5v0w4XcWm6mN8hJv1X0uHnE6m0B","token_id":12,"token_type":"Bearer","user_id":901},"properties":{"request_id":{"type":"string"},"role":{"type":"string"},"token":{"type":"string"},"token_id":{"type":"integer"},"token_type":{"type":"string"},"user_id":{"type":"integer"}},"type":"object"},"VPNAllocation":{"additionalProperties":false,"properties":{"cancellation_effective_date":{"format":"date","nullable":true,"type":"string"},"connection_allowance":{"type":"integer"},"created_at":{"format":"date-time","nullable":true,"type":"string"},"credentials_available":{"type":"boolean"},"credentials_url":{"type":"string"},"enduser_id":{"nullable":true,"type":"integer"},"expires_at":{"format":"date-time","nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"monthly_price":{"description":"Exact decimal ZAR amount.","pattern":"^\\d+\\.\\d{2}$","type":"string"},"payment_mode":{"enum":["cash","points"],"type":"string"},"plan_key":{"enum":["protector","shield","armor"],"type":"string"},"service_id":{"nullable":true,"type":"integer"},"status":{"enum":["active","revocation_pending","released"],"type":"string"}},"required":["id","plan_key","payment_mode","enduser_id","service_id","status","connection_allowance","monthly_price","expires_at","created_at","cancellation_effective_date","credentials_available","credentials_url"],"type":"object"},"VPNAllocationResponse":{"additionalProperties":false,"properties":{"allocation":{"$ref":"#/components/schemas/VPNAllocation"},"request_id":{"type":"string"},"shared_terms":{"type":"string"}},"required":["allocation","shared_terms","request_id"],"type":"object"},"VPNAllocationsResponse":{"additionalProperties":false,"properties":{"allocations":{"items":{"$ref":"#/components/schemas/VPNAllocation"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"request_id":{"type":"string"},"total":{"type":"integer"}},"required":["allocations","total","limit","offset","request_id"],"type":"object"},"VPNCheckoutInput":{"additionalProperties":false,"example":{"accept_shared_terms":true,"enduser_id":null,"payment_mode":"cash","plan_key":"shield","request_key":"cd8a4d11-2808-44c0-8346-445b8940c8da","signed_quote":"<signed_quote returned by /vpn/quotes>"},"properties":{"accept_shared_terms":{"enum":[true],"type":"boolean"},"enduser_id":{"minimum":1,"nullable":true,"type":"integer"},"payment_mode":{"default":"cash","enum":["cash","points"],"type":"string"},"plan_key":{"enum":["protector","shield","armor"],"type":"string"},"renew_allocation_id":{"format":"uuid","nullable":true,"type":"string"},"request_key":{"format":"uuid","type":"string"},"signed_quote":{"maxLength":8192,"minLength":1,"type":"string"}},"required":["request_key","plan_key","signed_quote","accept_shared_terms"],"type":"object"},"VPNCredentialsResponse":{"additionalProperties":false,"properties":{"allocation_id":{"format":"uuid","type":"string"},"credentials":{"additionalProperties":false,"properties":{"password":{"type":"string"},"username":{"type":"string"}},"required":["username","password"],"type":"object"},"downloads":{"additionalProperties":{"format":"uri","type":"string"},"type":"object"},"request_id":{"type":"string"},"shared_terms":{"type":"string"}},"required":["allocation_id","credentials","downloads","shared_terms","request_id"],"type":"object"},"VPNOffer":{"additionalProperties":false,"properties":{"cash_quote":{"$ref":"#/components/schemas/VPNPrice"},"connection_allowance":{"type":"integer"},"countries":{"items":{"type":"string"},"type":"array"},"currency":{"type":"string"},"includes_south_africa":{"type":"boolean"},"name":{"type":"string"},"network_tier":{"type":"string"},"payment_modes":{"items":{"enum":["cash","points"],"type":"string"},"type":"array"},"plan_key":{"enum":["protector","shield","armor"],"type":"string"},"points_price":{"nullable":true,"type":"integer"},"rrp_monthly":{"description":"Exact decimal ZAR amount.","pattern":"^\\d+\\.\\d{2}$","type":"string"}},"required":["plan_key","name","rrp_monthly","connection_allowance","network_tier","countries","includes_south_africa","currency","payment_modes","points_price","cash_quote"],"type":"object"},"VPNOffersResponse":{"additionalProperties":false,"properties":{"offers":{"items":{"$ref":"#/components/schemas/VPNOffer"},"type":"array"},"ordering_available":{"type":"boolean"},"request_id":{"type":"string"},"shared_terms":{"type":"string"}},"required":["offers","ordering_available","shared_terms","request_id"],"type":"object"},"VPNPrice":{"additionalProperties":false,"properties":{"billing_date":{"format":"date","type":"string"},"currency":{"enum":["ZAR"],"type":"string"},"discount_percentage":{"description":"Exact decimal ZAR amount.","pattern":"^\\d+\\.\\d{2}$","type":"string"},"due_today":{"description":"Exact decimal ZAR amount.","pattern":"^\\d+\\.\\d{2}$","type":"string"},"first_renewal":{"format":"date","nullable":true,"type":"string"},"grant_days":{"nullable":true,"type":"integer"},"monthly_price":{"description":"Exact decimal ZAR amount.","pattern":"^\\d+\\.\\d{2}$","type":"string"},"payment_mode":{"enum":["cash","points"],"type":"string"},"plan_key":{"enum":["protector","shield","armor"],"type":"string"},"points":{"type":"integer"}},"required":["plan_key","payment_mode","currency","monthly_price","due_today","points","discount_percentage","billing_date","first_renewal","grant_days"],"type":"object"},"VPNQuoteInput":{"additionalProperties":false,"example":{"payment_mode":"cash","plan_key":"shield"},"properties":{"payment_mode":{"default":"cash","enum":["cash","points"],"type":"string"},"plan_key":{"enum":["protector","shield","armor"],"type":"string"}},"required":["plan_key"],"type":"object"},"VPNQuoteResponse":{"additionalProperties":false,"properties":{"expires_at":{"format":"date-time","nullable":true,"type":"string"},"quote":{"$ref":"#/components/schemas/VPNPrice"},"request_id":{"type":"string"},"shared_terms":{"type":"string"},"signed_quote":{"type":"string"}},"required":["quote","signed_quote","expires_at","shared_terms","request_id"],"type":"object"},"VPNReceipt":{"additionalProperties":false,"properties":{"allocation_id":{"format":"uuid","type":"string"},"cash_amount":{"description":"Exact decimal ZAR amount.","pattern":"^\\d+\\.\\d{2}$","type":"string"},"id":{"format":"uuid","type":"string"},"points_amount":{"type":"integer"}},"required":["id","cash_amount","points_amount","allocation_id"],"type":"object"},"VPNRequest":{"additionalProperties":false,"properties":{"allocation_id":{"format":"uuid","nullable":true,"type":"string"},"allocation_url":{"nullable":true,"type":"string"},"can_review":{"type":"boolean"},"enduser_id":{"nullable":true,"type":"integer"},"expires_at":{"format":"date-time","nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"message":{"type":"string"},"payment_mode":{"enum":["cash","points"],"type":"string"},"payment_taken":{"type":"boolean"},"plan_key":{"enum":["protector","shield","armor"],"type":"string"},"quote_expires_at":{"format":"date-time","nullable":true,"type":"string"},"reason":{"nullable":true,"type":"string"},"receipt":{"additionalProperties":false,"nullable":true,"properties":{"allocation_id":{"format":"uuid","type":"string"},"cash_amount":{"description":"Exact decimal ZAR amount.","pattern":"^\\d+\\.\\d{2}$","type":"string"},"id":{"format":"uuid","type":"string"},"points_amount":{"type":"integer"}},"required":["id","cash_amount","points_amount","allocation_id"],"type":"object"},"status":{"enum":["queued","creating_master","waiting_provider","completing","fulfilled","review_required","price_review_required","cancelled","failed"],"type":"string"},"status_url":{"type":"string"}},"required":["id","status","plan_key","payment_mode","enduser_id","payment_taken","can_review","reason","message","quote_expires_at","expires_at","allocation_id","status_url","allocation_url","receipt"],"type":"object"},"VPNRequestResponse":{"additionalProperties":false,"properties":{"request":{"$ref":"#/components/schemas/VPNRequest"},"request_id":{"type":"string"}},"required":["request","request_id"],"type":"object"},"VPNRequestsResponse":{"additionalProperties":false,"properties":{"request_id":{"type":"string"},"requests":{"items":{"$ref":"#/components/schemas/VPNRequest"},"type":"array"}},"required":["requests","request_id"],"type":"object"},"Wallet":{"example":{"balance":2515.72,"insufficient_credit_flag":false,"renewal_amount":9438.34,"renewal_line_items":[{"amount":74.26,"category":"MTN Mobile (GSM)","description":"Uncapped 10Mbps Basic","line_type":"service","msisdn":"0814285697","service_id":445,"sim_serial":"8927020215841211","status":"active"}],"renewal_service_count":18,"renewal_source":"active_service_calculator","request_id":"f7ac9b63d7b443f8a9ef6a0c2a61d11e","required_topup":0.0},"properties":{"balance":{"type":"number"},"insufficient_credit_flag":{"type":"boolean"},"renewal_amount":{"type":"number"},"renewal_line_items":{"description":"Exact calculator line items that add up to renewal_amount.","items":{"properties":{"amount":{"type":"number"},"category":{"type":"string"},"description":{"type":"string"},"domain":{"nullable":true,"type":"string"},"line_type":{"type":"string"},"msisdn":{"nullable":true,"type":"string"},"service_id":{"nullable":true,"type":"integer"},"sim_serial":{"nullable":true,"type":"string"},"status":{"nullable":true,"type":"string"}},"type":"object"},"type":"array"},"renewal_service_count":{"type":"integer"},"renewal_source":{"type":"string"},"request_id":{"type":"string"},"required_topup":{"type":"number"}},"type":"object"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"API Token","scheme":"bearer","type":"http"}}},"info":{"description":"Reseller-facing API for managing reseller accounts, services, orders, hosting, invoices, and end users. Admin-only endpoints are intentionally excluded. Rate limit: 100 requests per minute per token. Use the bearer token you generate in the reseller dashboard (API Tokens page).","title":"OpenWeb Reseller API","version":"2.3.3"},"openapi":"3.0.3","paths":{"/api/v1/auth/revoke":{"post":{"description":"Revokes the bearer token used for this request.","responses":{"200":{"content":{"application/json":{"examples":{"revoked":{"value":{"message":"Token revoked.","request_id":"c1b9a2df7aab4b048ba38a63d8d7c191","success":true}}}}},"description":"Revoked"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"}},"security":[{"bearerAuth":[]}],"summary":"Revoke current token","tags":["Auth"]}},"/api/v1/auth/token":{"post":{"description":"Exchange reseller email/username and password for a bearer token. Use the returned token in the Authorization header for all other calls. Example: Authorization: Bearer YOUR_TOKEN.","requestBody":{"content":{"application/json":{"examples":{"issueToken":{"summary":"Create token for CRM integration","value":{"email":"api@reseller.example","name":"CRM Integration","password":"StrongPassword1!"}}},"schema":{"properties":{"email":{"type":"string"},"name":{"type":"string"},"password":{"type":"string"}},"required":["email","password"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"examples":{"token":{"value":{"request_id":"3f6a9b1f4b2c41f6a8d5c0a3baf4f6aa","role":"reseller","token":"owr_live_9o2Hc5v0w4XcWm6mN8hJv1X0uHnE6m0B","token_id":12,"token_type":"Bearer","user_id":901}}},"schema":{"$ref":"#/components/schemas/TokenResponse"}}},"description":"Token issued"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid credentials"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not a reseller account"}},"summary":"Issue API token","tags":["Auth"]}},"/api/v1/auth/tokens":{"get":{"description":"List all tokens created by the authenticated reseller.","responses":{"200":{"content":{"application/json":{"examples":{"tokens":{"value":{"request_id":"f2e7f3d10b6b4c36b17e8f2c18a4c1b9","tokens":[{"created_at":"2025-12-01T09:00:00","id":12,"last_used_at":"2025-12-01T10:15:00","name":"CRM Integration","revoked_at":null}]}}}}},"description":"Token list"}},"security":[{"bearerAuth":[]}],"summary":"List reseller API tokens","tags":["Auth"]}},"/api/v1/endusers":{"get":{"description":"Returns endusers belonging to the reseller. Use q to search.","parameters":[{"description":"Search by username, email, or name.","in":"query","name":"q","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"examples":{"endusers":{"value":{"endusers":[{"created_at":"2025-11-05T12:10:03","email":"client1@example.com","first_name":"Client","id":2101,"is_active":true,"last_name":"One","mobile_number":"0821234567","username":"client1"}],"request_id":"9bb0c0d8a65d4f7c9e322bfc84df05d3"}}}}},"description":"Enduser list"}},"security":[{"bearerAuth":[]}],"summary":"List endusers","tags":["Endusers"]},"post":{"description":"Creates a new enduser under the reseller. A welcome message is queued automatically.","requestBody":{"content":{"application/json":{"examples":{"createEnduser":{"value":{"email":"client1@example.com","first_name":"Client","last_name":"One","mobile_number":"0821234567","password":"TempPass123!","username":"client1"}}},"schema":{"type":"object"}}}},"responses":{"201":{"content":{"application/json":{"examples":{"created":{"value":{"enduser_id":2101,"request_id":"5c64c7b8e20c4f1b9c50b1f5a1d9df6f","success":true}}}}},"description":"Enduser created"}},"security":[{"bearerAuth":[]}],"summary":"Create enduser","tags":["Endusers"]}},"/api/v1/endusers/assign-product":{"post":{"description":"Creates a pending order for an existing enduser, or creates a new enduser and assigns the product in the same call. Uses the same order payload fields as /api/v1/orders.","requestBody":{"content":{"application/json":{"examples":{"assignExisting":{"value":{"enduser_id":2101,"notes":"Assign to existing enduser via API.","product_id":15,"quantity":1,"sim_card_id":982}},"createAndAssign":{"value":{"enduser":{"email":"client2@example.com","first_name":"Client","last_name":"Two","mobile_number":"0827654321","password":"TempPass123!","username":"client2"},"notes":"Create enduser and assign product in one call.","product_id":15,"quantity":1,"sim_card_id":982}}},"schema":{"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"examples":{"assigned":{"value":{"enduser_created":false,"enduser_id":2101,"invoice_id":4451,"monthly_price":399.0,"order_id":9103,"order_number":"ORD1234567","request_id":"6f9b777f52d148d2b8479d3d9fd67db2","status":"pending","success":true,"total_due_today":350.79}}}}},"description":"Order created for enduser"}},"security":[{"bearerAuth":[]}],"summary":"Assign a product to an enduser","tags":["Endusers"]}},"/api/v1/endusers/{enduser_id}":{"put":{"description":"Updates enduser profile fields or password.","parameters":[{"in":"path","name":"enduser_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"examples":{"updateEnduser":{"value":{"is_active":true,"mobile_number":"0829876543"}}},"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"examples":{"updated":{"value":{"request_id":"d3a10e3e6b49451f8a04f302d79d79c3","success":true,"updated":{"mobile_number":"0829876543"}}}}}},"description":"Enduser updated"}},"security":[{"bearerAuth":[]}],"summary":"Update enduser","tags":["Endusers"]}},"/api/v1/fibre/coverage":{"post":{"description":"Requires a configured Axxess provider connection. Submit product_id, address, latitude and longitude. Only status=available returns a signed token valid for 30 minutes, bound to your reseller, product and address. When Fibre coverage enforcement is enabled, pass this token as fibre_coverage_token to /orders or /endusers/assign-product, with the returned address as openserve_installation_address. Other providers, planned coverage, missing or expired tokens cannot authorize checkout. 503 means verification is unavailable, not that the property has no coverage. No order or payment is created by this endpoint.","requestBody":{"content":{"application/json":{"schema":{"properties":{"address":{"maxLength":600,"minLength":10,"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"product_id":{"type":"integer"}},"required":["product_id","address","latitude","longitude"],"type":"object"}}},"required":true},"responses":{"200":{"description":"Coverage result; token only for live OpenServe"},"400":{"description":"Invalid input"},"401":{"description":"Authentication required"},"404":{"description":"Active Fibre product not found"},"429":{"description":"Six checks per minute exceeded"},"503":{"description":"Provider not configured or coverage verification failed"}},"security":[{"bearerAuth":[]}],"summary":"Verify live OpenServe coverage before ordering Fibre","tags":["Orders"]}},"/api/v1/health":{"get":{"description":"Simple health endpoint to confirm API availability.","responses":{"200":{"content":{"application/json":{"examples":{"healthy":{"value":{"status":"ok","timestamp":"2026-02-14T08:30:12","version":"2.3.3"}}}}},"description":"API healthy"}},"summary":"API health check","tags":["System"]}},"/api/v1/hosting/nameservers":{"post":{"description":"Update nameservers for an existing hosting service.","requestBody":{"content":{"application/json":{"examples":{"nameservers":{"value":{"ns1":"pns1.readarr.org","ns2":"pns2.readarr.org","service_id":516}}},"schema":{"properties":{"ns1":{"type":"string"},"ns2":{"type":"string"},"service_id":{"type":"integer"}},"required":["service_id","ns1","ns2"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"examples":{"updated":{"value":{"domain":"chatbunny.space","nameservers":["pns1.readarr.org","pns2.readarr.org"],"request_id":"3f4f18c4f8394b7f9c4aa5c3f4dc2c4a","service_id":516,"success":true}}}}},"description":"Nameservers updated"}},"security":[{"bearerAuth":[]}],"summary":"Update hosting nameservers","tags":["Hosting"]}},"/api/v1/hosting/products":{"get":{"description":"Returns hosting products and free-domain thresholds.","responses":{"200":{"content":{"application/json":{"examples":{"products":{"value":{"products":[{"base_price":79.0,"discounted_price":74.26,"free_domain_count":1,"free_domain_max_usd":0.95,"id":105,"name":"Starter","whm_package":"starter"}],"request_id":"9f67c2a642b14d1791ae0d6d8e746b98"}}}}},"description":"Hosting products"}},"security":[{"bearerAuth":[]}],"summary":"List hosting products","tags":["Hosting"]}},"/api/v1/hosting/provision":{"post":{"description":"Registers the selected domain, updates nameservers, creates the hosting account, and creates an active hosting service. Wallet is debited, with refund on failure.","requestBody":{"content":{"application/json":{"examples":{"provision":{"value":{"address1":"Unit 1, Raddical Park, 3 Gourley Rd","address2":"Ballito","city":"Ballito","domain":"chatbunny.space","email":"john@example.com","first_name":"John","job_id":"d7b3bdd32e5049d1962fba814b21d325","last_name":"Doe","organization":"OpenWeb","phone":"0119668082","postal_code":"4399","product_id":105,"state":"KZN"}}},"schema":{"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"examples":{"provisioned":{"value":{"cpanel_password":"G7mY3!w5qP9","cpanel_username":"chatbun","domain":"chatbunny.space","due_today":9.0,"monthly_price":74.26,"nameservers":["pns1.readarr.org","pns2.readarr.org"],"request_id":"4fbac7cb61aa4e63b27aab0b8f1a8c9f","service_id":516,"success":true}}}}},"description":"Hosting provisioned"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Insufficient credit"}},"security":[{"bearerAuth":[]}],"summary":"Provision hosting package (domain + WHM account)","tags":["Hosting"]}},"/api/v1/hosting/search":{"post":{"description":"Starts a hosting domain search and returns a job_id to poll for results. Recommended results always include .com, .co.za, .org.za and .net; local ZA domains are checked through Domains.co.za.","requestBody":{"content":{"application/json":{"examples":{"search":{"value":{"domain":"chatbunny","product_id":105}}},"schema":{"properties":{"domain":{"type":"string"},"product_id":{"type":"integer"}},"required":["product_id","domain"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"examples":{"job":{"value":{"job_id":"d7b3bdd32e5049d1962fba814b21d325","query":"chatbunny","request_id":"7e343d80d6a74e9ca1e5ad81c7e64c37","status_url":"/api/v1/hosting/search/d7b3bdd32e5049d1962fba814b21d325"}}}}},"description":"Search job started"}},"security":[{"bearerAuth":[]}],"summary":"Start hosting domain search","tags":["Hosting"]}},"/api/v1/hosting/search/{job_id}":{"get":{"description":"Returns domain search status and paginated results. Use results_page/results_per_page for pagination and free_domain_threshold to mark free domains. Each result includes its registrar provider and ZAR retail price.","parameters":[{"in":"path","name":"job_id","required":true,"schema":{"type":"string"}},{"in":"query","name":"results_page","schema":{"default":1,"type":"integer"}},{"in":"query","name":"results_per_page","schema":{"default":10,"type":"integer"}},{"in":"query","name":"free_domain_threshold","schema":{"default":0.95,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"examples":{"results":{"value":{"com_net_results":[{"currency":"USD","domain":"chatbunny.com","is_free":false,"register_price":9.99,"register_price_zar":377.72},{"currency":"USD","domain":"chatbunny.net","is_free":false,"register_price":12.99,"register_price_zar":491.08}],"domain_choice":{"currency":"USD","domain":"chatbunny.space","is_free":true,"register_price":0.88,"register_price_zar":33.12},"free_domain_threshold":0.95,"meta":{"api_calls":20,"duration_seconds":18.9},"request_id":"7e343d80d6a74e9ca1e5ad81c7e64c37","results":[{"currency":"USD","domain":"chatbunny.space","is_free":true,"register_price":0.88,"register_price_zar":33.12},{"currency":"USD","domain":"chatbunny.site","is_free":true,"register_price":0.92,"register_price_zar":34.61}],"results_page":1,"results_pages":12,"results_total":120,"status":"ready"}}}}},"description":"Search status and results"}},"security":[{"bearerAuth":[]}],"summary":"Get hosting domain search results","tags":["Hosting"]}},"/api/v1/invoices":{"get":{"description":"Returns all invoices for the reseller with download URLs.","responses":{"200":{"content":{"application/json":{"examples":{"invoices":{"value":{"invoices":[{"billing_period_end":"2025-12-31","billing_period_start":"2025-12-01","download_url":"https://reseller.openweb.co.za/reseller/invoices/988/download","due_date":"2025-12-01","id":988,"invoice_date":"2025-12-01","invoice_number":"INV-2025-12-00988","invoice_type":"monthly_renewal","status":"paid","total_amount":9438.34}],"request_id":"ed83c232afe5461da7a16f6d0f9c74f2"}}}}},"description":"Invoices list"}},"security":[{"bearerAuth":[]}],"summary":"List invoices","tags":["Wallet"]}},"/api/v1/me":{"get":{"description":"Returns reseller profile and support-panel settings.","responses":{"200":{"content":{"application/json":{"examples":{"profile":{"value":{"company_name":"OpenWeb LTE","created_at":"2024-11-01T10:22:11","email":"lte@openweb.co.za","first_name":"LTE","id":901,"last_name":"Tester","mobile_number":"0821234567","phone":"0119668082","physical_address":"Unit 1, Raddical Park, 3 Gourley Rd, Ballito, 4399","request_id":"1f06c3a0e2f74cc8a0e32fa6bbf2b1d0","role":"reseller","support_email":"reseller@openweb.co.za","support_panel_enabled":true,"support_phone":"0119668082","support_ticket_url":"https://reseller.openweb.co.za/support","support_whatsapp":"27821234567","username":"lte@openweb.co.za"}}}}},"description":"Profile payload"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"}},"security":[{"bearerAuth":[]}],"summary":"Get reseller profile","tags":["Profile"]},"put":{"description":"Update reseller contact fields and support panel details.","requestBody":{"content":{"application/json":{"examples":{"updateProfile":{"summary":"Update support panel details","value":{"support_email":"support@reseller.co.za","support_panel_enabled":true,"support_phone":"0111234567","support_ticket_url":"https://helpdesk.example.com","support_whatsapp":"27821234567"}}},"schema":{"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"examples":{"updated":{"value":{"request_id":"66a0c6a5205c4a679d1348d72d7d6f9a","success":true,"updated":{"support_panel_enabled":true}}}}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation error"}},"security":[{"bearerAuth":[]}],"summary":"Update reseller profile","tags":["Profile"]}},"/api/v1/orders":{"post":{"description":"Creates a pending service order for SIM, fibre or Voice products. Voice/SIP/VoIP/Cloud PBX needs no SIM and remains pending for admin QR upload and SIP provisioning. VPN purchases must use signed quotes and /api/v1/vpn/checkouts, never generic orders. Hosting orders must use the hosting workflow endpoints. Consumer and Business OpenServe Fibre use normal pro-rata, require openserve_installation_address, openserve_term_first_name, openserve_term_last_name and openserve_term_agreement=true for a signed six-month term. Business Fibre supports quantity=1 and optional add_static_ip=true for five usable static IPv4 addresses at a fixed R699/month, pro-rated at checkout. Other products cannot order this add-on. When Fibre coverage enforcement is enabled, first POST /api/v1/fibre/coverage and pass its token as fibre_coverage_token with the exact verified address. Invalid coverage blocks checkout before payment. Admin supplies the B Circuit and five IPs when activating the order.","requestBody":{"content":{"application/json":{"examples":{"order":{"value":{"notes":"Provision for client ABC","product_id":15,"quantity":1,"sim_card_id":345,"use_owp_prorata_waiver":false}}},"schema":{"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"examples":{"created":{"value":{"order_id":532,"request_id":"c4a08d2c1a3e4a7fb2f4c24fdc28d772","success":true}}}}},"description":"Order created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation error"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Insufficient credit"}},"security":[{"bearerAuth":[]}],"summary":"Create a new service order","tags":["Orders"]}},"/api/v1/products":{"get":{"description":"Returns active products with reseller-discounted pricing. Dedicated VPN offers are discovered via vpn_offers_url, never generic Product IDs. Cloud PBX products include cloud_pbx: included_minutes (integer or null), unlimited (boolean), concurrent_lines (integer or null), fulfillment=admin_pending, billing=monthly. Metered 50/100/500-minute plans each include 4 concurrent calls; unlimited plans have 1 or 2. This is included allowance, not Voice FUP. Other products have cloud_pbx=null.","responses":{"200":{"content":{"application/json":{"examples":{"products":{"value":{"products":[{"base_price":79.0,"category":"Telkom","category_id":3,"discounted_price":74.26,"display_title":"Uncapped 10Mbps Basic \u00b7 Telkom","id":15,"is_active":true,"name":"Uncapped 10Mbps Basic","product_type":"Data"}],"request_id":"c99f4e0f6e6b4b8c9a9f45c2fdcd1f77","vpn_offers_url":"/api/v1/vpn/offers"}}}}},"description":"Product list"}},"security":[{"bearerAuth":[]}],"summary":"List active products","tags":["Catalog"]}},"/api/v1/services":{"get":{"description":"Returns all services for the reseller. Optional filter by status. MTN Mobile records include authoritative MSISDN, sim_serial_number, account_type, mtn_static_ip, total usage, and active connection count from Masterfile. Legacy static_ip_requested and static_ip_enabled fields are not returned.","parameters":[{"description":"Filter by service status (active, scheduled, suspended).","in":"query","name":"status","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"examples":{"services":{"value":{"request_id":"b6a5c88f7cba4b7e85a35b6b3b7abf9b","services":[{"account_type":"MTN Mobile (GSM)","created_at":"2025-11-18T10:42:13","hosting_domain":null,"id":445,"monthly_fee":74.26,"msisdn":"0814285697","mtn_static_ip":"102.132.45.17","product_id":15,"product_name":"Uncapped 10Mbps Basic","reseller_id":901,"sim_serial":"8927020215841211","sim_serial_number":"8927020215841211","status":"active","usage":{"active_connections":1,"last_synced_at":"2026-08-28T10:05:00","source":"Masterfile","total_display":"126.42 GB","total_gb":126.42},"username":"0814285697@mobile.is.co.za"}]}}}}},"description":"Service list"}},"security":[{"bearerAuth":[]}],"summary":"List services","tags":["Services"]}},"/api/v1/services/{service_id}":{"get":{"description":"Returns a single complete service payload. For @mobile.is.co.za accounts, network identity and usage fields are refreshed from Masterfile's cached provider data.","parameters":[{"in":"path","name":"service_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"examples":{"service":{"value":{"request_id":"e52fb5db3f9c4b0b9cd4f6c6e4c7d4f1","service":{"account_type":"MTN Mobile (GSM)","created_at":"2025-11-18T10:42:13","hosting_domain":null,"id":445,"monthly_fee":74.26,"msisdn":"0814285697","mtn_static_ip":"102.132.45.17","product_id":15,"product_name":"Uncapped 10Mbps Basic","reseller_id":901,"sim_serial":"8927020215841211","sim_serial_number":"8927020215841211","status":"active","usage":{"active_connections":1,"last_synced_at":"2026-08-28T10:05:00","source":"Masterfile","total_display":"126.42 GB","total_gb":126.42},"username":"0814285697@mobile.is.co.za"}}}}}},"description":"Service detail"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"bearerAuth":[]}],"summary":"Get service","tags":["Services"]}},"/api/v1/services/{service_id}/cancel":{"post":{"description":"Schedules a cancellation based on the reseller cancellation policy. The response indicates the effective cancellation date and whether the 29th cut-off was missed.","parameters":[{"in":"path","name":"service_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"examples":{"cancel":{"value":{"reason":"Client requested cancellation"}}},"schema":{"properties":{"reason":{"type":"string"}},"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"examples":{"scheduled":{"value":{"effective_date":"2025-12-31","missed_cutoff":false,"note":"Current-month cancellation applied (cut-off is the 29th).","request_id":"c3a1b4d0f4b648b2aab6122a8b1c0b99","service_id":445,"success":true}}}}},"description":"Cancellation scheduled"}},"security":[{"bearerAuth":[]}],"summary":"Schedule service cancellation","tags":["Services"]}},"/api/v1/services/{service_id}/reactivate":{"post":{"description":"Reactivates a service previously scheduled for cancellation.","parameters":[{"in":"path","name":"service_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"examples":{"reactivated":{"value":{"request_id":"cb1a11dd7f7b48eebd4a85ea2b94bfc0","service_id":445,"status":"active","success":true}}}}},"description":"Service reactivated"}},"security":[{"bearerAuth":[]}],"summary":"Reactivate scheduled service","tags":["Services"]}},"/api/v1/sim-inventory":{"get":{"description":"Returns SIM inventory counts by category for the reseller.","responses":{"200":{"content":{"application/json":{"examples":{"inventory":{"value":{"inventory":[{"assigned":3,"available":4,"category_id":3,"category_name":"MTN Mobile 4G/5G Data","pending":1,"provisioned":1,"total":10,"used":1}],"request_id":"b00ef6f2a8bb49b7b99d2f3d4db8d6ea"}}}}},"description":"SIM inventory summary"}},"security":[{"bearerAuth":[]}],"summary":"SIM inventory summary","tags":["Catalog"]}},"/api/v1/transactions":{"get":{"description":"Returns wallet transactions. Use limit/offset for pagination.","parameters":[{"in":"query","name":"limit","schema":{"default":50,"type":"integer"}},{"in":"query","name":"offset","schema":{"default":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"examples":{"transactions":{"value":{"limit":50,"offset":0,"request_id":"0d29492a8a354b41ab48a2db1378b09f","transactions":[{"amount":-79.0,"created_at":"2025-12-01T08:31:45","description":"Order #529 \u00b7 Uncapped 10Mbps Basic","id":10291,"processed_at":"2025-12-01T08:31:45","status":"completed","type":"purchase"}]}}}}},"description":"Transaction list"}},"security":[{"bearerAuth":[]}],"summary":"List wallet transactions","tags":["Wallet"]}},"/api/v1/vpn/allocations":{"get":{"description":"Local metadata only, without secrets or shared inventory. Includes cash and OWP grants and historical states. Use limit=1..100 (default 50), offset>=0. No renewal or removal processing. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_allocations","parameters":[{"in":"query","name":"limit","schema":{"default":50,"maximum":100,"minimum":1,"type":"integer"}},{"in":"query","name":"offset","schema":{"default":0,"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNAllocationsResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"List owned VPN allocations","tags":["VPN"]}},"/api/v1/vpn/allocations/{allocation_id}":{"get":{"description":"Local allocation and cancellation metadata without credentials. Cash monthly_price is the captured discounted ordinary-billing fee; points grants have an expires_at boundary and no Service. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_allocation","parameters":[{"in":"path","name":"allocation_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNAllocationResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"Read owned VPN allocation metadata","tags":["VPN"]}},"/api/v1/vpn/allocations/{allocation_id}/credentials":{"get":{"description":"Explicit sensitive access to shared username/password and approved brandless downloads. No provider calls. Requires active, unexpired/not-suspended entitlement, active owned assigned enduser and assignable local capacity. Released, expired, cleanup or inactive allocations return 409. Never embed credentials in URLs or browser storage. No provider keys, raw responses, master identifiers, other owners or per-customer credential isolation are exposed. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_credentials","parameters":[{"in":"path","name":"allocation_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNCredentialsResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"Read active owned VPN credentials","tags":["VPN"]}},"/api/v1/vpn/checkouts":{"post":{"description":"Generate and persist request_key (UUID) BEFORE sending. Submit the reviewed signed_quote and accept_shared_terms=true. Reuse exactly the same UUID and checkout identity after timeout or retry; a committed receipt is authoritative even if the quote has since expired. Different plan/mode/enduser/renewal under the UUID is rejected. Optional enduser_id must be active and owned. 200 means a verified allocation and one committed wallet/OWP debit; 202 means funded demand queued, not payment or access. No provider writes occur in HTTP requests. The dedicated worker manages demand-backed capacity and validates the original terms/funds before allocation. One pending demand per reseller. Unfulfilled requests expire after 24 hours. When can_review=true, obtain a fresh quote, review it and explicitly resubmit the SAME UUID and identity; never let polling accept changed prices. For explicit Shield points renewal set renew_allocation_id and the original enduser_id; extends the current unexpired grant by 30 days from its old boundary. No automatic points renewal. Cash monthly renewals use ordinary billing, not this endpoint. No OWP is earned. No refunds or master operations are exposed. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_checkout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNCheckoutInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNRequestResponse"}}},"description":"Local result or committed checkout receipt"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNRequestResponse"}}},"description":"Durable capacity demand only; no reseller payment taken. The worker may later allocate and charge exactly once while the reviewed quote remains valid."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"Explicit idempotent VPN checkout","tags":["VPN"]}},"/api/v1/vpn/offers":{"get":{"description":"Read-only local catalogue. Protector R99/one connection, Shield R179/one, Armor+ R279/two. Cash estimates apply the reseller discount once and prorate remaining SAST calendar days. Shield also supports an explicit 250 OWP/30-day grant. No stock reservation or provider I/O. Countries=[\"*\"] means all supported countries; country/network restrictions require external setup, not API enforcement. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_offers","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNOffersResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"List VPN offers","tags":["VPN"]}},"/api/v1/vpn/quotes":{"post":{"description":"No purchase or provider I/O. Quote valid for 15 minutes and bound to reseller, plan, payment mode, discount, SAST billing date and immutable offer terms. Review shared_terms before checkout. Price/date/discount changes invalidate the quote. Points pricing has grant_days=30 and no monthly cash Service. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_quote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNQuoteInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNQuoteResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"Review a signed VPN quote","tags":["VPN"]}},"/api/v1/vpn/requests":{"get":{"description":"Local-only discovery of nonterminal demand references, including review-required requests. No provider refresh or writes. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_requests","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNRequestsResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"Find your pending VPN requests","tags":["VPN"]}},"/api/v1/vpn/requests/{request_key}":{"get":{"description":"LOCAL data only; no provider calls, new demand, quote refresh or payment. A committed receipt overrides stale pending progress. can_review requires human review and explicit checkout resubmission, never automatic repricing. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_request","parameters":[{"in":"path","name":"request_key","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNRequestResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"Poll a VPN request or receipt","tags":["VPN"]}},"/api/v1/vpn/requests/{request_key}/cancel":{"post":{"description":"Cancels local waiting demand only. If fulfilment committed first, returns its receipt without refunding, revoking credentials or cancelling the allocated service. Does not replay or cancel provider operations. Requires an active reseller bearer token; all data is owner-scoped. Responses, including errors, are private/no-store/no-referrer. Never log signed quotes or credentials.","operationId":"api_vpn_cancel_request","parameters":[{"in":"path","name":"request_key","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VPNRequestResponse"}}},"description":"Local result or committed checkout receipt"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid JSON/fields"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Inactive/non-owner access"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Owned resource not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Quote, funds, onboarding, capacity or entitlement conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Existing API rate limit exceeded"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unavailable or unconfirmed; poll the same UUID"}},"security":[{"bearerAuth":[]}],"summary":"Cancel unfulfilled VPN demand","tags":["VPN"]}},"/api/v1/wallet":{"get":{"description":"Returns the wallet balance and the authoritative renewal amount recalculated from all billable active, suspended, and period-eligible scheduled services.","responses":{"200":{"content":{"application/json":{"examples":{"wallet":{"value":{"balance":2515.72,"insufficient_credit_flag":false,"renewal_amount":9438.34,"renewal_line_items":[{"amount":74.26,"category":"MTN Mobile (GSM)","description":"Uncapped 10Mbps Basic","line_type":"service","msisdn":"0814285697","service_id":445,"sim_serial":"8927020215841211","status":"active"}],"renewal_service_count":18,"renewal_source":"active_service_calculator","request_id":"f7ac9b63d7b443f8a9ef6a0c2a61d11e","required_topup":0.0}}},"schema":{"$ref":"#/components/schemas/Wallet"}}},"description":"Wallet"}},"security":[{"bearerAuth":[]}],"summary":"Get credit wallet","tags":["Wallet"]}}},"servers":[{"url":"https://reseller.openweb.co.za"}],"tags":[{"description":"Authenticate and manage API tokens.","name":"Auth"},{"description":"Reseller profile management.","name":"Profile"},{"description":"Credit wallet balances and transactions.","name":"Wallet"},{"description":"Products and SIM inventory.","name":"Catalog"},{"description":"Active services, cancellations, and reactivations.","name":"Services"},{"description":"Create new service orders.","name":"Orders"},{"description":"Hosting domain search and provisioning.","name":"Hosting"},{"description":"Enduser account management.","name":"Endusers"},{"description":"Health check and diagnostics.","name":"System"},{"description":"Owned shared VPN offers, signed checkout, demand status and access.","name":"VPN"}]}
