Skip to content

Subscription format

Description of subscription formats, supported protocols, and HTTP headers.

Supported protocols

Protocol Scheme Description
VLESS vless:// Primary protocol
VMess vmess:// JSON-based configuration in base64
Trojan trojan:// Password authentication
Shadowsocks ss:// SIP002 and modern format
Hysteria2 hysteria2://, hy2:// Multi-port support
SOCKS5 socks://, socks5:// Proxying via SOCKS5
HTTP proxy http://user:pass@host:port HTTP proxy (must contain @ — otherwise the line is treated as a subscription URL)
WireGuard wireguard://, wg:// WireGuard tunneling
AmneziaWG amneziawg://, awg://, .conf in the body Obfuscated WireGuard. A single .conf or several servers in one subscription — see the "AmneziaWG / WireGuard .conf in the body" section below

The schemes ssr://, tuic://, hysteria:// are recognized by the app but not parsed — servers with these schemes will be skipped.

Subscription body formats

The most common format. The response body is base64, and when decoded contains links, one per line:

base64(
  vless://uuid@server1:443?security=tls#Server1
  vless://uuid@server2:443?security=tls#Server2
)

URL-safe Base64 is supported (- → +, _ → /).

Links in plain form, one per line:

vless://uuid@server1:443?security=tls#Server1
vmess://eyJhZGQiOiJzZXJ2ZXIyIn0=
trojan://password@server3:443#Server3
socks://user:pass@server4:1080#Server4
wireguard://secretKey@server5:51820?publickey=KEY&address=10.0.0.2#Server5
amneziawg://<base64url-conf>#Server6

3. JSON formats

Array of full xray configs:

[
    { "outbounds": [...], "routing": {...} },
    { "outbounds": [...], "routing": {...} }
]

Full xray config (single object with inbounds and outbounds):

{
    "inbounds": [...],
    "outbounds": [...],
    "routing": {...},
    "dns": {...}
}

Read more: full-xray-config.md.

4. Mixed format

Server links + routing strings + metadata in a single body:

vless://uuid@server1:443?security=tls#Server1
vless://uuid@server2:443?security=tls#Server2
://autorouting/onadd/https://example.com/routing.json
#announce: Scheduled maintenance tomorrow

Supported special strings in the body:

Pattern Description
://autorouting/onadd/{url} Auto-updated routing profile (URL, with sourceURL)
://autorouting/onadd/{base64} Inline routing profile (base64)
://autorouting/add/{url} Auto-updated routing profile (URL, with sourceURL)
://routing/onadd/{url} One-time profile import by URL (no auto-update)
://routing/onadd/{base64} Static routing profile
://routing/add/{base64} Static routing profile
://onadd/{url or base64} Short form (no auto-update)
://routing/{base64} Short form
#announce: text Announcement (supports base64:...)
#profile-title: text Subscription name (supports base64:...)
#support-url: URL Support link
#profile-web-page-url: URL Provider website link
#announce-url: URL Announcement link
#profile-update-interval: number Update interval (hours)

Special strings are extracted from the body and do not appear in the server list.

Priority: values from HTTP headers take precedence over values from the body. Inline metadata in the body is used as a fallback when the corresponding header is absent.

5. AmneziaWG / WireGuard .conf in the body

The subscription body can be a raw WireGuard or AmneziaWG .conf file (a multi-line INI with [Interface] and [Peer] sections). The app recognizes it by the presence of [Interface] + PrivateKey and routes it to the same parser used for file import / paste. AmneziaWG is detected by its obfuscation parameters (Jc, Jmin, Jmax, S1–S4, H1–H4, I1–I5).

The new AmneziaWG 3.0 and 3.1 parameters are also supported (the engine is updated to amneziawg-go v3.1). All are optional and forwarded to the engine as-is:

