Polyfence is in Early Access — APIs and limits may change while we ship. We appreciate every piece of feedback.

API errors

Every HTTP error the Polyfence API can return: status code, the exact message string, when it fires, and what to do. Use this when writing retry logic, error handling, or onboarding integrations.

HTTP status codes

Polyfence uses standard HTTP semantics. The status code tells you the failure class; the message tells you the specific cause.

StatusMeaning
200/201/204Success. 200 for GET, 201 for resource creation, 204 for successful deletion.
400Bad request — validation failed, malformed body, or invalid parameter.
401Unauthorized — missing or invalid API key / session.
403Forbidden — authenticated, but key lacks the required scope.
404Not found — resource missing OR not accessible to your tenant (intentionally indistinguishable).
409Conflict — duplicate, stale state, or in-progress operation.
413Payload too large.
429Rate limit OR tier quota hit. Rate-limit responses include a Retry-After header; tier-limit responses currently don’t.
500Internal server error. Report to hello@polyfence.io with the request ID.
504Gateway timeout — typically a connector sync exceeded its time budget.

Error envelope

Every error response shares the same envelope. Switch on the HTTP status for failure class; switch on the error string for the specific cause.

{
  "success": false,
  "error": "Zone not found"
}

// Some endpoints add a "details" array with field-level breakdowns:
{
  "success": false,
  "error": "Validation failed",
  "details": [
    { "field": "radius_meters", "rule": "min", "value": 0 }
  ]
}

Authentication

Most endpoints accept either a session cookie or an `x-api-key` header (dual auth). A few are session-only (e.g. device provisioning, account deletion) and a few are key-only — the row below names the case where it matters.

401Authentication required. Provide either a valid session token or x-api-key header.
When
Request hit a dual-auth endpoint with no session cookie AND no x-api-key header.
Fix
Pass your API key via x-api-key (case-insensitive). Generate one at /account?tab=keys.
401Invalid API key
When
The x-api-key value doesn't match any stored key (SHA-256 hash lookup miss).
Fix
Verify the key is copied correctly — keys have a `pf_` prefix. If unsure, regenerate at /account?tab=keys.
401Unauthorized
When
Generic auth failure on a single-mode (API-key-only or session-only) endpoint.
Fix
Same as above — check your auth method matches the endpoint's requirement.
403Forbidden
When
Your key authenticated, but doesn't have the scope required for this endpoint (e.g. attempted DELETE with a `zones:read` key).
Fix
Issue a new key with the required scope. Scopes are immutable on existing keys.
403Key not authorised for this device
When
Device-bound key attempted to access a device_id it isn't bound to.
Fix
Use the device-bound key only for its target device. Use a tenant-scope key for cross-device operations.
401Session required. Sign in on the dashboard to provision a device.
When
Provisioning endpoint hit with an API key instead of a dashboard session.
Fix
Provisioning is dashboard-only — sign in at polyfence.io/auth/login first.

Rate limits

Polyfence enforces 50 requests per second per identifier during Early Access. 429 responses include a Retry-After header.

429Rate limit exceeded
When
You exceeded 50 req/sec from a single API key or session.
Fix
Read the Retry-After header and back off. Implement exponential backoff with jitter for retries.
429Too many requests. Please try again shortly.
When
Per-endpoint stricter limit hit (e.g. checkout endpoint has a tighter cap to prevent abuse).
Fix
Wait per Retry-After and retry.
429Too many checkout attempts. Please try again shortly.
When
Checkout endpoint specifically — capped tighter than the general rate limit.
Fix
Wait at least 60s before retrying. If checkout repeatedly fails, contact support.

Tier limits

Polyfence's tier system enforces quotas on zones, API calls, and active API keys. Hits return 429 with a tier-specific message. During Early Access most quotas are unlimited; pricing flip moves these to Free/Pro tier values.

429Zone limit reached
When
You attempted to create a zone when your account is at the tier's zone cap.
Fix
Delete unused zones, or contact hello@polyfence.io to upgrade.
429Zone limit exceeded
When
Bulk import would push your zone count above the tier cap.
Fix
Reduce the import batch, delete unused zones, or upgrade.
429API call limit reached
When
Your monthly API call quota is exhausted for the billing period.
Fix
Wait until the next billing cycle, or upgrade for a higher cap.
429API key limit reached
When
You already have the maximum number of active API keys (10 by default).
Fix
Revoke an unused key at /account?tab=keys before creating a new one.

Validation

Request bodies are validated with Zod schemas. Validation failures return 400 with a specific message naming the offending field or rule.

400Invalid JSON
When
Request body isn't valid JSON.
Fix
Verify Content-Type: application/json and that the body is well-formed.
400Validation failed
When
Zod schema rejected one or more fields. Response includes a `details` array with per-field errors.
Fix
Inspect the `details` array; each entry names the field and the rule that failed.
400Zone data validation failed
When
Zone payload failed deep validation (e.g. polygon self-intersects, radius out of range, coordinate out of [-90, 90] or [-180, 180]).
Fix
POST the payload to /api/zones/validate first to surface the exact violation, or inspect the `details` array on the error response.
400Circle zones require center coordinates and radius
When
POST /zones with type=circle but missing center_lat, center_lng, or radius_meters.
Fix
Include all three fields for circle zones.
400Polygon zones require polygon coordinates
When
POST /zones with type=polygon but missing or empty polygon_coordinates array.
Fix
Provide a polygon_coordinates array with at least 3 [lng, lat] pairs.
400Invalid dev_eui — must be 16 hexadecimal characters
When
Device provisioning payload had a malformed DevEUI.
Fix
DevEUI must be exactly 16 hex characters (case-insensitive).
400Invalid zone ID format
When
zone_id parameter isn't a valid UUID.
Fix
Zone IDs are UUIDs (e.g. `123e4567-e89b-12d3-a456-426614174000`).
400Invalid scopes provided
When
API key creation request included scopes not in the allowed set.
Fix
Use scopes from: zones:read, zones:write, zones:delete, zones:* (and admin:* for admin keys).
400Missing Idempotency-Key header (required for deduplication)
When
An idempotent endpoint (e.g. provisioning) was called without an Idempotency-Key header.
Fix
Generate a UUID per logical operation and pass it as Idempotency-Key — retries with the same key are deduplicated.

