-
Notifications
You must be signed in to change notification settings - Fork 0
Integration Paths
The kit supports four integration shapes. Pick one based on what you already have. Mixing paths is rarely a good idea — they have different boundaries and trust models.
Do you have an app already?
│
├── No / starting fresh
│ └──▶ PATH A — clone apps/starter/ (full Next.js storefront)
│
└── Yes
│
├── It's a React/Next.js app
│ └──▶ PATH B — <PoolPortal /> drop-in component
│
├── It's a non-React JS app (Express, Fastify, Hono, Bun, Deno, Workers)
│ └──▶ PATH C — @proxies-sx/pool-sdk only
│
└── It's not JavaScript at all (PHP, Python, Go, Ruby, Rust, Elixir, etc.)
└──▶ PATH D — REST API directly
You get: a complete app — landing page, pricing, magic-link auth, Stripe checkout, customer dashboard, webhook handler, Postgres schema — all under 1,000 LOC.
Effort: ~10 minutes to running locally with test Stripe.
Use when: you don't already have a customer-facing app and want to get to first revenue fast.
git clone https://github.com/bolivian-peru/proxy-reseller-kit.git my-shop
cd my-shop/apps/starter
cp .env.example .env
# Edit .env: PROXIES_SX_API_KEY, STRIPE_*, AUTH_SECRET, EMAIL settings
pnpm install
docker compose up -d db
pnpm db:migrate
pnpm devIn another terminal:
stripe listen --forward-to localhost:3000/api/stripe/webhookVisit http://localhost:3000. Sign in (dev mode prints the magic link to your terminal — no SMTP needed). Buy a plan with 4242 4242 4242 4242. Webhook mints a pak_. Dashboard shows your live proxy URL.
Customize everything in apps/starter/src/config.ts — brand name, colors, logo, pricing, supported countries, CTA copy.
Deploy to anywhere that runs Node + Postgres: Vercel + Neon, Fly.io, Railway, your own VPS with Docker Compose. All examples documented in apps/starter/README.md.
You get: a drop-in customer dashboard component that handles pak display, usage stats, proxy URL builder, session spawner. Plus a createPoolApiHandlers() route factory for the backend.
Effort: ~15 minutes if your auth and routing are already wired.
Use when: you have a Next.js / Remix / Vite-React app and want to add a proxy dashboard page without rebuilding it.
npm install @proxies-sx/pool-portal-react @proxies-sx/pool-sdk
# npm serves the current releases (pool-portal-react 0.9.0, pool-sdk 0.8.1) -
# the GitHub Release tarball workaround from the 0.6.x era is no longer neededFrontend — drop in a page:
// app/dashboard/page.tsx
'use client';
import { PoolPortal } from '@proxies-sx/pool-portal-react';
import '@proxies-sx/pool-portal-react/styles.css';
export default function Dashboard() {
return <PoolPortal apiRoute="/api/pool" branding={{ name: 'AcmeProxies' }} />;
}Backend — one catchall route:
// app/api/pool/[...path]/route.ts
import { ProxiesClient } from '@proxies-sx/pool-sdk';
import { createPoolApiHandlers } from '@proxies-sx/pool-portal-react/server';
import { auth } from '@/lib/auth';
import { db } from '@/lib/db'; // maps userId -> pakKeyId
const proxies = new ProxiesClient({
apiKey: process.env.PROXIES_SX_API_KEY!,
proxyUsername: process.env.PROXIES_SX_USERNAME!,
});
export const { GET, POST, DELETE } = createPoolApiHandlers({
proxies,
// Who is making this request? null = handlers return 401.
getSessionUserId: async () => (await auth())?.user?.id ?? null,
// Scope each authenticated customer to their own pak. CRITICAL - see below.
getUserKeyId: async (userId) => (await db.customers.get(userId))?.pakKeyId ?? null,
});The getSessionUserId + getUserKeyId callbacks are your security boundary. The first resolves who is asking (your auth system); the second maps that user to the pak_ they own (your DB). Without them, /me and /regenerate are unscoped — any logged-in user could touch any pak. Do not skip this.
For full multi-tenant scoping (sessions, etc.), see packages/react/README.md → "Session routes — multi-tenant security."
You get: a typed TS/JS client. Mint paks, list, update, top-up, regenerate, delete, build proxy URLs.
Effort: ~10 minutes.
Use when: you're on Node/Bun/Deno/Workers and either you're not using React, or you want to build your own UI.
npm install @proxies-sx/pool-sdkimport { ProxiesClient } from '@proxies-sx/pool-sdk';
const proxies = new ProxiesClient({
apiKey: process.env.PROXIES_SX_API_KEY!,
proxyUsername: process.env.PROXIES_SX_USERNAME!,
});
// Mint a 10 GB key that lasts 60 days
const key = await proxies.poolKeys.create({
label: 'customer:alice@example.com',
trafficCapGB: 10,
expiresAt: new Date(Date.now() + 60 * 86_400_000).toISOString(),
idempotencyKey: stripeEvent.id, // critical for webhook handlers
});
// Build a proxy URL the customer can use
const url = proxies.buildProxyUrl(key.key, {
country: 'us',
rotation: 'sticky',
sid: 'alice_session_1',
});
console.log(url);
// → http://psx_yourUsername-mbl-us-sid-alice_session_1-rot-sticky:pak_xxx@gw.proxies.sx:7000Full SDK API surface in packages/sdk/README.md.
You get: direct access to the same endpoints the SDK wraps. Plain HTTP, JSON, X-API-Key auth.
Effort: ~5 minutes per language.
Use when: your backend is PHP, Python, Go, Ruby, Rust, Elixir — anything that's not JavaScript.
| Method | Path | Purpose |
|---|---|---|
POST |
/v1/reseller/pool-keys |
Mint a pak (accepts Idempotency-Key header) |
GET |
/v1/reseller/pool-keys |
List paks + usage |
GET |
/v1/reseller/pool-keys/:id |
Fetch one pak |
PATCH |
/v1/reseller/pool-keys/:id |
Update label / cap / enabled / expiresAt |
POST |
/v1/reseller/pool-keys/:id/topup |
Atomic cap + expiry extension |
POST |
/v1/reseller/pool-keys/:id/regenerate |
Rotate the secret |
DELETE |
/v1/reseller/pool-keys/:id |
Delete |
Full OpenAPI spec: api.proxies.sx/docs/api-json · Swagger UI: api.proxies.sx/docs/api
# bash + curl
curl -X POST https://api.proxies.sx/v1/reseller/pool-keys \
-H "X-API-Key: $PROXIES_SX_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $STRIPE_EVENT_ID" \
-d '{"label":"alice","trafficCapGB":10,"expiresAt":"2026-08-30T00:00:00Z"}'# Python
import os, requests
r = requests.post(
'https://api.proxies.sx/v1/reseller/pool-keys',
headers={
'X-API-Key': os.environ['PROXIES_SX_API_KEY'],
'Idempotency-Key': stripe_event_id,
},
json={'label': 'alice', 'trafficCapGB': 10},
)
r.raise_for_status()
key = r.json()// PHP
<?php
$ch = curl_init('https://api.proxies.sx/v1/reseller/pool-keys');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . getenv('PROXIES_SX_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ' . $stripeEventId,
],
CURLOPT_POSTFIELDS => json_encode(['label' => 'alice', 'trafficCapGB' => 10]),
CURLOPT_RETURNTRANSFER => true,
]);
$key = json_decode(curl_exec($ch), true);// Go
body, _ := json.Marshal(map[string]any{"label": "alice", "trafficCapGB": 10})
req, _ := http.NewRequest("POST", "https://api.proxies.sx/v1/reseller/pool-keys", bytes.NewReader(body))
req.Header.Set("X-API-Key", os.Getenv("PROXIES_SX_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", stripeEventID)
resp, _ := http.DefaultClient.Do(req)# Ruby
require 'net/http'; require 'json'
uri = URI('https://api.proxies.sx/v1/reseller/pool-keys')
req = Net::HTTP::Post.new(uri, {
'X-API-Key' => ENV['PROXIES_SX_API_KEY'],
'Content-Type' => 'application/json',
'Idempotency-Key' => stripe_event_id,
})
req.body = { label: 'alice', trafficCapGB: 10 }.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }The proxy URL the customer uses is the same plain HTTP Basic-Auth format regardless of how the pak was minted:
http://psx_RESELLER-mbl-us-sid-CUSTOMER_SESSION-rot-sticky:pak_CUSTOMER_KEY@gw.proxies.sx:7000
These apply no matter which path you pick:
- Idempotency-Key on every mint — critical for webhook handlers. Use the upstream payment system's event ID, not a random UUID.
-
Never expose
psx_keys to the browser — server-side only. - Customer onboarding UX — they need to know the username token format, not just the pak. See "Show your customers HOW to use their pak" in the README.
- Top up your wholesale balance — see Getting-Started step 1.
- Pak lifecycle — what happens at expiry, at cap, on regenerate. See Pak-Key-Lifecycle.
- Sticky semantics — read Sticky-Sessions-and-Rotation before responding to "the IP changes" tickets.
- Pak-Key-Lifecycle — what happens after you mint
- Sticky-Sessions-and-Rotation — what sticky guarantees
- Troubleshooting — when things break
Wiki for bolivian-peru/proxy-reseller-kit · Built by Proxies.sx · Edit on GitHub