> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lupin.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Proxies

# Proxies (white-label)

Lupin resells residential proxy capacity through its own API. Your end
customers create proxy users, receive connection credentials, and never
think about traffic again — quotas refill themselves from your pool.
They never see the upstream provider.

## The whole integration

```bash theme={null}
curl -X POST https://api.lupin.sh/v0/proxies/users \
  -H "Authorization: Bearer $LUPIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "customer-1", "threads": 20, "quota_mb": 1024}'
```

That's it. The customer gets a `connection` block (host, port, username,
password, `http` + `socks5`) and a 1024 MB quota that tops itself back up
whenever it runs low. No top-up calls, no balance checks.

```json theme={null}
{
  "proxy_user_id": "pxu_…",
  "username": "customer-1",
  "quota_mb": 1024,
  "used_mb": 0,
  "auto_refill": true,
  "status": "active",
  "connection": {
    "host": "…",
    "port": 0,
    "username": "customer-1",
    "password": "…",
    "schemes": ["http", "socks5"]
  }
}
```

Hand the `connection` block to your customer as-is. No upstream names,
dashboards, or provider references are ever included in responses.

## How autopilot works

* **Quota** (`quota_mb`) is the balance we maintain per user. Omit it and the
  user is created with whatever `traffic_mb` you pass (or 0).
* **Usage** (`used_mb`) is derived as cumulative allocated minus upstream
  remaining, synced on every read plus a 5-minute background sweep.
* **Refill**: when remaining balance drops below the low-water mark (10% of
  quota, min 100 MB, or your custom `low_threshold_mb`), we move traffic
  from your pool automatically. `auto_refill: false` opts a user out.
* **Pool dry**: if a refill fails, you get a `proxy.pool.low` webhook event —
  you top up the pool once instead of every customer managing balances.

```bash theme={null}
# watch usage, not balances
curl https://api.lupin.sh/v0/proxies/users/pxu_… -H "Authorization: Bearer ***"
# => { quota_mb: 1024, used_mb: 386, traffic_mb: 1024, ... }
```

## Advanced (ops only)

These exist for your own tooling — your customers should never need them:

```bash theme={null}
# manual adjustment (negative mb subtracts)
curl -X POST https://api.lupin.sh/v0/proxies/users/pxu_…/topup \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{"mb": 512}'

# your pool balance
curl https://api.lupin.sh/v0/proxies/balance -H "Authorization: Bearer ***"
# => { "provider": "dataimpulse", "balance_mb": 102400 }
```

List responses use the standard envelope: `{count, limit?, next_page_token?, items}` —
here the items key is `proxy_users`.

## Errors

| code                           | meaning                                                               |
| ------------------------------ | --------------------------------------------------------------------- |
| `validation_error`             | Bad `username`, `threads`, or `mb` (top-up `mb` must be non-zero).    |
| `proxy_upstream_error` (`502`) | Upstream provider call failed — retry, or check reseller credentials. |
| `not_found`                    | Unknown `proxy_user_id`.                                              |

Error payloads follow the standard shape
`{name, code?, message, fix?, docs?}` — see [Conventions](/conventions).

## SDK / CLI

```ts theme={null}
await lupin.proxies.createUser({ username: "customer-1", quota_mb: 1024 });
const u = await lupin.proxies.getUser(proxyUserId); // { quota_mb, used_mb, ... }
await lupin.proxies.topup(proxyUserId, 512); // ops only
```

```bash theme={null}
lupin proxies create --username customer-1 --threads 20 --traffic-mb 1024
lupin proxies get --proxy-user-id pxu_…
```
