mirror of
https://github.com/fosrl/docs-v2.git
synced 2026-10-29 15:09:10 +01:00
401 lines
14 KiB
Plaintext
401 lines
14 KiB
Plaintext
---
|
||
title: "Common API Routes"
|
||
description: "API routes and patterns for the most common actions and flows"
|
||
---
|
||
|
||
All routes in this guide require an API key. See [Integration API](/manage/integration-api) for creating and using API keys (Bearer token in the `Authorization` header). Many routes use an organization ID in the path — see [Organization ID](/manage/organizations/org-id) for where to find it.
|
||
|
||
<Note>
|
||
This guide is a **minimal** walkthrough for creating core Pangolin components (sites, resources, targets) via the API. It is not exhaustive — some elements are omitted for simplicity — but everything shown here works and illustrates patterns used elsewhere in the API. For full coverage of endpoints (get, update, delete, list, etc.), use the [Swagger docs](https://api.pangolin.net/v1/docs).
|
||
</Note>
|
||
|
||
OpenAPI-compliant documentation is available at https://api.pangolin.net/v1/openapi.json and https://api.pangolin.net/v1/openapi.yaml
|
||
|
||
## What is an identifier?
|
||
|
||
In the Pangolin app, sites, public resources, private resources, and clients have an **Identifier**. In the API, this value is called `niceId`.
|
||
|
||
The identifier is a human-readable, organization-unique value you can use when you want something more stable and memorable than a numeric ID. For example, a site might have a numeric `siteId` like `8723` and a `niceId` like `warehouse-west`.
|
||
|
||
Identifiers are especially useful for automation:
|
||
|
||
- In [Blueprints](/manage/blueprints), resource and site keys act as stable identifiers. A target can reference a site by its identifier, such as `site: warehouse-west`.
|
||
- In the API, responses include both the numeric ID and `niceId` when the object supports identifiers. Some routes can also look up objects by `niceId`, such as `GET /org/{orgId}/site/{niceId}` for sites.
|
||
|
||
Use the numeric ID when an endpoint asks for `siteId`, `resourceId`, or `siteResourceId`. Use `niceId` when an endpoint or blueprint field asks for an identifier.
|
||
|
||
## Create site
|
||
|
||
This section assumes you're creating a **Pangolin Site**. For all Site endpoints, see [Site API (Swagger)](https://api.pangolin.net/v1/docs/#/Site).
|
||
|
||
**PUT** `/org/{orgId}/site`
|
||
|
||
**Path**
|
||
- `orgId` (string) — organization ID
|
||
|
||
**Body (Newt)**
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `name` | string | Yes | Display name for the site |
|
||
| `type` | string | Yes | Use `"newt"` |
|
||
| `niceId` | string | No | Identifier for the site. Must be unique in the org. Generated if omitted. |
|
||
|
||
If you omit them, Pangolin generates the Newt credentials (`newtId` and `secret`) and the site's address. Only the create response includes the secret, so save it then.
|
||
|
||
**Example Response**
|
||
```json
|
||
{
|
||
"data": {
|
||
"siteId": 8723,
|
||
"niceId": "quiet-lerista-labialis",
|
||
"name": "My Site",
|
||
"type": "newt",
|
||
"online": false,
|
||
"address": "100.90.128.0/24",
|
||
"newtId": "se897rmyjvamy5q",
|
||
"secret": "ystsahnvqetgx1fhn2udylq0zqgv611zbfp6ya3jvfrprn7q"
|
||
},
|
||
"success": true,
|
||
"error": false,
|
||
"message": "Site created successfully",
|
||
"status": 201
|
||
}
|
||
```
|
||
|
||
### Advanced option: pick-site-defaults
|
||
|
||
Use this when you want to generate some template values and control the site creation.
|
||
|
||
**GET** `/org/{orgId}/pick-site-defaults`
|
||
|
||
Returns values you pass into the create-site endpoint. The response also includes exit node fields (`exitNodeId`, `subnet`, `endpoint`, etc.). Newt sites don't use them because Newt picks its exit node when it connects.
|
||
|
||
**Path**
|
||
- `orgId` (string) — organization ID
|
||
|
||
**Example Response**
|
||
```json
|
||
{
|
||
"data": {
|
||
"newtId": "jwhk5154mfmos0s",
|
||
"newtSecret": "8afipi4i79jjbsxjqpkgc0xe2ge143s54oi64mw5567mxgr8",
|
||
"clientAddress": "100.90.128.0"
|
||
},
|
||
"success": true,
|
||
"error": false,
|
||
"message": "Site defaults chosen successfully",
|
||
"status": 200
|
||
}
|
||
```
|
||
|
||
### Create site
|
||
|
||
**PUT** `/org/{orgId}/site`
|
||
|
||
**Path**
|
||
- `orgId` (string) — organization ID
|
||
|
||
**Body (Newt)**
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `name` | string | Yes | Display name for the site |
|
||
| `type` | string | Yes | Use `"newt"` |
|
||
| `address` | string | No | From pick-site-defaults `clientAddress`. Must be an IP inside the org subnet that no other site or client uses. |
|
||
| `newtId` | string | No | From pick-site-defaults `newtId` |
|
||
| `secret` | string | No | From pick-site-defaults `newtSecret` |
|
||
| `niceId` | string | No | Identifier for the site. Must be unique in the org. |
|
||
|
||
**Example Response**
|
||
```json
|
||
{
|
||
"data": {
|
||
"siteId": 8723,
|
||
"niceId": "quiet-lerista-labialis",
|
||
"name": "My Site",
|
||
"type": "newt",
|
||
"online": false,
|
||
"address": "100.90.128.0/24",
|
||
"newtId": "se897rmyjvamy5q",
|
||
"secret": "ystsahnvqetgx1fhn2udylq0zqgv611zbfp6ya3jvfrprn7q"
|
||
},
|
||
"success": true,
|
||
"error": false,
|
||
"message": "Site created successfully",
|
||
"status": 201
|
||
}
|
||
```
|
||
|
||
## Create public HTTP resource
|
||
|
||
You need a **domain ID** before creating a resource. List your org's domains, then create the resource with the chosen domain. For all public resource endpoints, see [Public Resource API (Swagger)](https://api.pangolin.net/v1/docs/#/Public%20Resource).
|
||
|
||
<Steps>
|
||
<Step title="List domains">
|
||
Call the list-domains endpoint to get available domains and their `domainId` values.
|
||
</Step>
|
||
<Step title="Create the resource">
|
||
Call the create-resource endpoint with `mode: "http"` and the `domainId` from step 1.
|
||
</Step>
|
||
<Step title="Add targets to the resource">
|
||
Call the create-target endpoint for each backend (site + ip:port) that should serve traffic for the resource.
|
||
</Step>
|
||
</Steps>
|
||
|
||
### List domains
|
||
|
||
**GET** `/org/{orgId}/domains`
|
||
|
||
Returns all domains for the organization. Use `domainId` from a domain when creating a resource.
|
||
|
||
**Path**
|
||
- `orgId` (string) — organization ID
|
||
|
||
**Query** (optional)
|
||
| Param | Type | Default | Description |
|
||
|-------|------|---------|-------------|
|
||
| `limit` | number | 1000 | Max domains to return |
|
||
| `offset` | number | 0 | Pagination offset |
|
||
|
||
**Example Response**
|
||
```json
|
||
{
|
||
"data": {
|
||
"domains": [
|
||
{
|
||
"domainId": "pg3i1k4lhibhl3i",
|
||
"baseDomain": "pangolin.net",
|
||
"verified": true,
|
||
"type": "ns"
|
||
},
|
||
{
|
||
"domainId": "q1ngj5341k7oydo",
|
||
"baseDomain": "bitwarden.pangolin.net",
|
||
"verified": true,
|
||
"type": "cname"
|
||
}
|
||
],
|
||
"pagination": {
|
||
"total": 2,
|
||
"limit": 1000,
|
||
"offset": 0
|
||
}
|
||
},
|
||
"success": true,
|
||
"error": false,
|
||
"message": "Domains retrieved successfully",
|
||
"status": 200
|
||
}
|
||
```
|
||
|
||
### Create public HTTP resource
|
||
|
||
**PUT** `/org/{orgId}/public-resource`
|
||
|
||
**Path**
|
||
- `orgId` (string) — organization ID
|
||
|
||
**Body (HTTP resource)**
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `name` | string | Yes | Display name for the resource (1–255 chars) |
|
||
| `mode` | string | Yes | Use `"http"` for a public HTTP resource |
|
||
| `domainId` | string | Yes | From list-domains |
|
||
| `subdomain` | string \| null | No | See below. |
|
||
|
||
<Note>
|
||
Older integrations sent `http: true` and `protocol: "tcp"` instead of `mode`. The API still accepts those fields, but they're deprecated. Use `mode` instead.
|
||
</Note>
|
||
|
||
**Subdomain and domain types**
|
||
|
||
Domains come in three types: **ns** | **cname** | **wildcard**.
|
||
|
||
<Note>
|
||
**Wildcard** is only available in self-hosted Pangolin. Pangolin Cloud uses **ns** and **cname** only.
|
||
</Note>
|
||
|
||
- **ns** — You can use the base domain (set `subdomain` to `null`) or set a subdomain (e.g. `my-app` → `my-app.digpangolin.io`).
|
||
- **cname** — Only the base domain is used; set `subdomain` to `null` (the domain’s `baseDomain` is the FQDN).
|
||
- **wildcard** — Same as ns for subdomain behavior (self-hosted only).
|
||
|
||
The `subdomain` value is combined with the base domain from `domainId` to form the FQDN. Omit `subdomain` or pass `null` when using the base domain alone.
|
||
|
||
**Example request**
|
||
```json
|
||
{
|
||
"name": "My Resource",
|
||
"mode": "http",
|
||
"subdomain": "my-subdomain",
|
||
"domainId": "pg3i1k4lhibhl3i"
|
||
}
|
||
```
|
||
|
||
**Example Response**
|
||
```json
|
||
{
|
||
"data": {
|
||
"resourceId": 9942,
|
||
"niceId": "decent-louisiana-waterthrush",
|
||
"name": "My Resource",
|
||
"subdomain": "my-subdomain",
|
||
"fullDomain": "my-subdomain.pangolin.net",
|
||
"domainId": "pg3i1k4lhibhl3i"
|
||
},
|
||
"success": true,
|
||
"error": false,
|
||
"message": "Http resource created successfully",
|
||
"status": 201
|
||
}
|
||
```
|
||
|
||
### Add targets to the resource
|
||
|
||
**PUT** `/public-resource/{resourceId}/target`
|
||
|
||
Add a target (backend) to a resource. Use the numeric `resourceId` from the create-resource response. The target is the site and address (ip + port) that will receive traffic for the resource. For all target endpoints, see [Resource Target API (Swagger)](https://api.pangolin.net/v1/docs/#/Resource%20Target).
|
||
|
||
**Path**
|
||
- `resourceId` (number) — From create-resource response (e.g. `9942`)
|
||
|
||
**Body**
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `siteId` | number | Yes | Site that hosts the backend (from create-site or list sites) |
|
||
| `ip` | string | Yes | Backend IP or hostname |
|
||
| `port` | number | Yes | Backend port |
|
||
| `method` | string | Yes (HTTP) | Scheme Pangolin uses to reach the backend: `"http"`, `"https"`, or `"h2c"`. HTTP resources need it; Pangolin skips HTTP targets that have no method. |
|
||
|
||
**Example request**
|
||
```json
|
||
{
|
||
"ip": "localhost",
|
||
"port": 8080,
|
||
"method": "http",
|
||
"siteId": 8723
|
||
}
|
||
```
|
||
|
||
**Example response**
|
||
```json
|
||
{
|
||
"data": {
|
||
"targetId": 11280,
|
||
"resourceId": 9942,
|
||
"siteId": 8723,
|
||
"ip": "localhost",
|
||
"method": "http",
|
||
"port": 8080
|
||
},
|
||
"success": true,
|
||
"error": false,
|
||
"message": "Target created successfully",
|
||
"status": 201
|
||
}
|
||
```
|
||
|
||
## Create private resource
|
||
|
||
You need an existing site. For more endpoints, see [Private Resource API (Swagger)](https://api.pangolin.net/v1/docs/#/Private%20Resource).
|
||
|
||
### Create private resource
|
||
|
||
**PUT** `/org/{orgId}/private-resource`
|
||
|
||
**Path**
|
||
- `orgId` (string) — organization ID
|
||
|
||
**Body**
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `name` | string | Yes | Display name (1–255 chars) |
|
||
| `siteIds` | number[] | Yes | Sites that can reach the resource (from create-site or list sites) |
|
||
| `mode` | string | Yes | `"host"` \| `"cidr"`. This guide covers these two; see Swagger for `http`, `ssh`, `inference`, and `gateway`. |
|
||
| `destination` | string | Yes | For `host`: IPv4 address or hostname (e.g. `localhost`). For `cidr`: CIDR (e.g. `10.0.0.0/24`). |
|
||
| `alias` | string | For host+domain | Alias as a fully qualified domain name (e.g. `private-resource.internal`). Wildcards are allowed (e.g. `*.example.internal`). Required when `destination` is a hostname; optional for an IP. Must be unique in the org. |
|
||
| `roleIds` | number[] | Yes | Role IDs that can access. Pass `[]` for none. |
|
||
| `userIds` | string[] | Yes | User IDs that can access. Pass `[]` for none. |
|
||
| `clientIds` | number[] | Yes | Machine client IDs that can access. Pass `[]` for none. |
|
||
| `tcpPortRangeString` | string | No | See below. Default `"*"`. |
|
||
| `udpPortRangeString` | string | No | See below. Default `"*"`. |
|
||
| `disableIcmp` | boolean | No | Default `false` |
|
||
| `niceId` | string | No | Identifier for the resource. Generated if omitted. |
|
||
|
||
<Note>
|
||
The older `siteId` (single number) field still works and is merged into `siteIds`, but it's deprecated.
|
||
</Note>
|
||
|
||
**TCP/UDP port range strings:** Control which ports are allowed for the private resource.
|
||
|
||
- **`"*"`** — Allow all ports (common for TCP when you want full access to the host).
|
||
- **`""`** (empty string) — Allow no ports. Use when you don’t need that protocol (e.g. `udpPortRangeString: ""` if only TCP is used).
|
||
- **Specific ports or ranges** — Comma-separated list: single ports (e.g. `"80,443"`) or ranges (e.g. `"8000-9000"`). Example: `"80,443,8080-8090"` allows 80, 443, and 8080–8090.
|
||
|
||
Use `tcpPortRangeString` and `udpPortRangeString` independently (e.g. TCP all, UDP none, or vice versa).
|
||
|
||
The org Admin role always gets access. The roles, users, and clients you list get access in addition to it.
|
||
|
||
**Example request**
|
||
```json
|
||
{
|
||
"name": "My Private Resource",
|
||
"siteIds": [8723],
|
||
"mode": "host",
|
||
"destination": "localhost",
|
||
"alias": "private-resource.internal",
|
||
"tcpPortRangeString": "*",
|
||
"udpPortRangeString": "",
|
||
"disableIcmp": false,
|
||
"roleIds": [],
|
||
"userIds": [],
|
||
"clientIds": []
|
||
}
|
||
```
|
||
|
||
**Example response**
|
||
```json
|
||
{
|
||
"data": {
|
||
"siteResourceId": 1165,
|
||
"networkId": 412,
|
||
"niceId": "unsung-round-tailed-ground-squirrel",
|
||
"name": "My Private Resource",
|
||
"mode": "host",
|
||
"destination": "localhost",
|
||
"alias": "private-resource.internal",
|
||
"tcpPortRangeString": "*",
|
||
"udpPortRangeString": "",
|
||
"disableIcmp": false
|
||
},
|
||
"success": true,
|
||
"error": false,
|
||
"message": "Site resource created successfully",
|
||
"status": 201
|
||
}
|
||
```
|
||
|
||
## Assign users and roles to a resource (public or private)
|
||
|
||
You can grant access to a **public resource** or a **private resource** by adding roles or users. First list roles and users in the org to get IDs, then call the add endpoints. The Admin role cannot be assigned via these endpoints.
|
||
|
||
### Get role and user IDs
|
||
|
||
**GET** `/org/{orgId}/roles` returns `data.roles`, the roles in the org. Use a role's `roleId` (number) when adding it to a resource. Optional query parameters: `page` (default `1`), `pageSize` (default `20`), and `query` (filter by name).
|
||
|
||
**GET** `/org/{orgId}/users` returns `data.users`, the users in the org. Use a user's `id` (string) as `userId` when adding them to a resource. Optional query parameters: `page` (default `1`), `pageSize` (default `20`), and `query` (search).
|
||
|
||
### Public resource
|
||
|
||
**POST** `/public-resource/{resourceId}/roles/add` — **Path:** `resourceId` (number, from create public-resource). **Body:** `{ "roleId": number }`. Admin role not allowed.
|
||
|
||
**POST** `/public-resource/{resourceId}/users/add` — **Path:** `resourceId` (number). **Body:** `{ "userId": string }`.
|
||
|
||
Both return `{ "data": {}, "success": true, "error": false, "message": "...", "status": 201 }`.
|
||
|
||
### Private resource
|
||
|
||
**POST** `/private-resource/{siteResourceId}/roles/add` — **Path:** `siteResourceId` (number, from create private-resource). **Body:** `{ "roleId": number }`. Admin role not allowed.
|
||
|
||
**POST** `/private-resource/{siteResourceId}/users/add` — **Path:** `siteResourceId` (number). **Body:** `{ "userId": string }`.
|
||
|
||
Same response shape as above. Role must belong to the same org as the private resource. For more endpoints (list/remove), see [Public Resource API](https://api.pangolin.net/v1/docs/#/Public%20Resource) and [Private Resource API](https://api.pangolin.net/v1/docs/#/Private%20Resource).
|