Links and deep links
mins understands four kinds of links: server links (one server each), subscription add links, routing links, and https:// redirect links for places that only allow HTTPS (Telegram buttons). Whatever the source, a link never changes anything silently: the app always shows a preview and waits for the user to confirm.
Server links
Section titled “Server links”| Scheme | Protocol | Notes |
|---|---|---|
vless:// |
VLESS | TLS, REALITY or none; raw, WebSocket, gRPC, XHTTP, HTTPUpgrade, mKCP. |
hysteria2://, hy2:// |
Hysteria2 | Salamander obfuscation, port hopping, certificate pinning. |
wireguard:// |
WireGuard and AmneziaWG | AmneziaWG parameters are plain query parameters. |
vpn:// |
AmneziaWG | Amnezia’s exported container. |
any other (vmess://, trojan://, ss://, …) |
not supported | The link stays in the list as an unsupported server with the reason “Protocol X is not supported”. |
Common rules for all server links:
- Name is the fragment after
#, percent-encoded. A flag emoji at its start sets the country (see The mins block). - Description goes inside the fragment:
#Finland?serverDescription=<base64 of UTF-8 text>. If the base64 contains+,/or=, percent-encode them or use the URL-safe alphabet. - Credentials (the part before
@) must be percent-encoded. A second@in them is an error. - IPv6 hosts are written in brackets. Ports are 1 to 65535.
- Query parameter names are case-sensitive. An unknown parameter is ignored with a warning; a repeated or empty parameter name is an error.
vless://<uuid>@<host>:<port>?<params>#<name>| Parameter | Value | Notes |
|---|---|---|
type |
raw (alias tcp), ws, grpc, xhttp, httpupgrade, kcp (alias mkcp) |
Default raw. http, h2, h3 and quic are rejected with “Transport removed; use XHTTP”. |
security |
none, tls, reality |
Default none. REALITY does not work with ws, httpupgrade or kcp. |
encryption |
string | Default none. Must not be empty. |
flow |
string | For example xtls-rprx-vision. |
sni |
host name | Default: the link’s host. |
fp |
fingerprint | Default chrome. Required for REALITY. |
alpn |
comma-separated list | For example h2,http/1.1. |
ech |
string | ECH config list. |
pcs |
string | Pinned peer certificate SHA-256. |
vcn |
string | Verify the peer certificate by this name. |
pbk |
string | REALITY public key. Required for REALITY. |
sid |
string | REALITY short id. |
pqv |
string | REALITY ML-DSA-65 verification key. |
spx |
string | REALITY spider X. |
path |
string | ws, httpupgrade, xhttp; default /. For raw with headerType=http, the request path. |
host |
string | ws, httpupgrade, xhttp; for raw with headerType=http, the Host header. |
mode |
gRPC: gun (default), multi. XHTTP: auto (default), packet-up, stream-up, stream-one |
gRPC guna is parsed but the server is marked unsupported (“gRPC guna uses a custom codec not supported by Xray; use gun or multi”). |
serviceName, authority |
string | gRPC. |
extra |
JSON object | XHTTP extra settings. |
mtu, tti |
integer | mKCP. |
seed |
string | mKCP. |
headerType |
string | mKCP header type, or http for raw. |
fm |
JSON | finalmask settings. |
mux |
JSON | Mux settings of this outbound. |
fragment |
length,interval,packets[,maxSplit] |
TLS fragmentation of this server, for example 50-100,10-20,tlshello. |
noises |
type,packet,delay[,applyTo] joined by ; |
Noise packets of this server. |
allowInsecure, insecure |
true, false, 1, 0 |
Disables certificate verification. Sending both is an error. The server shows a warning tag. |
testpre, testseed |
JSON or string | Passed to the user record unchanged. |
node_id, xtls |
any | Accepted and ignored. |
sni, fp, alpn and path must not be present with an empty value.
vless://11111111-1111-4111-8111-111111111111@fi.example.com:443?type=raw&security=reality&sni=www.example.com&fp=chrome&pbk=AbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-abcde&sid=6ba85179e30d4fc2&flow=xtls-rprx-vision#Finland%20Realityvless://11111111-1111-4111-8111-111111111111@xh.example.com:443?type=xhttp&security=tls&sni=xh.example.com&host=xh.example.com&path=%2Fx&mode=auto&alpn=h2#XHTTPvless://11111111-1111-4111-8111-111111111111@fi.example.com:443?type=grpc&security=tls&serviceName=svc&mode=multi#gRPCHysteria2
Section titled “Hysteria2”hysteria2://<auth>@<host>[:<port>]/?<params>#<name>hy2:// is the same. The port defaults to 443. The authentication string may be empty.
| Parameter | Value | Notes |
|---|---|---|
sni |
host name | TLS server name. |
pinSHA256 |
string | One pinned certificate fingerprint. |
ech |
string | ECH config list. |
insecure, allowInsecure |
true, false, 1, 0 |
Disables certificate verification. Sending both is an error. |
obfs |
salamander |
The only supported obfuscation; any other value is an error. |
obfs-password |
string | Required with obfs. |
mport |
ports | Port hopping range or list, such as 20000-30000 or 443,500-600. A reversed range is an error. |
mportHopInt |
seconds | Hop interval: 30, 30s or a range 20-40. At least 5; default 30. Used when mport hops. |
hysteria2://s3cret@de.example.com:443/?sni=de.example.com&obfs=salamander&obfs-password=pass#Germanyhy2://s3cret@de.example.com:443/?sni=de.example.com&mport=20000-30000&mportHopInt=30#Germany%20hopLinks with the hysteria2+realm scheme are rejected: “Hysteria realm is not supported”.
WireGuard and AmneziaWG
Section titled “WireGuard and AmneziaWG”wireguard://<private key>@<host>:<port>?publickey=<key>&address=<cidr>[&...]#<name>Percent-encode the private key: base64 contains +, / and =.
| Parameter | Value | Notes |
|---|---|---|
publickey |
base64 key | Required. |
presharedkey |
base64 key | |
address |
comma-separated CIDRs | The tunnel addresses, for example 10.0.0.2/32. |
dns |
comma-separated IPs | |
mtu |
integer | |
allowedips |
comma-separated CIDRs | Default 0.0.0.0/0,::/0. |
keepalive |
integer or range | Persistent keepalive, seconds. |
reserved |
three bytes | 1,2,3 or base64. |
jc, jmin, jmax, s1 to s4, h1 to h4, i1 to i5 |
numbers, ranges (h1..h4), strings (i1..i5) |
AmneziaWG obfuscation. jmin must not exceed jmax; h1 to h4 ranges must not overlap. |
headerprotectionkey, contentpaddingaddition, randomtrailers, disablecookies |
AmneziaWG extensions (randomtrailers, disablecookies are booleans). headerprotectionkey requires s1 to s4 of at least 12. |
|
rekeyaftertime, rekeytimeout, rejectaftertime, keepalivetimeout, maxhandshakeattempts, persistentkeepalive |
numbers | AmneziaWG timers. |
wireguard://AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE%3D@nl.example.com:51820?publickey=AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI%3D&address=10.0.0.2%2F32&dns=1.1.1.1&mtu=1280&jc=4&jmin=40&jmax=70&s1=15&s2=18#NetherlandsA subscription URL (or pasted text, or a file) may also be a single WireGuard or AmneziaWG INI file. It must start with [Interface] (with a required PrivateKey) followed by one or more [Peer] sections (each with Endpoint, up to 256 peers). Keys are case-insensitive; comments start with # or ;.
[Interface]PrivateKey = AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=Address = 10.0.0.2/32DNS = 1.1.1.1MTU = 1280Jc = 4Jmin = 40Jmax = 70
[Peer]PublicKey = AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=Endpoint = nl.example.com:51820AllowedIPs = 0.0.0.0/0, ::/0PersistentKeepalive = 25vpn:// (Amnezia)
Section titled “vpn:// (Amnezia)”vpn://<base64> is the link that the Amnezia client exports. mins reads the AmneziaWG container inside it (a zlib-compressed JSON with a 4-byte length prefix, up to 16 MiB), takes the nested last_config, replaces $PRIMARY_DNS and $SECONDARY_DNS with dns1 and dns2 and uses description as the server name. A vpn:// link without an AmneziaWG container is rejected.
Subscription add link
Section titled “Subscription add link”A link that opens the normal “add subscription” flow for a URL. Three forms are accepted:
mins://add/https://sub.example.com/abc?token=1happ://add/https://sub.example.com/abc?token=1incy://add/https://sub.example.com/abc?token=1<url>is taken as written if it starts withhttp://orhttps://. Otherwise it is percent-decoded once, somins://add/https%3A%2F%2Fsub.example.com%2Fabcworks too.- Only
mins://is registered with the operating system (Android, iOS, macOS, Windows and Linux), so onlymins://opens the app from a browser or another app.happ://add/andincy://add/are accepted when the user pastes them, scans them as a QR code or opens them from a file. - The link starts the same flow as pasting the URL: HTTPS is required, plain HTTP only after the user confirms, then the subscription is fetched and shown in a preview, including any settings that need approval. The user taps Confirm import. There is never a silent import.
- For QR codes, encode the
mins://add/...link as the QR text.
Other mins:// addresses: mins://open only brings the app to the front (the widgets use it), and the routing links below.
Routing links
Section titled “Routing links”| Link | Effect |
|---|---|
mins://routing/add/<payload> |
Saves a routing profile. |
mins://routing/onadd/<payload> |
Saves it and makes it the active profile. |
mins://routing/off |
Turns the active routing profile off. |
happ://routing/... and incy://routing/... with add or onadd are accepted the same way, and incy://autorouting/add|onadd/... too. They work from the clipboard, a QR code, pasted text, a file, the routing header or a line in a subscription body; only mins:// is registered with the system.
<payload> is one of:
- base64 of the routing profile JSON (any base64 alphabet, padded or not). To build one:
base64 -w0 profile.json | tr '+/' '-_' | tr -d '='. - an
https://URL, percent-encoded. The app downloads the profile (no redirects; 9 s to connect, 20 s to receive) and shows it in the preview. Profiles added from a URL are downloaded again every time the app starts, keeping their name. Any other scheme, includinghttp://, is rejected with “Routing profile URL must use HTTPS”.
mins://routing/onadd/https%3A%2F%2Fexample.com%2Frouting%2Fru.jsonmins://routing/add/eyJOYW1lIjoiRXhhbXBsZSIsIkdsb2JhbFByb3h5IjpmYWxzZX0mins://routing/offThe profile format is in Routing profiles.
Telegram buttons and other HTTPS-only places
Section titled “Telegram buttons and other HTTPS-only places”Telegram’s inline buttons accept only https:// URLs, not custom schemes, so a button cannot point to mins://add/... directly. Use the redirector at https://sr.minsclient.com: it is a static page that immediately opens mins with the link you give it.
https://sr.minsclient.com/#add/https://sub.example.com/abcThe link is read from the fragment (the part after #). Browsers never send the fragment to the server, so the subscription URL, which usually contains the user’s token, stays on the device.
| Input | Meaning |
|---|---|
https://sr.minsclient.com/#add/<url> |
Add subscription <url>. |
https://sr.minsclient.com/#mins://add/<url> |
Same, with the scheme written out. |
https://sr.minsclient.com/#routing/add/<payload>, #routing/onadd/<payload>, #routing/off (also with a leading mins://) |
Routing links. |
https://sr.minsclient.com/?url=<percent-encoded mins link> |
Fallback for senders that drop fragments, for example ?url=mins%3A%2F%2Fadd%2Fhttps%3A%2F%2Fsub.example.com%2Fabc. |
Only these actions are accepted, and an add target must be https://. Anything else (other schemes, javascript:, http:// targets, links longer than 8 KiB) is rejected: the page shows “This is not a mins link” and does not navigate.
What happens on a valid link: the page redirects at once, passing the subscription address in percent-encoded form, so characters such as #, ; and & inside it survive. On Android it opens an intent:// URL for the mins app and, if mins is not installed, falls back to the download page at https://minsclient.com/#download; on other systems it opens mins://.... Some browsers block the automatic jump (Telegram’s built-in browser, iOS), so the page also shows a large Open in mins button and a No mins? Download link. The page is shown in Russian or English by the browser’s language and loads no external resources.
A Telegram Bot API message with an inline button (reply_markup):
{ "chat_id": 123456789, "text": "Your subscription is ready.", "reply_markup": { "inline_keyboard": [ [ { "text": "Add to mins", "url": "https://sr.minsclient.com/#add/https://sub.example.com/abc" } ] ] }}Send it to https://api.telegram.org/bot<token>/sendMessage with Content-Type: application/json.
Not supported
Section titled “Not supported”| What | Why |
|---|---|
Encrypted happ://crypt... and incy://crypt... links |
Rejected: “Encrypted Happ/INCY links are not supported”. Send a normal link or a subscription URL. |
Server links of other protocols (vmess://, trojan://, ss://, tuic://, …) |
Not implemented by the engine. The link is kept as an unsupported server with the reason “Protocol X is not supported”. |
hysteria2+realm:// |
“Hysteria realm is not supported”. |
VLESS transports http, h2, h3, quic |
Removed in Xray; use XHTTP. |
| Clash and sing-box formats | Only Xray configuration imports are supported. |
| Importing with no preview | Every link, QR code and deep link ends in a confirmation screen. |
Custom schemes other than mins:// as system links |
Only mins:// is registered, so as not to clash with Happ or INCY installed on the same device. |
Links inside subscription headers
Section titled “Links inside subscription headers”Link-type headers (support-url, announce-url and so on) accept https://, and for six of them also mins://; a mins:// link there opens the import preview, never imports directly. See Response headers.