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

# Client API

Integrate VPN into any application

## 1. Overall API flow

Generally, API calls are divided by 3 groups:

1. Authorizing-related - provides authentication token to access further API calls.
2. Discovery-related - provides credentials to target VPN node.
3. Tunnel-related - manages actual [wireguard](https://www.wireguard.com/) tunnel

Please, take a look to next diagram to get actual requests sequence.

![](https://2764393371-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mk7crxOeL0P9LVXnxAa%2Fsync%2Ff09df5a23d33df5faa66ca0f75e40c7252307cc0.svg?generation=1632306196630165\&alt=media)

## 2. Errors

### 2.1. Generic description

All errors are returned with meaningful HTTP error code and JSON body with more detailed information. For example, syntax error will return HTTP 400 error with next body:

```
{
    result: "INVALID_ARGUMENT",
    error: "Invalid request",
    details: "invalid character '\\n' in string literal"
}
```

Check table below for fields description.

| Field     | Description                                                                               |
| --------- | ----------------------------------------------------------------------------------------- |
| `result`  | A predefined set of strings, identifying the type of happened error. See below all types. |
| `error`   | More detailed error information.                                                          |
| `details` | Some extra information about error.                                                       |

### 2.1. List of errors

| HTTP code | Result code            | Description                                                                             |
| --------- | ---------------------- | --------------------------------------------------------------------------------------- |
| `500`     | `INTERNAL_ERROR`       | Any unexpected behavior of backend components.                                          |
| `400`     | `INVALID_ARGUMENT`     | Invalid request data, like syntax or type errors.                                       |
| `404`     | `NOT_FOUND`            | Managed entity is not found (for example, when ping request is done for outdated peer). |
| `409`     | `ENTRY_EXISTS`         | Can't create entry due to conflict. Can happen when wireguard key is already used.      |
| `500`     | `STORAGE_ERROR`        | Internal storage failure.                                                               |
| `500`     | `TUNNEL_ERROR`         | Tunnel management failure.                                                              |
| `401`     | `UNAUTHORIZED`         | Request requires authorization.                                                         |
| `401`     | `AUTH_FAILED`          | Request can't be done with provided authorization data.                                 |
| `507`     | `INSUFFICIENT_STORAGE` | Out of resoures to perform request.                                                     |
| `503`     | `SERVICE_UNAVAILABLE`  | Happens with request is done while service is shutting down or restarting.              |

## 3. Authorizer API

### 3.1. Authorize client

**URL:** <https://thefend.com/api/client/signin>

**Method:** POST

**Authentication:** None

**Query string:** None

**Request body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
OBJECT 

{
  project*: string
  client_platform*: string
  client_version*: string
  device_id*: uuid
  auth_type*: string
  auth_info*: string
}
```

{% endtab %}

{% tab title="Description" %}

| Name                 | Format   | Description                                        |
| -------------------- | -------- | -------------------------------------------------- |
| `project` \*         | `string` | Name of the project                                |
| `client_platform` \* | `string` | Client platform (i.e. ios, android, etc.)          |
| `client_version` \*  | `string` | Version of client application release (i.e. 1.2.3) |
| `device_id` \*       | `string` | Device ID in UUID format                           |
| `auth_type` \*       | `string` | Authentication scheme                              |
| `auth_info` \*       | `string` | Authentication information                         |

**Fields, marked by \* are required.**
{% endtab %}

{% tab title="Example" %}

```
{
  "project": "FendVPNTestProject",
  "client_platform": "iphone",
  "client_version": "1.0.1",
  "device_id": "f7df7c7a-3569-401c-b612-f49b775bb19e",
  "auth_type": "firebase",
  "auth_info": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
```

{% endtab %}
{% endtabs %}

**Response body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
OBJECT

{
    access_token*: string
    refresh_token: string
    discovery_addresses: [uri]
}
```

{% endtab %}

{% tab title="Description" %}

| Name                  | Format   | Description                                                  |
| --------------------- | -------- | ------------------------------------------------------------ |
| `access_token` \*     | `string` | JWT token, used for all further operations with backend      |
| `refresh_token`       | `string` | JWT token, used to get new access token.                     |
| `discovery_addresses` | `array`  | Array of URLs for requesting locations and node credentials. |

**Fields, marked by \* are required.**&#x20;

**Notes:**&#x20;

1. If refresh\_token is not specified, you should use authenticate again to get next token.
2. If discovery\_addresses is not specified or empty, you should keep using same base URL (i.e. <https://thefend.com>) for discovery requests.
   {% endtab %}

{% tab title="Example" %}

```
{
    access_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6IjBmNmQwZGFiLWU0NzgtNGQ1Ni1hZGJhLTU5M2ZhYmE4ZTRhYiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ2cG4tbm9kZSIsImV4cCI6MTYyNDk0OTk0NywianRpIjoiNTUxNzYyODQtZDdkZS0xMWViLWE2NDUtMDI0MmFjMTIwMDAyIiwiaWF0IjoxNjI0ODYzNTQ3LCJpc3MiOiJUZXN0UHJvamVjdC1BdXRoLTEtMiIsIm5iZiI6MTYyNDg2MzU0Nywic3ViIjoiRmVuZFZQTlRlc3RQcm9qZWN0L2ZpcmViYXNlL0pvaG5Eb2UifQ.REIfc3DB4e4pU7GyNU7vmJfsJBDu-0PLghV06KIdEM0Vhg4jVFA4MzgRU_j151PrAalSrpj0gLJGW8qZfqSLyDshRQCjgKYEt_X5PmNVkFmAjUz6hwCBJ8tMRxfhG2vcG1WZF80aTLLx6LQ0NwGT6hfAYEemzAecVwvnTuE1BQow3c1T71uZSByLDP1WAa0ZYnNZjceOGeikSXK5RtHogSyZkud7zfi_dKhDzW8Pgd_06HYEmexogv3LMpbJ0NY0nrRnxqPlJ1-uqQ6OIktQe1RvLe7BTGBthp_wrAv3Z9YdGSzJrlJhr1hg6qTzzdo0UHdvCpMeIun8bpr7OWVEIg",
    refresh_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6IjBmNmQwZGFiLWU0NzgtNGQ1Ni1hZGJhLTU5M2ZhYmE4ZTRhYiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZWZyZXNoIiwiZXhwIjoxNjMwMTMzOTQ3LCJqdGkiOiI1NTE3NjI4NC1kN2RlLTExZWItYTY0NS0wMjQyYWMxMjAwMDIiLCJpYXQiOjE2MjQ4NjM1NDcsImlzcyI6IlRlc3RQcm9qZWN0LUF1dGgtMS0yIiwibmJmIjoxNjI0ODYzNTQ3LCJzdWIiOiJGZW5kVlBOVGVzdFByb2plY3QvZmlyZWJhc2UvSm9obkRvZSJ9.GbhbrSxLcoj7yJFQnh1Pq2_3OXMnffihRaRyOsHay8RLyZzwHTpHN_uEcT-5z7l_kbrcWWMuswChrnt6zYuzzMa_qPFXpSIwkf3xXrFOr4Og-wLQqsy6Uh7-hJB546mMt-bT7tCFXZbNRtCm_lzIW6mPAcIJtofJsqSPaFL-xWrJEl0m4vRNV4NusGwlCuE7uvjQPLUF5gS98QLLWNWyHipVWxjSEK57I0ngrsOU7b4jETHxtgYYQ_O2S5GoOUe0YrwYFkrKP1LB3eE_aAw-toOJSP6eVm_Y345MBVJj8NcGa3Kbdm5SzZhHs2P-ZoKc8Yp2FdiFPyCq6VDmQ2PyXw",
    discovery_addresses: [
        "https://discovery.project.io/api/client/locations"
    ]
}
```

{% endtab %}
{% endtabs %}

## 4. Discovery API

### 4.1. List locations

**URL:** <https://thefend.com/api/client/locations>

**Method:** GET

**Authentication:** Bearer

**Query string:** None

**Request body:** None

**Response body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
ARRAY

[
    {
        id*: string
        name*: string
        labels: {}
    }
]
```

{% endtab %}

{% tab title="Description" %}
Response contains an array of locations. User should choose one before connect.

| Name      | Format   | Description                           |
| --------- | -------- | ------------------------------------- |
| `id` \*   | `string` | Location identifier.                  |
| `name` \* | `string` | Location user-friendly name.          |
| `labels`  | `array`  | Map of location labels, if specified. |

**Fields, marked by \* are required.**&#x20;
{% endtab %}

{% tab title="Example" %}

```
[
    {
        id: "US",
        name: "United States",
        labels: { },
    }
]
```

{% endtab %}
{% endtabs %}

### 4.2. Get location nodes

**URL:** <https://thefend.com/api/client/credentials>

**Method:** GET

**Authentication:** Bearer

**Query string:** location=\<LOCATION\_ID>

**Request body:** None

**Response body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
ARRAY

[
    {
        id*: string
        connection_addresses: [string]
    }
]
```

{% endtab %}

{% tab title="Description" %}
Response contains an array of a server nodes in order to try them. I.e. application should always start from first entry in array, if it's failed - go to second, etc.

| Name                   | Format | Description               |
| ---------------------- | ------ | ------------------------- |
| `id` \*                | string | Server unique identifier. |
| `connection_addresses` | string | Array of servers URLs.    |

**Fields, marked by \* are required.**&#x20;

**Notes:**&#x20;

1. If connection\_addresses is not specified or empty, you should keep using same base URL to establish tunnel connections. This can happen when you use demo backend.&#x20;
   {% endtab %}

{% tab title="Example" %}

```
[
    {
        id: "US",
        name: "United States",
        labels: { },
    }
]
```

{% endtab %}
{% endtabs %}

## 5. Tunneling API

### 5.1. Initiate connection.

**URL:** <https://some-server-name.thefend.com/api/client/connect>

**Method:** POST

**Authentication:** Bearer

**Query string:** None

**Request body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
OBJECT 

{
    type*: enum

    info_wireguard *: {
        public_key: string
    }

    identifiers* {
        installation_id: uuid
        session_id: uuid
    }

    location: string
}
```

{% endtab %}

{% tab title="Description" %}

| Name                 | Format   | Description                                                                                            |
| -------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `type` \*            | `enum`   | Tunneling type. Only wireguard is supported now.                                                       |
| `info_wireguard`     | `object` | Wireguard-specific connection information.                                                             |
| `public_key` \*      | `string` | Client wireguard public key.                                                                           |
| `identifiers`        | `object` | User-specific identifiers.                                                                             |
| `installation_id` \* | `uuid`   | <p>Application installation ID. Generated during </p><p>installation or first application startup.</p> |
| `session_id` \*      | `uuid`   | <p>Session ID. Generated every time when user </p><p>starts connection.</p>                            |
| `location`           | `string` | Location ID.                                                                                           |

**Fields, marked by \* are required.**
{% endtab %}

{% tab title="Example" %}

```
{
  "type": "wireguard",
  "info_wireguard": {
    "public_key": "ljs1lRH1YtZPlppYl1gQVX+JTNmTQsX57cIDf7oB6Qc="
  },
  "identifiers": {
    "installation_id": "d1a1b2e2-d84b-4537-9a93-c4d3cd412598",
    "session_id": "de9e0337-fb16-4669-b07d-9f261c329461"
  }
}
```

{% endtab %}
{% endtabs %}

**Response body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
OBJECT

{
    info_wireguard: {
        server_public_key*: string
        server_ipv4*: ipv4
        server_port*: integer
        tunnel_ipv4*: ipv4
        keepalive*: integer
        allowed_ips*: [string]
        dns*: [ipv4]
        ping_interval*: integer
    }
}
```

{% endtab %}

{% tab title="Description" %}

| Name                | Format        | Description                                                            |
| ------------------- | ------------- | ---------------------------------------------------------------------- |
| `info_wireguard`    | `object`      | Wireguard-specific connection information.                             |
| `server_public_key` | `string`      | Server wireguard public key.                                           |
| `server_ipv4`       | `ipv4`        | Server ipv4.                                                           |
| `server_port`       | `integer`     | Server port.                                                           |
| `tunnel_ipv4`       | `ipv4`        | Client in-tunnel ipv4 address.                                         |
| `keepalive`         | `integer`     | <p>Wireguard keepalive interval. </p><p>Zero means no keepalive.</p>   |
| `allowed_ips`       | `array[cidr]` | <p>List of allowed IPs in terms of </p><p>wireguard configuration.</p> |
| `dns`               | `array[ipv4]` | List of DNS addresses to use.                                          |
| `ping_interval`     | `integer`     | Interval to do a heartbeat request.                                    |

**Fields, marked by \* are required.**&#x20;
{% endtab %}

{% tab title="Example" %}

```
{
    info_wireguard: {
        server_public_key: "b0o7b8vXx9EH3uLJucOJUed0slvSppv7RCmC8jXRr1o=",
        server_ipv4: "1.2.3.4",
        server_port: 3000,
        tunnel_ipv4: "192.168.2.2",
        keepalive: 60,
        allowed_ips: [
            "0.0.0.0/0"
        ],
        dns: [
            "198.51.100.42"
        ],
        ping_interval: 60
    }
}
```

{% endtab %}
{% endtabs %}

### 5.3. Heartbeat request.

**URL:** <https://some-server-name.thefend.com/api/client/ping>

**Method:** POST

**Authentication:** Bearer

**Query string:** None

**Request body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
OBJECT 

{
    installation_id: uuid
    session_id: uuid
}
```

{% endtab %}

{% tab title="Description" %}

| Name                 | Format | Description                                                                                            |
| -------------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| `installation_id` \* | `uuid` | <p>Application installation ID. Generated during </p><p>installation or first application startup.</p> |
| `session_id` \*      | `uuid` | <p>Session ID. Generated every time when user </p><p>starts connection.</p>                            |

**Fields, marked by \* are required.**
{% endtab %}

{% tab title="Example" %}

```
{
  "installation_id": "d1a1b2e2-d84b-4537-9a93-c4d3cd412598",
  "session_id": "de9e0337-fb16-4669-b07d-9f261c329461"
}
```

{% endtab %}
{% endtabs %}

**Response body:** None

### 5.3. Drop connection.

**URL:** <https://some-server-name.thefend.com/api/client/disconnect>

**Method:** POST

**Authentication:** Bearer

**Query string:** None

**Request body:** JSON

{% tabs %}
{% tab title="Schema" %}

```javascript
OBJECT 

{
    installation_id: uuid
    session_id: uuid
}
```

{% endtab %}

{% tab title="Description" %}

| Name                 | Format | Description                                                                                            |
| -------------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| `installation_id` \* | `uuid` | <p>Application installation ID. Generated during </p><p>installation or first application startup.</p> |
| `session_id` \*      | `uuid` | <p>Session ID. Generated every time when user </p><p>starts connection.</p>                            |

**Fields, marked by \* are required.**
{% endtab %}

{% tab title="Example" %}

```
{
  "installation_id": "d1a1b2e2-d84b-4537-9a93-c4d3cd412598",
  "session_id": "de9e0337-fb16-4669-b07d-9f261c329461"
}
```

{% endtab %}
{% endtabs %}

**Response body:** None
