# PasarGuard REST API / OpenAPI Compatibility Reference
## Production integration reference for Sonnet 5

> **Purpose:** Give Sonnet 5 a conservative, version-aware reference for implementing a PHP integration with PasarGuard.
>
> **Core rule:** Never invent an endpoint, field, enum, permission, request schema, or response schema. If the exact contract is not verified from an official PasarGuard source/test/OpenAPI artifact, mark it as **NOT VERIFIED** and do not implement it as fact.

---

## 1. Scope

This document is designed for a long-lived PasarGuard integration rather than a client hard-coded to one release.

The current official release history inspected for this document shows these recent stable releases:

- `v5.4.0` — latest release shown by the official GitHub Releases page
- `v5.3.0`
- `v5.2.1`
- `v5.2.0`

The release page explicitly marks `v5.4.0` as **Latest**. Do not assume a future release has the same contract. Re-run compatibility verification when a new release is adopted.

Official source:

https://github.com/PasarGuard/panel/releases

---

# 2. Version support policy

The integration should support a **rolling window of recent PasarGuard v5 releases**, not one hard-coded version.

Recommended production policy:

```text
PRIMARY:
    newest verified v5.x release

SUPPORTED:
    newest verified release
    previous verified minor release
    previous verified minor release

OUTSIDE SUPPORT:
    older releases unless explicitly tested
```

For the currently verified release history, the initial compatibility target is:

```text
v5.4.x  -> primary
v5.3.x  -> supported
v5.2.x  -> supported
```

This does NOT mean every endpoint is identical across those versions.

The compatibility layer must account for documented differences.

---

# 3. Why OpenAPI is the primary contract

PasarGuard is a FastAPI backend and the repository has an official OpenAPI generation workflow.

The official Makefile contains:

```bash
make gen-api
```

which executes:

```bash
uv run python scripts/export_openapi.py --gen-client
```

The Makefile describes this as dumping the OpenAPI schema offline and feeding it to Orval.

Official source:

https://github.com/PasarGuard/panel/blob/main/Makefile

The official contributing documentation also states that Swagger/ReDoc can be enabled through the `DOCS` flag and that the frontend API client is generated from the API schema.

Source:

https://github.com/PasarGuard/panel/blob/main/CONTRIBUTING.md

Therefore the source-of-truth hierarchy for implementation is:

```text
1. OpenAPI schema of the target installed version
2. Source code of that exact version
3. Official tests of that exact version
4. Official PasarGuard documentation
5. This reference document
6. Nothing from unofficial SDKs/blogs/bots should override the above
```

---

# 4. REST API vs OpenAPI

These are not competing choices.

```text
REST API
    = the HTTP interface the bot actually calls

OpenAPI
    = the machine-readable contract describing that interface
```

For Sonnet:

```text
OpenAPI
   ↓
exact endpoint/request/response contract

Official source/tests
   ↓
behavioral verification

Compatibility layer
   ↓
version differences

PHP services
   ↓
Telegram bot/business logic
```

---

# 5. Version detection and compatibility architecture

Do NOT write the whole application around:

```php
if ($version === '5.4.0') { ... }
```

Instead use a compatibility layer.

Recommended structure:

```text
PasarGuardClient
│
├── VersionDetector
├── CapabilityRegistry
├── CompatibilityLayer
│
├── AuthService
├── UserService
├── SubscriptionService
├── HwidService
├── TrafficService
├── GroupService
├── TemplateService
├── AdminService
├── NodeService
├── HostService
├── CoreService
├── StatisticsService
└── ApiKeyService
```

Business code should call stable methods such as:

```php
$client->users()->findById($userId);
$client->users()->create($payload);
$client->users()->update($userId, $changes);
$client->devices()->list($userId);
$client->subscriptions()->get($userId);
```

It should NOT construct raw PasarGuard URLs throughout the application.

---

# 6. ID-based API migration is important

Official PasarGuard migration guidance says username-based API endpoints are being deprecated in v5.

The documented migration path is:

```text
Deprecated:
    /api/user/{username}

Current replacement:
    /api/user/by-id/{user_id}

Future format described by the project:
    /api/user/{id}
```

Official discussion:

https://github.com/PasarGuard/panel/discussions/457

Therefore:

## New integration rule

Internally store and use:

```text
PasarGuard user ID
```

as the canonical identifier.

Username may still be stored as a user attribute.

