# Omniflow > Open-source VPN service platform for Remnawave ## Start - [Omniflow](https://omniflow-vpn.mintlify.app/index.md): What Omniflow is, who each of its four surfaces serves, and where every feature is documented - [Quickstart](https://omniflow-vpn.mintlify.app/getting-started/quickstart.md): Bring the Compose stack up, create the first owner, enable two-factor authentication, and prove the installation is healthy - [Your first sale](https://omniflow-vpn.mintlify.app/getting-started/first-sale.md): Turn an installed platform into a working shop — a plan, a priced version, a payment provider, the bot, and a test purchase you can follow all the way to Remnawave - [Configuration](https://omniflow-vpn.mintlify.app/getting-started/configuration.md): Every environment variable Omniflow reads, which process reads it, and what changes when you set it - [Comparison with alternatives](https://omniflow-vpn.mintlify.app/comparison.md): How Omniflow differs from Bedolaga Bot, Bedolaga Cabinet, and RemnaCore. ## Architecture - [Architecture overview](https://omniflow-vpn.mintlify.app/architecture/overview.md): The processes Omniflow runs, how a purchase becomes provisioned access, and why the whole thing is one modular monolith - [Data ownership](https://omniflow-vpn.mintlify.app/architecture/data-ownership.md): Which system is authoritative for every category of data, how the two are joined, and what goes wrong when the boundary is crossed - [Technology stack](https://omniflow-vpn.mintlify.app/architecture/technology.md): Every component Omniflow runs on, why it is there instead of the obvious alternative, and what is deliberately absent - [Threat model](https://omniflow-vpn.mintlify.app/architecture/threat-model.md): What Omniflow defends against on each surface, the mitigation actually in the code, and the residual risk stated plainly where one exists - [Architecture decisions](https://omniflow-vpn.mintlify.app/architecture/decisions.md): The decision records that constrain how Omniflow is built, what each one costs, and the conditions under which it would be reopened - [0001 — One module, three entrypoints, no broker](https://omniflow-vpn.mintlify.app/architecture/decision-0001-modular-monolith.md): Why Omniflow is a modular monolith with separate api, bot, and worker processes, and the condition under which that would stop being right - [0002 — PostgreSQL is the only durable store](https://omniflow-vpn.mintlify.app/architecture/decision-0002-postgresql-only-durability.md): Why Valkey holds nothing that cannot be lost, and why the job queue and the event outbox live in the same database as the money - [0003 — The OpenAPI file is the contract](https://omniflow-vpn.mintlify.app/architecture/decision-0003-contract-first-api.md): Why api/openapi.yaml is written first and both sides are generated from it, why it is 3.0.3 rather than 3.1, and where the generated code stops - [0004 — Remnawave owns VPN state; Omniflow records intent](https://omniflow-vpn.mintlify.app/architecture/decision-0004-remnawave-boundary.md): Why entitlement is stored separately from observed Remnawave state, why nothing writes to Remnawave storage, and what that costs - [0005 — Customer and operator surfaces are one application](https://omniflow-vpn.mintlify.app/architecture/decision-0005-one-web-application.md): Why /admin and the customer panel share a Next.js project and a component system, and how they stay separate where it matters - [0006 — AI and MCP are optional and can never act alone](https://omniflow-vpn.mintlify.app/architecture/decision-0006-ai-is-optional.md): Why every AI feature is off until an owner turns it on, why one gateway is the only path to a provider, and why a model may recommend but never execute - [0007 — Customers do not sign in with a password](https://omniflow-vpn.mintlify.app/architecture/decision-0007-no-customer-password.md): Why the customer panel offers Telegram, OIDC, and a bot-delivered link but no password, what that costs an installation, and the condition under which it would be added ## For customers - [Telegram bot](https://omniflow-vpn.mintlify.app/customer/telegram-bot.md): Every command, screen, and flow a customer can reach in the Omniflow bot — from the first /start to buying, connecting, topping up, gifting, and opening a support ticket - [Customer web panel](https://omniflow-vpn.mintlify.app/customer/web-panel.md): Everything a customer can do from a browser: sign-in, subscriptions, checkout, the shop, support, referrals, and their own data. - [Connecting a device](https://omniflow-vpn.mintlify.app/customer/connection.md): What a subscription link is, how it reaches a phone or a laptop, which clients Omniflow documents, how connected devices are listed and removed, and how to rotate a link that leaked - [Notifications and announcements](https://omniflow-vpn.mintlify.app/customer/notifications.md): How Omniflow decides whether a customer may be messaged, what a customer can turn off, and where announcements are kept - [Personal data and privacy](https://omniflow-vpn.mintlify.app/customer/privacy.md): What a customer can see, download, and ask to have removed, what is kept regardless, and what Omniflow deliberately never holds ## Commerce - [Catalog and promotions](https://omniflow-vpn.mintlify.app/commerce/catalog-promotions.md): How plans, immutable plan versions, prices, trials, promo codes, and personal offers are authored and what each one refuses - [Add-ons and the subscription configurator](https://omniflow-vpn.mintlify.app/commerce/addons-configurator.md): Plan-scoped squad sets, add-on versions, mid-period capacity, and the proration rules behind each price - [Subscriptions and entitlements](https://omniflow-vpn.mintlify.app/commerce/subscriptions.md): What a subscription and an entitlement each are, how concurrency is bounded, and how a period is extended, changed, expired, and recovered - [Orders, payments, and refunds](https://omniflow-vpn.mintlify.app/commerce/orders-payments.md): How every purchase becomes one order record, how money reaches it, and what happens when a payment is duplicated, mismatched, late, or refunded - [Wallet and ledger](https://omniflow-vpn.mintlify.app/commerce/wallet-ledger.md): The double-entry model behind every balance, why entries are append-only, how credit expires, and what an operator must supply to move a customer's money - [Wallet top-up and saved carts](https://omniflow-vpn.mintlify.app/commerce/wallet-topup-cart.md): Customer-initiated funding, deferred purchase, the exact limits an operator configures, and the invariants that keep both safe - [Entitlements and fulfillment](https://omniflow-vpn.mintlify.app/commerce/fulfillment.md): How a paid order becomes working Remnawave access, and what happens when it does not - [Recurring payments](https://omniflow-vpn.mintlify.app/commerce/recurring-payments.md): Auto-renew consent, the charge schedule, the bounded dunning retries, and what a customer can turn off - [Gifts and digital goods](https://omniflow-vpn.mintlify.app/commerce/gifts-and-goods.md): Buying a subscription for somebody else, and selling Telegram Premium and Stars alongside VPN access. - [Customers and imports](https://omniflow-vpn.mintlify.app/commerce/customers-imports.md): Adopting an existing Remnawave user base into Omniflow, and exporting the customer list - [Audience segments and campaigns](https://omniflow-vpn.mintlify.app/commerce/campaigns.md): Define a reviewable audience, build and schedule a send, and read what a campaign actually reached - [Referrals and loyalty](https://omniflow-vpn.mintlify.app/commerce/referrals-loyalty.md): Configure what an invite pays, understand exactly when a referral qualifies, and publish loyalty tiers a customer cannot silently fall out of - [News and customer communication](https://omniflow-vpn.mintlify.app/commerce/referrals-communication.md): Publish announcements, understand how every customer notification is classified, and know exactly why one was suppressed or held - [Financial invariants](https://omniflow-vpn.mintlify.app/commerce/financial-invariants.md): The rules every commerce change must preserve, where each one is enforced, and what each one prevents ## Integrations - [Remnawave](https://omniflow-vpn.mintlify.app/integrations/remnawave.md): How Omniflow connects to a Remnawave 3.2.2 panel, every call it makes, and the boundary it never crosses - [Payment providers](https://omniflow-vpn.mintlify.app/integrations/payments.md): Set up Telegram Stars, CryptoBot, YooKassa, or manual payments — credentials, currencies, webhooks, testing, and how refunds behave on each - [Telegram](https://omniflow-vpn.mintlify.app/integrations/telegram.md): Create the bot, choose long polling or a webhook, bind the Mini App domain, and set up the operator group with topics - [OIDC sign-in](https://omniflow-vpn.mintlify.app/integrations/oidc.md): Add an OpenID Connect provider for operators or for customers: the fields, the redirect URI, claim mapping, and the guards that stop an account being taken over - [Integrating with the API](https://omniflow-vpn.mintlify.app/integrations/api.md): How a script or another service drives Omniflow through /v1/admin: the bearer token, idempotency keys, cursors, RFC 9457 problems, request IDs, and rate limits - [Extension policy](https://omniflow-vpn.mintlify.app/integrations/extending.md): Which surfaces are public and safe to build on, which are internal and will change, what can be extended without touching the code, and what deliberately cannot be extended at all ## API reference ### system - [Get healthz](https://omniflow-vpn.mintlify.app/api-reference/system/get-healthz.md): Retained for compatibility. New deployments should probe /livez and /readyz. - [Get livez](https://omniflow-vpn.mintlify.app/api-reference/system/get-livez.md): Answers as soon as the process is running. It never touches a dependency. - [Get readyz](https://omniflow-vpn.mintlify.app/api-reference/system/get-readyz.md): Reports every registered dependency. Failures carry a stable classification, never an underlying driver message, so the response cannot leak a connection string or a credential. - [Get metrics](https://omniflow-vpn.mintlify.app/api-reference/system/get-metrics.md): Prometheus text exposition. Every label is a bounded, non-identifying value such as a route template, a provider name, or an outcome. ### catalog - [Get v1catalogplans](https://omniflow-vpn.mintlify.app/api-reference/catalog/get-v1catalogplans.md) - [Get v1pages](https://omniflow-vpn.mintlify.app/api-reference/catalog/get-v1pages.md): Public. The information pages an operator has published and listed: the FAQ, the terms, the offer, the privacy policy. It is unauthenticated by design — a payment provider's reviewer and an application store's reviewer both have to read the offer and the privacy policy without an account, and a cust… - [Get v1pages 1](https://omniflow-vpn.mintlify.app/api-reference/catalog/get-v1pages-1.md): Public. One published document, returned as a parsed block structure rather than as HTML or as the operator's source text. The browser renders text nodes from it, so nothing an operator typed can become markup on the origin that holds the session cookie — there is no sanitiser in the path because th… - [Get v1branding](https://omniflow-vpn.mintlify.app/api-reference/catalog/get-v1branding.md): Public. What an installation looks like, read by both panels before they paint: the service name, the operator's palette rendered as ready-to-inline declarations, and the addresses of the brand images. It deliberately carries nothing else, because everything here is readable by anyone who can reach… - [Get v1brandingassets](https://omniflow-vpn.mintlify.app/api-reference/catalog/get-v1brandingassets.md): Public. One brand image. The `v` parameter carries the image's own SHA-256; a request naming the current one is answered as immutable, so a browser holds the bytes until the operator replaces them. - [Post v1adminplans](https://omniflow-vpn.mintlify.app/api-reference/catalog/post-v1adminplans.md) - [Post v1adminplans versions](https://omniflow-vpn.mintlify.app/api-reference/catalog/post-v1adminplans-versions.md) - [Post v1adminpromotions](https://omniflow-vpn.mintlify.app/api-reference/catalog/post-v1adminpromotions.md) ### public - [Get v1analytics](https://omniflow-vpn.mintlify.app/api-reference/public/get-v1analytics.md): What an anonymous visitor's browser renders. Public, because the storefront is read before anybody signs in and the answer is the operator's configuration rather than anything about a person. `measurable` is false when nothing would run even with consent, which is what suppresses the consent request… ### customers - [Patch v1admincustomers](https://omniflow-vpn.mintlify.app/api-reference/customers/patch-v1admincustomers.md) - [Post v1admincustomers identities](https://omniflow-vpn.mintlify.app/api-reference/customers/post-v1admincustomers-identities.md) - [Delete v1admincustomers identities](https://omniflow-vpn.mintlify.app/api-reference/customers/delete-v1admincustomers-identities.md) - [Post v1admincustomers contacts](https://omniflow-vpn.mintlify.app/api-reference/customers/post-v1admincustomers-contacts.md) - [Post v1admincustomers consents](https://omniflow-vpn.mintlify.app/api-reference/customers/post-v1admincustomers-consents.md) - [Post v1admincustomers lifecycle](https://omniflow-vpn.mintlify.app/api-reference/customers/post-v1admincustomers-lifecycle.md) ### imports - [Post v1adminimportsremnawavepreview](https://omniflow-vpn.mintlify.app/api-reference/imports/post-v1adminimportsremnawavepreview.md) - [Post v1adminimportsremnawave resume](https://omniflow-vpn.mintlify.app/api-reference/imports/post-v1adminimportsremnawave-resume.md) - [Post v1adminimportsremnawave apply](https://omniflow-vpn.mintlify.app/api-reference/imports/post-v1adminimportsremnawave-apply.md) ### orders - [Post v1adminorders](https://omniflow-vpn.mintlify.app/api-reference/orders/post-v1adminorders.md) - [Get v1adminorders](https://omniflow-vpn.mintlify.app/api-reference/orders/get-v1adminorders.md) - [Post v1adminorders cancel](https://omniflow-vpn.mintlify.app/api-reference/orders/post-v1adminorders-cancel.md) ### payments - [Post v1adminorders payments](https://omniflow-vpn.mintlify.app/api-reference/payments/post-v1adminorders-payments.md) - [Post v1paymentswebhooks](https://omniflow-vpn.mintlify.app/api-reference/payments/post-v1paymentswebhooks.md) - [Post v1adminpayments reconcile](https://omniflow-vpn.mintlify.app/api-reference/payments/post-v1adminpayments-reconcile.md) - [Post v1adminpayments refunds](https://omniflow-vpn.mintlify.app/api-reference/payments/post-v1adminpayments-refunds.md) - [Post v1adminpayments manual decision](https://omniflow-vpn.mintlify.app/api-reference/payments/post-v1adminpayments-manual-decision.md) ### wallet - [Post v1admincustomers wallet adjustments](https://omniflow-vpn.mintlify.app/api-reference/wallet/post-v1admincustomers-wallet-adjustments.md) ### fulfillment - [Get v1adminfulfillmentdrifts](https://omniflow-vpn.mintlify.app/api-reference/fulfillment/get-v1adminfulfillmentdrifts.md) - [Post v1adminentitlements operations](https://omniflow-vpn.mintlify.app/api-reference/fulfillment/post-v1adminentitlements-operations.md) ### operations - [Get v1adminjobs](https://omniflow-vpn.mintlify.app/api-reference/operations/get-v1adminjobs.md): Durable job visibility. The default filter is the dead-letter queue: jobs that will not be retried again without an operator. Job arguments are never echoed and error text is redacted. - [Post v1adminjobs retry](https://omniflow-vpn.mintlify.app/api-reference/operations/post-v1adminjobs-retry.md): Re-queues a dead-lettered job. The underlying fulfillment operation carries its own idempotency key, so a retry cannot repeat a side effect that already succeeded. - [Post v1adminjobs cancel](https://omniflow-vpn.mintlify.app/api-reference/operations/post-v1adminjobs-cancel.md) - [Get v1adminwebhooks](https://omniflow-vpn.mintlify.app/api-reference/operations/get-v1adminwebhooks.md): Provider webhook intake history with verification status and outcome. The raw provider body is never returned. - [Get v1adminoutbox](https://omniflow-vpn.mintlify.app/api-reference/operations/get-v1adminoutbox.md): Unpublished domain-event count and the age of the oldest one. - [Get v1adminbackups](https://omniflow-vpn.mintlify.app/api-reference/operations/get-v1adminbackups.md): Backup history with size, integrity digest presence, and retention. Creating and restoring a backup is done from the bot, not the API. - [Get v1adminmaintenance](https://omniflow-vpn.mintlify.app/api-reference/operations/get-v1adminmaintenance.md) - [Put v1adminmaintenance](https://omniflow-vpn.mintlify.app/api-reference/operations/put-v1adminmaintenance.md): Activates or clears maintenance mode manually. Maintenance blocks new purchases and holds outbound provisioning; it never cancels, refunds, or expires an order that is already paid. Automatic detection may clear a window it opened itself, but never one an operator opened. ### panel - [Get v1panelbootstrap](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelbootstrap.md): Reports whether the installation still needs its first owner. - [Post v1panelbootstrap](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelbootstrap.md): Redeems the one-time setup token printed by the API on first start and creates the first owner. Refused once any operator account exists. - [Post v1panelauthlogin](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthlogin.md): Password factor. Answers identically for a wrong password, an unknown address, and a disabled account, so operator accounts cannot be enumerated. On success it sets the session cookie; when the account has TOTP enrolled the session authorizes nothing until the challenge passes. - [Post v1panelauthchallenge](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthchallenge.md): Completes the second factor with a TOTP code or a single-use recovery code. - [Post v1panelauthpassword reset](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthpassword-reset.md): Always answers 202, whether or not the address matched an account, so the endpoint never discloses which addresses are registered. It issues no token: it records the request and notifies the owner. A reset token is issued only on the server by `api admin reset-password `. - [Post v1panelauthpassword resetcomplete](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthpassword-resetcomplete.md) - [Get v1panelauthsession](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelauthsession.md): The current operator, their effective permissions, and the CSRF token. The panel renders from these permissions, which are the same set the API enforces. - [Post v1panelauthlogout](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthlogout.md) - [Post v1panelauthlogout all](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthlogout-all.md): Ends every other session, sparing the one making the request. - [Patch v1panelauthprofile](https://omniflow-vpn.mintlify.app/api-reference/panel/patch-v1panelauthprofile.md) - [Post v1panelauthpassword](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthpassword.md): Rotates the password and ends every other session. - [Get v1panelauthsessions](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelauthsessions.md) - [Delete v1panelauthsessions](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelauthsessions.md) - [Post v1panelauthtotp](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthtotp.md): Issues a secret and otpauth URI. The secret is stored unconfirmed and satisfies no login challenge until the first code is verified. - [Delete v1panelauthtotp](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelauthtotp.md): Removes the second factor. The password is re-proven first. - [Post v1panelauthtotpconfirm](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthtotpconfirm.md): Activates the second factor and returns the recovery codes exactly once. - [Post v1panelauthrecovery codes](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthrecovery-codes.md): Replaces the whole set, invalidating any unused code. - [Get v1panelauthpasskey](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelauthpasskey.md): Whether this installation can offer passkeys. A credential is bound to an origin, so an installation without a public URL has none and the sign-in screen omits the button rather than offering a broken one. - [Post v1panelauthpasskeyloginbegin](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthpasskeyloginbegin.md): Issues a WebAuthn assertion challenge. Public by necessity: the assertion is what proves who the operator is. The challenge is returned to the browser and also sealed into a short-lived cookie, so the second step answers the challenge this step issued. - [Post v1panelauthpasskeyloginfinish](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthpasskeyloginfinish.md): Verifies the assertion and starts a complete session. challengeRequired is always false: the authenticator proved possession and verified the person, so no second factor remains to ask for. - [Get v1panelauthpasskeys](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelauthpasskeys.md): The signed-in operator's own credentials. No public key is returned. - [Post v1panelauthpasskeysregisterbegin](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthpasskeysregisterbegin.md): Issues a WebAuthn creation challenge for a discoverable credential with user verification required, which is what lets the key sign in alone. - [Post v1panelauthpasskeysregisterfinish](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelauthpasskeysregisterfinish.md): Stores the new credential. The label travels in the query because the body is the credential and has a shape fixed by WebAuthn. - [Delete v1panelauthpasskeys](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelauthpasskeys.md): Removes one credential. Never an account-recovery event: the password and its second factor are untouched, so the last key can go too. - [Patch v1panelauthpasskeys](https://omniflow-vpn.mintlify.app/api-reference/panel/patch-v1panelauthpasskeys.md) - [Get v1paneladmins](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1paneladmins.md): Requires admins.read. - [Post v1paneladmins](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1paneladmins.md): Requires admins.write, which only the owner role holds. - [Get v1paneladmins 1](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1paneladmins-1.md) - [Put v1paneladmins roles](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1paneladmins-roles.md): Requires admins.roles. Removing the last active owner is refused, and only an owner may grant the owner role. An operator cannot change their own roles (409 `self_role_change`), and every role added or removed must carry only permissions the actor already holds (403 `role_escalation`). Saving an unc… - [Get v1paneladminsinvitations](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1paneladminsinvitations.md): Requires admins.read. Open invitations (neither accepted nor revoked), newest first, at most 100. Expired ones are included so the inviter can see that a link lapsed. - [Post v1paneladminsinvitations](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1paneladminsinvitations.md): Requires admins.write. Issues a single-use link valid for 72 hours that creates an operator with the listed roles. The token is in this response only. Inviting an address again retires its earlier link. An address that already has an account is refused (409), and so is a role set carrying a permissi… - [Delete v1paneladminsinvitations](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1paneladminsinvitations.md): Requires admins.write. Retires an open invitation. Audited as `admin.invitation.revoked`. - [Post v1panelinvitationspreview](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelinvitationspreview.md): Public. Reads what an invitation offers so the acceptance screen can show it. An unknown, expired, revoked, or used token all answer the same 404 `invitation_invalid`. Rate limited per client address. - [Post v1panelinvitationsaccept](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelinvitationsaccept.md): Public. Redeems an invitation: creates the account with the invited roles and the chosen name and password, consumes the link, audits `admin.invitation.accepted`, queues a `security` operator notice, and signs the new operator in. Rate limited per client address. - [Post v1paneladmins status](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1paneladmins-status.md): Requires admins.write. Suspending an operator revokes their live sessions immediately; suspending the last active owner is refused. - [Get v1panelaudit](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelaudit.md): Requires audit.read. The trail is append-only and exposes reads only. - [Get v1panelauditactions](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelauditactions.md) - [Get v1panelauditexport](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelauditexport.md): Requires audit.export. Streams the filtered trail as CSV with the same columns the browser shows. Nothing writes a secret into an audit row. - [Get v1panelrbaccatalog](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelrbaccatalog.md): The compiled-in permission catalogue and the built-in role definitions. - [Get v1panelsearch](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsearch.md): Record search for the command palette. Any signed-in operator may call it; each result group is governed by the read permission of the screen it links to (customers → customers.read, orders → finance.read, tickets → support.read, gifts → gifts.read, accessCodes → catalog.read), and a group the calle… - [Get v1paneloverviewdashboard](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1paneloverviewdashboard.md): Requires system.read. Each metric carries the identifier of its own definition, every timestamp is UTC, and revenue is reported as three separate figures rather than one that double-counts wallet credit. - [Get v1paneloverviewincidents](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1paneloverviewincidents.md): Requires system.read. Recent maintenance activations and recoveries. - [Get v1panelcustomers](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers.md): Requires customers.read. `q` accepts an Omniflow customer identifier, a Telegram identifier, or a Remnawave username and is resolved by shape. There is deliberately no free-text search over contact values. - [Get v1panelcustomers 1](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-1.md): Requires customers.read. - [Get v1panelcustomers subscriptions](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-subscriptions.md): Requires customers.read. Every concurrent subscription with its own Remnawave mapping. - [Get v1panelcustomers orders](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-orders.md): Requires customers.read. - [Get v1panelcustomers tickets](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-tickets.md): Requires customers.read. - [Get v1panelcustomers consents](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-consents.md): Requires customers.read. The latest recorded decision per purpose. - [Get v1panelcustomers wallet](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-wallet.md): Requires finance.read rather than customer access: a support operator can see that a customer has a balance without reading every movement that produced it. The ledger is append-only and exposes reads only. - [Get v1panelcustomers payment methods](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-payment-methods.md): Requires finance.read. A saved method is a provider token and a masked label the provider supplied; neither the token nor any card data is returned. - [Delete v1panelcustomers payment methods](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelcustomers-payment-methods.md): Requires finance.write. Marks the method revoked and keeps the row, so a historical charge still resolves to the method that made it. - [Get v1panelcustomers charges](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-charges.md): Requires finance.read. Automatic renewal attempts for this customer. - [Get v1panelcustomers risk](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-risk.md): Requires risk.read. Blocklist matches recorded against this customer. - [Post v1panelcustomers status](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcustomers-status.md): Requires customers.write and a reason. Suspension does not disable a Remnawave user directly: the fulfillment pipeline observes customer state, and bypassing it would leave the two disagreeing. - [Patch v1panelcustomers subscriptions](https://omniflow-vpn.mintlify.app/api-reference/panel/patch-v1panelcustomers-subscriptions.md): Requires subscriptions.write. The label is the only subscription attribute the panel edits directly; expiry, traffic, limits, squads, and enabled state belong to Remnawave and change through fulfillment. - [Put v1panelcustomers allowlist](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelcustomers-allowlist.md): Requires risk.write and a reason. Without an override an operator's decision would be undone the moment the source republished the entry. - [Delete v1panelcustomers allowlist](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelcustomers-allowlist.md): Requires risk.write. - [Get v1panelcatalogplans](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcatalogplans.md): Requires catalog.read. Archived plans are included, with how many order lines reference each, so archiving one in active use is a visible decision. - [Get v1panelcatalogplans 1](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcatalogplans-1.md): Requires catalog.read. The plan with its localisations and priced versions. - [Patch v1panelcatalogplans](https://omniflow-vpn.mintlify.app/api-reference/panel/patch-v1panelcatalogplans.md): Requires catalog.write. Visibility, ordering, and the per-plan concurrency cap are saved together, so two operators editing one plan cannot each drop the other's change. - [Post v1panelcatalogplans archive](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcatalogplans-archive.md): Requires catalog.write. Archiving stops new purchases and leaves every order that already bought the plan priced against its immutable version. - [Put v1panelcatalogplans localizations](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelcatalogplans-localizations.md): Requires catalog.write. - [Post v1panelcatalogplan versions retire](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcatalogplan-versions-retire.md): Requires catalog.write. Retiring is forward-looking: every order that bought the version keeps its price. - [Get v1panelcatalogaddons](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcatalogaddons.md): Requires catalog.read. - [Get v1panelcatalogaddons versions](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcatalogaddons-versions.md): Requires catalog.read. Each version carries its documented proration rule. - [Get v1panelcatalogpromotions](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcatalogpromotions.md): Requires catalog.read. Promotions with their redemption analytics. - [Patch v1panelcatalogpromotions](https://omniflow-vpn.mintlify.app/api-reference/panel/patch-v1panelcatalogpromotions.md): Requires catalog.write. The reward kind is not editable: changing a live promotion's kind would silently change what every unredeemed code is worth. - [Get v1panelcatalogpromotions codes](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcatalogpromotions-codes.md): Requires catalog.read. - [Post v1panelcatalogpromo codes status](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcatalogpromo-codes-status.md): Requires catalog.write. A code is never deleted: the redemptions that reference it are financial records. - [Get v1paneloffers](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1paneloffers.md): Requires marketing.read. - [Post v1paneloffers](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1paneloffers.md): Requires marketing.write. Both locales are required: an offer with copy in one language renders as a blank card for half the customer base. - [Post v1paneloffers revoke](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1paneloffers-revoke.md): Requires marketing.write. Only an offer that has not been redeemed may be withdrawn. - [Get v1panelfinanceorders](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelfinanceorders.md): Requires finance.read. - [Get v1panelfinanceorders 1](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelfinanceorders-1.md): Requires finance.read. Payment intents with their event timeline and every refund. The raw provider payload is deliberately absent; it is reachable through the webhook diagnostics with its own permission. - [Post v1panelfinanceorders refund](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelfinanceorders-refund.md): Requires finance.write and a reason. The refund itself is executed by the payment service against the provider's own capabilities; this records the decision and its rationale. - [Post v1panelfinancepayments reconcile](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelfinancepayments-reconcile.md): Requires finance.write. Re-polling is performed by the idempotent payment service; the panel never decides how a payment settles. - [Get v1panelfinancestuck payments](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelfinancestuck-payments.md): Requires finance.read. Intents in flight longer than any supported provider should take. - [Get v1panelfinancefailed charges](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelfinancefailed-charges.md): Requires finance.read. A review surface: a failed charge retries on its own bounded schedule and nothing here re-triggers one. - [Get v1panelfinanceexport](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelfinanceexport.md): Requires finance.read. Streams the filtered orders as CSV. The schema is stable — a later version appends a column and never inserts one. Amounts are integers in the currency's minor unit and every timestamp is UTC. - [Get v1panelsystemjobs](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsystemjobs.md): Requires system.read. Filtering on the failed status is the dead-letter view. The error code is a classification, never a provider message. - [Get v1panelsystemjobs history](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsystemjobs-history.md): Requires system.read. Status, correlation, and error classification only: the stored request and response summaries can carry a subscription link and are not returned. - [Post v1panelsystemjobs retry](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsystemjobs-retry.md): Requires system.write. The retry keeps the operation's idempotency key, so the worker still performs it exactly once. A succeeded operation is not retryable. - [Post v1panelsystemjobs cancel](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsystemjobs-cancel.md): Requires system.write. A completed provisioning cannot be retracted. - [Get v1panelsystemwebhooks](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsystemwebhooks.md): Requires system.read. The raw body is not returned: it can contain a provider payload, and "did this arrive and was it accepted" needs only the status. - [Post v1panelsystemwebhooks replay](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsystemwebhooks-replay.md): Requires system.write. Only a failed or ignored event may be replayed. Reprocessing is keyed on the provider event identifier, so a second pass reaches the same terminal state instead of applying twice. - [Get v1panelsystemdrift](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsystemdrift.md): Requires system.read. Unresolved differences between Omniflow and Remnawave. - [Get v1panelsystemoutbox](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsystemoutbox.md): Requires system.read. Topic and age only: an outbox payload is a domain event that can name a customer and an amount. - [Get v1panelsettingscommerce](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingscommerce.md): Requires settings.read. - [Put v1panelsettingscommercetopup](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingscommercetopup.md): Requires settings.write. A preset outside the limits is a button that always fails. - [Put v1panelsettingscommercesubscriptions](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingscommercesubscriptions.md): Requires settings.write. Turning concurrency off closes nothing: customers who already hold several keep them. - [Get v1panelcontentpages](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcontentpages.md): Requires marketing.read. Every page, drafts included. - [Put v1panelcontentpages](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelcontentpages.md): Requires marketing.write. Creates or updates a page and its translations. A language with a title and no body, or the reverse, is refused: half a translation renders a heading over nothing. A page with no language at all is refused for the same reason. - [Get v1panelcontentpages 1](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcontentpages-1.md): Requires marketing.read. One page with every translation's source text, for the editor. - [Delete v1panelcontentpages](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelcontentpages.md): Requires marketing.write. Deletes the page and takes its address with it. Withdrawing publication instead is the reversible operation, and is what an operator wants unless they mean to break a URL a payment provider approved. - [Post v1panelcontentpages publication](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcontentpages-publication.md): Requires marketing.write. Publishing and withdrawing are the same operation with a boolean, because the difference is one nullable column and splitting them would let the two drift. - [Post v1panelcustomers subscriptions pause](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcustomers-subscriptions-pause.md): Requires subscriptions.write and an operator reason. Suspends access without spending the subscription's remaining days: the Remnawave user is disabled and the entitlement's clock stops, in one transaction. This is what `disable` is not. A disabled subscription keeps running out while the customer c… - [Post v1panelcustomers subscriptions resume](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcustomers-subscriptions-resume.md): Requires subscriptions.write and an operator reason. Gives back exactly the time the pause took: `ends_at` moves forward by the elapsed pause, `paused_seconds` records the same amount, and the Remnawave user has the new expiry pushed to it before being re-enabled — in that order, which is why it is… - [Get v1panelcodesbatches](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcodesbatches.md): Requires catalog.read. Wholesale batches with their counts. It carries no codes: only their SHA-256 is stored, so there is nothing here that could produce one. - [Post v1panelcodesbatches](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcodesbatches.md): Requires catalog.write. Generates a batch and returns its codes. This is the only response in the API that carries redeemable codes, and it carries every code in the batch. It cannot be replayed: asking again generates a different batch rather than returning this one, because nothing is stored that… - [Get v1panelcodesbatches codes](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcodesbatches-codes.md): Requires catalog.read. One batch's codes by hint and status. The hint is the last four characters, which is enough to match a support question to a row and far too little to guess the other twelve. - [Post v1panelcodesbatches revoke](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcodesbatches-revoke.md): Requires catalog.write and a reason. Kills every code in the batch that nobody has redeemed, and returns how many that was. Redeemed codes are untouched by design: somebody is using the subscription each one produced, and taking that back is a different decision. The reason is required because this… - [Get v1panelcustomers mergepreview](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-mergepreview.md): Requires customers.read. What merging this account into another would do, and every reason it cannot happen. A merge cannot be undone, so everything that would move is counted before anything does. The blockers are recomputed when the merge is applied rather than trusted from here: the two are separ… - [Post v1panelcustomers merge](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcustomers-merge.md): Requires customers.write and a reason. Merges the account in the path into the one in the body: subscriptions take new slots, orders, identities, contacts, consents, tickets, and saved methods are reassigned, the wallet balance moves as a compensating pair of ledger entries, and the source is closed… - [Get v1panelcustomers notifications](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-notifications.md): Requires customers.read. What this customer was actually sent. There is no message body here. The record says that a notice of a kind happened, not what it said, and reconstructing text from a template and today's data would show an operator something that was never sent. `reason` is the useful colu… - [Get v1panelcustomers notificationssummary](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelcustomers-notificationssummary.md): Requires customers.read. The shape of the whole history per kind, which is what somebody wants before reading any single row of it. - [Post v1panelcustomers notificationstest](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelcustomers-notificationstest.md): Requires support.write. Queues one test message to this customer through the ordinary outbox. Queued, not sent — and that is the point. The panel process holds no Telegram connection; the notifier does. A test that took its own path would prove the test path works, which nobody asked. This one is cl… - [Get v1panelnotices](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelnotices.md): Requires settings.read. Every transactional notice an operator may reword, with the shipped wording, the values it carries, and whatever has been written for it. These are the messages the installation sends on its own initiative because something happened to a subscription — expiry, traffic, renewa… - [Put v1panelnotices](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelnotices.md): Requires settings.write. Stores one body for one locale. One locale at a time, deliberately: rewriting the English expiry warning decides nothing about the Russian one, and a document-shaped save would let this screen overwrite a translation somebody else was working on. Refused with 422 when the bo… - [Delete v1panelnotices](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelnotices.md): Requires settings.write. Removes the override so the shipped wording applies again. A delete rather than a write of the current default: an installation that reverts today and upgrades tomorrow gets tomorrow's improved wording, which a copied-in default would have frozen. Reverting something that wa… - [Post v1panelnotices preview](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelnotices-preview.md): Requires settings.write — a preview stores nothing, but it runs the renderer over caller-supplied text, and a read permission should not be a way to do that. Renders a body against the notice's sample values, so an operator judges a message rather than a template. An empty body previews the shipped… - [Post v1panelnotices test](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelnotices-test.md): Requires settings.write. Queues one rendered copy for the operator group's notice-preview topic. Never for a customer. A transactional notice has a trigger — a subscription about to expire, a charge that failed — and manufacturing one against a real subscription to see how the wording reads would te… - [Get v1panelnotices tests](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelnotices-tests.md): Requires settings.read. What has been queued for this notice, so a preview in flight looks like one rather than like a button that did nothing. - [Get v1panelanalytics](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelanalytics.md): Requires settings.read. The operator's own advertising measurement: counter identifiers, webmaster verification tags, and the closed set of each this build can render. This is never project telemetry. Nothing here is reported to anybody, and an installation that configures none of it renders nothing… - [Put v1panelanalytics](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelanalytics.md): Requires settings.write and the current `version`. A counter is a provider this repository knows and an identifier validated against its shape — never a snippet. The flexibility given up is exactly the ability to run arbitrary code in a customer's browser, and a customer's browser holds subscription… - [Get v1panelreportschannels](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelreportschannels.md): Requires finance.read. What each channel brought in over the period, which is the question an operator asks before exporting anything. Keyed on settlement rather than on the order's creation: an advertisement is credited when the money arrived. - [Get v1panelreportsconversions](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelreportsconversions.md): Requires finance.read. The offline-conversion export: settled orders that came from an advertisement, with the click identifier the platform issued. Payment happens on the backend, often a day after the click, so the browser session that carried the identifier is long gone by the time there is money… - [Get v1panelreportssales](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelreportssales.md): Requires finance.read. What was sold over a chosen period, by kind of sale, by plan version, and by day, with trial conversion and refunds issued. The period keys on `orders.paid_at`, which is set once when an order first settles and never moved afterwards, so re-running a report over a closed perio… - [Get v1panelreportssalesexport](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelreportssalesexport.md): Requires finance.read. The same report as CSV, one file with a `section` column rather than four files, because the breakdowns are four views of one period. Amounts are minor units, unscaled: dividing by a hundred is wrong for every zero-decimal currency and is something the reader can do knowing th… - [Get v1panelreportspayments](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelreportspayments.md): Requires finance.read. Settlement, latency, and webhook intake per payment adapter, so an acquirer that starts refusing is a number that moved rather than a growing stuck-payment queue. Two rates, because a customer abandoning a checkout and a gateway refusing a card are not the same event and only… - [Get v1panelreportstraffic](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelreportstraffic.md): Requires customers.read. Node saturation and the heaviest users, read live from Remnawave on every request. Omniflow stores none of it. Remnawave is authoritative for traffic, nodes, and connections, and this repository has no table for a node or for a byte a customer used — keeping one would be the… - [Get v1panelreportstrafficexport](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelreportstrafficexport.md): Requires customers.read. The same figures as CSV. Byte counts are unscaled: a gigabyte is 1000³ to some readers and 1024³ to others, and choosing one in an export makes the file wrong for the other half. - [Get v1panelsettingstheme](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingstheme.md): Requires settings.read. The stored palette, the stylesheet it renders as, the tokens this build honours, and every contrast problem the palette has. The warnings are recomputed on each read rather than stored, so they cannot go stale against a palette edited elsewhere. - [Put v1panelsettingstheme](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingstheme.md): Requires settings.write. Refused when any text pair falls below 3:1 against its own background, which is under the loosest threshold WCAG 2.2 defines for any text. A pair between 3:1 and 4.5:1 is saved and reported as a warning, because a brand tone that fails AA is a decision an operator is entitle… - [Get v1panelsettingsthemeassets](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingsthemeassets.md): Requires settings.read. The image slots with their size, type, and checksum. The bytes are not returned here; they are served publicly by the branding asset route. - [Put v1panelsettingsthemeassets](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingsthemeassets.md): Requires settings.write. The body is the image itself rather than a form or a base64 field, so its size is bounded before a byte is read. The declared content type must match the magic number of the bytes. SVG is not accepted: it can carry a script, and these files are served from the same origin as… - [Delete v1panelsettingsthemeassets](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelsettingsthemeassets.md): Requires settings.write. Clears the slot, returning the installation to the shipped mark. - [Get v1panelsettingsconnect](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingsconnect.md): Requires settings.read. Every platform and every recommended application, including the ones an operator has switched off — a disabled row is what somebody came to this screen to turn back on. The customer surfaces read only the enabled ones. - [Put v1panelsettingsconnectplatforms](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingsconnectplatforms.md): Requires settings.write. Creates or updates a platform. A missing Russian label is stored as the English one rather than left to render as a gap. - [Delete v1panelsettingsconnectplatforms](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelsettingsconnectplatforms.md): Requires settings.write. Deletes the platform and, by cascade, every application documented for it. Disabling it instead keeps the entries. - [Put v1panelsettingsconnectclients](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingsconnectclients.md): Requires settings.write. Creates when `id` is absent and updates when it is present. The import scheme is refused unless it matches a scheme followed by `://` and a conservative path, and never when it names javascript, data, vbscript, or file — the value is concatenated with the subscription link a… - [Delete v1panelsettingsconnectclients](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelsettingsconnectclients.md): Requires settings.write. - [Get v1panelsettingsproviders](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingsproviders.md): Requires settings.read. Reports what the operator configured beside what each compiled-in adapter declares. No secret is ever returned; the `credentialsSet` flags report only whether one is stored. - [Put v1panelsettingsproviders](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingsproviders.md): Requires settings.write. A null credential means "keep what is stored", so the form can be re-saved without echoing a secret. Recurring is not writable here. - [Post v1panelsettingsproviders recurring](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsettingsproviders-recurring.md): Requires settings.write. Records a capability test and, only on a pass, may enable automatic charging. Refused for an adapter that cannot bind a payment method, whatever the test reported. - [Get v1panelsettingsaiproviders](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingsaiproviders.md): Requires settings.read. The model backends an owner has approved. The API key is never returned; `keyConfigured` reports only whether one is held. What a provider does with the data is recorded from its own terms, which Omniflow cannot verify. - [Put v1panelsettingsaiproviders](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingsaiproviders.md): Requires settings.write. An omitted `apiKey` leaves the stored credential alone, so a display name can be corrected without re-pasting a secret. - [Delete v1panelsettingsaiproviders](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelsettingsaiproviders.md): Requires settings.write. Refused while a feature still points at the provider: removing it would switch that feature off without saying so. - [Post v1panelsettingsaiproviders test](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsettingsaiproviders-test.md): Requires settings.write. Makes one small completion against the stored credential and records the outcome on the provider, so "is this configured correctly?" is answerable without spending a real request later. The prompt is Omniflow's own text; no customer content is involved. - [Get v1panelrisksources](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelrisksources.md): Requires risk.read. The stored authorization header is never returned. - [Put v1panelrisksources](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelrisksources.md): Requires risk.write. The URL must be HTTPS: plain HTTP would let anyone on the path decide who this installation refuses to serve. - [Delete v1panelrisksources](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelrisksources.md): Requires risk.write. Removes the source, its entries, and the matches it produced. - [Get v1panelriskmatches](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelriskmatches.md): Requires risk.read. The review queue. - [Post v1panelriskmatches decision](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelriskmatches-decision.md): Requires risk.write and a reason. Recording a decision does not suspend anybody: the adverse action is a separate customer mutation with its own permission and audit event. - [Post v1panelriskmatches appeal](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelriskmatches-appeal.md): Requires risk.write and a reason. Returns a blocked match to the review queue. - [Get v1panelriskrules](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelriskrules.md): Requires risk.read. Thresholds are in each metric's own unit. - [Put v1panelriskrules](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelriskrules.md): Requires risk.write. A rule that would be skipped at evaluation time — no window, or an alert threshold below the warning — is refused here instead. - [Get v1panelriskanomalies](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelriskanomalies.md): Requires risk.read. Evidence carries the aggregate numbers that produced the signal and never customer content. - [Post v1panelriskanomalies review](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelriskanomalies-review.md): Requires risk.write. Reviewing a signal changes nothing about the customer it names, which is what makes automated detection safe to run. - [Get v1panelgifts](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelgifts.md): Requires gifts.read. Only the digest of a claim code is stored; the four characters returned as a hint identify a gift without redeeming it. - [Post v1panelgifts revoke](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelgifts-revoke.md): Requires gifts.write and a reason. A claimed gift is not revocable: the recipient already holds what it bought, and taking that back is a refund decision against their entitlement. - [Post v1panelgifts refund](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelgifts-refund.md): Requires gifts.write. Only a gift that was never redeemed qualifies, which is what stops a sender being refunded while the recipient keeps the entitlement. - [Get v1panelgoodsproviders](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelgoodsproviders.md): Requires goods.read. The credential is never returned; the balance and the low-balance flag are, because a shop that sells past an exhausted balance produces paid orders that can only be refunded. - [Put v1panelgoodsproviders](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelgoodsproviders.md): Requires goods.write. A null credential means "keep what is stored". - [Get v1panelgoodsproducts](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelgoodsproducts.md): Requires goods.read. - [Post v1panelgoodsproducts](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelgoodsproducts.md): Requires goods.write. Premium is sold by duration and Stars by quantity; each kind carries exactly the one that applies to it. - [Patch v1panelgoodsproducts](https://omniflow-vpn.mintlify.app/api-reference/panel/patch-v1panelgoodsproducts.md): Requires goods.write. The kind, duration, and quantity are immutable once created: an order references the product it bought. - [Put v1panelgoodsproducts localizations](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelgoodsproducts-localizations.md): Requires goods.write. - [Put v1panelgoodsproducts pricing](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelgoodsproducts-pricing.md): Requires goods.write. Every rounding mode rounds up, and the markup itself rounds up rather than truncating. - [Get v1panelgoodsorders](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelgoodsorders.md): Requires goods.read. The recipient username is the only recipient detail retained anywhere: delivery needs it and support needs to answer "where did it go". - [Get v1panelgoodsorders attempts](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelgoodsorders-attempts.md): Requires goods.read. Every provider exchange, with its failure classification. - [Get v1panelgoodsreview](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelgoodsreview.md): Requires goods.read. Deliveries whose outcome nobody could resolve automatically. The gateway honours no idempotency key, so a lost answer means the goods may or may not have been sent; retrying could deliver twice and refunding could give money back for goods the recipient received. - [Post v1panelgoodsorders resolve](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelgoodsorders-resolve.md): Requires goods.write and a reason. Records what an operator confirmed with the provider: `delivered` completes the order, and anything else releases the ordinary refund path and credits the customer's wallet. - [Post v1panelgoodsorders cancel](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelgoodsorders-cancel.md): Requires goods.write and a reason. A delivered order cannot be cancelled: the recipient already has what was bought. - [Get v1panelbulk](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelbulk.md): Requires customers.read. - [Post v1panelbulk](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelbulk.md): Requires customers.write, a reason, and an idempotency key. This only previews: nothing is applied until the operation is started, and only a previewed operation may start. - [Post v1panelbulk start](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelbulk-start.md): Requires customers.write. Only a previewed operation may start. - [Post v1panelbulk cancel](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelbulk-cancel.md): Requires customers.write. A running operation is not cancellable: some of its targets have already changed, and reporting "cancelled" would misrepresent what happened. - [Get v1panelbulk items](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelbulk-items.md): Requires customers.read. The per-item outcome an operator reviews after a run. - [Get v1panelsupportqueues](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsupportqueues.md): Requires support.read. Every live queue with its open, unassigned, and breached counts. - [Put v1panelsupportqueues](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsupportqueues.md): Requires settings.write, not support.write: a queue's targets are the promise an operator works under, not day-to-day support work. Upserts by code; `isDefault: true` clears the previous default in the same transaction. - [Get v1panelsupporttickets](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsupporttickets.md): Requires support.read. The queue, oldest activity first, on a keyset cursor over (lastMessageAt, id). The breach flag is computed in SQL so "overdue" means the same thing here, in the queue counters, and in the report. - [Get v1panelsupporttickets 1](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsupporttickets-1.md): Requires support.read. The conversation with its messages, each carrying its attachments and the push outcome, and the operators' internal notes as a separate list so no client can mistake one for the other. - [Get v1panelsupporttickets attachments](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsupporttickets-attachments.md): Requires support.read. Serves a file the customer uploaded through the web panel, scoped to its ticket, with an attachment disposition and `nosniff`. A file that arrived through Telegram answers 409 `attachment_remote` and is never fetched: Omniflow holds a reference, not the bytes, and will not pro… - [Post v1panelsupporttickets assign](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-assign.md): Requires support.write. An empty assigneeId releases the ticket. - [Post v1panelsupporttickets queue](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-queue.md): Requires support.write. - [Post v1panelsupporttickets priority](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-priority.md): Requires support.write. Priority does not change the SLA target, which belongs to the queue. - [Post v1panelsupporttickets status](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-status.md): Requires support.write. `merged` cannot be set here; merging is its own action. Setting `resolved` or `closed` writes a system message in the customer's language that the bot pushes like a reply. - [Post v1panelsupporttickets merge](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-merge.md): Requires support.write. Folds this ticket into the survivor: messages and unread counts move, the absorbed row stays with status `merged` and a pointer, and the customer is told on the survivor. A merge across customers or into itself is 422; into a ticket that was itself merged is 409. - [Post v1panelsupporttickets reply](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-reply.md): Requires support.write and an Idempotency-Key, which becomes the message's dedupe key; a repeated key answers 200 with an empty message. The reply is written here and pushed by the bot's support loop. A merged ticket is refused with 409. - [Post v1panelsupporttickets notes](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-notes.md): Requires support.write. A private note, never delivered; its body is not copied into the audit trail. - [Post v1panelsupporttickets read](https://omniflow-vpn.mintlify.app/api-reference/panel/post-v1panelsupporttickets-read.md): Requires support.write. Clears the operator-side unread counter. - [Put v1panelsupporttickets tags](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsupporttickets-tags.md): Requires support.write. The path parameter is the tag code; attaching twice is a no-op. - [Delete v1panelsupporttickets tags](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelsupporttickets-tags.md): Requires support.write. - [Get v1panelsupporttags](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsupporttags.md): Requires support.read. - [Put v1panelsupporttags](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsupporttags.md): Requires settings.write. Upserts by code. - [Get v1panelsupportcanned](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsupportcanned.md): Requires support.read. Most-used first; a response whose `requiresPermission` the signed-in operator lacks is filtered out server-side rather than shown disabled. - [Put v1panelsupportcanned](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsupportcanned.md): Requires settings.write. Both languages are mandatory. Upserts by code. - [Delete v1panelsupportcanned](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelsupportcanned.md): Requires settings.write. Archives rather than deletes; archiving twice is 404. - [Get v1panelsupportreport](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsupportreport.md): Requires support.read. The workload and response-time figures over a window capped at 90 days. - [Get v1panelsettingscustomer oidc](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingscustomer-oidc.md): Requires settings.read. The customer panel's sign-in providers. The client secret is never returned; `hasClientSecret` says whether one is held. - [Put v1panelsettingscustomer oidc](https://omniflow-vpn.mintlify.app/api-reference/panel/put-v1panelsettingscustomer-oidc.md): Requires settings.write. Creating or updating a provider. Disabling one also ends the sessions it established, because a provider that is off while its access remains open is not off. - [Get v1panelsettingscustomer oidcpresets](https://omniflow-vpn.mintlify.app/api-reference/panel/get-v1panelsettingscustomer-oidcpresets.md): Requires settings.read. Shipped starting points for well-known providers. A preset is data that prefills the form; nothing downstream branches on which one a provider came from. - [Delete v1panelsettingscustomer oidc](https://omniflow-vpn.mintlify.app/api-reference/panel/delete-v1panelsettingscustomer-oidc.md): Requires settings.write. Removes a provider and ends its sessions. The linked identities are kept: deleting them would silently take a sign-in method away from customers who may have no other one. ### account - [Get v1accountauthmethods](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountauthmethods.md): Which sign-in routes this installation actually offers. The sign-in screen renders from this, so an installation with no bot token never shows a Telegram button that cannot work. - [Post v1accountauthtelegram](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountauthtelegram.md): Verifies a Telegram Login Widget payload. The hash is checked against the bot token and the auth_date must be recent, so a captured payload cannot be replayed indefinitely. The body is the object the widget hands to its `data-onauth` callback, posted as is: `id` and `auth_date` are JSON numbers, the… - [Post v1accountauthtelegramminiapp](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountauthtelegramminiapp.md): Verifies the initData a Telegram Mini App hands to its own page. Requires Content-Type application/json and a same-origin request. - [Get v1accountauthlink](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountauthlink.md): Where a one-time link the bot delivered lands. It signs nobody in: a GET can be triggered by any page, so opening a session here would let a link requested for one account be planted in another person's browser. It checks the token without spending it and redirects to /account/sign-in/confirm#token=… - [Post v1accountauthlink](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountauthlink.md): Redeems a one-time link from the confirmation page and opens a session. The token is consumed in the same transaction. Requires Content-Type application/json and a same-origin request. - [Get v1accountauthoidc start](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountauthoidc-start.md) - [Get v1accountauthoidc callback](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountauthoidc-callback.md) - [Get v1accountme](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountme.md): The signed-in customer, how they authenticated, and whether a sensitive action needs a fresh sign-in. - [Patch v1accountme](https://omniflow-vpn.mintlify.app/api-reference/account/patch-v1accountme.md) - [Post v1accountauthlogout](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountauthlogout.md) - [Post v1accountauthlogout all](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountauthlogout-all.md): Ends every session. keepCurrent spares the caller's own, which is what "sign out my other devices" means; leaving it false is what a suspected compromise needs. - [Get v1accountsessions](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsessions.md) - [Delete v1accountsessions](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountsessions.md) - [Get v1accountsecurity events](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsecurity-events.md): The customer's own account history. The vocabulary is closed and carries no amount, link, or other party's identifier. - [Get v1accountsign in methods](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsign-in-methods.md) - [Delete v1accountsign in methods](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountsign-in-methods.md): Refuses to remove the last usable method. Needs a recent sign-in, because a session left open must not be enough to strip somebody's way back into their own account. - [Get v1accountsign in methodsoidc start](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsign-in-methodsoidc-start.md): Attaches a provider to the account already signed in. This is the only route by which an existing customer gains an OIDC method: signing in with an unlinked subject never adopts an existing account by email. - [Get v1accountoverview](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountoverview.md) - [Get v1accountsubscriptions](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsubscriptions.md) - [Patch v1accountsubscriptions](https://omniflow-vpn.mintlify.app/api-reference/account/patch-v1accountsubscriptions.md): The customer's own label, which every screen and notification uses to name the subscription. - [Get v1accountsubscriptions connection](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsubscriptions-connection.md): The access link plus the documented clients for one platform. The catalogue is the operator's own and is read through one query that the bot reads too, so the two surfaces cannot recommend different applications. - [Post v1accountsubscriptions rotate link](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountsubscriptions-rotate-link.md): Issues a new access link and invalidates the old one. Every connected device stops working until the new link is imported, so it needs both an explicit confirmation and a recent sign-in. - [Get v1accountsubscriptions devices](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsubscriptions-devices.md): Connected devices. No hardware identifier and no IP address is returned; a device is named by an opaque handle the server resolves back. - [Delete v1accountsubscriptions devices](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountsubscriptions-devices.md): Disconnects every device at once. Needs confirmation and a recent sign-in. - [Delete v1accountsubscriptions devices 1](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountsubscriptions-devices-1.md) - [Get v1accountplans](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountplans.md): The comparable catalogue, with this customer's eligibility already decided. `operations` names the lifecycle actions the configured plan policy allows against their current subscriptions, so the panel offers renew, upgrade, or downgrade because the server said so rather than because React worked it… - [Get v1accountplans 1](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountplans-1.md) - [Get v1accountcheckout](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountcheckout.md): The open checkout with a live quote. A customer has at most one, shared with the Telegram bot, so opening one in a browser supersedes one left open in a chat. `404` means none is open, which is an ordinary state rather than a failure. - [Post v1accountcheckout](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountcheckout.md) - [Delete v1accountcheckout](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountcheckout.md) - [Patch v1accountcheckout](https://omniflow-vpn.mintlify.app/api-reference/account/patch-v1accountcheckout.md): Every field is optional and an absent field is left unchanged, so a screen that changes the provider never has to resend the squad selection it did not touch. - [Post v1accountcheckoutpromo](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountcheckoutpromo.md): A refused code answers 200 carrying `promoRejection`, not an error status. The customer asked a legitimate question and the answer is "not this one, because…"; failing the request would leave the screen with nothing to say. - [Delete v1accountcheckoutpromo](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountcheckoutpromo.md) - [Post v1accountcheckoutaddons](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountcheckoutaddons.md) - [Post v1accountcheckoutconfirm](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountcheckoutconfirm.md): Turns the open checkout into an order. The checkout's own idempotency key is reused for the order, so a duplicate confirmation resolves to the order already created rather than a second one. - [Get v1accountorders](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountorders.md) - [Get v1accountorders 1](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountorders-1.md): One order with its payment, provisioning progress, and refunds. Provisioning comes from the fulfillment operation the order created, so a refresh, a second tab, or a switch to Telegram all show the same state. - [Post v1accountorders payment](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountorders-payment.md): Creates or resumes the provider payment. The key is scoped to the order and the chosen adapter, so switching provider is a new payment while pressing the same button twice is not. The provider defaults to the one the order's payment already names, then to the one the checkout that became this order… - [Post v1accountorders refresh](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountorders-refresh.md): Re-reads the provider so a customer who returns before the webhook arrives still sees the settled state. - [Post v1accountorders cancel](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountorders-cancel.md) - [Get v1accountwallet](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountwallet.md): Balances per currency with the top-up policy and the ledger. Currencies are isolated: a balance in one never funds an order in another. - [Post v1accountwallettop up](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountwallettop-up.md): Opens a top-up order through the same order, webhook, and reconciliation pipeline a plan uses. The caller's key becomes the order's key, so a retried request credits the same top-up rather than a second one. The order and its payment are two steps: when the order exists but the provider refused the… - [Get v1accountreferrals](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountreferrals.md): The customer's code, its shareable link, the program's terms, and the reward history. A reversed reward is excluded from the earned total and from the cap count, because an operator reverses one when the referral turns out to be abuse and the slot it consumed should come back. - [Post v1accountreferralsattribution](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountreferralsattribution.md): Records the inviter behind a web sign-up from the `?ref=` code the sign-in screen carried, under the same rule the bot applies to `/start ref_`: first write wins, self-referral is impossible, and the row is written only while the programme is enabled and only for a customer who has not yet pai… - [Get v1accountloyalty](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountloyalty.md) - [Get v1accountcontacts](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountcontacts.md): Flags only. A stored contact value is never returned to the browser. - [Post v1accountcontacts](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountcontacts.md): A value already registered elsewhere answers `contact_unavailable` and nothing more. Saying that another account holds it would turn the panel into an account-existence oracle, so the remedy is a support handoff. - [Delete v1accountcontacts](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountcontacts.md) - [Get v1accountprivacy](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountprivacy.md) - [Post v1accountprivacyexport](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountprivacyexport.md): Produces the customer's own records synchronously as a download. It is not queued because a queued export would need a delivery channel, a retention window, and a link that authenticates on its own — three further disclosure decisions to answer a question that fits in one response. The document decl… - [Post v1accountprivacydeletion](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountprivacydeletion.md): Records a deletion request and nothing else. The retention workflow an operator already governs is what deletes, so an irreversible action never happens on the strength of one browser session. It needs a recent sign-in, and answers 202 because the work has been accepted rather than done. - [Delete v1accountprivacydeletion](https://omniflow-vpn.mintlify.app/api-reference/account/delete-v1accountprivacydeletion.md): Withdraws a pending request. It carries no recent-sign-in gate, because it only ever prevents an irreversible action. - [Get v1accountshopproducts](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountshopproducts.md): The digital-goods catalogue. No price is quoted here: a quote is a promise with an expiry attached, and issuing one per row would fill a page with promises that start expiring while it is being read. A product with `priceKnown: false` has no published price and is priced when it is opened. - [Get v1accountshopproducts 1](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountshopproducts-1.md): One product with a live quote. The quote and its expiry are one object, so a client cannot read the price without also being handed the moment it stops applying. - [Post v1accountshoprecipient](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountshoprecipient.md): Normalises a recipient handle and hands it back for confirmation. It changes nothing: the review step exists so the customer sees the exact string the gateway will be given before anybody is charged, and a step that also acted would defeat that. A mistyped username is unrecoverable once a gateway ha… - [Post v1accountshoppurchase](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountshoppurchase.md): Opens the order for a reviewed recipient against a live quote. The displayed quote is echoed back and the server re-quotes before charging, refusing with `quote_expired` when the window has passed and `price_changed` when the number moved. The recipient must be byte-identical to the reviewed form, s… - [Get v1accountshoporders](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountshoporders.md) - [Get v1accountshoporders 1](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountshoporders-1.md): One purchase and its delivery. There is deliberately no retry route: the gateway that fronts Fragment honours no idempotency key, so an ambiguous delivery is genuinely ambiguous and parks in the operator review queue rather than being retried or refunded automatically. - [Get v1accountsupportlimits](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsupportlimits.md): The attachment rules and the open-conversation quota, so the panel can state them before an upload rather than only after one is refused. - [Get v1accountsupporttickets](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsupporttickets.md): The customer's conversations, newest activity first, with whether an operator's reply can be pushed to them in Telegram at all. - [Post v1accountsupporttickets](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountsupporttickets.md): Opens a conversation and answers it whole, with its first message. The number a customer may hold open at once is bounded, so a loop in a client cannot fill the operator queue faster than people can empty it, and the route is rate-limited per customer on top. - [Get v1accountsupporttickets 1](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsupporttickets-1.md): The conversation as the customer sees it. Internal operator notes live in their own table and no query on this surface touches it. - [Post v1accountsupporttickets messages](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountsupporttickets-messages.md): Appends a message and answers the whole conversation. A message on a resolved ticket reopens it, which takes an open slot and is refused with 409 `too_many_open_tickets` when the customer has none; a closed or merged ticket answers 409 `ticket_closed`. - [Post v1accountsupporttickets read](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountsupporttickets-read.md): Read state is a property of the message, not of the surface it was read on. A reply opened here stops being unread in Telegram too, because both surfaces stamp the same column. - [Post v1accountsupporttickets close](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountsupporttickets-close.md) - [Post v1accountsupporttickets reopen](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountsupporttickets-reopen.md): Reopening takes an open slot and is refused with 409 `too_many_open_tickets` when the customer already holds the maximum; a merged ticket answers 422. - [Post v1accountsupporttickets attachments](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountsupporttickets-attachments.md): Uploads one file as a new message on the conversation and answers the attachment it created. The accepted media types are an allowlist, so an unknown type is refused rather than guessed at. The idempotency key becomes the message's dedupe key, so a replayed upload finds the message and the file it a… - [Get v1accountsupportattachments](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountsupportattachments.md): Serves a file the calling customer's own conversation carries. A file attached in Telegram is not stored here and answers `attachment_remote` instead, because offering a download that cannot work is worse than saying where the file actually lives. - [Get v1accountnews](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountnews.md) - [Post v1accountnews read](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountnews-read.md) - [Post v1accountorders attribution](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountorders-attribution.md): Records where one of the caller's own orders came from, for the operator's own advertising measurement. A separate call rather than a field on the checkout: a checkout is created from the bot as well as from the browser, and the bot has no URL to have carried anything. The storefront sends this only… - [Get v1accountnotifications](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountnotifications.md): What this customer was actually sent, newest first. The preferences screen says what should arrive; this says what did. A status other than `sent` carries a `reason`, and the ones an installation produces on purpose — `quiet_hours`, `frequency_cap`, `no_consent` — tell the customer their own setting… - [Get v1accountpreferences](https://omniflow-vpn.mintlify.app/api-reference/account/get-v1accountpreferences.md): Notification choices, marketing consent, contact-channel flags, and any suppression. A contact's address is never returned — only whether it is verified and what it is used for. - [Patch v1accountpreferences](https://omniflow-vpn.mintlify.app/api-reference/account/patch-v1accountpreferences.md): Each field is optional and an absent field is left unchanged, so a screen that renders one toggle never has to send back a document it did not read. A marketing decision appends a consent record rather than overwriting one, because the history of who opted in and when is the thing that has to surviv… - [Post v1accountpreferencesunsubscribe](https://omniflow-vpn.mintlify.app/api-reference/account/post-v1accountpreferencesunsubscribe.md): Stops marketing messages. Transactional messages about a payment, an expiry, or a support reply continue, because they are the ones a customer needs in order to keep using what they bought. ### telemetry - [Post v1telemetryevents](https://omniflow-vpn.mintlify.app/api-reference/telemetry/post-v1telemetryevents.md) ## Operator panel - [Admin panel](https://omniflow-vpn.mintlify.app/operations/admin-panel.md): First-owner setup, operator roles, sessions, and the audit trail. - [Admin operations](https://omniflow-vpn.mintlify.app/operations/admin-operations.md): The day-to-day operator surfaces — dashboard, customers, catalogue, finance, jobs, and settings. - [Sales and payment reporting](https://omniflow-vpn.mintlify.app/operations/sales-reporting.md): What was sold over a period you choose, whether the money arrived, and the rules that decide which figures are ever added together - [Advertising measurement](https://omniflow-vpn.mintlify.app/operations/advertising-measurement.md): Counters, site verification, and the click identifier that connects an advertisement to the order it produced — your own analytics, off by default, never reported to the project - [Traffic reporting](https://omniflow-vpn.mintlify.app/operations/traffic-reporting.md): Which node is filling up and who consumed the most, read live from Remnawave, and why nothing on this screen is stored - [Installation settings](https://omniflow-vpn.mintlify.app/operations/installation-settings.md): What the Settings screen stores, which of it changes behaviour today, and where each running value actually comes from - [White-label appearance](https://omniflow-vpn.mintlify.app/operations/branding.md): The palette, corners, spacing, modes, and images an installation renders under, what refuses a save, and why the token list is short - [Connection catalogue](https://omniflow-vpn.mintlify.app/operations/connection-catalogue.md): The platforms and client applications an installation recommends, edited from the panel, read by the bot and the browser from the same rows - [Information pages](https://omniflow-vpn.mintlify.app/operations/information-pages.md): Publishing the FAQ, terms, offer, and privacy policy at addresses of your own, and why the body is never HTML - [Message wording](https://omniflow-vpn.mintlify.app/operations/message-wording.md): Rewording the transactional notices the installation sends on its own, with a variable reference, a preview, and a test send that never reaches a customer - [Support desk](https://omniflow-vpn.mintlify.app/operations/support-desk.md): Work a ticket queue, keep internal notes away from customers, and read a response-time report whose definitions travel with the numbers - [Mandatory channel subscription](https://omniflow-vpn.mintlify.app/operations/required-channels.md): Requiring Telegram channel membership, and the rules that stop it punishing people unfairly. - [Risk controls](https://omniflow-vpn.mintlify.app/operations/risk-controls.md): External blocklists and anomaly detection — evidence for an operator, never an automatic sanction. - [Operator notifications](https://omniflow-vpn.mintlify.app/operations/operator-notifications.md): The Telegram group, the topics the bot creates itself, what those messages deliberately omit, and the caps that keep them readable ## AI and MCP - [AI privacy and the gateway](https://omniflow-vpn.mintlify.app/operations/ai-privacy.md): What leaves the installation, what stops it leaving, and why AI is off until you turn it on. - [AI providers and local models](https://omniflow-vpn.mintlify.app/operations/ai-providers.md): Registering a model provider, pointing one at a local model, routing each feature to its own model and budget, and reading the state the screen shows you - [AI governance and evaluation](https://omniflow-vpn.mintlify.app/operations/ai-governance.md): Turning features on one at a time, bounding what they can spend, keeping what you choose to keep, and knowing when the answers get worse. - [MCP connections](https://omniflow-vpn.mintlify.app/operations/mcp.md): Connecting Omniflow to Model Context Protocol servers, and exposing Omniflow as one, without either becoming a way around your permissions. ## Running Omniflow - [Deployment](https://omniflow-vpn.mintlify.app/operations/deployment.md): The compose topology service by service, what has to persist, how migrations are applied, and how to upgrade and roll back - [Compatibility and upgrades](https://omniflow-vpn.mintlify.app/operations/compatibility.md): The supported version matrix, how release artifacts are versioned and signed, the upgrade window, and the deprecation policy - [Reverse proxy](https://omniflow-vpn.mintlify.app/operations/reverse-proxy.md): Use Caddy, Traefik, Nginx, or another compatible ingress. - [Jobs and queues](https://omniflow-vpn.mintlify.app/operations/jobs-and-queues.md): The durable queue and the state changes that enqueue it, the job kinds, the dead-letter view, the periodic passes and the loops that stay in their process, the retention sweeps, and the outbox - [Health, metrics, and logs](https://omniflow-vpn.mintlify.app/operations/observability.md): The probes each process exposes, the metrics that are actually written, how logging and redaction work, and the real state of tracing - [Maintenance mode](https://omniflow-vpn.mintlify.app/operations/maintenance-mode.md): Pausing purchases and provisioning without disturbing money already taken, from the panel or the bearer API, and what automatic detection actually watches - [Backup, restore, and disaster recovery](https://omniflow-vpn.mintlify.app/operations/backup-restore.md): Encrypted PostgreSQL backups, verified restores, key handling, and the upgrade and rollback procedure - [Troubleshooting](https://omniflow-vpn.mintlify.app/operations/troubleshooting.md): Real symptoms, what actually causes them, and where to look in this installation - [Anonymous telemetry](https://omniflow-vpn.mintlify.app/operations/telemetry.md): The exact event Omniflow sends, why it exists, how to verify it, and how to switch it off - [Privacy](https://omniflow-vpn.mintlify.app/operations/privacy.md): What Omniflow holds, why, for how long, and how to verify what actually leaves the installation - [Reliability and operations](https://omniflow-vpn.mintlify.app/operations/reliability.md): Service-level indicators, dashboards and alerts, what's bounded and reconciled, graceful degradation, and capacity guidance for a single-server installation - [Security](https://omniflow-vpn.mintlify.app/operations/security.md): The whole security posture: the three credential surfaces, password, TOTP and passkey handling, session storage and rotation, CSRF, secret sealing, trusted proxies, headers, and what is never logged ## Contributing - [Development](https://omniflow-vpn.mintlify.app/contributing/development.md): Set up the workspace, run the generators, start the stack locally, and pass every check CI runs - [Database changes](https://omniflow-vpn.mintlify.app/contributing/database.md): The schema-first Atlas and sqlc workflow, how a migration is reviewed and applied, and which tables may never be rewritten - [Testing strategy](https://omniflow-vpn.mintlify.app/contributing/testing.md): What runs by default, what needs Docker, and what every financial change owes. - [Documentation](https://omniflow-vpn.mintlify.app/contributing/documentation.md): How the Mintlify source is organised, which components to use, how to validate it locally, and the rule that keeps the Russian tree in step - [Release process](https://omniflow-vpn.mintlify.app/contributing/releases.md): How a merged commit becomes a version, what the release workflow gates and publishes, and what a maintainer and an operator each check afterwards ## OpenAPI Specs - [openapi](https://raw.githubusercontent.com/kewldan/omniflow/main/api/openapi.yaml) > The links below point to documentation indexes. Follow each `/_llms/` index recursively until you reach documentation pages. ## Indexes - [Russian (387 pages)](https://omniflow-vpn.mintlify.app/_llms/ru.txt): Documentation for Russian. This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.