TunnelSats uses standardized machine-readable error codes. Since our API involves real-world Lightning transactions, understanding these codes is the difference between a smooth automation and a broken one.
All error responses follow the ApiError schema: {"error": "CODE", "message": "Reason"}.
| Code | HTTP | Meaning | Mitigation |
|---|---|---|---|
ERR_INVALID_INPUT | 400 | Invalid serverId, duration, malformed pubkey, bandwidth usage below threshold (<70%), or expired subscription. | Check request payload and verify subscription status. |
ERR_UNAUTHORIZED | 401 | Missing or invalid Authorization header. | Use Bearer <api_key> or Nostr <token>. |
ERR_PAYMENT_REQUIRED | 402 | Subscription exists but hasn't been paid yet. | Poll the status until it returns paid. |
ERR_MIGRATION_REQUIRED | 403 | Attempting to renew on a legacy server (us1). | Use the migration tool to move to us3. |
ERR_RESOURCE_NOT_FOUND | 404 | No subscription found for that paymentHash. | Verify your hash from the /create call. |
ERR_RATE_LIMIT_EXCEEDED | 429 | Rate limit exceeded or maximum monthly bandwidth resets reached (max 2/month). | Implement exponential backoff or wait for monthly reset counter to refresh. |
ERR_INTERNAL_ERROR | 500 | Internal server error. | Wait 30 seconds and retry. |
503 | 503 | Backend VPN server unavailable or syncing (retryable). | Retry with exponential backoff. This is common during bandwidth-reset webhook processing if the VPN manager is temporarily unreachable. |
Two layers protect the API: a Cloudflare WAF burst limiter (10 req/10s per IP across all /api/public/*) and per-endpoint application limits:
| Endpoint | Limit | Window | Key |
|---|---|---|---|
GET /servers | 60/min | 60s | Per IP |
POST /create | 10/min | 60s | Per IP |
POST /claim | 10/min | 60s | Per IP |
GET /{paymentHash} | 60/min | 60s | Per IP |
POST /status | 30/min | 60s | Per IP |
POST /renew | 20/min | 60s | Per IP |
POST /bandwidth-reset | 10/min | 60s | Per IP |
POST /{paymentHash}/sync | 5/min | 60s | Per subscription |
GET /list | 30/min | 60s | Per account |
POST /ping/lookup | 5/min | 60s | Per IP |
POST /ping/test | 3/min | 60s | Per IP |
WARNING
402 vs. 404
If a user waits too long to pay, the invoice will expire.
ERR_PAYMENT_REQUIRED.paymentHash may eventually return ERR_RESOURCE_NOT_FOUND./create.DANGER
ERR_RATE_LIMIT_EXCEEDED
Our public endpoints are rate-limited to ensure availability for all node operators.
INFO
Cloudflare Access Bypass
Requests to dev2.tunnelsats.com require Cloudflare Service Tokens.
CF-Access-Client-IdCF-Access-Client-Secret302 Redirect to a login HTML page.