Do not design the application's database around username as the immutable PasarGuard key.

---

# 7. Authentication — verified behavior

Official API tests authenticate an admin through:

```http
POST /api/admin/token
Content-Type: application/x-www-form-urlencoded
```

with form data including:

```text
username=<ADMIN_USERNAME>
password=<ADMIN_PASSWORD>
grant_type=password
```

The authenticated requests in the official tests use:

```http
Authorization: Bearer <ACCESS_TOKEN>
```

The exact token response schema should be read from the target version's OpenAPI schema before generating DTOs.

Do not hard-code undocumented token fields.

---

# 8. Users — verified REST operations

Official PasarGuard API tests exercise user operations including:

```http
POST   /api/user
GET    /api/user/{username}
PUT    /api/user/{username}
DELETE /api/user/{username}
GET    /api/users
```

These paths are evidence of existing API behavior in the tested project version.

However, because PasarGuard is migrating from username-based routes, a new integration should prefer the current ID-based operation when it is available in the target version's OpenAPI.

Do not blindly use username-based routes just because an old test contains them.

---

# 9. User creation — verified fields/examples

Official tests create users using fields including:

```json
{
  "username": "example",
  "proxy_settings": {},
  "expire": "2026-01-01T00:00:00",
  "data_limit": 10737418240,
  "data_limit_reset_strategy": "no_reset",
  "status": "active"
}
```

Other tested fields include:

```text
group_ids
hwid_limit
note
on_hold_timeout
on_hold_expire_duration
```

IMPORTANT:

This is not a claim that the object above is the complete `UserCreate` schema.

The complete request model must come from the target OpenAPI/source.

Official user tests:

https://github.com/PasarGuard/panel/blob/main/tests/api/test_user.py

---

# 10. User modification

Official tests demonstrate user modification through:

```http
PUT /api/user/{username}
```

with fields such as:

```text
note
data_limit
expire
hwid_limit
```

The tests also verify policy/permission validation.

For example, a limited administrator can be rejected when attempting to make certain resources unlimited.

Do not treat these validation rules as universal for every admin role.

They depend on the configured role/policy.

---

# 11. User listing and filters

Official user API tests exercise the following query filters:

```text
load_sub
online
online_after
online_before
no_data_limit
no_expire
data_limit_min
data_limit_max
expire_min
expire_max
data_limit_reset_strategy
admin_ids
```

These are verified test-level behaviors.

The target OpenAPI remains authoritative for exact types, defaults, descriptions and complete query schema.

---

# 12. User traffic/accounting

Official tests use user-usage models including:

```text
NodeUserUsage
UserUsageResetLogs
```

and verify user-level lifetime traffic behavior.

One official test verifies:

```text
lifetime_used_traffic
```

after a usage-reset record is present.

Therefore:

```text
User-level traffic/accounting: VERIFIED
```

Do not equate:

```text
lifetime_used_traffic
```

with current-period traffic without checking the exact response semantics.

---

# 13. Per-device traffic — NOT VERIFIED

PasarGuard definitely has:

```text
user traffic accounting
+
HWID/device registration
```

But the inspected official API evidence does NOT establish a public API returning:

```text
HWID A -> 5 GB
HWID B -> 2 GB
HWID C -> 800 MB
```

Therefore Sonnet MUST NOT invent:

```http
GET /api/user/{id}/hwids/{hwid}/traffic
```

or equivalent.

Do not calculate per-device traffic by guessing a relationship between:

```text
NodeUserUsage
```

and:

```text
UserHWID
```

unless the exact target source/schema proves that relationship.

---

# 14. HWID / device identification

The official HWID API tests verify device registration through the subscription URL using:

```http
X-HWID: <device-id>
X-Device-OS: <os>
X-Ver-OS: <os-version>
X-Device-Model: <model>
```

The device can then be retrieved through:

```http
GET /api/user/{user_id}/hwids
```

The tested response contains:

```text
count
hwids[]
    hwid
    device_os
    os_version
    device_model
```

Official test source:

https://github.com/PasarGuard/panel/blob/main/tests/api/test_hwid.py

---

# 15. Device deletion

The official HWID tests verify:

```http
DELETE /api/user/{user_id}/hwids/{hwid}
```

The integration may expose a "remove device" function based on this verified behavior.

The exact response schema/status should still be read from the target OpenAPI.

---

