name: pangolin
description: Manage Pangolin reverse proxy — sites, resources (HTTP/TCP services), targets (backends), domains, roles, users, clients, and access control via the Pangolin Integration API. Use when the user asks about their Pangolin tunnels, reverse proxy resources, exposed services, or site connectivity.
argument-hint: [action] [resource] [options]
allowed-tools: Bash(curl *) Read
Pangolin API Skill
Manage a self-hosted Pangolin tunneled reverse proxy via the Integration API.
Authentication
The API key is stored in PANGOLIN_API_KEY and base URL in PANGOLIN_URL.
Authorization: Bearer $PANGOLIN_API_KEY
The key format is {apiKeyId}.{apiKeySecret}.
Base URL
https://api.$PANGOLIN_URL/v1
The Integration API lives at api. prefixed to the Pangolin domain, path prefix /v1.
Standard Headers
-H "Authorization: Bearer $PANGOLIN_API_KEY" \
-H "Content-Type: application/json"
How to Make Requests
curl -s "https://api.$PANGOLIN_URL/v1/org/$PANGOLIN_ORG/sites" \
-H "Authorization: Bearer $PANGOLIN_API_KEY" \
-H "Content-Type: application/json" | jq .
Organization
The org ID for this instance is: wulf-consulting
Always use PANGOLIN_ORG="wulf-consulting" in requests.
Response Format
{
"data": {
"resources": [ ... ],
"pagination": {
"total": 42,
"limit": 25,
"offset": 0
}
},
"success": true,
"error": false,
"message": "Human-readable message",
"status": 200
}
Always check the success field.
List endpoints return a pagination object inside data with total, limit, and offset fields. The default limit is typically 25 or 1000 depending on the endpoint.
To paginate, add ?limit=N&offset=N query parameters:
# First page
curl -s "$BASE/org/$ORG/resources?limit=25&offset=0" ...
# Second page
curl -s "$BASE/org/$ORG/resources?limit=25&offset=25" ...
Always check pagination.total against the number of items returned. If total exceeds the current page size, fetch additional pages until all items are retrieved. Example pattern:
# Fetch all pages of a resource list
OFFSET=0
LIMIT=100
while true; do
RESP=$(curl -s "$BASE/org/$ORG/resources?limit=$LIMIT&offset=$OFFSET" \
-H "Authorization: Bearer $PANGOLIN_API_KEY")
# Process items from this page...
TOTAL=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['pagination']['total'])")
OFFSET=$((OFFSET + LIMIT))
if [ $OFFSET -ge $TOTAL ]; then break; fi
done
When listing resources, sites, targets, users, etc., always use ?limit=1000 to minimize round trips, and paginate if total exceeds the returned count.
Important: HTTP Method Conventions
Pangolin uses non-standard HTTP method semantics:
- PUT = Create a new resource
- POST = Update an existing resource
- DELETE = Delete
- GET = Read/list
This is the opposite of typical REST conventions. Be careful!
API Endpoints Reference
Organizations
| Action |
Method |
Endpoint |
| Check org ID |
GET |
/org/checkId |
| Create org |
PUT |
/org |
| List orgs |
GET |
/orgs (root key only) |
| Get org |
GET |
/org/:orgId |
| Update org |
POST |
/org/:orgId |
| Delete org |
DELETE |
/org/:orgId |
Sites (Tunnel Endpoints)
| Action |
Method |
Endpoint |
| List sites |
GET |
/org/:orgId/sites |
| Create site |
PUT |
/org/:orgId/site |
| Get site defaults |
GET |
/org/:orgId/pick-site-defaults |
| Get site by niceId |
GET |
/org/:orgId/site/:niceId |
| Get site by ID |
GET |
/site/:siteId |
| Update site |
POST |
/site/:siteId |
| Delete site |
DELETE |
/site/:siteId |
Site types: "newt" (tunnel client), "wireguard", "local"
Resources (Public HTTP/TCP Services)
| Action |
Method |
Endpoint |
| List resources |
GET |
/org/:orgId/resources |
| List resource names |
GET |
/org/:orgId/resource-names |
| Create resource |
PUT |
/org/:orgId/resource |
| Create for site |
PUT |
/org/:orgId/site/:siteId/resource |
| Get resource |
GET |
/resource/:resourceId |
| Get by niceId |
GET |
/org/:orgId/resource/:niceId |
| Update resource |
POST |
/resource/:resourceId |
| Delete resource |
DELETE |
/resource/:resourceId |
| List site resources |
GET |
/site/:siteId/resources |
Create HTTP resource body:
{
"name": "My App",
"subdomain": "app",
"http": true,
"protocol": "tcp",
"domainId": "domain-id",
"stickySession": false
}
Update resource fields: name, subdomain, ssl, sso, blockAccess, enabled, emailWhitelistEnabled, domainId, stickySession, tlsServerName, setHostHeader
Targets (Backends/Upstreams)
| Action |
Method |
Endpoint |
| List targets |
GET |
/resource/:resourceId/targets |
| Create target |
PUT |
/resource/:resourceId/target |
| Get target |
GET |
/target/:targetId |
| Update target |
POST |
/target/:targetId |
| Delete target |
DELETE |
/target/:targetId |
Target body:
{
"siteId": 1,
"ip": "192.168.1.100",
"port": 8080,
"method": "round-robin",
"enabled": true
}
Health check fields: hcEnabled, hcPath, hcScheme, hcInterval (min 5s), hcTimeout (min 1s), hcStatus
Resource Rules
| Action |
Method |
Endpoint |
| List rules |
GET |
/resource/:resourceId/rules |
| Create rule |
PUT |
/resource/:resourceId/rule |
| Update rule |
POST |
/resource/:resourceId/rule/:ruleId |
| Delete rule |
DELETE |
/resource/:resourceId/rule/:ruleId |
Resource Access Control
| Action |
Method |
Endpoint |
| List roles |
GET |
/resource/:resourceId/roles |
| List users |
GET |
/resource/:resourceId/users |
| Set roles |
POST |
/resource/:resourceId/roles |
| Set users |
POST |
/resource/:resourceId/users |
| Add role |
POST |
/resource/:resourceId/roles/add |
| Remove role |
POST |
/resource/:resourceId/roles/remove |
| Add user |
POST |
/resource/:resourceId/users/add |
| Remove user |
POST |
/resource/:resourceId/users/remove |
| Set password auth |
POST |
/resource/:resourceId/password |
| Set pincode auth |
POST |
/resource/:resourceId/pincode |
| Set header auth |
POST |
/resource/:resourceId/header-auth |
| Set email whitelist |
POST |
/resource/:resourceId/whitelist |
| Get email whitelist |
GET |
/resource/:resourceId/whitelist |
| Add to whitelist |
POST |
/resource/:resourceId/whitelist/add |
| Remove from whitelist |
POST |
/resource/:resourceId/whitelist/remove |
Access Tokens
| Action |
Method |
Endpoint |
| Generate token |
POST |
/resource/:resourceId/access-token |
| Delete token |
DELETE |
/access-token/:accessTokenId |
| List org tokens |
GET |
/org/:orgId/access-tokens |
| List resource tokens |
GET |
/resource/:resourceId/access-tokens |
Domains
| Action |
Method |
Endpoint |
| List domains |
GET |
/org/:orgId/domains |
| Create domain |
PUT |
/org/:orgId/domain |
| Get domain |
GET |
/org/:orgId/domain/:domainId |
| Update domain |
PATCH |
/org/:orgId/domain/:domainId |
| Delete domain |
DELETE |
/org/:orgId/domain/:domainId |
| Get DNS records |
GET |
/org/:orgId/domain/:domainId/dns-records |
| Restart cert |
POST |
/org/:orgId/domain/:domainId/restart |
Domain types: "ns", "cname", "wildcard"
Users
| Action |
Method |
Endpoint |
| Create user |
PUT |
/org/:orgId/user |
| List users |
GET |
/org/:orgId/users |
| Get user |
GET |
/org/:orgId/user/:userId |
| Get by username |
GET |
/org/:orgId/user-by-username |
| Update user |
POST |
/org/:orgId/user/:userId |
| Remove user |
DELETE |
/org/:orgId/user/:userId |
Roles
| Action |
Method |
Endpoint |
| List roles |
GET |
/org/:orgId/roles |
| Create role |
PUT |
/org/:orgId/role |
| Get role |
GET |
/role/:roleId |
| Update role |
POST |
/role/:roleId |
| Delete role |
DELETE |
/role/:roleId |
Clients (VPN / Olm Devices)
| Action |
Method |
Endpoint |
| List clients |
GET |
/org/:orgId/clients |
| Create client |
PUT |
/org/:orgId/client |
| Get client |
GET |
/client/:clientId |
| Get by niceId |
GET |
/org/:orgId/client/:niceId |
| Update client |
POST |
/client/:clientId |
| Delete client |
DELETE |
/client/:clientId |
| Archive |
POST |
/client/:clientId/archive |
| Unarchive |
POST |
/client/:clientId/unarchive |
| Block |
POST |
/client/:clientId/block |
| Unblock |
POST |
/client/:clientId/unblock |
Site Resources (Private / VPN-accessible)
| Action |
Method |
Endpoint |
| List all |
GET |
/org/:orgId/site-resources |
| List for site |
GET |
/org/:orgId/site/:siteId/resources |
| Create |
PUT |
/org/:orgId/site-resource |
| Get |
GET |
/site-resource/:id |
| Update |
POST |
/site-resource/:id |
| Delete |
DELETE |
/site-resource/:id |
Invitations
| Action |
Method |
Endpoint |
| List invitations |
GET |
/org/:orgId/invitations |
| Create invite |
POST |
/org/:orgId/create-invite |
| Remove invite |
DELETE |
/org/:orgId/invitations/:inviteId |
Blueprints (IaC)
| Action |
Method |
Endpoint |
| Apply blueprint |
PUT |
/org/:orgId/blueprint |
| List blueprints |
GET |
/org/:orgId/blueprints |
| Get blueprint |
GET |
/org/:orgId/blueprint/:blueprintId |
Audit Logs
| Action |
Method |
Endpoint |
| Query request logs |
GET |
/org/:orgId/logs/request |
| Export request logs |
GET |
/org/:orgId/logs/request/export |
| Query analytics |
GET |
/org/:orgId/logs/analytics |
API Keys (Org-scoped)
| Action |
Method |
Endpoint |
| List keys |
GET |
/org/:orgId/api-keys |
| Create key |
PUT |
/org/:orgId/api-key |
| Get key |
GET |
/org/:orgId/api-key/:apiKeyId |
| Delete key |
DELETE |
/org/:orgId/api-key/:apiKeyId |
| List actions |
GET |
/org/:orgId/api-key/:apiKeyId/actions |
| Set actions |
POST |
/org/:orgId/api-key/:apiKeyId/actions |
Identity Providers
| Action |
Method |
Endpoint |
| List IdPs |
GET |
/idp |
| Create OIDC IdP |
PUT |
/idp/oidc |
| Update IdP |
POST |
/idp/:idpId/oidc |
| Delete IdP |
DELETE |
/idp/:idpId |
Health & Info
| Action |
Method |
Endpoint |
| Health check |
GET |
/ |
| Server info |
GET |
/server-info |
Known Configuration
Sites (10 total)
| ID |
Name |
Type |
NiceId |
| 1 |
215-node01 |
newt |
infamous-common-box-turtle |
| 2 |
hq-dev01 |
newt |
great-scytodes-thoracica |
| 3 |
Seubert-DC |
newt |
well-groomed-texas-coral-snake |
| 7 |
kaercher5630 |
newt |
rapid-texas-lined-snake |
| 8 |
WulfHQDev |
newt |
edible-forficula-auricularia |
| 9 |
WulfHQProd |
newt |
yawning-campbells-dwarf-hamster |
| 10 |
WulfExp |
newt |
flawed-indian-desert-jird |
| 11 |
Seubert-HQ |
newt |
productive-chilean-rock-rat |
| 13 |
wulfhqclaw |
newt |
flickering-common-box-turtle |
| 14 |
robot01 |
newt |
impartial-savannah-forest-tree-frog |
Domains
| ID |
Domain |
Type |
Verified |
| domain1 |
wulfconsulting.cloud |
wildcard |
yes |
| rkkbfx2qxp8bvi5 |
wulf.cloud |
wildcard |
yes |
| 4zl519ktyb1acra |
kaercherfamily.com |
wildcard |
yes |
Common Workflows
Check what's exposed publicly
- List all resources:
GET /org/wulf-consulting/resources
- For each resource, list targets:
GET /resource/:resourceId/targets
Add a new service
- Create resource:
PUT /org/wulf-consulting/resource with subdomain, domain, name
- Add target:
PUT /resource/:resourceId/target with site, IP, port
- Optionally configure SSO, whitelist, or password auth
Check site connectivity
- List sites:
GET /org/wulf-consulting/sites — check online field
- Get specific site:
GET /site/:siteId for detailed status
Manage access
- Get resource:
GET /resource/:resourceId
- Set SSO:
POST /resource/:resourceId with {"sso": true}
- Add email whitelist:
POST /resource/:resourceId/whitelist/add with {"email": "..."}
Argument Handling
$ARGUMENTS contains the full user request
- If no specific action given, show: sites (with online status), resources, and domains
- Parse natural language: "what's exposed", "list sites", "add service", "check tunnels"