> For the complete documentation index, see [llms.txt](https://incy.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://incy.gitbook.io/docs/docs-en/deep-links.md).

# Deep Links

The app supports deep links for VPN control, configuration import, and routing setup.

## Supported schemes

The app handles links with any registered scheme (`incy://`, etc.). Direct protocol links are also supported.

## VPN control

| Link                          | Description                |
| ----------------------------- | -------------------------- |
| `://connect` or `://open`     | Connect VPN                |
| `://disconnect` or `://close` | Disconnect VPN             |
| `://toggle`                   | Toggle VPN state           |
| `://status`                   | Open the app (show status) |

## Import configurations

| Link                  | Description                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `://import/{data}`    | Automatic detection of data type (subscription URL, server configuration, multiple URLs, raw WireGuard/AmneziaWG `.conf`) |
| `://add/{url}`        | Add a subscription or configuration directly                                                                              |
| `://crypt1/{payload}` | Encrypted (obfuscated) variant `://add/` — see [Encrypted crypt1 links](#шифрованные-ссылки-crypt1) below                 |

`://import/{data}` also accepts **raw `.conf`** WireGuard/AmneziaWG (multi-line INI with `[Interface]`/`[Peer]`). For reliable transfer, wrap `.conf` in base64: `incy://import/{base64-conf}` (plain text is also supported). AmneziaWG is detected by obfuscation parameters (`Jc`, `S1`–`S4`, `H1`–`H4`, `I1`–`I5`). More about `.conf` — [subscription-format.md](/docs/docs-en/subscription-format.md).

### Protocol links

Directly add servers via protocol links:

```
vless://uuid@server:443?security=tls&type=ws&sni=example.com#Server Name
vmess://eyJhZGQiOiJzZXJ2ZXIiLCJwb3J0Ijo0NDN9
trojan://password@server:443?security=tls&sni=example.com#Server Name
ss://method:password@server:8388#Server Name
hysteria2://password@server:443?sni=example.com#Server Name
socks://user:pass@server:1080#Server Name
wireguard://secretKey@server:51820?publickey=KEY&address=10.0.0.2#Server Name
```

## Routing

| Link                         | Description                                                   |
| ---------------------------- | ------------------------------------------------------------- |
| `://routing/add/{base64}`    | Add routing profile                                           |
| `://routing/onadd/{base64}`  | Add and immediately activate the profile                      |
| `://routing/onadd/{url}`     | Download the profile by URL (one-time import, no auto-update) |
| `://autorouting/onadd/{url}` | Download the profile by URL and enable auto-update            |
| `://autorouting/add/{url}`   | Download the profile by URL and enable auto-update            |
| `://onadd/{url}`             | Short form (one-time import, no auto-update)                  |

More: [routing.md](/docs/docs-en/routing.md), [autorouting.md](/docs/docs-en/autorouting.md).

### Query parameter

For compatibility, passing data via query parameter is supported `data` (Android, iOS):

```
://routing/add?data={base64}
://routing/onadd?data={base64}
```

### Data type detection

The type is determined **by the link scheme**:

* `://autorouting/` — auto-update (`sourceURL` is set)
* `://routing/` — one-time import (without `sourceURL`, no auto-update)

If the data after `onadd/` — URL (`http://`/`https://`), the profile is downloaded from this URL. If base64 — decoded directly.

## Examples

### Connect VPN

```
incy://connect
```

### Import subscription

```
incy://import/https://example.com/api/subscription/abc123
```

### Adding a server

```
incy://add/vless://uuid@server:443?security=tls#MyServer
```

### Adding routing from GitHub

```
incy://autorouting/onadd/https://github.com/user/repo/blob/main/profile.json
```

***

## Encrypted crypt1 links

`incy://crypt1/<payload>` — obfuscated (encrypted) variant `://add/<url>`. Inside is the same subscription link as in the regular `://add/`, but the base64url payload outside prevents regexes and scanners from recognizing the VPN URL.

### When to use

| Scenario                                        | Recommendation                                                  |
| ----------------------------------------------- | --------------------------------------------------------------- |
| Sending a link in a Telegram chat / channel     | crypt1 — Telegram moderation does not recognize the VPN pattern |
| Publishing on a website / in FAQ                | crypt1 — reduces the chance of auto-blocking based on content   |
| Internal link sharing in the admin panel        | plain `://add/` — no point in encrypting                        |
| Screenshots / documentation / internal messages | crypt1 — the user won't "leak" the URL even by accident         |

### Wire format

```
incy://crypt1/<base64url(iv(12) || ciphertext || tag(16))>
```

The decrypted payload is compact UTF-8 JSON:

```json
{
  "url": "https://sub.example.com/abc123token",
  "v": 1,
  "n": "MyProvider VPN"
}
```

| Field | Type    | Description                                                                                                  |
| ----- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `url` | string  | Subscription URL (the same as in `://add/{url}`)                                                             |
| `v`   | integer | Payload schema version, currently `1`                                                                        |
| `n`   | string? | Optional provider name — the app will show it in the import confirmation window and prefill the "Name" field |

### Encryption

* **AES-256-GCM**
* Key K1 is hardcoded into iOS / Android / Desktop clients and published as an [NPM package `@incy/link-encoder`](https://www.npmjs.com/package/@incy/link-encoder) — this allows providers to assemble crypt1 links from their bots and websites
* If the key is compromised, a new client version will introduce `crypt2/` with fresh keymat. **Existing `crypt1/` links continue to work indefinitely** — old decoder schemes are never removed.

> ⚠️ **This is obfuscation, not cryptography.** The goal is to prevent automated scanners from recognizing the VPN URL. A reverse engineer with Frida will extract the key from the client in about an hour. Do not use crypt1 in tasks where real secret protection is needed.

### How to generate links

#### Browser / online tool

Landing page [incy.cc/encrypt](https://incy.cc/encrypt) makes crypt1 entirely on the client via Web Crypto API — nothing is sent to the server.

#### NPM package (Node.js, backend, bots)

```bash
npm install @incy/link-encoder
```

```js
import { encryptLink } from '@incy/link-encoder';

const link = encryptLink('https://sub.your-provider.example/abc123token', {
  name: 'My Provider VPN',
});

console.log(link);
// → incy://crypt1/AAECAwQFBgcICQoLNyIQL3rDwRZqnyoD8pGK…
```

API:

```ts
encryptLink(url: string, opts?: { name?: string }): string
decryptLink(link: string): { url: string; name?: string }
```

Learn more — [package README](https://github.com/INCY-DEV/incy-link-encoder).

### App behavior when importing

| Step | What the client does                                                                                                                                                    |
| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | User taps / scans / pastes `incy://crypt1/<payload>`                                                                                                                    |
| 2    | Client decodes the payload, restores `url`                                                                                                                              |
| 3    | The import confirmation window opens (as in `://add/`), subscription URL **hidden** under `••••` so it doesn't "leak" in the screenshot                                 |
| 4    | If the payload contains `n` — displayed as the provider name in this window and prefilled in the "Name" field                                                           |
| 5    | After confirmation — the usual subscription import flow                                                                                                                 |
| 6    | Internal flag `importedViaCrypt1=true` is saved with the created subscription. On subsequent Share/Copy/QR, the client **again** emits crypt1, without exposing the URL |

### Preserving the wire format when sharing

When tapping **Share** / **Copy URL** / **Show QR** on the subscription card, the client emits the same format in which the subscription was added:

* Added via `https://...` → Share outputs `https://...`
* Added via `incy://crypt1/...` → Share outputs `incy://crypt1/...`

This ensures that the obfuscation is not lost down the forwarding chain: if the provider originally published a crypt1 link, all forwards of its copy in Telegram chats remain crypt1-form.

### Compatibility

| INCY version                                        | Support                                               |
| --------------------------------------------------- | ----------------------------------------------------- |
| iOS / Android / Desktop ≥ June 2026                 | Yes, native handler `incy://crypt1/`                  |
| Older versions                                      | The link does not open — the user needs to update     |
| Third-party VPN clients (V2Box, Shadowrocket, Happ) | Not supported, the scheme is registered only for INCY |

### crypt1 examples

#### Simple link

```
incy://crypt1/FZEVXuV39UEX1yHB3nkrgdPdrJ3syVxcQm_Y-lY0oKWAT5yRn00xe6ohg06aVWjWRrGJ7BAeEzuoFzv8XBosLnqnqqCMbnAJmR7EN2hII4Yyql1FtWlLlLs
```

#### With provider branding in the QR code

A QR code generated from a crypt1 link with the field `n`, when scanned in INCY, will show the user “MyProvider VPN — Confirm import?” — the provider is branded in the confirmation window without a server round trip.