# 16. Device reset

The official HWID tests verify:

```http
POST /api/user/{user_id}/hwids/reset
```

This resets the registered HWIDs for the user.

Again, use the target OpenAPI for the exact response schema.

---

# 17. Device limits

The official tests verify HWID limits and a configured fallback limit.

A test environment uses:

```text
fallback_limit = 3
```

and verifies rejection when the applicable limit is exceeded.

The rejection includes:

```text
Device limit reached
```

and HTTP:

```text
403
```

IMPORTANT:

`3` is a test configuration.

NEVER hard-code:

```php
$maxDevices = 3;
```

The actual effective limit must come from the PasarGuard user/configuration/policy.

---

# 18. Forced HWID

The official tests verify a forced-HWID mode.

When enabled, a subscription request without:

```http
X-HWID
```

can be rejected with:

```text
403 Forbidden
```

and:

```text
HWID header required
```

Whether this mode is enabled is configuration-dependent.

---

# 19. HWID last-used timestamp

The official source/test behavior verifies internal updating of:

```text
last_used_at
```

when an existing user/HWID registration is refreshed.

However:

```text
Internal HWID last_used_at: VERIFIED
Public GET /hwids last_used_at field: NOT GUARANTEED
```

Do not create a public DTO containing `last_used_at` unless the target OpenAPI explicitly includes it.

---

# 20. Device online status

The official user API tests verify user-level online filtering:

```text
online
online_after
online_before
```

This does NOT establish:

```text
HWID A is online
HWID B is offline
```

Therefore:

```text
User online state: VERIFIED
Per-device online state: NOT VERIFIED
```

Do not infer one from the other.

---

# 21. Subscription/configuration endpoints

Official tests verify the user's subscription URL and the following subscription formats:

```text
(empty/default)
info
usage
apps
sing_box
clash_meta
clash
outline
links
links_base64
wireguard
xray
```

The official test also verifies:

```http
HEAD <subscription-url>
```

returns successfully without a response body in the tested behavior.

The exact content type, response headers, token handling and format semantics must be obtained from the target version's OpenAPI/source.

Do not parse a subscription response as JSON merely because the API is REST.

---

# 22. Subscription URL security

Subscription URLs contain user-specific authentication material.

Never log:

```text
full subscription URL
subscription token
Bearer token
API key
admin password
```

If a subscription URL must be displayed to a user, treat it as a credential.

---

# 23. API Keys

PasarGuard introduced API-key management with CRUD operations and permissions in v5.1.

The official release notes explicitly record:

```text
api_keys: Implement API key management with CRUD operations and permissions
```

Source:

https://github.com/PasarGuard/panel/releases

However, this document intentionally does NOT invent the exact v5.4.0 API-key endpoint paths.

For API-key implementation, Sonnet MUST obtain the exact target OpenAPI schema and generate the client from it.

---

# 24. Groups

Groups are part of the official PasarGuard backend and are exercised by the API test suite.

Official user tests create groups and associate users through:

```text
group_ids
```

The exact complete Group API must be generated from the target OpenAPI.

Do not invent CRUD routes based solely on database model names.

---

# 25. Templates

PasarGuard has user/client template concepts and the official test suite contains template-related helpers and role restrictions.

Templates should be integrated through the target OpenAPI/source.

Do not assume a template endpoint exists merely because a template database model exists.

---

# 26. Admin roles and permissions

The API supports role-based permissions.

Official tests create admin roles with permissions such as:

```json
{
  "permissions": {
    "users": {
      "create": true,
      "read": true,
      "update": true,
      "delete": true
    }
  }
}
```

The exact permission tree is version-dependent.

Do not hard-code an incomplete permission list.

Use the target OpenAPI/source.

---

# 27. Nodes / Hosts / Core

PasarGuard has substantial API/backend functionality around:

```text
nodes
hosts
cores
Xray configuration
WireGuard
subscriptions
```

The v5.4.0 release includes changes to node operations, node health checks, Xray inbound configuration, user statistics and related backend behavior.

Official release:

https://github.com/PasarGuard/panel/releases

For production implementation:

```text
NodeService
HostService
CoreService
```

should only expose operations whose exact routes/schemas have been extracted from the target OpenAPI.

This document intentionally does NOT invent a "complete Node/Host/Core API" from model names.

---

# 28. Version compatibility matrix

## v5.2.x