.conf key Version Description
HeaderProtectionKey 3.0 Packet-header protection key (set server-side; requires S1–S4 ≥ 12). Accepted as base64 (what awg genkey emits) or as hex
ContentPaddingAddition 3.0 Random content-padding range (uint32 or a min-max range)
RekeyAfterTime 3.0 Time (seconds) after which the client initiates a rehandshake
RekeyTimeout 3.0 Timeout (seconds) before the handshake is repeated
RejectAfterTime 3.0 Time (seconds) after which the client forces a rehandshake and rejects incoming data
KeepaliveTimeout 3.0 Time (seconds) since the last data send before a keepalive is sent
MaxHandshakeAttempts 3.0 Maximum number of handshake retries
RandomTrailers 3.1 Random packet trailers. Off by default
DisableCookies 3.1 Disables the WireGuard cookie mechanism. Off by default
[Interface]
PrivateKey = <base64>
Address = 10.8.0.2/32
DNS = 1.1.1.1
Jc = 4
Jmin = 40
Jmax = 70
S1 = 86
S2 = 574
H1 = 1234567890

[Peer]
PublicKey = <base64>
PresharedKey = <base64>
AllowedIPs = 0.0.0.0/0
Endpoint = server.example.com:51820

The [Device] section. AmneziaWG 3.x moves obfuscation and tuning into a separate [Device] section. It is supported alongside [Interface] — both describe the same device, so any key from the table above may go in either one:

[Device]
Jc = 4
S1 = 12
S2 = 12
HeaderProtectionKey = <hex>
RandomTrailers = true

[Interface]
PrivateKey = <base64>
Address = 10.8.0.2/32

[Peer]
PublicKey = <base64>
AllowedIPs = 0.0.0.0/0
Endpoint = server.example.com:51820

[Interface] stays mandatory — it carries PrivateKey. A config with only [Device] is rejected.

Value forms. The client normalises what server .conf files actually contain:

Type Accepted Sent to the engine
Boolean (RandomTrailers, DisableCookies) on/off, yes/no, true/false, 1/0, enabled/disabled true / false
Range (PersistentKeepalive, ContentPaddingAddition, timings, H1–H4) 25 or 25-35 (upper bound ≥ lower) as-is
32-byte key (PrivateKey, PublicKey, PresharedKey, HeaderProtectionKey) base64 or hex hex

Since AmneziaWG 3.x, PersistentKeepalive may be a range — the keepalive interval is picked at random within it.

The body may be plain text or base64-wrapped. The same payload can be delivered via the deep link incy://import/{base64-conf} — see deep-links.md.

A raw .conf in the body is parsed as a single server entry; the AmneziaWG obfuscation is applied by the engine at connect time (the client stores the .conf verbatim).

Multiple AmneziaWG servers in one subscription

iOS and Android only. The Desktop client does not support AmneziaWG — a .conf in the body is parsed there as plain WireGuard, and the amneziawg:///awg:// schemes and the JSON container are ignored.

A single .conf = one server. To deliver several AmneziaWG locations in one subscription, use one of two formats. In both, each .conf is encoded as url-safe base64 (-→+, _→/, padding optional), and the server name comes from the # fragment or the name field.

