AI Gateway is now available: identity-aware access to any AI provider, eliminate API keys, and tunnel to self-hosted models. Get started

Common API Routes

API routes and patterns for the most common actions and flows

All routes in this guide require an API key. See 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 for where to find it.

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.

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, 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).

PUT /org/{orgId}/site

Path

  • orgId (string) — organization ID

Body (Newt)

FieldTypeRequiredDescription
namestringYesDisplay name for the site
typestringYesUse "newt"

Example Response

{
  "data": {
    "siteId": 8723,
    "niceId": "quiet-lerista-labialis",
    "name": "My Site",
    "type": "newt",
    "online": false,
    "address": "100.90.128.0",
    "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.

Path

  • orgId (string) — organization ID

Example Response

{
  "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)

FieldTypeRequiredDescription
namestringYesDisplay name for the site
addressstringYesFrom pick-site-defaults clientAddress
typestringYesUse "newt"
newtIdstringYesFrom pick-site-defaults newtId
secretstringYesFrom pick-site-defaults newtSecret

Example Response

{
  "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).

List domains

Call the list-domains endpoint to get available domains and their domainId values.

Create the resource

Call the create-resource endpoint with http: true and the domainId from step 1.

Add targets to the resource

Call the create-target endpoint for each backend (site + ip:port) that should serve traffic for the resource.

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)

ParamTypeDefaultDescription
limitnumber1000Max domains to return
offsetnumber0Pagination offset

Example Response

{
  "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)

FieldTypeRequiredDescription
namestringYesDisplay name for the resource (1–255 chars)
httpbooleanYesUse true for public HTTP resource
domainIdstringYesFrom list-domains
protocolstringYes"tcp"
subdomainstring | nullNoSee below.

Subdomain and domain types

Domains come in three types: ns | cname | wildcard.

Wildcard is only available in self-hosted Pangolin. Pangolin Cloud uses ns and cname only.

  • 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

{
  "name": "My Resource",
  "http": true,
  "subdomain": "my-subdomain",
  "domainId": "pg3i1k4lhibhl3i",
  "protocol": "tcp"
}

Example Response

{
  "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).

Path

  • resourceId (number) — From create-resource response (e.g. 9943)

Body

FieldTypeRequiredDescription
siteIdnumberYesSite that hosts the backend (from create-site or list sites)
ipstringYesBackend IP or hostname
portnumberYesBackend port
methodstringYese.g. "http" for HTTP resources

Example request

{
  "ip": "localhost",
  "port": 8080,
  "method": "http",
  "siteId": 5165
}

Example response

{
  "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).

Create private resource

PUT /org/{orgId}/private-resource

Path

  • orgId (string) — organization ID

Body

FieldTypeRequiredDescription
namestringYesDisplay name (1–255 chars)
siteIdnumberYesSite that hosts the resource (from create-site or list sites)
modestringYes"host" | "cidr"
destinationstringYesFor host: IP or hostname (e.g. localhost). For cidr: CIDR (e.g. 10.0.0.0/24).
aliasstringFor host+domainAlias hostname (e.g. private-resource.internal). Required when destination is a domain; optional for IP. Must be unique in the org.
tcpPortRangeStringstringYesSee below.
udpPortRangeStringstringYesSee below.
disableIcmpbooleanNoDefault false
authDaemonModestringNo"site" | "remote"
roleIdsnumber[]NoRole IDs that can access (default [])
userIdsstring[]NoUser IDs that can access (default [])

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).

If you omit roleIds/userIds, the org admin role is granted access by default. Add IDs to restrict access.

Example request

{
  "name": "My Private Resource",
  "siteId": 8723,
  "mode": "host",
  "destination": "localhost",
  "alias": "private-resource.internal",
  "tcpPortRangeString": "*",
  "udpPortRangeString": "",
  "disableIcmp": false,
  "authDaemonMode": "site",
  "roleIds": [],
  "userIds": []
}

Example response

{
  "data": {
    "siteResourceId": 1165,
    "siteId": 8723,
    "niceId": "unsung-round-tailed-ground-squirrel",
    "name": "My Private Resource",
    "mode": "host",
    "destination": "localhost",
    "alias": "private-resource.internal",
    "tcpPortRangeString": "*",
    "udpPortRangeString": "",
    "disableIcmp": false,
    "authDaemonPort": 22123,
    "authDaemonMode": "site"
  },
  "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 roles in the org. Use roleId (number) when adding a role to a resource. Query: limit, offset (optional).

GET /org/{orgId}/users — Returns users in the org. Use id (string) as userId when adding a user to a resource. Query: limit, offset (optional).

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 and Private Resource API.

Was this page helpful?

⌘I

On this page