Not found

Polyfence returns 404 both for genuinely missing resources and for cross-tenant access attempts. The two are intentionally indistinguishable to avoid resource enumeration.

404Zone not found
When
Zone ID doesn't exist, or exists under a different tenant (`profile_user_id` filter doesn't match).
Fix
Verify the zone belongs to your account. Listing GET /zones shows everything your key can access.
404Device not found
When
Device ID doesn't exist or belongs to a different tenant.
Fix
Same — verify the device is provisioned to your account.
404Profile not found for authenticated user
When
The authenticated user has no row in the profiles table (rare — usually a sync issue with Supabase trigger).
Fix
Sign out and sign back in to trigger the profile-creation hook. If it persists, contact support.
404Not found
When
Generic 404 for unmatched routes or missing related resources (e.g. an event whose parent zone was deleted).
Fix
Check the URL path and any referenced IDs.
404Assignment not found
When
Device-to-zone assignment lookup missed.
Fix
Verify the device is currently assigned to the zone via GET /v1/devices/{deviceId}.

Conflict / idempotency

Mostly relevant to device provisioning and bulk operations. These responses signal a state collision rather than a malformed request.

409Duplicate zone name
When
POST /zones with a name that's already used in your account (the unique constraint is per-tenant).
Fix
Pick a unique name. Zone names don't need to be globally unique — only within your account.
409DevEUI collision — retry the provisioning request
When
The auto-generated DevEUI collided with an existing device (rare; ~2^-64 odds per attempt).
Fix
Retry with the same Idempotency-Key — provisioning will pick a fresh DevEUI.
409Provisioning already in progress for this device — retry
When
A concurrent provisioning request for the same device is still completing.
Fix
Wait ~1s and retry with the same Idempotency-Key.
400One or more zone_ids are not accessible to this account
When
Bulk operation referenced zone IDs that don't belong to your tenant.
Fix
Filter the list to zones your account owns. The error doesn't reveal which IDs failed (no enumeration).
400Too many zones in bulk import
When
Bulk import payload exceeds the 500-zone cap.
Fix
Split into multiple batches of ≤500 zones each.

Connector / SSRF

Data connector endpoints reject URLs targeting private networks, localhost, link-local, and cloud metadata services. Validation runs both at config-save time AND at fetch time (DNS rebinding protection).

400Only HTTP and HTTPS protocols are allowed
When
Connector URL uses a non-HTTP scheme (file://, ftp://, gopher://, etc.).
Fix
Use https:// in production. http:// is allowed but discouraged.
400Cannot connect to localhost
When
Resolved DNS pointed at 127.0.0.1, ::1, or localhost.
Fix
Use a publicly addressable host. Connectors run from Polyfence's infrastructure — your localhost isn't reachable.
400Cannot connect to private IP address (10.x.x.x)
When
Resolved DNS pointed at the 10.0.0.0/8 RFC1918 block.
Fix
Expose the service publicly (Cloudflare Tunnel, ngrok, or a public ingress) and use that hostname.
400Cannot connect to private IP address (172.16-31.x.x)
When
DNS resolved into 172.16.0.0/12.
Fix
Same — expose publicly.
400Cannot connect to private IP address (192.168.x.x)
When
DNS resolved into 192.168.0.0/16.
Fix
Same — expose publicly.
400Cannot connect to link-local address
When
DNS resolved into 169.254.0.0/16 (link-local) or fe80::/10 (IPv6 link-local).
Fix
Use a routable address.
400Cannot connect to cloud metadata service
When
DNS resolved into 169.254.169.254 (AWS/GCP/Azure metadata IP).
Fix
These IPs are blocked unconditionally — they're an SSRF risk regardless of cloud provider.
400Invalid URL format
When
URL didn't parse as a valid HTTP(S) URL.
Fix
Use a standard URL form: `https://host[:port]/path?query`.
413Response too large. Maximum size is 10MB.
When
Connector source returned a response body larger than 10MB.
Fix
Paginate at the source or use a connector that streams.

Subscription

Billing-related endpoints (currently routed via Polar). Most users won't hit these during Early Access.

400Invalid tier for checkout
When
Checkout request named a tier that doesn't have a Polar product configured.
Fix
Use `pro` as the tier. Other tiers aren't wired for self-service checkout yet.
400No product configured for this tier.
When
POLAR_PRO_PRODUCT_ID env is unset on the server.
Fix
Contact hello@polyfence.io — this is a configuration issue on our side, not yours.
409You are already on this plan.
When
Checkout attempted for the tier the account is currently on.
Fix
No action needed — the API is preventing a redundant subscription.
404No subscription to reactivate.
When
Reactivation endpoint hit but the account has no canceled subscription on record.
Fix
Start a fresh checkout via POST /api/checkout if you want to subscribe.

Looking for the rest of the docs? Back to developer documentation.

Polyfence API errors — catalog