# Proxies.sx Reseller API - Complete LLM Reference Base URL: https://api.proxies.sx/v1 Authentication: X-API-Key: psx_xxx OR Authorization: Bearer Content-Type: application/json --- ## CRITICAL: How Billing Works IMPORTANT: All charges deduct from YOUR RESELLER BALANCE, not customer balance. 1. Port Creation: Deducts wholesale cost ($2/day shared, $3/day private) from YOUR balance 2. Customer Topup: Transfers from YOUR balance to customer's internal balance 3. Your Profit: resellerPrice - wholesaleCost (based on your pricebook margin) Example: 30-day shared port with 20% margin - Wholesale cost (charged to you): $2 × 30 = $60 - Reseller price (charge your customer): $60 × 1.20 = $72 - Your profit: $12 --- ## Authentication ### Login (get JWT token) POST /login/signin Body: {"email": "you@example.com", "password": "yourpass"} Response: {"token": "eyJ...", "user": {...}} ### Create API Key (recommended for integrations) POST /api-keys Headers: Authorization: Bearer Body: {"name": "My Integration", "scopes": ["customers:read", "customers:write", "ports:read", "ports:write", "webhooks:read", "webhooks:write", "billing:read"]} Response: {"apiKey": "psx_abc123...", "keyInfo": {...}} ### Available Scopes customers:read, customers:write, ports:read, ports:write, ports:rotate, webhooks:read, webhooks:write, billing:read, billing:write, traffic:read --- ## Customers (Sub-accounts) ### Create Customer POST /reseller/customers Body: { "email": "customer@example.com", // required, unique per reseller "name": "Customer Name", // required "company": "Company Inc", // optional "allocatedPortSlots": 10, // optional, default 0 "allocatedTrafficGB": 100, // optional, default 0 "externalId": "your-crm-id", // optional, for your reference "initialBalance": 0 // optional, requires YOUR balance } Response: {"_id": "...", "email": "...", "status": "active", ...} ### List Customers GET /reseller/customers?status=active&search=term&limit=20&skip=0 Response: {"customers": [...], "total": 50, "skip": 0, "limit": 20} ### Get Single Customer GET /reseller/customers/:id Response: {full customer object} ### Update Customer PATCH /reseller/customers/:id Body: {"name": "New Name", "company": "New Co", "notes": "VIP"} ### Delete Customer (soft delete) DELETE /reseller/customers/:id Response: {"success": true, "message": "Customer deleted successfully"} ### Find by External ID GET /reseller/customers/by-external-id/:externalId Response: {customer object} ### Suspend Customer POST /reseller/customers/:id/suspend Body: {"reason": "Payment overdue"} ### Unsuspend Customer POST /reseller/customers/:id/unsuspend ### Top Up Customer Balance (DEDUCTS FROM YOUR BALANCE!) POST /reseller/customers/:id/topup Body: {"amount": 100, "description": "Monthly credit", "referenceId": "INV-001"} Note: Checks YOUR reseller balance first, then transfers to customer ### Set Customer Quota (absolute) PUT /reseller/customers/:id/quota Body: {"portSlots": 20, "trafficGB": 200} ### Adjust Customer Quota (incremental) PATCH /reseller/customers/:id/quota Body: {"portSlotsDelta": 5, "trafficGBDelta": 50} ### Get Customer Usage Stats GET /reseller/customers/:id/usage Response: { "customer": {...}, "quotaUsage": {"portSlotsUsed": 5, "portSlotsAllocated": 10, "portSlotsPercent": 50, ...}, "balanceStats": {"currentBalance": 100, "totalTopUp": 500, "totalSpent": 400} } ### Get Customer Quota Status GET /reseller/customers/:id/quota-status Response: { "customerId": "...", "portSlots": {"used": 5, "allocated": 10, "available": 5, "isAtCapacity": false}, "traffic": {"usedGB": 50, "allocatedGB": 100, "availableGB": 50, "isExceeded": false}, "canCreatePorts": true, "canRotatePorts": true, "warnings": [] } --- ## Balance Management (NEW!) ### Deduct from Customer Balance POST /reseller/customers/:id/deduct Body: {"amount": 60, "reason": "Port purchase: Germany 30-day", "referenceId": "ORD-001"} Response: { "success": true, "customer": {"balance": 40, "totalSpent": 60}, "transaction": {"type": "deduct", "amount": -60, "balanceBefore": 100, "balanceAfter": 40} } Note: Does NOT return money to reseller. Use /refund for that. ### Refund to Reseller Balance (CREDITS YOUR BALANCE!) POST /reseller/customers/:id/refund Body: {"amount": 30, "reason": "Port cancellation refund", "referenceId": "REF-001"} Response: { "success": true, "message": "Refunded $30.00 from customer to reseller balance", "customer": {"balance": 70}, "transaction": {"type": "refund", "resellerBalanceAffected": true} } Note: Deducts from customer AND credits YOUR reseller balance back. ### Adjust Balance (No Reseller Impact) POST /reseller/customers/:id/adjust-balance Body: {"amount": 20, "reason": "Goodwill credit", "referenceId": "ADJ-001"} Body: {"amount": -10, "reason": "Billing correction"} // negative to deduct Response: { "success": true, "customer": {"balance": 120}, "transaction": {"type": "adjust", "resellerBalanceAffected": false} } Note: Does NOT affect your reseller balance. For corrections/goodwill only. ### Get Customer Transaction History GET /reseller/customers/:id/transactions?type=deduct&startDate=2026-01-01&limit=20 Response: { "transactions": [ {"type": "topup", "amount": 100, "balanceBefore": 0, "balanceAfter": 100, "reason": "Initial topup"}, {"type": "deduct", "amount": -60, "balanceBefore": 100, "balanceAfter": 40, "reason": "Port purchase"} ], "total": 2, "skip": 0, "limit": 20 } Types: topup, deduct, refund, adjust, port_charge --- ## Ports (CRITICAL SECTION) ### Create Port for Customer (CHARGES YOUR BALANCE!) POST /reseller/ports Body: { "customerId": "customer_id", // required "countryId": "country_id", // required - get from device availability "cityId": "city_id", // optional "carrierId": "carrier_id", // optional "days": 30, // required, 1-365 "exclusive": false, // optional, dedicated device "isPrivate": false // optional, private port (1.5x cost) } Response: { "port": { "_id": "port_id", "displayName": "psx_abc_xyz", "httpPort": 10001, "socksPort": 10002, "proxyLogin": "user123", "proxyPassword": "pass456", "serverIp": "1.2.3.4", "expiresAt": 1740000000000, "status": "active" }, "pricing": { "costToReseller": 60.00, // What was charged to YOUR balance "resellerPrice": 72.00, // What you should charge your customer "margin": 12.00 // Your profit } } Error 402: Insufficient balance (your account) Error 400: Quota exceeded (customer's port slots) ### List All Ports GET /reseller/ports?customerId=&status=active&limit=20&skip=0 Response: {"ports": [...], "total": 100, "skip": 0, "limit": 20} ### List Customer's Ports GET /reseller/ports/customer/:customerId Response: {"ports": [...], "total": 5} ### Get Single Port GET /reseller/ports/:portId Response: {full port object with connection details} ### Get Expiring Ports GET /reseller/ports/expiring?hours=24 Response: {"ports": [...], "total": 3} ### Get Port Summary Stats GET /reseller/ports/summary Response: { "total": 100, "active": 85, "expired": 10, "suspended": 5, "expiringIn24h": 3, "byCustomer": [{"customerId": "...", "customerName": "...", "count": 10}, ...] } ### Check Quota Before Creating POST /reseller/ports/check-quota Body: {"customerId": "customer_id"} Response: { "canCreate": true, "reason": null, "quotaStatus": {"used": 5, "allocated": 10, "available": 5} } ### Calculate Price Before Creating POST /reseller/ports/calculate-price Body: {"customerId": "customer_id", "countryId": "country_id", "days": 30} Response: { "basePrice": 60.00, "resellerPrice": 72.00, "marginAmount": 12.00, "marginPercent": 20, "appliedRules": ["Margin: +20%", "30 days"] } ### Delete Port DELETE /reseller/ports/:portId Response: {"success": true, "message": "Port deleted"} ### Rotate Port (new IP/device) POST /reseller/ports/:portId/rotate Response: {"success": true, "message": "Port rotated successfully", "portId": "...", "newDeviceId": "..."} ### Extend Port Time POST /reseller/ports/:portId/extend Body: {"days": 30} // Number of days to extend Response: {"success": true, "message": "...", "portId": "...", "daysAdded": 30, "newExpiresAt": "..."} ### Suspend Port POST /reseller/ports/:portId/suspend Body: {"reason": "Customer request"} ### Unsuspend Port POST /reseller/ports/:portId/unsuspend --- ## Pricebook (Your Pricing Configuration) ### Get Your Pricebook GET /reseller/pricebook Response: { "_id": "...", "pricingMode": "margin", // margin | fixed | multiplier "portPricing": {"marginPercent": 20}, "trafficPricing": {"marginPercent": 20}, "defaultMarginPercent": 20, "countryOverrides": [], "customerOverrides": [] } ### Update Pricebook PUT /reseller/pricebook Body: { "pricingMode": "margin", "defaultMarginPercent": 25, "portPricing": {"marginPercent": 25, "minimumPrice": 1.50}, "trafficPricing": {"marginPercent": 30} } ### Calculate Price (preview using pricebook rules) POST /reseller/pricebook/calculate Body: { "type": "port", // required: "port" or "traffic" "basePrice": 2.00, // optional: base price from tariff "days": 30, // optional: for port type "trafficGB": 10, // optional: for traffic type "countryId": "...", // optional: for country override "customerId": "..." // optional: for customer override } Response: {"basePrice": 60, "resellerPrice": 75, "marginAmount": 15, "marginPercent": 25, "appliedRules": [...]} ### Add Country Override (fixed price for specific country) POST /reseller/pricebook/country-override Body: {"countryId": "...", "portPricePerDay": 3.50, "trafficPricePerGB": 0.75} ### Remove Country Override DELETE /reseller/pricebook/country-override/:countryId --- ## Webhooks (Real-time Event Notifications) ### Create Webhook POST /reseller/webhooks Body: { "name": "Production Webhook", "url": "https://your-server.com/webhook", "events": ["port.created", "port.expired", "customer.quota_exceeded"], "secret": "your-hmac-secret", // optional, for signature verification "headers": {"X-Custom": "value"} // optional } ### List Webhooks GET /reseller/webhooks?isActive=true&limit=20 Response: {"webhooks": [...], "total": 3, "skip": 0, "limit": 20} ### Get Single Webhook GET /reseller/webhooks/:id ### Get Available Event Types GET /reseller/webhooks/event-types Response: ["port.created", "port.deleted", "port.expiring", "port.expired", "port.rotated", "port.suspended", "port.unsuspended", "customer.created", "customer.updated", "customer.suspended", "customer.unsuspended", "customer.quota_warning", "customer.quota_exceeded", "traffic.threshold_50", "traffic.threshold_80", "traffic.threshold_100", "balance.low", "balance.topup", "purchase.completed"] ### Update Webhook PUT /reseller/webhooks/:id Body: {"url": "https://new-url.com/webhook", "events": [...], "isActive": true} ### Delete Webhook DELETE /reseller/webhooks/:id Response: {"success": true, "message": "Webhook deleted"} ### Test Webhook (sends test payload) POST /reseller/webhooks/:id/test Body: {"eventType": "port.created"} Response: {"success": true, "status": 200, "durationMs": 145} ### Get Webhook Delivery History GET /reseller/webhooks/:id/deliveries?status=failed&limit=20 Response: {"deliveries": [...], "total": 50} ### Webhook Payload Format { "id": "evt_abc123", "type": "port.created", "created": "2026-01-19T12:00:00.000Z", "data": { "portId": "...", "customerId": "...", ... } } ### Verify Webhook Signature (if secret configured) Header: X-Webhook-Signature: sha256=abc123... Verify: sha256=HMAC-SHA256(JSON.stringify(payload), secret) --- ## Analytics & Revenue ### Dashboard Overview GET /reseller/analytics/dashboard Response: { "customers": {"total": 50, "active": 45, "suspended": 5, "newThisMonth": 10}, "ports": {"totalActive": 450, "expiringIn24h": 15}, "revenue": {"thisMonth": 4500.00, "lastMonth": 4200.00, "growthPercent": 7.14}, "margin": {"thisMonth": 900.00, "lastMonth": 840.00, "averagePercent": 20} } ### Revenue Summary GET /reseller/analytics/revenue?startDate=2026-01-01&endDate=2026-01-31 Response: { "totalRevenue": 5000.00, "totalCost": 4000.00, "totalMargin": 1000.00, "marginPercent": 20, "transactionCount": 50, "byType": {"port_purchase": {...}, "port_extension": {...}} } ### Revenue by Period GET /reseller/analytics/revenue/by-period?period=day&startDate=...&endDate=... period: day | week | month Response: [{"period": "2026-01-19", "revenue": 150, "cost": 120, "margin": 30, "transactionCount": 5}, ...] ### Revenue by Customer GET /reseller/analytics/revenue/by-customer?limit=10&startDate=...&endDate=... Response: [{"customerId": "...", "customerName": "...", "customerEmail": "...", "totalRevenue": 500, "totalMargin": 100}, ...] ### Commission History GET /reseller/analytics/commissions?customerId=&type=port_purchase&startDate=&endDate=&limit=20 Response: {"commissions": [...], "total": 100, "skip": 0, "limit": 20} --- ## Rate Limits | Endpoint | Limit | |----------|-------| | POST /reseller/customers | 10/min | | PATCH /reseller/customers/:id | 30/min | | POST /reseller/customers/:id/topup | 20/min | | POST /reseller/ports | 5/10sec | | POST /reseller/ports/:id/rotate | 10/min | | POST /reseller/webhooks/:id/test | 5/min | | GET /reseller/analytics/* | 30/min | | Other endpoints | 100/min | --- ## Error Codes | Code | Meaning | |------|---------| | 400 | Bad Request - Invalid input | | 401 | Unauthorized - Invalid/missing auth | | 402 | Payment Required - Insufficient balance | | 403 | Forbidden - Missing permissions/scope | | 404 | Not Found - Resource doesn't exist | | 409 | Conflict - Duplicate resource | | 429 | Too Many Requests - Rate limited | --- ## Common Workflows ### 1. Onboard New Customer POST /reseller/customers {email, name, allocatedPortSlots: 10} POST /reseller/customers/:id/topup {amount: 100} ### 2. Create Port for Customer POST /reseller/ports/check-quota {customerId} POST /reseller/ports/calculate-price {customerId, countryId, days} POST /reseller/ports {customerId, countryId, days} ### 3. Monitor Expiring Ports GET /reseller/ports/expiring?hours=48 ### 4. Setup Webhooks GET /reseller/webhooks/event-types POST /reseller/webhooks {url, events, secret} POST /reseller/webhooks/:id/test {eventType: "port.created"} ### 5. Check Revenue GET /reseller/analytics/dashboard GET /reseller/analytics/revenue/by-customer?limit=10 --- ## Account Balance Check GET /account/summary Response: {"balance": 1500.00, "topUp": 2000.00, "spent": 500.00} --- Full interactive docs: https://api.proxies.sx/docs/seller (user: admin, pass: proxies2025secure)