Verified release family:

```text
v5.2.0
v5.2.1
```

Relevant official release changes include:

```text
HWID settings changes
HWID/manual subscription behavior changes
user subscription statistics
date parameter changes
REALITY target scanner
WireGuard improvements
Xray compatibility changes
```

Source:

https://github.com/PasarGuard/panel/releases

## v5.3.x

Verified stable release:

```text
v5.3.0
```

Relevant changes include:

```text
subscription formatting variables
group validation improvements
API-key dashboard behavior
node usage coefficient validation
role-based node/shared-storage changes
Swagger UI changes
```

Source:

https://github.com/PasarGuard/panel/releases

## v5.4.x

Verified stable release:

```text
v5.4.0
```

It is currently marked **Latest** on the official Releases page inspected for this document.

Relevant changes include:

```text
Xray inbound X-Padding Bytes
enhanced log structure
node health-check changes
node force-start option
notification worker handling
Host/Core error handling
SQLite WAL mode
user statistics retrieval improvements
user model/traffic reset handling
```

Source:

https://github.com/PasarGuard/panel/releases

---

# 29. Compatibility strategy

Do not duplicate the entire application for every PasarGuard version.

Use:

```text
Common domain model
        ↓
PasarGuard compatibility adapter
        ↓
Version-specific HTTP contract
```

Example:

```php
interface PasarGuardUserGateway
{
    public function findById(int $id): User;
    public function create(UserCreateData $data): User;
    public function update(int $id, UserUpdateData $data): User;
    public function delete(int $id): void;
}
```

Then:

```text
PasarGuardV5UserGateway
    ├── v5.2 compatibility
    ├── v5.3 compatibility
    └── v5.4 compatibility
```

Only isolate the differences.

---

# 30. Capability detection

Where possible, prefer capability detection over brittle version checks.

Example conceptual capabilities:

```text
supports_id_based_user_api
supports_hwid_api
supports_api_keys
supports_subscription_head
supports_usage_endpoint
supports_user_statistics
```

But a capability MUST NOT be declared merely because the feature is logically expected.

It should be established by:

```text
OpenAPI route presence
OR
official source/test verification
```

---

# 31. Version matrix should be generated, not guessed

For every supported PasarGuard release, generate:

```text
routes
operationIds
request schemas
response schemas
security schemes
enums
deprecated fields
deprecated routes
```

Then compare releases.

Conceptual output:

```text
Feature                v5.2     v5.3     v5.4
------------------------------------------------
User by ID              ?        ?        ?
HWID list               ✓        ✓        ✓
HWID reset              ✓        ✓        ✓
API keys                ✓        ✓        ✓
Subscription HEAD       ✓        ✓        ✓
User usage              ✓        ✓        ✓
Per-HWID traffic        ?        ?        ?
```

A `?` means:

```text
not verified
```

It does NOT mean:

```text
does not exist
```

---

# 32. OpenAPI extraction workflow

When the target PasarGuard source/tag is available:

```bash
make gen-api
```

The project invokes:

```bash
uv run python scripts/export_openapi.py --gen-client
```

The resulting OpenAPI artifact should be archived with the integration build.

Recommended:

```text
docs/pasarguard/
    v5.2/
        openapi.json
    v5.3/
        openapi.json
    v5.4/
        openapi.json
```

Then generate:

```text
docs/pasarguard/
    compatibility-matrix.md
```

---

# 33. Required OpenAPI extraction fields

For every operation, Sonnet should record:

```text
path
method
operationId
tags
summary
description
deprecated
security
path parameters
query parameters
header parameters
request body
request schema
required fields
response status codes
response schemas
error schemas
enums
```

For every schema:

```text
type
properties
required
nullable
default
enum
format
minimum
maximum
pattern
items
additionalProperties
```

---

# 34. Error handling

The HTTP client must preserve:

```text
HTTP status
response body
structured error detail
request identifier if supplied
```

Do not assume every endpoint has the same error schema.

The exact error response for an operation must come from its OpenAPI schema or official test.

Generic client categories may be:

```text
400 → request/business validation
401 → authentication
403 → permission/policy
404 → resource missing
409 → conflict where defined
422 → schema validation where defined
5xx → server-side failure
```

These are client-handling categories, not a claim that every route returns every status.

---

# 35. Security requirements

Never persist or log:

```text
admin password
Bearer token
API key
subscription token
full subscription URL
```

