Fundamentals
Everything needed to send requests and handle responses consistently.
Authentication
Send an API key in every request. Keys are created and revoked only in the dashboard.
Authorization: Bearer pw_v2_REPLACE_MEScopes
| Scope | Allows |
|---|---|
account:read | Account, usage and scope presets |
tunnels:read | Tunnels, sessions, usage and catalog |
tunnels:write | Create and configure tunnels |
tunnels:control | Start, stop and restart tunnels |
devices:read | List and inspect devices |
devices:write | Rename a device or disable remote control |
devices:control | Stop all tunnels or restart an agent |
operations:read | Read asynchronous action results |
webhooks:read | Read endpoints and delivery history |
webhooks:write | Create, edit, test and remove webhooks |
Response format
Successful responses contain data and meta. Dates use RFC 3339 in UTC. Use meta.request_id when contacting support.
{
"data": {"id": "..."},
"meta": {"request_id": "..."}
}Pagination
List endpoints that support pagination accept limit from 1 to 100. Pass meta.next_cursor unchanged as the next request's cursor. A null cursor means there are no more pages.
GET /tunnels?limit=25
GET /tunnels?limit=25&cursor=CURSOR_FROM_PREVIOUS_RESPONSESafe retries
Send a unique Idempotency-Key on every endpoint marked “Idempotency required”. Retry the same request with the same key. Do not reuse a key for a different body or resource.
| Result | Meaning |
|---|---|
| Original response | The first request completed. |
Idempotent-Replayed: true | The previous result was returned; the action was not repeated. |
409 idempotency_conflict | The key was reused for a different request. |
Errors
Errors use application/problem+json. Branch on code, not on the human-readable detail.
{
"type": "https://portwarp.com/docs/api/errors#device_offline",
"title": "Device Offline",
"status": 409,
"code": "device_offline",
"detail": "The device is offline; no command was queued.",
"request_id": "..."
}| Status | Handle it as |
|---|---|
400 | Missing or invalid request header |
401 | Missing, invalid or inactive key |
403 | Feature, scope, plan or Team role does not allow the action |
404 | Resource is absent or outside the key's scope |
409 | Current state conflicts with the requested action |
422 | Request fields failed validation |
429 | Wait for Retry-After before retrying |
feature_not_available means the requested API or remote action is not currently available for the account. Do not retry in a tight loop. Existing keys remain stored and can be used again when access becomes available.
Rate limits
Each key allows 120 requests per minute and 10,000 per day. Read X-RateLimit-Limit, X-RateLimit-Remaining and Retry-After.