API reference
The Pawpado API is a REST API that uses JSON for both requests and responses. It's the same API the portal and SDKs use; if you want to integrate Pawpado directly without an SDK, this page covers everything you need to authenticate and call it.
Base URL
https://pawpado.com
All API endpoints live under /api/v1/. There is no separate api. subdomain — the portal and the API are served from the same origin.
If you're pointing at a different deployment (staging, local), override the base URL via PAWPADO_BASE_URL in the SDKs or CLI.
Authentication
Every authenticated request carries a Huudis-issued bearer token in the Authorization header:
Authorization: Bearer <huudis-access-token>
That's it. No HMAC signing, no signature header, no timestamp window. Pawpado validates the JWT, reads sub as your huudisUserId, and scopes the response to your account.
Tokens are issued by Huudis at sign-in. The three ways to obtain one:
| Audience | How to get a token |
|---|---|
| Browser session | Automatic — sign in to pawpado.com and the cookie carries it. |
| Server-side script | pawpado auth login then read ~/.pawpado/credentials, or copy from Settings → Tokens. |
| Long-running integration | Pair an access token with its refresh token. The SDKs handle rotation when you pass Session instead of apiKey. |
Access tokens expire after 1 hour. Refresh tokens are good until revoked but rotate on every use — see Auth overview for the rotation rules.
If you call an endpoint with an expired or missing token, you get:
{ "error": "UNAUTHENTICATED", "message": "Missing or invalid bearer token" }
with HTTP 401.
Endpoints at a glance
The API surface is small — one user, one session, one wallet:
Sessions
| Method | Path | Purpose |
|---|---|---|
POST |
/api/v1/sessions/start |
Provision and start an instance. |
POST |
/api/v1/sessions/stop |
Stop the running instance. |
GET |
/api/v1/sessions/poll |
Current state + readiness. |
POST |
/api/v1/sessions/pair |
Mint a four-digit Moonlight pairing PIN. |
GET |
/api/v1/sessions/connect |
Tailnet IP + port for the streaming server. |
POST |
/api/v1/sessions/tailscale-key |
Generate an ephemeral Tailscale auth key. |
Credits
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/credits/me |
Current balance, currency, storage usage. |
POST |
/api/v1/credits/topup |
Create a Plugipay top-up checkout session. |
Billing
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/billing/storage |
Current storage tier and billing period. |
Settings
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/settings |
Read preferences. |
PATCH |
/api/v1/settings |
Update preferences (partial). |
Account
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/session |
Current Huudis identity + cookie expiry. |
DELETE |
/api/v1/account/delete |
Schedule account + EBS deletion (30-day grace). |
Health
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/health |
Liveness probe. No auth required. |
Response shape
Most successful responses are flat JSON of the resource itself, not wrapped in an envelope. For example, GET /api/v1/sessions/poll:
{
"state": "running",
"instanceId": "i-0a1b2c3d4e5f6g7h8",
"publicIp": "13.214.x.x",
"tailnetIp": "100.64.x.x",
"startedAt": "2026-05-12T10:42:00Z",
"ready": true,
"message": null
}
Errors share a consistent shape:
{
"error": "INSUFFICIENT_CREDITS",
"message": "Balance is below the 15-minute minimum to start a session"
}
HTTP status codes follow REST norms: 200 on success, 204 on success with no body, 400 for client errors, 401 for missing/invalid auth, 404 for missing resources, 409 for state conflicts (e.g. starting a session when one is already running), 429 for rate limits, 5xx for server errors.
Error catalog
The errors you're most likely to hit:
| Code | Means |
|---|---|
UNAUTHENTICATED |
Missing or invalid bearer token. |
INSUFFICIENT_CREDITS |
Balance too low to start a session. |
SESSION_ALREADY_RUNNING |
Calling start while a session is active. |
SESSION_NOT_RUNNING |
Calling stop / pair / connect with no session. |
NOT_READY |
Calling pair before ready === true. |
AWS_CAPACITY |
Jakarta region out of capacity. Transient. |
VALIDATION_ERROR |
Bad request body. The message field names the offending field. |
RATE_LIMITED |
Too many requests. Retry after the Retry-After header value. |
Rate limits
Default per-account limits:
| Endpoint class | Limit |
|---|---|
Read (GET) |
60 req/min |
Write (POST, PATCH, DELETE) |
20 req/min |
sessions/poll |
30 req/min — polling is expected; we tolerate it. |
Limits are returned in response headers:
X-RateLimit-Limit— the bucket size.X-RateLimit-Remaining— how many requests left in the window.X-RateLimit-Reset— epoch seconds when the bucket refills.
On 429, you'll see a Retry-After header in seconds.
Webhooks (none, yet)
Pawpado doesn't publish webhooks today. Top-ups are settled by Plugipay's webhooks coming inbound to Pawpado — if you want to be notified of credit changes, poll GET /api/v1/credits/me.
If your integration needs push notifications for session state changes or low-balance alerts, email support@forjio.com — we may publish them based on demand.
OpenAPI
A machine-readable OpenAPI 3.0 spec is at pawpado.com/openapi.json. Use it with Postman, openapi-generator, or any other OpenAPI tooling to build clients in languages we don't ship SDKs for.
Next
- Auth overview — how Huudis tokens work.
- SDK overview — high-level wrappers.
- Installation — install the CLI or SDK.