Use:

```text
HTTPS
TLS certificate verification
reasonable connect timeout
reasonable request timeout
retry only idempotent/safe operations
exponential backoff for transient failures
```

Do not blindly retry:

```text
POST create
POST reset
DELETE
PUT
```

unless the operation is known to be idempotent or protected with an application-level idempotency strategy.

---

# 36. PHP integration architecture

Recommended:

```text
src/
├── PasarGuard/
│   ├── Client/
│   │   ├── HttpClient.php
│   │   ├── Authentication.php
│   │   └── VersionDetector.php
│   ├── Contract/
│   ├── DTO/
│   ├── Exception/
│   ├── Service/
│   │   ├── UserService.php
│   │   ├── SubscriptionService.php
│   │   ├── HwidService.php
│   │   ├── TrafficService.php
│   │   ├── GroupService.php
│   │   ├── TemplateService.php
│   │   ├── AdminService.php
│   │   ├── NodeService.php
│   │   ├── HostService.php
│   │   └── CoreService.php
│   └── Compatibility/
│       ├── V52Adapter.php
│       ├── V53Adapter.php
│       └── V54Adapter.php
```

The Telegram bot should depend on services/interfaces, not raw HTTP URLs.

---

# 37. Database design recommendation

Store:

```text
pasarguard_panel_url
pasarguard_detected_version
pasarguard_user_id
pasarguard_username
```

Use:

```text
pasarguard_user_id
```

as the canonical external identifier.

Store username as mutable metadata.

For device records:

```text
user_id
hwid
device_os
os_version
device_model
```

only if these fields are actually returned by the target API.

Do not create a `device_traffic` column unless a verified data source exists.

---

# 38. Device feature that can safely be implemented

The bot can safely expose a user device menu such as:

```text
📱 Devices

1. iPhone 14
   iOS 16.5
   HWID: ...

2. Samsung ...
   Android ...
   HWID: ...
```

Supported operations:

```text
list devices
remove one device
reset devices
show device count
```

The underlying traffic number for each device must NOT be displayed unless a verified per-device usage source is added.

---

# 39. Traffic feature that can safely be implemented

The bot can expose user-level values that the verified API provides, such as:

```text
current/returned usage fields
lifetime_used_traffic
data_limit
```

But the code must distinguish:

```text
current-period usage
```

from:

```text
lifetime usage
```

Do not derive one from the other without verified semantics.

---

# 40. Revoke / disable / delete caution

Do not assume that:

```text
DELETE user
```

means:

```text
revoke subscription
```

These are potentially different business operations.

For a bot selling subscriptions, the implementation must locate the exact documented operation for:

```text
disable
revoke
reset
delete
```

and use the correct one.

Never substitute `DELETE /user` merely because the business requirement says "revoke".

---

# 41. Testing strategy

The integration should include automated tests for:

```text
authentication
user creation
user lookup by ID
user update
user deletion
subscription retrieval
HWID list
HWID deletion
HWID reset
traffic retrieval
permission failures
404 behavior
401 behavior
403 behavior
version compatibility
```

For every supported PasarGuard version, run the same contract test suite.

---

# 42. Contract test strategy

Before deploying against a new PasarGuard release:

```text
1. obtain OpenAPI
2. compare routes
3. compare schemas
4. compare enums
5. compare deprecated operations
6. run integration tests
7. update compatibility adapter if needed
8. only then mark the version supported
```

This is the mechanism that keeps the bot from becoming obsolete immediately after a PasarGuard update.

---

# 43. What Sonnet must NOT assume

```text
DO NOT assume:
- the latest release has the same API as the previous release
- username routes will remain forever
- internal DB models are public API schemas
- a feature visible in the dashboard has a public API
- HWID implies per-device traffic accounting
- user online implies device online
- a test configuration value is a production default
- a third-party SDK is authoritative
- an undocumented route exists because its name is intuitive
- an old blog post describes the current API
```

---

# 44. Evidence labels

Every API entry in the generated detailed reference should have one of:

```text
VERIFIED_OPENAPI
VERIFIED_SOURCE
VERIFIED_OFFICIAL_TEST
VERSION_DEPENDENT
NOT_VERIFIED
DEPRECATED
```

Rules:

