Phaze Enterprise API
Please note: This is an early v1 release of the Phaze Enterprise API. We may need to introduce breaking changes to this version in the future. For example, we plan to change how Phaze Relays are managed within an organization, which may affect parts of this API. Before introducing that change, we will email all customers who have created an API key to make sure they're aware of the expected change.
Overview
The Phaze Enterprise API is a REST interface for managing your enterprise from your own tools and automation. With an API key created in the Phaze admin dashboard, you can invite and manage members, organize them into organizations and groups, control who can reach which machines, and run the locations and relays that carry connections inside your network. This API is available to enterprises on a Phaze Enterprise plan.
Authentication
Every request must carry an enterprise API key as a Bearer token in the Authorization header (Authorization: Bearer <key>).
Keys are created by enterprise admins in the Phaze web app and are scoped to a single enterprise. Every resource in this API is resolved within that enterprise, so an organization or user that belongs to another enterprise is reported as 404 not_found.
Unknown, revoked, or expired keys receive 401 unauthorized.
Rate limits
Limits are enforced per enterprise, so all keys of one enterprise share the same budget. Exceeding a limit returns 429 too_many_requests and starts a lockout during which every request in that scope is rejected.
| Scope | Limit | Lockout after exceeding |
|---|---|---|
| Default, all endpoints | 200 requests per minute | 5 minutes |
Email sending: POST /invites and POST /invites/resend when send_email is true |
10 requests per 10 minutes | 10 minutes |
Rate limit headers
Every response includes the current rate limit state for its scope.
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum number of requests allowed in the current window |
X-RateLimit-Remaining |
Requests left in the current window, 0 while rejected |
Retry-After |
Seconds to wait before retrying. Only present on 429 responses |
Pagination
List endpoints accept the following query parameters. Out-of-range values are clamped rather than rejected. The response count is the total number of matching items, so offset + limit < count means more pages remain.
| Parameter | Default | Description |
|---|---|---|
limit |
50 |
Number of items per page, from 1 to 200 |
offset |
0 |
Number of items to skip, 0 or greater |
Requests
- The base URL is
https://public-api.phaze.app/enterprise/v1. All paths in this reference are relative to it. - Request bodies must be JSON and sent with
Content-Type: application/json. Other media types receive415. - Requests time out after 10 seconds.
PATCHendpoints are partial updates. Send only the fields to change; omitted fields keep their current value. A body with no updatable fields is rejected with400.- Bulk endpoints take an array of items with a per-endpoint maximum. Duplicates are rejected with
422. Where an endpoint can partially succeed, valid items are applied and the rest are returned underrejectedwith areason.
Responses
Every JSON response carries a status field with the HTTP status text. Single resources are returned under data. Lists return an array under data plus a count of the total items matching the query across all pages. 204 No Content responses have no body.
// single resource
{ "status": "OK", "data": { ... } }
// paginated resources
{ "status": "OK", "data": [ ... ], "count": 0 }
Errors
The API uses standard HTTP status codes. Unsuccessful responses carry an errors array where each item has a machine-readable code and a human-readable message. Validation failures return 422 with one item per invalid field, where field names the request body field.
// error
{ "status": "Not Found", "errors": [ { "code": "not_found", "message": "..." } ] }
// validation error
{ "status": "Unprocessable Entity", "errors": [ { "code": "validation_failed", "field": "role", "message": "..." } ] }
| Status | Code | When |
|---|---|---|
| 400 | bad_request |
Malformed JSON, invalid path or query parameter, or an empty update |
| 401 | unauthorized |
Missing, unknown, or expired API key |
| 403 | forbidden |
The operation is disabled for this enterprise |
| 403 | limit_reached |
A resource limit was hit, the message names the limit |
| 404 | not_found |
The resource does not exist in this enterprise |
| 405 | method_not_allowed |
Unsupported method for the path |
| 409 | conflict |
A resource with the same name already exists |
| 409 | org_not_empty |
The organization cannot be deleted while it has machines or relays |
| 409 | location_has_relays |
The location cannot be deleted while it has relays |
| 415 | unsupported_media_type |
Request body is not application/json |
| 422 | validation_failed |
One or more request body fields are invalid |
| 429 | too_many_requests |
Rate limit exceeded, see Retry-After |
| 500 | internal_server_error |
Unexpected server error |
Timestamps
Timestamps in request and response bodies are RFC 3339 strings in UTC, for example 2026-09-04T18:21:07Z.
Resource limits
Creating a resource beyond these limits fails with 403 limit_reached.
| Resource | Limit |
|---|---|
| Organizations per enterprise | 20 |
| Members per enterprise, pending invites included | 5000 |
| Members per organization | 2000 |
| Machine keys per organization | 100 |
| Groups per organization | 200 |
| Members per group | 500 |
| Locations per organization | 100 |
| Relays per organization | 200 |
SCIM
When directory sync (SCIM) is enabled for the enterprise, the identity provider owns the member lifecycle. Creating, resending, or deleting invites and deleting members return 403 forbidden. Role changes and organization assignments remain available.