mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-25 15:25:01 +03:00
0516a1f87c
Integrations could only find a client by subscription ID through a substring search over the paged client list followed by a second lookup by email. GET /panel/api/clients/get/subId/:subId now returns the full client records (settings, attached inbounds, external links, traffic, tunnel addresses) for an exact subId match, as a list because older rows may share an ID. It sits behind the same admin login or admin-token check as the email lookup.
685 lines
65 KiB
Plaintext
685 lines
65 KiB
Plaintext
---
|
||
title: Clients
|
||
description: Manage clients as first-class entities that can be attached to one
|
||
or more inbounds. A single client row drives the settings.clients entry in
|
||
every inbound it belongs to. Endpoints live under /panel/api/clients.
|
||
full: true
|
||
_openapi:
|
||
preload:
|
||
- ./public/openapi.json
|
||
toc:
|
||
- depth: 2
|
||
title: List every client with its attached inbound IDs and traffic record. The
|
||
reverse field, if set, is returned as a nested JSON object (legacy
|
||
JSON-encoded-string form is still accepted on write).
|
||
url: '#list-every-client-with-its-attached-inbound-ids-and-traffic-record-the-reverse-field-if-set-is-returned-as-a-nested-json-object-legacy-json-encoded-string-form-is-still-accepted-on-write'
|
||
- depth: 2
|
||
title: 'Filter, sort, and paginate clients on the server. Each item is a slim
|
||
row (no uuid/password/auth/flow/security/reverse/tgId) so the clients
|
||
page can ship 25-ish rows in a few KB instead of the full table. The
|
||
response also includes a summary computed across the full DB row set so
|
||
dashboard counters stay stable as the user paginates or filters: the
|
||
*Count fields are exact, while the email arrays beside them stop at 200
|
||
entries so the payload does not grow with the panel. Page size capped at
|
||
200; fetch /get/:email to obtain the full per-client payload for an
|
||
edit/info modal.'
|
||
url: '#filter-sort-and-paginate-clients-on-the-server-each-item-is-a-slim-row-no-uuidpasswordauthflowsecurityreversetgid-so-the-clients-page-can-ship-25-ish-rows-in-a-few-kb-instead-of-the-full-table-the-response-also-includes-a-summary-computed-across-the-full-db-row-set-so-dashboard-counters-stay-stable-as-the-user-paginates-or-filters-the-count-fields-are-exact-while-the-email-arrays-beside-them-stop-at-200-entries-so-the-payload-does-not-grow-with-the-panel-page-size-capped-at-200-fetch-getemail-to-obtain-the-full-per-client-payload-for-an-editinfo-modal'
|
||
- depth: 2
|
||
title: Fetch one client by email, including the inbound IDs and external config
|
||
IDs it is attached to.
|
||
url: '#fetch-one-client-by-email-including-the-inbound-ids-and-external-config-ids-it-is-attached-to'
|
||
- depth: 2
|
||
title: Fetch clients by subscription ID.
|
||
url: '#fetch-clients-by-subscription-id'
|
||
- depth: 2
|
||
title: Fetch clients by Telegram user ID. Returns an array since multiple
|
||
clients can share the same Telegram ID.
|
||
url: '#fetch-clients-by-telegram-user-id-returns-an-array-since-multiple-clients-can-share-the-same-telegram-id'
|
||
- depth: 2
|
||
title: Create a new client and attach it to one or more inbounds in a single
|
||
call. Body is JSON. Per-protocol secrets are generated server-side when
|
||
omitted, so callers can send only the universal fields.
|
||
url: '#create-a-new-client-and-attach-it-to-one-or-more-inbounds-in-a-single-call-body-is-json-per-protocol-secrets-are-generated-server-side-when-omitted-so-callers-can-send-only-the-universal-fields'
|
||
- depth: 2
|
||
title: Preview client auto-renewal dates without saving or resetting anything.
|
||
url: '#preview-client-auto-renewal-dates-without-saving-or-resetting-anything'
|
||
- depth: 2
|
||
title: Update an existing client by email. Changes propagate to every attached
|
||
inbound. Body is the JSON client payload — supply the full set of fields
|
||
you want to keep (the server replaces the row, it does not patch).
|
||
url: '#update-an-existing-client-by-email-changes-propagate-to-every-attached-inbound-body-is-the-json-client-payload--supply-the-full-set-of-fields-you-want-to-keep-the-server-replaces-the-row-it-does-not-patch'
|
||
- depth: 2
|
||
title: Delete a client by email. Removes it from every attached inbound and
|
||
drops its traffic record unless keepTraffic=1 is passed.
|
||
url: '#delete-a-client-by-email-removes-it-from-every-attached-inbound-and-drops-its-traffic-record-unless-keeptraffic1-is-passed'
|
||
- depth: 2
|
||
title: Attach an existing client to one or more additional inbounds. Body is
|
||
JSON.
|
||
url: '#attach-an-existing-client-to-one-or-more-additional-inbounds-body-is-json'
|
||
- depth: 2
|
||
title: Detach a client from one or more inbounds without deleting the client.
|
||
url: '#detach-a-client-from-one-or-more-inbounds-without-deleting-the-client'
|
||
- depth: 2
|
||
title: Replace a client's external links and external subscriptions. Sends the
|
||
full set; the server replaces all rows. Disabled rows stay saved for
|
||
editing but are not emitted in generated subscriptions. The owning
|
||
client's disabled or expired state also stops these rows from being
|
||
emitted on future subscription fetches; credentials already imported by
|
||
an app remain valid until the external provider revokes them.
|
||
url: '#replace-a-clients-external-links-and-external-subscriptions-sends-the-full-set-the-server-replaces-all-rows-disabled-rows-stay-saved-for-editing-but-are-not-emitted-in-generated-subscriptions-the-owning-clients-disabled-or-expired-state-also-stops-these-rows-from-being-emitted-on-future-subscription-fetches-credentials-already-imported-by-an-app-remain-valid-until-the-external-provider-revokes-them'
|
||
- depth: 2
|
||
title: Reset the up/down counters for every client globally. Quotas and expiry
|
||
are not affected. Triggers an Xray restart if any counter actually
|
||
moved.
|
||
url: '#reset-the-updown-counters-for-every-client-globally-quotas-and-expiry-are-not-affected-triggers-an-xray-restart-if-any-counter-actually-moved'
|
||
- depth: 2
|
||
title: Delete every client whose traffic quota is exhausted (used >= total, when
|
||
reset is disabled) or whose expiry has passed. Returns the deleted count
|
||
and triggers an Xray restart when any client was on a running inbound.
|
||
url: '#delete-every-client-whose-traffic-quota-is-exhausted-used--total-when-reset-is-disabled-or-whose-expiry-has-passed-returns-the-deleted-count-and-triggers-an-xray-restart-when-any-client-was-on-a-running-inbound'
|
||
- depth: 2
|
||
title: Delete every client that is not attached to any inbound, along with its
|
||
traffic record, IP log, HWID devices, and external links. Useful for
|
||
clearing clients left unattached after their inbounds were removed.
|
||
Returns the deleted count. Cannot be undone.
|
||
url: '#delete-every-client-that-is-not-attached-to-any-inbound-along-with-its-traffic-record-ip-log-hwid-devices-and-external-links-useful-for-clearing-clients-left-unattached-after-their-inbounds-were-removed-returns-the-deleted-count-cannot-be-undone'
|
||
- depth: 2
|
||
title: Return every client as a {client, inboundIds, traffic} array — the shape
|
||
/import accepts — so the payload round-trips straight back through
|
||
/import. traffic carries the usage counters (up, down, resetCount,
|
||
lastOnline, lastSubFetch) and is omitted for a client with no traffic
|
||
row; the quota itself stays in client.totalGB. Clients with no inbound
|
||
attachment are included with an empty inboundIds list. The UI shows this
|
||
in a CodeMirror viewer (copy / download); programmatic callers get the
|
||
array in obj.
|
||
url: '#return-every-client-as-a-client-inboundids-traffic-array--the-shape-import-accepts--so-the-payload-round-trips-straight-back-through-import-traffic-carries-the-usage-counters-up-down-resetcount-lastonline-lastsubfetch-and-is-omitted-for-a-client-with-no-traffic-row-the-quota-itself-stays-in-clienttotalgb-clients-with-no-inbound-attachment-are-included-with-an-empty-inboundids-list-the-ui-shows-this-in-a-codemirror-viewer-copy--download-programmatic-callers-get-the-array-in-obj'
|
||
- depth: 2
|
||
title: 'Import clients from a JSON body { "data": "<json>" }, where data is a
|
||
string-encoded array produced by /export ([{client, inboundIds,
|
||
traffic}]). Items with inboundIds are created and attached to those
|
||
inbounds; items with an empty inboundIds list are restored as unattached
|
||
client records. An optional traffic object restores the usage counters,
|
||
only for clients this import creates. Existing emails are never
|
||
overwritten — they are returned in skipped, and their live counters are
|
||
left untouched. Triggers a single Xray restart at the end if any target
|
||
inbound was running; a failure while restoring counters still reports
|
||
success=false after the clients were created.'
|
||
url: '#import-clients-from-a-json-body--data-json--where-data-is-a-string-encoded-array-produced-by-export-client-inboundids-traffic-items-with-inboundids-are-created-and-attached-to-those-inbounds-items-with-an-empty-inboundids-list-are-restored-as-unattached-client-records-an-optional-traffic-object-restores-the-usage-counters-only-for-clients-this-import-creates-existing-emails-are-never-overwritten--they-are-returned-in-skipped-and-their-live-counters-are-left-untouched-triggers-a-single-xray-restart-at-the-end-if-any-target-inbound-was-running-a-failure-while-restoring-counters-still-reports-successfalse-after-the-clients-were-created'
|
||
- depth: 2
|
||
title: 'Shift expiry and/or traffic quota for many clients in one call.
|
||
addDays/addBytes may be negative. Clients with unlimited expiry
|
||
(expiryTime=0) or unlimited traffic (totalGB=0) are skipped for the
|
||
corresponding field — bulk extend never converts unlimited to limited. A
|
||
client that was auto-disabled solely because it was depleted (expired or
|
||
over quota) is automatically re-enabled — locally and on its node — when
|
||
the adjustment lifts it out of depletion; a manually-disabled or
|
||
still-depleted client is left disabled. The optional flow directive sets
|
||
the XTLS flow on every client: "none" clears it,
|
||
"xtls-rprx-vision"/"xtls-rprx-vision-udp443" set it where the inbound
|
||
supports it (omit or "" to leave it unchanged). The optional limitHwid
|
||
sets maximum registered devices (0 = unlimited). The optional adTag sets
|
||
MTProto Telegram sponsor channel ("none" clears). Returns the adjusted
|
||
count and per-email skip reasons.'
|
||
url: '#shift-expiry-andor-traffic-quota-for-many-clients-in-one-call-adddaysaddbytes-may-be-negative-clients-with-unlimited-expiry-expirytime0-or-unlimited-traffic-totalgb0-are-skipped-for-the-corresponding-field--bulk-extend-never-converts-unlimited-to-limited-a-client-that-was-auto-disabled-solely-because-it-was-depleted-expired-or-over-quota-is-automatically-re-enabled--locally-and-on-its-node--when-the-adjustment-lifts-it-out-of-depletion-a-manually-disabled-or-still-depleted-client-is-left-disabled-the-optional-flow-directive-sets-the-xtls-flow-on-every-client-none-clears-it-xtls-rprx-visionxtls-rprx-vision-udp443-set-it-where-the-inbound-supports-it-omit-or--to-leave-it-unchanged-the-optional-limithwid-sets-maximum-registered-devices-0--unlimited-the-optional-adtag-sets-mtproto-telegram-sponsor-channel-none-clears-returns-the-adjusted-count-and-per-email-skip-reasons'
|
||
- depth: 2
|
||
title: Enable many clients in one call. Emails are grouped by inbound and
|
||
applied with a single read-modify-write per inbound; the running Xray
|
||
(local or remote node) is updated to add each user. Note that enabling a
|
||
client whose quota is exhausted or whose expiry has passed only flips
|
||
the flag — the traffic loop will disable it again on the next tick.
|
||
Returns the changed count and per-email skip reasons.
|
||
url: '#enable-many-clients-in-one-call-emails-are-grouped-by-inbound-and-applied-with-a-single-read-modify-write-per-inbound-the-running-xray-local-or-remote-node-is-updated-to-add-each-user-note-that-enabling-a-client-whose-quota-is-exhausted-or-whose-expiry-has-passed-only-flips-the-flag--the-traffic-loop-will-disable-it-again-on-the-next-tick-returns-the-changed-count-and-per-email-skip-reasons'
|
||
- depth: 2
|
||
title: Disable many clients in one call. Emails are grouped by inbound and
|
||
applied with a single read-modify-write per inbound; the running Xray
|
||
(local or remote node) is updated to remove each user. Returns the
|
||
changed count and per-email skip reasons.
|
||
url: '#disable-many-clients-in-one-call-emails-are-grouped-by-inbound-and-applied-with-a-single-read-modify-write-per-inbound-the-running-xray-local-or-remote-node-is-updated-to-remove-each-user-returns-the-changed-count-and-per-email-skip-reasons'
|
||
- depth: 2
|
||
title: Delete many clients in one call. The server processes the list
|
||
sequentially so each delete sees the committed state of the previous one
|
||
— avoids the race the per-email fan-out had on the panel side. Pass
|
||
keepTraffic=true to retain the xray_client_traffic rows after deletion.
|
||
url: '#delete-many-clients-in-one-call-the-server-processes-the-list-sequentially-so-each-delete-sees-the-committed-state-of-the-previous-one--avoids-the-race-the-per-email-fan-out-had-on-the-panel-side-pass-keeptraffictrue-to-retain-the-xray_client_traffic-rows-after-deletion'
|
||
- depth: 2
|
||
title: Create many clients in one call. Body is a JSON array of {client,
|
||
inboundIds} payloads — the same shape /add accepts. Items are processed
|
||
sequentially; per-email skip reasons are returned for items that fail
|
||
(e.g., duplicate email). Triggers a single Xray restart at the end if
|
||
any inbound was running.
|
||
url: '#create-many-clients-in-one-call-body-is-a-json-array-of-client-inboundids-payloads--the-same-shape-add-accepts-items-are-processed-sequentially-per-email-skip-reasons-are-returned-for-items-that-fail-eg-duplicate-email-triggers-a-single-xray-restart-at-the-end-if-any-inbound-was-running'
|
||
- depth: 2
|
||
title: Add many clients to a group in one call. Updates clients.group_name and
|
||
patches the matching client entry inside every owning inbound's settings
|
||
JSON in a single transaction. If the group name does not yet exist (in
|
||
client_groups or as a derived label), it is auto-created as a persistent
|
||
group. To clear the group label, use /groups/bulkRemove instead.
|
||
url: '#add-many-clients-to-a-group-in-one-call-updates-clientsgroup_name-and-patches-the-matching-client-entry-inside-every-owning-inbounds-settings-json-in-a-single-transaction-if-the-group-name-does-not-yet-exist-in-client_groups-or-as-a-derived-label-it-is-auto-created-as-a-persistent-group-to-clear-the-group-label-use-groupsbulkremove-instead'
|
||
- depth: 2
|
||
title: Clear the group label on many clients in one call. Inverse of
|
||
/groups/bulkAdd. Clients themselves are kept — only the group label is
|
||
cleared from clients.group_name and from each owning inbound's settings
|
||
JSON. Groups become empty if all their members are removed.
|
||
url: '#clear-the-group-label-on-many-clients-in-one-call-inverse-of-groupsbulkadd-clients-themselves-are-kept--only-the-group-label-is-cleared-from-clientsgroup_name-and-from-each-owning-inbounds-settings-json-groups-become-empty-if-all-their-members-are-removed'
|
||
- depth: 2
|
||
title: Attach many existing clients to many inbounds in one call. Each client
|
||
keeps its identity (email/UUID/password/subId) and a shared traffic row;
|
||
all clients are added to a target inbound in a single AddInboundClient
|
||
call. Clients already present on a target are reported under skipped.
|
||
Returns per-email attached/skipped/errors lists and triggers a single
|
||
Xray restart if any target inbound was running.
|
||
url: '#attach-many-existing-clients-to-many-inbounds-in-one-call-each-client-keeps-its-identity-emailuuidpasswordsubid-and-a-shared-traffic-row-all-clients-are-added-to-a-target-inbound-in-a-single-addinboundclient-call-clients-already-present-on-a-target-are-reported-under-skipped-returns-per-email-attachedskippederrors-lists-and-triggers-a-single-xray-restart-if-any-target-inbound-was-running'
|
||
- depth: 2
|
||
title: "Mirror of bulkAttach: detach many existing clients from many inbounds in
|
||
one call. For each email, intersects the client's current inbounds with
|
||
the requested set and detaches from those only; (email, inbound) pairs
|
||
where the client is not currently attached are silently no-ops. Emails
|
||
not attached to any of the requested inbounds are reported under
|
||
skipped. Client records are kept even if they become orphaned — use
|
||
bulkDel for full removal. Returns per-email detached/skipped/errors
|
||
lists and triggers a single Xray restart if any target inbound was
|
||
running."
|
||
url: '#mirror-of-bulkattach-detach-many-existing-clients-from-many-inbounds-in-one-call-for-each-email-intersects-the-clients-current-inbounds-with-the-requested-set-and-detaches-from-those-only-email-inbound-pairs-where-the-client-is-not-currently-attached-are-silently-no-ops-emails-not-attached-to-any-of-the-requested-inbounds-are-reported-under-skipped-client-records-are-kept-even-if-they-become-orphaned--use-bulkdel-for-full-removal-returns-per-email-detachedskippederrors-lists-and-triggers-a-single-xray-restart-if-any-target-inbound-was-running'
|
||
- depth: 2
|
||
title: Zero up/down counters for many clients in one call. Loops the
|
||
single-reset path so each client is re-enabled across its attached
|
||
inbounds and pushed to Xray/remote nodes. Returns the count of
|
||
successfully reset clients.
|
||
url: '#zero-updown-counters-for-many-clients-in-one-call-loops-the-single-reset-path-so-each-client-is-re-enabled-across-its-attached-inbounds-and-pushed-to-xrayremote-nodes-returns-the-count-of-successfully-reset-clients'
|
||
- depth: 2
|
||
title: List all client groups with their member counts. Merges persisted groups
|
||
(rows in client_groups, including empty placeholders) with the distinct
|
||
group_name values currently set on clients. Sorted alphabetically
|
||
(case-insensitive).
|
||
url: '#list-all-client-groups-with-their-member-counts-merges-persisted-groups-rows-in-client_groups-including-empty-placeholders-with-the-distinct-group_name-values-currently-set-on-clients-sorted-alphabetically-case-insensitive'
|
||
- depth: 2
|
||
title: Return just the email list of clients that currently belong to the given
|
||
group. Useful for fanning a single bulk action over an entire group
|
||
without round-tripping the full client list.
|
||
url: '#return-just-the-email-list-of-clients-that-currently-belong-to-the-given-group-useful-for-fanning-a-single-bulk-action-over-an-entire-group-without-round-tripping-the-full-client-list'
|
||
- depth: 2
|
||
title: Create a new empty (placeholder) group. The group becomes selectable in
|
||
client forms and the filter drawer even before any client is added to
|
||
it. Errors if a group with the same name already exists.
|
||
url: '#create-a-new-empty-placeholder-group-the-group-becomes-selectable-in-client-forms-and-the-filter-drawer-even-before-any-client-is-added-to-it-errors-if-a-group-with-the-same-name-already-exists'
|
||
- depth: 2
|
||
title: Rename a group. The new name is applied to the client_groups row AND
|
||
propagated to every matching client (both clients.group_name and the
|
||
client entry inside every owning inbound's settings JSON) in a single
|
||
transaction. Returns the number of clients whose label was updated.
|
||
url: '#rename-a-group-the-new-name-is-applied-to-the-client_groups-row-and-propagated-to-every-matching-client-both-clientsgroup_name-and-the-client-entry-inside-every-owning-inbounds-settings-json-in-a-single-transaction-returns-the-number-of-clients-whose-label-was-updated'
|
||
- depth: 2
|
||
title: Remove a group. Deletes the client_groups row and clears the group label
|
||
from every matching client (both clients.group_name and the inbound
|
||
settings JSON). The clients themselves are NOT deleted — use /bulkDel
|
||
after filtering by group for that. Returns the count of clients whose
|
||
label was cleared.
|
||
url: '#remove-a-group-deletes-the-client_groups-row-and-clears-the-group-label-from-every-matching-client-both-clientsgroup_name-and-the-inbound-settings-json-the-clients-themselves-are-not-deleted--use-bulkdel-after-filtering-by-group-for-that-returns-the-count-of-clients-whose-label-was-cleared'
|
||
- depth: 2
|
||
title: Reset only the group-level traffic counter shown on the groups page.
|
||
Snapshots the current up/down sum of the group's members as a baseline
|
||
so the group total reads zero, while leaving each client's own counters
|
||
(and their quotas) untouched. No Xray restart is triggered. Creates the
|
||
client_groups row if the group exists only as a derived label.
|
||
url: '#reset-only-the-group-level-traffic-counter-shown-on-the-groups-page-snapshots-the-current-updown-sum-of-the-groups-members-as-a-baseline-so-the-group-total-reads-zero-while-leaving-each-clients-own-counters-and-their-quotas-untouched-no-xray-restart-is-triggered-creates-the-client_groups-row-if-the-group-exists-only-as-a-derived-label'
|
||
- depth: 2
|
||
title: Zero out a single client’s up/down counters. Re-enables the client across
|
||
every attached inbound and pushes the change to Xray (or the remote
|
||
node) so depleted users can connect again immediately.
|
||
url: '#zero-out-a-single-clients-updown-counters-re-enables-the-client-across-every-attached-inbound-and-pushes-the-change-to-xray-or-the-remote-node-so-depleted-users-can-connect-again-immediately'
|
||
- depth: 2
|
||
title: Manually adjust a client’s upload + download counters. Useful for
|
||
migrations from external accounting systems.
|
||
url: '#manually-adjust-a-clients-upload--download-counters-useful-for-migrations-from-external-accounting-systems'
|
||
- depth: 2
|
||
title: List source IPs that have connected with the given client’s credentials.
|
||
Returns an array of "ip (timestamp)" strings.
|
||
url: '#list-source-ips-that-have-connected-with-the-given-clients-credentials-returns-an-array-of-ip-timestamp-strings'
|
||
- depth: 2
|
||
title: Reset the recorded IP list for a client.
|
||
url: '#reset-the-recorded-ip-list-for-a-client'
|
||
- depth: 2
|
||
title: List registered HWID devices for a client with a short fingerprint. Full
|
||
hashes are not exposed.
|
||
url: '#list-registered-hwid-devices-for-a-client-with-a-short-fingerprint-full-hashes-are-not-exposed'
|
||
- depth: 2
|
||
title: Clear all registered HWID devices for a client so new devices can
|
||
register again.
|
||
url: '#clear-all-registered-hwid-devices-for-a-client-so-new-devices-can-register-again'
|
||
- depth: 2
|
||
title: Remove a single registered HWID device by its id, freeing one slot under
|
||
the HWID limit.
|
||
url: '#remove-a-single-registered-hwid-device-by-its-id-freeing-one-slot-under-the-hwid-limit'
|
||
- depth: 2
|
||
title: List the emails of currently connected clients (last seen within the
|
||
heartbeat window), deduped across every node.
|
||
url: '#list-the-emails-of-currently-connected-clients-last-seen-within-the-heartbeat-window-deduped-across-every-node'
|
||
- depth: 2
|
||
title: Online client emails grouped by the panelGuid of the node that physically
|
||
hosts each client. The local panel uses its own GUID; each node (at any
|
||
depth in a chain) uses its GUID. Lets the inbounds page attribute online
|
||
status to the real node instead of the intermediate one it syncs
|
||
through.
|
||
url: '#online-client-emails-grouped-by-the-panelguid-of-the-node-that-physically-hosts-each-client-the-local-panel-uses-its-own-guid-each-node-at-any-depth-in-a-chain-uses-its-guid-lets-the-inbounds-page-attribute-online-status-to-the-real-node-instead-of-the-intermediate-one-it-syncs-through'
|
||
- depth: 2
|
||
title: Per-client source IPs grouped by the panelGuid of the node that observed
|
||
them. Lets the central panel attribute and enforce per-client IP limits
|
||
using the real visitor IPs each node sees, instead of the address of the
|
||
intermediate panel it syncs through.
|
||
url: '#per-client-source-ips-grouped-by-the-panelguid-of-the-node-that-observed-them-lets-the-central-panel-attribute-and-enforce-per-client-ip-limits-using-the-real-visitor-ips-each-node-sees-instead-of-the-address-of-the-intermediate-panel-it-syncs-through'
|
||
- depth: 2
|
||
title: Inbound tags that carried traffic within the heartbeat window, grouped by
|
||
the hosting node's panelGuid. Pairs with onlinesByGuid so the inbounds
|
||
page only marks a multi-inbound client online on the inbounds it
|
||
actually used. Nodes that do not report per-inbound activity are absent.
|
||
url: '#inbound-tags-that-carried-traffic-within-the-heartbeat-window-grouped-by-the-hosting-nodes-panelguid-pairs-with-onlinesbyguid-so-the-inbounds-page-only-marks-a-multi-inbound-client-online-on-the-inbounds-it-actually-used-nodes-that-do-not-report-per-inbound-activity-are-absent'
|
||
- depth: 2
|
||
title: Map of client email → last-seen unix timestamp.
|
||
url: '#map-of-client-email--last-seen-unix-timestamp'
|
||
- depth: 2
|
||
title: Traffic counters for a client identified by email.
|
||
url: '#traffic-counters-for-a-client-identified-by-email'
|
||
- depth: 2
|
||
title: Return every protocol URL (vless://, vmess://, trojan://, ss://,
|
||
hysteria://, hy2://) for clients matching the subscription ID. Same
|
||
result set as the configured subPath endpoint, but as a JSON array — no
|
||
base64. When an inbound has streamSettings.externalProxy set, one URL is
|
||
emitted per external proxy. Empty array when the subId has no enabled
|
||
clients.
|
||
url: '#return-every-protocol-url-vless-vmess-trojan-ss-hysteria-hy2-for-clients-matching-the-subscription-id-same-result-set-as-the-configured-subpath-endpoint-but-as-a-json-array--no-base64-when-an-inbound-has-streamsettingsexternalproxy-set-one-url-is-emitted-per-external-proxy-empty-array-when-the-subid-has-no-enabled-clients'
|
||
- depth: 2
|
||
title: 'Generate a fresh Happ crypt5 link locally from the current client
|
||
subscription URL when Happ link generation is enabled. The panel applies
|
||
a resource limit of 8192 UTF-8 bytes to the source URL; this is not a
|
||
Happ client maximum. Longer sources return success: false with msg:
|
||
happ_source_too_long and obj: null. The source URL is not sent to a
|
||
generation provider, and the result is not stored or reused.'
|
||
url: '#generate-a-fresh-happ-crypt5-link-locally-from-the-current-client-subscription-url-when-happ-link-generation-is-enabled-the-panel-applies-a-resource-limit-of-8192-utf-8-bytes-to-the-source-url-this-is-not-a-happ-client-maximum-longer-sources-return-success-false-with-msg-happ_source_too_long-and-obj-null-the-source-url-is-not-sent-to-a-generation-provider-and-the-result-is-not-stored-or-reused'
|
||
- depth: 2
|
||
title: 'Return every URL for one client across all attached inbounds, one per
|
||
advertised endpoint: the managed hosts of the inbound, else its
|
||
streamSettings.externalProxy entries, else its own address. Supported
|
||
protocols: vmess, vless, trojan, shadowsocks, hysteria, mtproto.
|
||
Protocols without a URL form (socks, http, mixed, wireguard, dokodemo,
|
||
tunnel) contribute nothing.'
|
||
url: '#return-every-url-for-one-client-across-all-attached-inbounds-one-per-advertised-endpoint-the-managed-hosts-of-the-inbound-else-its-streamsettingsexternalproxy-entries-else-its-own-address-supported-protocols-vmess-vless-trojan-shadowsocks-hysteria-mtproto-protocols-without-a-url-form-socks-http-mixed-wireguard-dokodemo-tunnel-contribute-nothing'
|
||
structuredData:
|
||
headings:
|
||
- content: List every client with its attached inbound IDs and traffic record. The
|
||
reverse field, if set, is returned as a nested JSON object (legacy
|
||
JSON-encoded-string form is still accepted on write).
|
||
id: list-every-client-with-its-attached-inbound-ids-and-traffic-record-the-reverse-field-if-set-is-returned-as-a-nested-json-object-legacy-json-encoded-string-form-is-still-accepted-on-write
|
||
- content: 'Filter, sort, and paginate clients on the server. Each item is a slim
|
||
row (no uuid/password/auth/flow/security/reverse/tgId) so the clients
|
||
page can ship 25-ish rows in a few KB instead of the full table. The
|
||
response also includes a summary computed across the full DB row set
|
||
so dashboard counters stay stable as the user paginates or filters:
|
||
the *Count fields are exact, while the email arrays beside them stop
|
||
at 200 entries so the payload does not grow with the panel. Page size
|
||
capped at 200; fetch /get/:email to obtain the full per-client payload
|
||
for an edit/info modal.'
|
||
id: filter-sort-and-paginate-clients-on-the-server-each-item-is-a-slim-row-no-uuidpasswordauthflowsecurityreversetgid-so-the-clients-page-can-ship-25-ish-rows-in-a-few-kb-instead-of-the-full-table-the-response-also-includes-a-summary-computed-across-the-full-db-row-set-so-dashboard-counters-stay-stable-as-the-user-paginates-or-filters-the-count-fields-are-exact-while-the-email-arrays-beside-them-stop-at-200-entries-so-the-payload-does-not-grow-with-the-panel-page-size-capped-at-200-fetch-getemail-to-obtain-the-full-per-client-payload-for-an-editinfo-modal
|
||
- content: Fetch one client by email, including the inbound IDs and external
|
||
config IDs it is attached to.
|
||
id: fetch-one-client-by-email-including-the-inbound-ids-and-external-config-ids-it-is-attached-to
|
||
- content: Fetch clients by subscription ID.
|
||
id: fetch-clients-by-subscription-id
|
||
- content: Fetch clients by Telegram user ID. Returns an array since multiple
|
||
clients can share the same Telegram ID.
|
||
id: fetch-clients-by-telegram-user-id-returns-an-array-since-multiple-clients-can-share-the-same-telegram-id
|
||
- content: Create a new client and attach it to one or more inbounds in a single
|
||
call. Body is JSON. Per-protocol secrets are generated server-side
|
||
when omitted, so callers can send only the universal fields.
|
||
id: create-a-new-client-and-attach-it-to-one-or-more-inbounds-in-a-single-call-body-is-json-per-protocol-secrets-are-generated-server-side-when-omitted-so-callers-can-send-only-the-universal-fields
|
||
- content: Preview client auto-renewal dates without saving or resetting anything.
|
||
id: preview-client-auto-renewal-dates-without-saving-or-resetting-anything
|
||
- content: Update an existing client by email. Changes propagate to every attached
|
||
inbound. Body is the JSON client payload — supply the full set of
|
||
fields you want to keep (the server replaces the row, it does not
|
||
patch).
|
||
id: update-an-existing-client-by-email-changes-propagate-to-every-attached-inbound-body-is-the-json-client-payload--supply-the-full-set-of-fields-you-want-to-keep-the-server-replaces-the-row-it-does-not-patch
|
||
- content: Delete a client by email. Removes it from every attached inbound and
|
||
drops its traffic record unless keepTraffic=1 is passed.
|
||
id: delete-a-client-by-email-removes-it-from-every-attached-inbound-and-drops-its-traffic-record-unless-keeptraffic1-is-passed
|
||
- content: Attach an existing client to one or more additional inbounds. Body is
|
||
JSON.
|
||
id: attach-an-existing-client-to-one-or-more-additional-inbounds-body-is-json
|
||
- content: Detach a client from one or more inbounds without deleting the client.
|
||
id: detach-a-client-from-one-or-more-inbounds-without-deleting-the-client
|
||
- content: Replace a client's external links and external subscriptions. Sends the
|
||
full set; the server replaces all rows. Disabled rows stay saved for
|
||
editing but are not emitted in generated subscriptions. The owning
|
||
client's disabled or expired state also stops these rows from being
|
||
emitted on future subscription fetches; credentials already imported
|
||
by an app remain valid until the external provider revokes them.
|
||
id: replace-a-clients-external-links-and-external-subscriptions-sends-the-full-set-the-server-replaces-all-rows-disabled-rows-stay-saved-for-editing-but-are-not-emitted-in-generated-subscriptions-the-owning-clients-disabled-or-expired-state-also-stops-these-rows-from-being-emitted-on-future-subscription-fetches-credentials-already-imported-by-an-app-remain-valid-until-the-external-provider-revokes-them
|
||
- content: Reset the up/down counters for every client globally. Quotas and expiry
|
||
are not affected. Triggers an Xray restart if any counter actually
|
||
moved.
|
||
id: reset-the-updown-counters-for-every-client-globally-quotas-and-expiry-are-not-affected-triggers-an-xray-restart-if-any-counter-actually-moved
|
||
- content: Delete every client whose traffic quota is exhausted (used >= total,
|
||
when reset is disabled) or whose expiry has passed. Returns the
|
||
deleted count and triggers an Xray restart when any client was on a
|
||
running inbound.
|
||
id: delete-every-client-whose-traffic-quota-is-exhausted-used--total-when-reset-is-disabled-or-whose-expiry-has-passed-returns-the-deleted-count-and-triggers-an-xray-restart-when-any-client-was-on-a-running-inbound
|
||
- content: Delete every client that is not attached to any inbound, along with its
|
||
traffic record, IP log, HWID devices, and external links. Useful for
|
||
clearing clients left unattached after their inbounds were removed.
|
||
Returns the deleted count. Cannot be undone.
|
||
id: delete-every-client-that-is-not-attached-to-any-inbound-along-with-its-traffic-record-ip-log-hwid-devices-and-external-links-useful-for-clearing-clients-left-unattached-after-their-inbounds-were-removed-returns-the-deleted-count-cannot-be-undone
|
||
- content: Return every client as a {client, inboundIds, traffic} array — the
|
||
shape /import accepts — so the payload round-trips straight back
|
||
through /import. traffic carries the usage counters (up, down,
|
||
resetCount, lastOnline, lastSubFetch) and is omitted for a client with
|
||
no traffic row; the quota itself stays in client.totalGB. Clients with
|
||
no inbound attachment are included with an empty inboundIds list. The
|
||
UI shows this in a CodeMirror viewer (copy / download); programmatic
|
||
callers get the array in obj.
|
||
id: return-every-client-as-a-client-inboundids-traffic-array--the-shape-import-accepts--so-the-payload-round-trips-straight-back-through-import-traffic-carries-the-usage-counters-up-down-resetcount-lastonline-lastsubfetch-and-is-omitted-for-a-client-with-no-traffic-row-the-quota-itself-stays-in-clienttotalgb-clients-with-no-inbound-attachment-are-included-with-an-empty-inboundids-list-the-ui-shows-this-in-a-codemirror-viewer-copy--download-programmatic-callers-get-the-array-in-obj
|
||
- content: 'Import clients from a JSON body { "data": "<json>" }, where data is a
|
||
string-encoded array produced by /export ([{client, inboundIds,
|
||
traffic}]). Items with inboundIds are created and attached to those
|
||
inbounds; items with an empty inboundIds list are restored as
|
||
unattached client records. An optional traffic object restores the
|
||
usage counters, only for clients this import creates. Existing emails
|
||
are never overwritten — they are returned in skipped, and their live
|
||
counters are left untouched. Triggers a single Xray restart at the end
|
||
if any target inbound was running; a failure while restoring counters
|
||
still reports success=false after the clients were created.'
|
||
id: import-clients-from-a-json-body--data-json--where-data-is-a-string-encoded-array-produced-by-export-client-inboundids-traffic-items-with-inboundids-are-created-and-attached-to-those-inbounds-items-with-an-empty-inboundids-list-are-restored-as-unattached-client-records-an-optional-traffic-object-restores-the-usage-counters-only-for-clients-this-import-creates-existing-emails-are-never-overwritten--they-are-returned-in-skipped-and-their-live-counters-are-left-untouched-triggers-a-single-xray-restart-at-the-end-if-any-target-inbound-was-running-a-failure-while-restoring-counters-still-reports-successfalse-after-the-clients-were-created
|
||
- content: 'Shift expiry and/or traffic quota for many clients in one call.
|
||
addDays/addBytes may be negative. Clients with unlimited expiry
|
||
(expiryTime=0) or unlimited traffic (totalGB=0) are skipped for the
|
||
corresponding field — bulk extend never converts unlimited to limited.
|
||
A client that was auto-disabled solely because it was depleted
|
||
(expired or over quota) is automatically re-enabled — locally and on
|
||
its node — when the adjustment lifts it out of depletion; a
|
||
manually-disabled or still-depleted client is left disabled. The
|
||
optional flow directive sets the XTLS flow on every client: "none"
|
||
clears it, "xtls-rprx-vision"/"xtls-rprx-vision-udp443" set it where
|
||
the inbound supports it (omit or "" to leave it unchanged). The
|
||
optional limitHwid sets maximum registered devices (0 = unlimited).
|
||
The optional adTag sets MTProto Telegram sponsor channel ("none"
|
||
clears). Returns the adjusted count and per-email skip reasons.'
|
||
id: shift-expiry-andor-traffic-quota-for-many-clients-in-one-call-adddaysaddbytes-may-be-negative-clients-with-unlimited-expiry-expirytime0-or-unlimited-traffic-totalgb0-are-skipped-for-the-corresponding-field--bulk-extend-never-converts-unlimited-to-limited-a-client-that-was-auto-disabled-solely-because-it-was-depleted-expired-or-over-quota-is-automatically-re-enabled--locally-and-on-its-node--when-the-adjustment-lifts-it-out-of-depletion-a-manually-disabled-or-still-depleted-client-is-left-disabled-the-optional-flow-directive-sets-the-xtls-flow-on-every-client-none-clears-it-xtls-rprx-visionxtls-rprx-vision-udp443-set-it-where-the-inbound-supports-it-omit-or--to-leave-it-unchanged-the-optional-limithwid-sets-maximum-registered-devices-0--unlimited-the-optional-adtag-sets-mtproto-telegram-sponsor-channel-none-clears-returns-the-adjusted-count-and-per-email-skip-reasons
|
||
- content: Enable many clients in one call. Emails are grouped by inbound and
|
||
applied with a single read-modify-write per inbound; the running Xray
|
||
(local or remote node) is updated to add each user. Note that enabling
|
||
a client whose quota is exhausted or whose expiry has passed only
|
||
flips the flag — the traffic loop will disable it again on the next
|
||
tick. Returns the changed count and per-email skip reasons.
|
||
id: enable-many-clients-in-one-call-emails-are-grouped-by-inbound-and-applied-with-a-single-read-modify-write-per-inbound-the-running-xray-local-or-remote-node-is-updated-to-add-each-user-note-that-enabling-a-client-whose-quota-is-exhausted-or-whose-expiry-has-passed-only-flips-the-flag--the-traffic-loop-will-disable-it-again-on-the-next-tick-returns-the-changed-count-and-per-email-skip-reasons
|
||
- content: Disable many clients in one call. Emails are grouped by inbound and
|
||
applied with a single read-modify-write per inbound; the running Xray
|
||
(local or remote node) is updated to remove each user. Returns the
|
||
changed count and per-email skip reasons.
|
||
id: disable-many-clients-in-one-call-emails-are-grouped-by-inbound-and-applied-with-a-single-read-modify-write-per-inbound-the-running-xray-local-or-remote-node-is-updated-to-remove-each-user-returns-the-changed-count-and-per-email-skip-reasons
|
||
- content: Delete many clients in one call. The server processes the list
|
||
sequentially so each delete sees the committed state of the previous
|
||
one — avoids the race the per-email fan-out had on the panel side.
|
||
Pass keepTraffic=true to retain the xray_client_traffic rows after
|
||
deletion.
|
||
id: delete-many-clients-in-one-call-the-server-processes-the-list-sequentially-so-each-delete-sees-the-committed-state-of-the-previous-one--avoids-the-race-the-per-email-fan-out-had-on-the-panel-side-pass-keeptraffictrue-to-retain-the-xray_client_traffic-rows-after-deletion
|
||
- content: Create many clients in one call. Body is a JSON array of {client,
|
||
inboundIds} payloads — the same shape /add accepts. Items are
|
||
processed sequentially; per-email skip reasons are returned for items
|
||
that fail (e.g., duplicate email). Triggers a single Xray restart at
|
||
the end if any inbound was running.
|
||
id: create-many-clients-in-one-call-body-is-a-json-array-of-client-inboundids-payloads--the-same-shape-add-accepts-items-are-processed-sequentially-per-email-skip-reasons-are-returned-for-items-that-fail-eg-duplicate-email-triggers-a-single-xray-restart-at-the-end-if-any-inbound-was-running
|
||
- content: Add many clients to a group in one call. Updates clients.group_name and
|
||
patches the matching client entry inside every owning inbound's
|
||
settings JSON in a single transaction. If the group name does not yet
|
||
exist (in client_groups or as a derived label), it is auto-created as
|
||
a persistent group. To clear the group label, use /groups/bulkRemove
|
||
instead.
|
||
id: add-many-clients-to-a-group-in-one-call-updates-clientsgroup_name-and-patches-the-matching-client-entry-inside-every-owning-inbounds-settings-json-in-a-single-transaction-if-the-group-name-does-not-yet-exist-in-client_groups-or-as-a-derived-label-it-is-auto-created-as-a-persistent-group-to-clear-the-group-label-use-groupsbulkremove-instead
|
||
- content: Clear the group label on many clients in one call. Inverse of
|
||
/groups/bulkAdd. Clients themselves are kept — only the group label is
|
||
cleared from clients.group_name and from each owning inbound's
|
||
settings JSON. Groups become empty if all their members are removed.
|
||
id: clear-the-group-label-on-many-clients-in-one-call-inverse-of-groupsbulkadd-clients-themselves-are-kept--only-the-group-label-is-cleared-from-clientsgroup_name-and-from-each-owning-inbounds-settings-json-groups-become-empty-if-all-their-members-are-removed
|
||
- content: Attach many existing clients to many inbounds in one call. Each client
|
||
keeps its identity (email/UUID/password/subId) and a shared traffic
|
||
row; all clients are added to a target inbound in a single
|
||
AddInboundClient call. Clients already present on a target are
|
||
reported under skipped. Returns per-email attached/skipped/errors
|
||
lists and triggers a single Xray restart if any target inbound was
|
||
running.
|
||
id: attach-many-existing-clients-to-many-inbounds-in-one-call-each-client-keeps-its-identity-emailuuidpasswordsubid-and-a-shared-traffic-row-all-clients-are-added-to-a-target-inbound-in-a-single-addinboundclient-call-clients-already-present-on-a-target-are-reported-under-skipped-returns-per-email-attachedskippederrors-lists-and-triggers-a-single-xray-restart-if-any-target-inbound-was-running
|
||
- content: "Mirror of bulkAttach: detach many existing clients from many inbounds
|
||
in one call. For each email, intersects the client's current inbounds
|
||
with the requested set and detaches from those only; (email, inbound)
|
||
pairs where the client is not currently attached are silently no-ops.
|
||
Emails not attached to any of the requested inbounds are reported
|
||
under skipped. Client records are kept even if they become orphaned —
|
||
use bulkDel for full removal. Returns per-email
|
||
detached/skipped/errors lists and triggers a single Xray restart if
|
||
any target inbound was running."
|
||
id: mirror-of-bulkattach-detach-many-existing-clients-from-many-inbounds-in-one-call-for-each-email-intersects-the-clients-current-inbounds-with-the-requested-set-and-detaches-from-those-only-email-inbound-pairs-where-the-client-is-not-currently-attached-are-silently-no-ops-emails-not-attached-to-any-of-the-requested-inbounds-are-reported-under-skipped-client-records-are-kept-even-if-they-become-orphaned--use-bulkdel-for-full-removal-returns-per-email-detachedskippederrors-lists-and-triggers-a-single-xray-restart-if-any-target-inbound-was-running
|
||
- content: Zero up/down counters for many clients in one call. Loops the
|
||
single-reset path so each client is re-enabled across its attached
|
||
inbounds and pushed to Xray/remote nodes. Returns the count of
|
||
successfully reset clients.
|
||
id: zero-updown-counters-for-many-clients-in-one-call-loops-the-single-reset-path-so-each-client-is-re-enabled-across-its-attached-inbounds-and-pushed-to-xrayremote-nodes-returns-the-count-of-successfully-reset-clients
|
||
- content: List all client groups with their member counts. Merges persisted
|
||
groups (rows in client_groups, including empty placeholders) with the
|
||
distinct group_name values currently set on clients. Sorted
|
||
alphabetically (case-insensitive).
|
||
id: list-all-client-groups-with-their-member-counts-merges-persisted-groups-rows-in-client_groups-including-empty-placeholders-with-the-distinct-group_name-values-currently-set-on-clients-sorted-alphabetically-case-insensitive
|
||
- content: Return just the email list of clients that currently belong to the
|
||
given group. Useful for fanning a single bulk action over an entire
|
||
group without round-tripping the full client list.
|
||
id: return-just-the-email-list-of-clients-that-currently-belong-to-the-given-group-useful-for-fanning-a-single-bulk-action-over-an-entire-group-without-round-tripping-the-full-client-list
|
||
- content: Create a new empty (placeholder) group. The group becomes selectable in
|
||
client forms and the filter drawer even before any client is added to
|
||
it. Errors if a group with the same name already exists.
|
||
id: create-a-new-empty-placeholder-group-the-group-becomes-selectable-in-client-forms-and-the-filter-drawer-even-before-any-client-is-added-to-it-errors-if-a-group-with-the-same-name-already-exists
|
||
- content: Rename a group. The new name is applied to the client_groups row AND
|
||
propagated to every matching client (both clients.group_name and the
|
||
client entry inside every owning inbound's settings JSON) in a single
|
||
transaction. Returns the number of clients whose label was updated.
|
||
id: rename-a-group-the-new-name-is-applied-to-the-client_groups-row-and-propagated-to-every-matching-client-both-clientsgroup_name-and-the-client-entry-inside-every-owning-inbounds-settings-json-in-a-single-transaction-returns-the-number-of-clients-whose-label-was-updated
|
||
- content: Remove a group. Deletes the client_groups row and clears the group
|
||
label from every matching client (both clients.group_name and the
|
||
inbound settings JSON). The clients themselves are NOT deleted — use
|
||
/bulkDel after filtering by group for that. Returns the count of
|
||
clients whose label was cleared.
|
||
id: remove-a-group-deletes-the-client_groups-row-and-clears-the-group-label-from-every-matching-client-both-clientsgroup_name-and-the-inbound-settings-json-the-clients-themselves-are-not-deleted--use-bulkdel-after-filtering-by-group-for-that-returns-the-count-of-clients-whose-label-was-cleared
|
||
- content: Reset only the group-level traffic counter shown on the groups page.
|
||
Snapshots the current up/down sum of the group's members as a baseline
|
||
so the group total reads zero, while leaving each client's own
|
||
counters (and their quotas) untouched. No Xray restart is triggered.
|
||
Creates the client_groups row if the group exists only as a derived
|
||
label.
|
||
id: reset-only-the-group-level-traffic-counter-shown-on-the-groups-page-snapshots-the-current-updown-sum-of-the-groups-members-as-a-baseline-so-the-group-total-reads-zero-while-leaving-each-clients-own-counters-and-their-quotas-untouched-no-xray-restart-is-triggered-creates-the-client_groups-row-if-the-group-exists-only-as-a-derived-label
|
||
- content: Zero out a single client’s up/down counters. Re-enables the client
|
||
across every attached inbound and pushes the change to Xray (or the
|
||
remote node) so depleted users can connect again immediately.
|
||
id: zero-out-a-single-clients-updown-counters-re-enables-the-client-across-every-attached-inbound-and-pushes-the-change-to-xray-or-the-remote-node-so-depleted-users-can-connect-again-immediately
|
||
- content: Manually adjust a client’s upload + download counters. Useful for
|
||
migrations from external accounting systems.
|
||
id: manually-adjust-a-clients-upload--download-counters-useful-for-migrations-from-external-accounting-systems
|
||
- content: List source IPs that have connected with the given client’s
|
||
credentials. Returns an array of "ip (timestamp)" strings.
|
||
id: list-source-ips-that-have-connected-with-the-given-clients-credentials-returns-an-array-of-ip-timestamp-strings
|
||
- content: Reset the recorded IP list for a client.
|
||
id: reset-the-recorded-ip-list-for-a-client
|
||
- content: List registered HWID devices for a client with a short fingerprint.
|
||
Full hashes are not exposed.
|
||
id: list-registered-hwid-devices-for-a-client-with-a-short-fingerprint-full-hashes-are-not-exposed
|
||
- content: Clear all registered HWID devices for a client so new devices can
|
||
register again.
|
||
id: clear-all-registered-hwid-devices-for-a-client-so-new-devices-can-register-again
|
||
- content: Remove a single registered HWID device by its id, freeing one slot
|
||
under the HWID limit.
|
||
id: remove-a-single-registered-hwid-device-by-its-id-freeing-one-slot-under-the-hwid-limit
|
||
- content: List the emails of currently connected clients (last seen within the
|
||
heartbeat window), deduped across every node.
|
||
id: list-the-emails-of-currently-connected-clients-last-seen-within-the-heartbeat-window-deduped-across-every-node
|
||
- content: Online client emails grouped by the panelGuid of the node that
|
||
physically hosts each client. The local panel uses its own GUID; each
|
||
node (at any depth in a chain) uses its GUID. Lets the inbounds page
|
||
attribute online status to the real node instead of the intermediate
|
||
one it syncs through.
|
||
id: online-client-emails-grouped-by-the-panelguid-of-the-node-that-physically-hosts-each-client-the-local-panel-uses-its-own-guid-each-node-at-any-depth-in-a-chain-uses-its-guid-lets-the-inbounds-page-attribute-online-status-to-the-real-node-instead-of-the-intermediate-one-it-syncs-through
|
||
- content: Per-client source IPs grouped by the panelGuid of the node that
|
||
observed them. Lets the central panel attribute and enforce per-client
|
||
IP limits using the real visitor IPs each node sees, instead of the
|
||
address of the intermediate panel it syncs through.
|
||
id: per-client-source-ips-grouped-by-the-panelguid-of-the-node-that-observed-them-lets-the-central-panel-attribute-and-enforce-per-client-ip-limits-using-the-real-visitor-ips-each-node-sees-instead-of-the-address-of-the-intermediate-panel-it-syncs-through
|
||
- content: Inbound tags that carried traffic within the heartbeat window, grouped
|
||
by the hosting node's panelGuid. Pairs with onlinesByGuid so the
|
||
inbounds page only marks a multi-inbound client online on the inbounds
|
||
it actually used. Nodes that do not report per-inbound activity are
|
||
absent.
|
||
id: inbound-tags-that-carried-traffic-within-the-heartbeat-window-grouped-by-the-hosting-nodes-panelguid-pairs-with-onlinesbyguid-so-the-inbounds-page-only-marks-a-multi-inbound-client-online-on-the-inbounds-it-actually-used-nodes-that-do-not-report-per-inbound-activity-are-absent
|
||
- content: Map of client email → last-seen unix timestamp.
|
||
id: map-of-client-email--last-seen-unix-timestamp
|
||
- content: Traffic counters for a client identified by email.
|
||
id: traffic-counters-for-a-client-identified-by-email
|
||
- content: Return every protocol URL (vless://, vmess://, trojan://, ss://,
|
||
hysteria://, hy2://) for clients matching the subscription ID. Same
|
||
result set as the configured subPath endpoint, but as a JSON array —
|
||
no base64. When an inbound has streamSettings.externalProxy set, one
|
||
URL is emitted per external proxy. Empty array when the subId has no
|
||
enabled clients.
|
||
id: return-every-protocol-url-vless-vmess-trojan-ss-hysteria-hy2-for-clients-matching-the-subscription-id-same-result-set-as-the-configured-subpath-endpoint-but-as-a-json-array--no-base64-when-an-inbound-has-streamsettingsexternalproxy-set-one-url-is-emitted-per-external-proxy-empty-array-when-the-subid-has-no-enabled-clients
|
||
- content: 'Generate a fresh Happ crypt5 link locally from the current client
|
||
subscription URL when Happ link generation is enabled. The panel
|
||
applies a resource limit of 8192 UTF-8 bytes to the source URL; this
|
||
is not a Happ client maximum. Longer sources return success: false
|
||
with msg: happ_source_too_long and obj: null. The source URL is not
|
||
sent to a generation provider, and the result is not stored or
|
||
reused.'
|
||
id: generate-a-fresh-happ-crypt5-link-locally-from-the-current-client-subscription-url-when-happ-link-generation-is-enabled-the-panel-applies-a-resource-limit-of-8192-utf-8-bytes-to-the-source-url-this-is-not-a-happ-client-maximum-longer-sources-return-success-false-with-msg-happ_source_too_long-and-obj-null-the-source-url-is-not-sent-to-a-generation-provider-and-the-result-is-not-stored-or-reused
|
||
- content: 'Return every URL for one client across all attached inbounds, one per
|
||
advertised endpoint: the managed hosts of the inbound, else its
|
||
streamSettings.externalProxy entries, else its own address. Supported
|
||
protocols: vmess, vless, trojan, shadowsocks, hysteria, mtproto.
|
||
Protocols without a URL form (socks, http, mixed, wireguard, dokodemo,
|
||
tunnel) contribute nothing.'
|
||
id: return-every-url-for-one-client-across-all-attached-inbounds-one-per-advertised-endpoint-the-managed-hosts-of-the-inbound-else-its-streamsettingsexternalproxy-entries-else-its-own-address-supported-protocols-vmess-vless-trojan-shadowsocks-hysteria-mtproto-protocols-without-a-url-form-socks-http-mixed-wireguard-dokodemo-tunnel-contribute-nothing
|
||
contents:
|
||
- content: Matches the exact subscription ID and orders results by client ID.
|
||
Returns an array of the same payloads as GET /clients/get/:email,
|
||
since multiple clients can share a subscription ID. Includes disabled,
|
||
expired and unattached clients. An unknown ID returns HTTP 200 with
|
||
success:false, obj:null and a record-not-found message, matching the
|
||
email lookup. Requires an admin token or a panel login session.
|
||
heading: fetch-clients-by-subscription-id
|
||
- content: >-
|
||
Fields the server fills in when they are omitted — a valid value sent
|
||
by the caller is never overwritten. Re-adding an email that already
|
||
exists, with its stored `subId`, reuses the stored `id`, `password`,
|
||
`auth` and `secret` instead of minting new ones, so the identity stays
|
||
in sync across its inbounds.
|
||
|
||
|
||
- **VLESS / VMess** — `id`, a fresh UUID
|
||
|
||
- **Trojan** — `password`
|
||
|
||
- **Shadowsocks** — `password`. On a `2022-blake3-*` inbound a
|
||
supplied password that does not base64-decode to the key length of the
|
||
cipher (16 or 32 bytes) is replaced by a generated key and the call
|
||
still succeeds, so read the client back if you did not let the server
|
||
pick. Legacy ciphers keep any non-empty password
|
||
|
||
- **Hysteria** — `auth`
|
||
|
||
- **mtproto** — `secret`, a FakeTLS secret derived from the fronting
|
||
domain of the inbound, or from `www.cloudflare.com` when it has none
|
||
|
||
- **WireGuard** — `privateKey` and `publicKey` when both are blank, or
|
||
`publicKey` alone when only a `privateKey` was sent, plus
|
||
`allowedIPs`: one free `/32` taken from the /24 the existing peers of
|
||
that inbound already sit in, or from `10.0.0.0/24` when it has none
|
||
|
||
|
||
Accepted on the same body but never generated: `preSharedKey` and
|
||
`keepAlive` (WireGuard), `adTag` (mtproto).
|
||
|
||
|
||
WireGuard is the only one of these that can fail. Allocation widens
|
||
the search to the containing /16 before giving up with `inbound <id>:
|
||
wireguard: no free address available in <scope>`, and an `allowedIPs`
|
||
supplied by the caller is validated instead of allocated: `inbound
|
||
<id>: wireguard: allowedIPs entry <entry> overlaps <address> used by
|
||
another client` when its range overlaps an address or prefix a
|
||
different client of that same inbound holds, or `... used by a client
|
||
on <inbound>` when the holder sits on another WireGuard or AmneziaWG
|
||
inbound. Ranges are compared, not strings, so `10.0.0.9/24` collides
|
||
with `10.0.0.5/32`; a `0.0.0.0/0` or `::/0` default route claims no
|
||
address. Allocation likewise skips every address inside a prefix
|
||
another client holds. The same validation runs on POST
|
||
/panel/api/clients/{email}/attach, where a client that already carries
|
||
an address brings it along.
|
||
|
||
|
||
An `inboundIds` entry that names no existing inbound rejects the whole
|
||
call before anything is written. Past that, the inbounds are applied
|
||
concurrently and independently: one that fails no longer stops the
|
||
others, so a `success:false` response can still have created the
|
||
client on the rest. Every error names the inbound it came from
|
||
(`inbound 7: <message>`), and several failures are reported together,
|
||
one per line. `limitHwid` is applied only when every inbound
|
||
succeeded, so re-run the call after fixing the failure.
|
||
heading: create-a-new-client-and-attach-it-to-one-or-more-inbounds-in-a-single-call-body-is-json-per-protocol-secrets-are-generated-server-side-when-omitted-so-callers-can-send-only-the-universal-fields
|
||
- content: Uses the same calendar and catch-up calculation as auto-renew in the
|
||
panel timezone. resetWeekday is 1 (Monday) to 7 (Sunday), 0 disables
|
||
weekly mode; it cannot be combined with positive reset or resetDay.
|
||
Existing resetDay takes precedence over reset. With expiryTime=0,
|
||
calendar modes suggest a first cutoff but do not activate renewal.
|
||
Negative expiryTime waits for first-use activation. resetMax and
|
||
resetCount simulate the existing per-period allowance limit; the
|
||
preview is informational and does not reserve an allowance or
|
||
guarantee node availability.
|
||
heading: preview-client-auto-renewal-dates-without-saving-or-resetting-anything
|
||
- content: 'The inbounds are applied concurrently and independently: one that
|
||
fails no longer stops the others. Every inbound error names the
|
||
inbound it came from (`inbound 7: <message>`), and several failures
|
||
are reported together, one per line. So a `success:false` response can
|
||
still have applied the edit to the remaining inbounds. The client
|
||
record is written after the inbounds, so a failure there is reported
|
||
without an `inbound <id>:` prefix and leaves the inbound edits in
|
||
place.'
|
||
heading: update-an-existing-client-by-email-changes-propagate-to-every-attached-inbound-body-is-the-json-client-payload--supply-the-full-set-of-fields-you-want-to-keep-the-server-replaces-the-row-it-does-not-patch
|
||
- content: 'The inbounds are applied concurrently and independently: one that
|
||
fails no longer stops the others. Every inbound error names the
|
||
inbound it came from (`inbound 7: <message>`), and several failures
|
||
are reported together, one per line. So a `success:false` response can
|
||
still have removed the client from the remaining inbounds; the client
|
||
record is kept in that case, so re-running the call retries exactly
|
||
the leftovers. The record and traffic rows are dropped after the
|
||
inbounds, so a failure there is reported without an `inbound <id>:`
|
||
prefix and leaves the client already removed from every inbound.'
|
||
heading: delete-a-client-by-email-removes-it-from-every-attached-inbound-and-drops-its-traffic-record-unless-keeptraffic1-is-passed
|
||
- content: 'A WireGuard client brings its stored `allowedIPs` into the new inbound
|
||
instead of being given a fresh address, so the call fails with
|
||
`inbound <id>: wireguard: allowedIPs entry <entry> overlaps <address>
|
||
used by another client` when its range overlaps an address or prefix a
|
||
different client of the target inbound holds. Free the address on that
|
||
inbound first — see POST /panel/api/clients/add for the full rule.
|
||
Inbounds are applied independently, so the remaining ones are still
|
||
attached and a `success:false` response can be partial.'
|
||
heading: attach-an-existing-client-to-one-or-more-additional-inbounds-body-is-json
|
||
- content: 'The inbounds are applied concurrently and independently: one that
|
||
fails no longer stops the others. Every inbound error names the
|
||
inbound it came from (`inbound 7: <message>`), and several failures
|
||
are reported together, one per line. So a `success:false` response can
|
||
still have detached the remaining inbounds. Detach writes nothing
|
||
beyond the inbounds, so every error carries the prefix.'
|
||
heading: detach-a-client-from-one-or-more-inbounds-without-deleting-the-client
|
||
---
|
||
|
||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||
|
||
export default function Layout(props) {
|
||
const { OpenAPIPage } = props.components ?? {};
|
||
return (
|
||
<>
|
||
{props.children}
|
||
<OpenAPIPage document="./public/openapi.json" webhooks={[]} operations={[{"path":"/panel/api/clients/list","method":"get"},{"path":"/panel/api/clients/list/paged","method":"get"},{"path":"/panel/api/clients/get/{email}","method":"get"},{"path":"/panel/api/clients/get/subId/{subId}","method":"get"},{"path":"/panel/api/clients/get/tgId/{tgId}","method":"get"},{"path":"/panel/api/clients/add","method":"post"},{"path":"/panel/api/clients/renewalPreview","method":"post"},{"path":"/panel/api/clients/update/{email}","method":"post"},{"path":"/panel/api/clients/del/{email}","method":"post"},{"path":"/panel/api/clients/{email}/attach","method":"post"},{"path":"/panel/api/clients/{email}/detach","method":"post"},{"path":"/panel/api/clients/{email}/externalLinks","method":"post"},{"path":"/panel/api/clients/resetAllTraffics","method":"post"},{"path":"/panel/api/clients/delDepleted","method":"post"},{"path":"/panel/api/clients/delOrphans","method":"post"},{"path":"/panel/api/clients/export","method":"get"},{"path":"/panel/api/clients/import","method":"post"},{"path":"/panel/api/clients/bulkAdjust","method":"post"},{"path":"/panel/api/clients/bulkEnable","method":"post"},{"path":"/panel/api/clients/bulkDisable","method":"post"},{"path":"/panel/api/clients/bulkDel","method":"post"},{"path":"/panel/api/clients/bulkCreate","method":"post"},{"path":"/panel/api/clients/groups/bulkAdd","method":"post"},{"path":"/panel/api/clients/groups/bulkRemove","method":"post"},{"path":"/panel/api/clients/bulkAttach","method":"post"},{"path":"/panel/api/clients/bulkDetach","method":"post"},{"path":"/panel/api/clients/bulkResetTraffic","method":"post"},{"path":"/panel/api/clients/groups","method":"get"},{"path":"/panel/api/clients/groups/{name}/emails","method":"get"},{"path":"/panel/api/clients/groups/create","method":"post"},{"path":"/panel/api/clients/groups/rename","method":"post"},{"path":"/panel/api/clients/groups/delete","method":"post"},{"path":"/panel/api/clients/groups/resetTraffic","method":"post"},{"path":"/panel/api/clients/resetTraffic/{email}","method":"post"},{"path":"/panel/api/clients/updateTraffic/{email}","method":"post"},{"path":"/panel/api/clients/ips/{email}","method":"post"},{"path":"/panel/api/clients/clearIps/{email}","method":"post"},{"path":"/panel/api/clients/hwids/{email}","method":"post"},{"path":"/panel/api/clients/hwids/{email}","method":"delete"},{"path":"/panel/api/clients/hwids/{email}/{id}","method":"delete"},{"path":"/panel/api/clients/onlines","method":"post"},{"path":"/panel/api/clients/onlinesByGuid","method":"post"},{"path":"/panel/api/clients/clientIpsByGuid","method":"post"},{"path":"/panel/api/clients/activeInbounds","method":"post"},{"path":"/panel/api/clients/lastOnline","method":"post"},{"path":"/panel/api/clients/traffic/{email}","method":"get"},{"path":"/panel/api/clients/subLinks/{subId}","method":"get"},{"path":"/panel/api/clients/happLink/{id}","method":"post"},{"path":"/panel/api/clients/links/{email}","method":"get"}]} showTitle />
|
||
</>
|
||
);
|
||
} |