```text
VERIFIED_*:
    implementation allowed

VERSION_DEPENDENT:
    implementation requires adapter/capability check

DEPRECATED:
    use only for backward compatibility where necessary

NOT_VERIFIED:
    do not implement as fact
```

---

# 45. Current verified evidence

## Official repository

https://github.com/PasarGuard/panel

The repository is the authoritative upstream source.

## Official Releases

https://github.com/PasarGuard/panel/releases

The current release page inspected for this document marks:

```text
v5.4.0
```

as the latest release, followed by:

```text
v5.3.0
v5.2.1
v5.2.0
```

## Official Makefile

https://github.com/PasarGuard/panel/blob/main/Makefile

Confirms:

```text
make gen-api
scripts/export_openapi.py
Orval client generation
```

## Official contributing guide

https://github.com/PasarGuard/panel/blob/main/CONTRIBUTING.md

Confirms:

```text
FastAPI backend
Swagger / ReDoc
API client generation
tests/api
```

## Official user API tests

https://github.com/PasarGuard/panel/blob/main/tests/api/test_user.py

Evidence for:

```text
user CRUD
user listing
filters
traffic/lifetime usage
subscription formats
online filtering
HWID-related behavior
```

## Official HWID tests

https://github.com/PasarGuard/panel/blob/main/tests/api/test_hwid.py

Evidence for:

```text
device registration
HWID listing
device metadata
device limits
HWID reset/delete behavior
```

## Official API deprecation guidance

https://github.com/PasarGuard/panel/discussions/457

Evidence for:

```text
username-based API migration
ID-based API direction
```

---

# 46. Important version correction

Earlier drafts of this document referenced `v5.4.1`.

That reference must NOT be treated as the current stable release contract.

The official GitHub Releases page inspected for this document lists:

```text
v5.4.0
```

as the latest release.

Therefore this document uses:

```text
v5.4.x
v5.3.x
v5.2.x
```

as the initial rolling compatibility target.

Do not tell Sonnet that `v5.4.1` is required.

---

# 47. Final Sonnet instruction

When implementing the PasarGuard integration:

```text
Build a version-aware integration layer.

Target the latest verified v5 release as primary.

Support the previous two verified v5 minor release families where the
OpenAPI/source/tests confirm compatibility.

Use OpenAPI as the machine-readable contract.

Use official source/tests to verify behavior.

Prefer ID-based user operations.

Keep username-based operations only as compatibility fallbacks when
the target version actually exposes them.

Do not invent endpoints.

Do not invent fields.

Do not infer per-device traffic from HWID.

Do not infer per-device online status from user online status.

Do not hard-code test configuration values.

Do not make the Telegram/business layer aware of PasarGuard URL differences.

Put version-specific behavior behind a compatibility adapter.

If an operation cannot be verified, mark it NOT_VERIFIED and stop rather
than guessing.

The integration must be designed so a new PasarGuard minor release can
be supported by updating the compatibility/OpenAPI layer rather than
rewriting the entire bot.
```

---

# 48. Important limitation

This document is intentionally conservative.

It is **not** a claim that every PasarGuard endpoint has been manually reconstructed in this Markdown file.

For a truly exhaustive endpoint inventory, the generated OpenAPI artifact from each supported release should be extracted and stored alongside the source.

The correct workflow is:

```text
PasarGuard release
        ↓
generate/extract OpenAPI
        ↓
store openapi.json
        ↓
diff against previous release
        ↓
generate Markdown reference
        ↓
run contract tests
        ↓
mark version supported
```

That process gives the project long-term maintainability without pretending that undocumented behavior is stable.

---

# 49. Final architectural goal

The desired result is:

```text
                    ┌─────────────────────┐
                    │ Telegram Bot        │
                    └──────────┬──────────┘
                               │
                    ┌──────────▼──────────┐
                    │ Business Services   │
                    └──────────┬──────────┘
                               │
                    ┌──────────▼──────────┐
                    │ PasarGuard Domain   │
                    │ Interfaces          │
                    └──────────┬──────────┘
                               │
                    ┌──────────▼──────────┐
                    │ Compatibility Layer │
                    ├─────────────────────┤
                    │ v5.2                │
                    │ v5.3                │
                    │ v5.4                │
                    └──────────┬──────────┘
                               │
                    ┌──────────▼──────────┐
                    │ REST API / OpenAPI  │
                    └─────────────────────┘
```

This architecture is preferred over hard-coding the bot directly to one PasarGuard release.