Format 1 — line-per-server (amneziawg:// / awg://). One link per line; can be mixed with vless:// and other protocols in the same body:

amneziawg://<base64url-conf>#Germany
awg://<base64url-conf>#Netherlands

amneziawg:// is canonical, awg:// is a short alias (equivalent). Everything after # is the display name.

Format 2 — JSON container. The body is a JSON object with type: "amneziawg":

{
  "type": "amneziawg",
  "version": 1,
  "servers": [
    { "name": "Germany",     "config": "<base64url-conf>" },
    { "name": "Netherlands", "config": "<base64url-conf>" }
  ]
}

Each servers[] entry → a separate server. name is optional (when absent, the name is taken from the # Name= comment inside the .conf or from the Endpoint host). The version field is reserved and is not currently validated.

The same container can also be an element of a JSON array, next to full xray configs. That lets one subscription ship both VLESS locations and AmneziaWG:

[
  { "remarks": "🇵🇱 Poland | TCP", "inbounds": [ /* ... */ ], "outbounds": [ /* ... */ ] },
  { "remarks": "🇩🇪 Germany | XHTTP", "inbounds": [ /* ... */ ], "outbounds": [ /* ... */ ] },
  {
    "type": "amneziawg",
    "version": 1,
    "servers": [
      { "name": "Germany",     "config": "<base64url-conf>" },
      { "name": "Netherlands", "config": "<base64url-conf>" }
    ]
  }
]

Order is preserved: the AmneziaWG servers appear in the list where the container sits. An array may contain more than one container.

Behavior in both formats: a malformed base64 entry is skipped — the other servers still load (one bad location does not break the whole subscription). Duplicates with an identical .conf collapse into a single server. On a subscription refresh, servers are added/updated/removed without duplication. HTTP headers stay at the subscription level (shared by all servers).


Server visibility by network type (Premium)

Premium subscriptions can show different servers on Wi-Fi and mobile networks. This feature is implemented in iOS, Android and Desktop (incy-linux), on both the home screen and the server list.

Include one of these exact markers in the server name:

Marker in the name Wi-Fi / Ethernet Mobile network
only Wi-Fi Visible Hidden
only Mobile Hidden Visible
No marker Visible Visible

For example: Germany only Wi-Fi and Germany only Mobile. The marker can appear anywhere in the name and remains visible to the user.

Requirements and limitations:

  • The subscription must have active Premium. Without it, these markers do not hide servers.
  • Matching is case-sensitive; the space and hyphen matter. WiFi only, WIFI only, LTE only and only LTE are not recognized. Use exactly only Wi-Fi or only Mobile.
  • only Mobile covers cellular networks, including LTE/4G and 5G, rather than LTE alone.
  • Ethernet is treated like Wi-Fi. If the network type is unknown, nothing is hidden by this filter.
  • Desktop detects the network type from network interfaces, checking roughly every 10 seconds. Unusual interface names can make detection inaccurate. Connecting to a phone's hotspot over Wi-Fi counts as Wi-Fi.
  • Use only one marker per server. This is a display filter; the markers do not configure routing rules or guarantee automatic reconnection when the network changes.

The name goes after #. Encode spaces as %20; the filter checks the decoded name:

vless://00000000-0000-4000-8000-000000000001@wifi.example.com:443?encryption=none&security=tls&type=tcp#Germany%20only%20Wi-Fi
vless://00000000-0000-4000-8000-000000000002@mobile.example.com:443?encryption=none&security=tls&type=tcp#Germany%20only%20Mobile

Replace the UUIDs and hosts with your server parameters. For a base64 subscription, add the markers to the link names first, then encode the subscription body as usual.

JSON subscription example

For a full Xray config, set the name using the top-level remarks field:

{
  "remarks": "Germany only Mobile"
}

This is a metadata fragment, not a complete configuration: add remarks to your existing object containing inbounds and outbounds. In a config array, set the marker separately for each object. It applies to the entire displayed entry, not individual outbounds within a balancer.


HTTP headers

Subscription headers

Header Type Description
profile-title string Subscription name (up to 25 characters). Supports base64
subscription-name string Alternative to profile-title (fallback)
profile-description string Subscription description. Supports base64
profile-update-interval int Update interval in hours
subscription-userinfo string Traffic statistics and expiration
support-url URL Support link
support-email email Support email — shows an "Email" button in the subscription card. Without the header the button is not shown
profile-web-page-url URL Provider website link. Alternative: homepage
homepage URL Fallback for profile-web-page-url
announce-url URL Announcement link
announce string Announcement text (up to 200 characters). Supports base64
autorouting string Routing profile with auto-update. The full link, including the scheme: incy://autorouting/onadd/https://… (a bare URL does not work)
routing string Routing profile (base64 or full link)
sort-order string Server sort order: ping, name, none
content-disposition string Fallback for the subscription name (.txt, .yaml extensions are stripped)
premium-url URL Link for the "Premium" button in the subscription card
hide-url 1/0/true/false Hide the subscription URL from Share/Copy/QR/backup
banner-text string Banner text (base64). Overrides the panel
banner-button-text string Banner button text
banner-button-url URL Banner button link
banner-bg-color hex Banner background color (#RRGGBB)
banner-button-color hex Banner button color (#RRGGBB)
fragmentation-enable 1/0 TCP fragmentation
fragmentation-length min-max Fragment length range
fragmentation-interval min-max Delay range between fragments
fragmentation-packets tlshello / 1-3 / all Which packets to apply to
noises-enable 1/0 Send noise packets before the handshake
noises-type rand / str / hex Noise content type
noises-packet string Noise payload (format depends on type)
noises-delay min-max ms Delay range between noises
server-address-resolve-enable 1/0 Pre-resolve the server address via DoH
server-address-resolve-dns-domain URL DoH server URL
server-address-resolve-dns-ip IP DoH server IP (bootstrap)
no-limit-enabled 1/0 (iOS) Memory-saving Network Extension mode (keeps the background process under the iOS 50 MB cap). Only enables
per-app-proxy-enable 1/0 (Android only) Enable per-app mode
per-app-proxy-mode bypass / proxy (Android only) Per-app mode
per-app-proxy-list CSV / URL (Android only) List of package names

This is a reference of subscription headers. A full description of each (value formats, display conditions, "header → panel" priority, #-fragment support in the body) is in App management, where the summary table is the authoritative source.

Profile Title

Supports two formats:

Plain text:

profile-title: My VPN

Base64 with description:

profile-title: base64:TWVNdiBWUE4KV2VsY29tZSB0byBvdXIgc2VydmljZQ==

When base64-decoded: the first line is the name, the rest is the description.

Subscription User Info

subscription-userinfo: upload=0;download=1073741824;total=10737418240;expire=1735689600
Field Type Description
upload int Outbound traffic (bytes)
download int Inbound traffic (bytes)
total int Traffic limit (bytes)
expire int Expiration date (Unix timestamp, seconds)

If expire > 32000000000 — the value is interpreted as milliseconds and converted to seconds.

Hiding the traffic block:

If the server returns subscription-userinfo: 0, the traffic block on the home screen is hidden entirely. Use this when traffic statistics are not provided.

Announce

The announcement text is shown on the home screen as a banner. Up to 5 lines of text are supported, after which the text is truncated with an ellipsis.

announce: Server update on March 15
announce: base64:0J7QsdC90L7QstC70LXQvdC40LUg0YHQtdGA0LLQtdGA0L7Qsg==

Sort Order

Sets the server sort order. On iOS and Android the value applies to this subscription (each subscription has its own ping/sort/style settings, like the routing profile); the user can override it in the subscription settings. On Desktop it applies to the global sort setting.

sort-order: ping
Value Description
none Default order (as in the subscription)
ping By ping (fastest first)
name Alphabetical

Request headers (client → server)

When refreshing a subscription, the app sends:

Header Description
User-Agent INCY/<version>/<platform>
Accept */*
Accept-Language Device language tag (e.g. ru-RU)
Accept-Encoding iOS only: gzip, deflate, br
x-app-version App version
x-device-locale Device language
x-client INCY

When HWID sending is enabled, additionally:

Header Description
x-hwid Hardware identifier (read more)
X-Device-ID Alias for x-hwid on Android (some server stacks expect exactly this header)
x-device-os Platform (iOS, Android, Linux, Windows)
x-ver-os OS version
x-device-model Device model

All HTTP headers are case-insensitive. The server can look at x-hwid or X-HWID — the same bytes will arrive.


Fallback hosts

If the primary subscription host is unavailable (network/timeout/5xx/429), the client cycles through fallback hosts — the same path and token, only the host changes. On 404/410 (the provider deleted the subscription) no cycling happens. The fallback host list does not arrive via a header — it comes in the premium-config body (settings.fallbackHosts) — see premium-api.md.