Skip to content

REST API Reference

The Surf Shield Panel exposes two API surfaces:

Surface Purpose Audience
/webapi/application/... Programmatic management for your own backend Server-to-server
Subscription URL (https://<panel>/sub/<token>) Per-client config document consumed by the SDK Mobile / desktop clients

This page documents the /webapi/application surface. For the local HTTP API exposed by the running core on 127.0.0.1 see the Runtime HTTP API.


Authentication

Every call requires:

Where Name Value
Header X-Api-Key Your application API key (one per application).
Query applicationId UUID of the application the call targets.
GET /webapi/application/clients?applicationId=APPLICATION_UUID
X-Api-Key: YOUR_API_KEY

Base URL: https://your-panel.example/webapi/application.

API keys have application-scoped permissions. Rotate them from the panel dashboard. Never ship an API key in a client app; all management calls belong in your own backend.


Conventions

  • Dates are ISO-ish strings in UTC, formatted yyyy-MM-dd HH:mm:ss.
  • Integer sizes like traffic, used_traffic are bytes.
  • download_speed_limit / upload_speed_limit are Mbps. 0 means "unlimited".
  • max_online_ips = 0 means unlimited concurrent connections.
  • class is a free-form integer reserved for your own tiering.

Traffic States

Value Meaning
traffic = -1 Blocked. Client is excluded from the active list and cannot connect.
traffic = 0 Unlimited traffic (standard quotas still apply via expiry_date).
traffic > 0 Hard allowance in bytes. Client is active while used_traffic <= traffic.

Response Envelope

All JSON responses are wrapped:

{ "status": "success", "data": { ... } }

Failures use HTTP status codes plus:

{ "status": "error", "message": "human-readable error" }

Common status codes:

Code Meaning
200 OK
201 Created
204 No Content (delete / update)
400 Invalid input
401 Missing / invalid API key
403 API key does not cover this application
404 Resource not found
500 Internal error

Client Management

Endpoints under /webapi/application/clients.

List clients (paginated)

GET /webapi/application/clients?applicationId=APP_ID&page=1&pageSize=25
X-Api-Key: YOUR_API_KEY
Query Default Description
page 0 0-based page index.
pageSize 10 Max items per page.

Response:

{
  "status": "success",
  "data": {
    "page": 1,
    "page_size": 25,
    "total_pages": 4,
    "total_items": 98,
    "items": [
      {
        "id": "uuid",
        "username": "user_001",
        "class": 0,
        "is_active": true,
        "created_at": "2025-01-14 11:02:31",
        "updated_at": "2025-02-01 09:11:54",
        "expiry_date": "2026-01-14 23:59:59",
        "traffic": 53687091200,
        "used_traffic": 15048293,
        "max_online_ips": 2,
        "online_ips": ["198.51.100.12"],
        "download_speed_limit": 0,
        "upload_speed_limit": 0,
        "application_id": "app-uuid",
        "subscription_url": "https://panel.example/sub/abcdef"
      }
    ]
  }
}

Create client

POST /webapi/application/clients?applicationId=APP_ID
X-Api-Key: YOUR_API_KEY
Content-Type: application/json

{
  "username": "user_001",
  "traffic": 53687091200,
  "used_traffic": 0,
  "expiry_date": "2025-12-31 23:59:59",
  "max_online_ips": 2,
  "download_speed_limit": 0,
  "upload_speed_limit": 0,
  "class": 0,
  "is_active": true
}

Response:

{ "status": "success", "data": "new-client-uuid" }

Fetch one client

GET /webapi/application/clients/CLIENT_UUID?applicationId=APP_ID
X-Api-Key: YOUR_API_KEY

Update client (partial)

PUT behaves as PATCH — fields you omit are left untouched.

PUT /webapi/application/clients/CLIENT_UUID?applicationId=APP_ID
X-Api-Key: YOUR_API_KEY
Content-Type: application/json

{
  "traffic": -1,
  "is_active": false
}

Use this to:

  • Add more traffic ("traffic": <new-allowance>).
  • Extend the expiry date ("expiry_date": "2026-06-30 23:59:59").
  • Ban instantly ("traffic": -1, "is_active": false).
  • Change tier ("class": 2).

Delete client

DELETE /webapi/application/clients/CLIENT_UUID?applicationId=APP_ID
X-Api-Key: YOUR_API_KEY

Immediately terminates any active session.

Lookup by username

GET /webapi/application/clients/username/user_001?applicationId=APP_ID
X-Api-Key: YOUR_API_KEY

Returns { "status": "success", "data": "uuid" } — useful when integrating with an external identity system where usernames are the primary key.

Traffic statistics

GET /webapi/application/clients/CLIENT_UUID/traffic?applicationId=APP_ID&page=1&pageSize=30
X-Api-Key: YOUR_API_KEY

Returns paginated hourly / daily traffic samples aggregated per node. Use it to drive graphs in your dashboard.

{
  "status": "success",
  "data": {
    "page": 1,
    "page_size": 30,
    "total_pages": 12,
    "total_items": 345,
    "items": [
      {
        "timestamp": "2025-10-18 12:00:00",
        "bytes_in": 2048576,
        "bytes_out": 5242880,
        "node_id": "node-uuid"
      }
    ]
  }
}

Subscription URL (per client)

The panel auto-generates a long-lived URL per client that returns a signed Leaf configuration document. Clients fetch it through the SDK via updateSubscription(clientId, ...) — you do not normally need to touch it from your backend.

If you need to rotate it manually use the panel UI or call:

POST /webapi/application/clients/CLIENT_UUID/rotate-subscription?applicationId=APP_ID
X-Api-Key: YOUR_API_KEY

Old URLs stop working immediately after rotation.


DTO Reference

ClientDTO

Field Type Required Notes
id UUID on read Server-generated.
application_id UUID required Must match the query parameter.
username string required Unique within an application.
class int required Your tier integer. Min: 0.
is_active bool required false blocks login.
traffic int64 required Bytes. -1 block / 0 unlimited.
used_traffic int64 required Bytes. Server-updated every few seconds from node telemetry.
expiry_date date required yyyy-MM-dd HH:mm:ss.
max_online_ips int required Concurrent connections. 0 unlimited.
online_ips string[] on read Currently connected source IPs.
download_speed_limit int64 required Mbps.
upload_speed_limit int64 required Mbps.
subscription_url string on read Long-lived URL consumed by the SDK.
created_at / updated_at date on read Audit timestamps.

PaginationDTO<T>

Field Description
items Array of T.
page 1-based page number.
page_size Items per page.
total_pages Total pages.
total_items Total matching items.

Typical flows

Provision a new user

  1. Your billing system calls POST /clients with traffic, expiry_date, max_online_ips.
  2. Panel returns the new client UUID.
  3. You display the UUID (or the deep link leafvpn://install?clientId=<uuid>) to the user.
  4. User opens the mobile / desktop app; the SDK calls updateSubscription(uuid) and fetches the signed config.

Suspend for non-payment

  1. Billing job calls PUT /clients/<uuid> with { "is_active": false, "traffic": -1 }.
  2. Active sessions drop on the next telemetry tick (usually < 30 s).

Top up traffic

  1. Billing job calls PUT /clients/<uuid> with { "traffic": <new-allowance>, "used_traffic": 0 }.
  2. Next updateSubscription call on the client mirrors the new quota into LeafPreferences.

See also