Skip to content

Subscription format

A subscription is an HTTPS URL that returns the server list in the body and the subscription settings in the response headers. This page covers the transport and the body. The headers are in Response headers.

Aspect Behavior
Method GET. The request headers are listed in Request headers.
Scheme HTTPS. A plain http:// URL is fetched only after the user confirms the Unencrypted subscription prompt for that subscription; without confirmation the client stops with “HTTPS required; confirm unencrypted HTTP explicitly”.
Redirects 301, 302, 303, 307 and 308 are followed, at most 5 times, and every hop must satisfy the same scheme rule. A redirect without a Location header, or a sixth redirect, is an error.
Status After redirects, any status of 300 or above is an error (“Subscription HTTP 403”). The body of an error response is not parsed.
Timeout 9 seconds by default, applied to connecting, to receiving and to the request as a whole. The provider can set 5 to 15 seconds with subscription-request-timeout.
Fallback URL If the request fails for any reason (timeout, error status, redirect problem), the client retries once with the fallback-url saved from an earlier successful response. The first import has no fallback yet.
Size The response text is limited to 16 MiB. A larger body is rejected as “Import exceeds 16 MiB”.
Encoding UTF-8. A leading byte order mark is ignored.
Content-Type Not checked.
Result On success the server list of the subscription is replaced by the new one. An answer without servers is an error (“Subscription contains no servers; previous servers kept”) and the old list stays. On any error the old servers stay and the message is shown on the subscription card.
Schedule Automatic updates run every profile-update-interval hours (6 hours when the provider sends none), and can be turned off per subscription.

mins looks at the body in this order and takes the first format that matches. Metadata lines at the top are read first and removed (see Metadata lines).

# The body is Result
1 A single line that is a routing link (mins://routing/..., happ://routing/..., incy://routing/..., incy://autorouting/...) A routing profile, no servers
2 A WireGuard or AmneziaWG INI configuration (first section is [Interface]) One server
3 JSON starting with {, [, // or /* Xray configurations, or a routing profile if the object has Name and no outbounds
4 Text where at least one line contains :// A list of links, one per line
5 Anything else Treated as base64; the decoded text is parsed again with rules 1 to 4

One link per line. Empty lines are skipped. Supported schemes are vless://, hysteria2:// (and hy2://), wireguard:// and vpn://; the formats are in Links and deep links.

  • A link with any other scheme, such as vmess:// or trojan://, is kept in the list as an unsupported server with the reason “Protocol X is not supported”. The rest of the list imports normally.
  • A link with a supported scheme that is malformed is not skipped: one invalid link rejects the whole subscription, and the error names the line (text.lines[N]). Validate your links before serving them. Examples of fatal errors: a transport that was removed (type=h2, http, h3, quic: “Transport removed; use XHTTP”), security= other than none, tls or reality, a missing publickey in a WireGuard link, an encrypted happ://crypt... or incy://crypt... link.
  • A routing link on its own line adds a routing profile to the subscription (see Routing profiles).

The whole list may be base64-encoded. All four alphabets are accepted: standard and URL-safe, with or without padding. Whitespace and line breaks inside the base64 text are ignored. The decoded text must be UTF-8 and is parsed as any other body; text that decodes to itself is rejected as “Input is not a share link or subscription”. Metadata lines can stay outside the encoded text, above it.

The body may be one JSON object or an array of objects. Each object is one server. Comments (//, /* */) and trailing commas are allowed.

  • An object without a top-level protocol is a full Xray configuration (with outbounds, optionally routing, dns and so on). It is used as is; mins changes only its own inbounds, geo references and, when the config has none, DNS. The traffic controls fragmentation-*, noises-*, mux-* and sniffing-enable do not apply to it.
  • An object with a top-level protocol is a single outbound. mins builds the rest of the configuration around it, so all the traffic controls apply.
  • The server name is remarks (or remark), overridden by mins.name; the description is meta.serverDescription, overridden by mins.description. See The mins block.
  • Supported outbound protocols are vless, hysteria (Hysteria2) and wireguard (including AmneziaWG), plus freedom, blackhole and dns as helpers. A server using any other protocol is listed as unsupported (“Unsupported protocols: trojan”); the rest imports.
  • An object with proxies or route (sing-box and Clash shapes) rejects the whole import: “Only Xray configuration imports are supported”.
[
{
"remarks": "Finland 1",
"meta": { "serverDescription": "Streaming" },
"mins": { "flag": "fi", "icon": "house" },
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": { "vnext": [ { "address": "fi.example.com", "port": 443,
"users": [ { "id": "11111111-1111-4111-8111-111111111111", "encryption": "none" } ] } ] },
"streamSettings": { "network": "ws", "security": "tls",
"tlsSettings": { "serverName": "fi.example.com" }, "wsSettings": { "path": "/ws" } }
},
{ "tag": "direct", "protocol": "freedom" }
]
}
]

Subscription settings can travel in the body as #key: value lines instead of, or in addition to, response headers. This is for hosts where you cannot set headers.

#profile-title: Example VPN
#profile-update-interval: 12
#announce: Planned maintenance on Friday
vless://11111111-1111-4111-8111-111111111111@fi.example.com:443?security=tls#Finland
Rule Detail
Where Lines at the very top of the body, in any body format (also above base64 text and above JSON). In a link list, # lines between links are read too.
Syntax #key: value. The key is split from the value at the first colon, or at the first whitespace if there is no colon. Spaces around both are trimmed.
Keys Case-insensitive, same names as the response headers.
Values Same rules as headers, including base64: text. An empty value is ignored.
Priority A response header wins over a body line with the same key, including aliases of the same setting.
Unknown keys A comment such as # note: hello is read as key note; it is ignored and listed under Not applied. Harmless, but avoid it.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
profile-title: base64:RXhhbXBsZSBWUE4=
profile-update-interval: 12
subscription-userinfo: upload=1073741824; download=5368709120; total=107374182400; expire=1798761600
support-url: https://t.me/example_support
profile-web-page-url: https://example.com
announce: base64:UGxhbm5lZCBtYWludGVuYW5jZSBvbiBGcmlkYXksIDAyOjAwLTAzOjAwIFVUQw==
#profile-description: Three locations, no logs
vless://11111111-1111-4111-8111-111111111111@fi.example.com:443?type=ws&security=tls&sni=fi.example.com&path=%2Fws&host=fi.example.com#%F0%9F%87%AB%F0%9F%87%AE%20Finland
hysteria2://s3cret@de.example.com:443/?sni=de.example.com&obfs=salamander&obfs-password=pass#Germany
wireguard://AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE%3D@nl.example.com:51820?publickey=AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI%3D&address=10.0.0.2%2F32&dns=1.1.1.1&mtu=1280#Netherlands

The first server shows a Finland flag: a leading flag emoji in the name sets the country and is removed from the displayed name.

  • Serve over HTTPS. Plain HTTP works only after an explicit confirmation by the user.
  • Return 200 with the list. Do not answer 403 or 429 with a body: the client treats it as an error and keeps the old servers.
  • Send each header once. Repeated headers with the same name are joined with a comma, which breaks most values.
  • Keep profile-title within 25 characters, announce within 200: longer text is cut, not rejected.
  • Test every link. One malformed link rejects the whole subscription.
  • Send subscription-userinfo on every response: mins draws traffic, expiry and the expiry notification from it.