Skip to content

API Keys & Enrollment Keys

Breeze RMM provides two distinct key types for different purposes:

  • API Keys grant programmatic access to the REST API for automation, integrations, and scripting.
  • Enrollment Keys are short-lived tokens used to onboard new agents into a specific organization and (optionally) site.

Both key types are organization-scoped and require appropriate permissions to manage.


API Keys

API keys allow external systems to authenticate against the Breeze API without a user session. Each key is tied to a single organization and can be scoped to restrict which operations it may perform.

Key Format

API keys use the brz_ prefix followed by 32 characters of base64url-encoded randomness:

brz_aBcDeFgHiJkLmNoPqRsTuVwXyZ012

Only the first 12 characters (the key prefix, e.g. brz_aBcDeFgH) are stored in plaintext for identification. The full key is SHA-256 hashed before storage and is only returned once at creation time.

Creating an API Key

  1. Authenticate with a user session that has the organizations:write permission.
  2. Send a POST request to /api/v1/api-keys with the key configuration.
  3. Copy the key field from the response and store it securely.
Terminal window
curl -X POST https://breeze.yourdomain.com/api/v1/api-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orgId": "ORG_UUID",
"name": "CI/CD Pipeline Key",
"scopes": ["devices:read", "scripts:execute"],
"expiresAt": "2026-12-31T23:59:59Z",
"rateLimit": 5000
}'

Request body fields:

FieldTypeRequiredDefaultDescription
orgIdstring (uuid)YesOrganization the key belongs to.
namestringYesHuman-readable label (1-255 characters).
scopesstring[]No[]Permissions granted to this key.
expiresAtstring (datetime)Nonull (never)ISO 8601 expiration timestamp.
rateLimitintegerNo1000Maximum requests per hour (1-100,000).

Scopes

Scopes restrict which API operations a key can perform. When a route is protected by requireApiKeyScope(), the key must include at least one of the required scopes.

  • A key with an empty scopes array ([]) can access unsecured endpoints but will be denied by any scope-protected route.
  • The wildcard scope "*" grants access to all scope-protected endpoints.
{
"scopes": ["devices:read", "devices:write", "scripts:execute"]
}

Scopes can be updated after creation via the PATCH endpoint without rotating the key material.

Authentication Flow

To authenticate with an API key, include it in the X-API-Key header:

Terminal window
curl -H "X-API-Key: brz_aBcDeFgHiJkLmNoPqRsTuVwXyZ012" \
https://breeze.yourdomain.com/api/v1/devices

The middleware performs these steps in order:

  1. Extract the key from the X-API-Key header.
  2. Validate the brz_ prefix format.
  3. Compute the SHA-256 hash and look up the matching record.
  4. Verify the key status is active (not revoked or expired).
  5. Check the expiresAt timestamp; if expired, mark the key as expired in the database and reject the request.
  6. Enforce rate limiting via Redis sliding window (per key, 1-hour window).
  7. Update lastUsedAt and usageCount asynchronously.
  8. Set the organization context for row-level access control.

Rate limit headers are returned on every authenticated response:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per hour.
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetUnix timestamp when the window resets.
Retry-AfterSeconds until the rate limit resets (only on 429 responses).

Dual Authentication

Some endpoints accept either a JWT bearer token or an API key. When both headers are present, the API key takes precedence. The eitherAuthMiddleware enables this by checking for X-API-Key first, then falling back to the Authorization bearer token.

Key Rotation

Rotation generates new key material for an existing key record. The old key is immediately invalidated. Usage statistics (usageCount, lastUsedAt) are reset.

Terminal window
curl -X POST https://breeze.yourdomain.com/api/v1/api-keys/KEY_UUID/rotate \
-H "Authorization: Bearer $TOKEN"

The response contains the new full key in the key field. All other attributes (name, scopes, rate limit, expiration) are preserved.

Revocation

Revoking an API key performs a soft delete — the record remains in the database with status: "revoked" but the key can no longer authenticate. Revoked keys cannot be rotated or updated.

Terminal window
curl -X DELETE https://breeze.yourdomain.com/api/v1/api-keys/KEY_UUID \
-H "Authorization: Bearer $TOKEN"

Key Statuses

StatusMeaning
activeKey can authenticate requests.
expiredThe expiresAt timestamp has passed. Set automatically on the next authentication attempt.
revokedManually disabled via the DELETE endpoint. Permanent.

Enrollment Keys

Enrollment keys are purpose-built tokens for agent onboarding. They are tied to an organization (and optionally a site) and enforce both a time-to-live (TTL) and a maximum usage count to limit exposure.

Key Format

Enrollment keys are 64-character hex strings generated from 32 random bytes. Before storage, the key is hashed with SHA-256 using a server-side pepper (configured via ENROLLMENT_KEY_PEPPER, APP_ENCRYPTION_KEY, SECRET_ENCRYPTION_KEY, or JWT_SECRET environment variable).

Creating an Enrollment Key

  1. Authenticate with a user session that has the organizations:write permission.
  2. Send a POST request to /api/v1/enrollment-keys.
  3. Copy the key field from the response and embed it in the agent installer or provisioning script.
Terminal window
curl -X POST https://breeze.yourdomain.com/api/v1/enrollment-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orgId": "ORG_UUID",
"siteId": "SITE_UUID",
"name": "NYC Office Deployment",
"maxUsage": 50,
"expiresAt": "2026-03-01T00:00:00Z"
}'

Request body fields:

FieldTypeRequiredDefaultDescription
orgIdstring (uuid)Depends on scopeInferred for org-scoped usersTarget organization. Required for partner and system scopes.
siteIdstring (uuid)NonullPin enrolled agents to a specific site.
namestringYesHuman-readable label (1-255 characters).
maxUsageintegerNo1Maximum number of agents that can enroll with this key (1-100,000).
expiresAtstring (datetime)NoNow + TTLISO 8601 expiration. Defaults to the current time plus ENROLLMENT_KEY_DEFAULT_TTL_MINUTES (default: 60 minutes).

TTL and Max Usage

Enrollment keys have two expiration mechanisms:

  • TTL (expiresAt): After this timestamp, the key cannot be used regardless of remaining usage. The default is 60 minutes from creation.
  • Max usage (maxUsage): Limits the number of agents that can enroll with this key. Each successful enrollment increments usageCount. Once usageCount >= maxUsage, the key is exhausted.

These constraints work together. A key with maxUsage: 50 and a 24-hour TTL will stop accepting enrollments when either 50 agents have used it or 24 hours have passed, whichever comes first.

Key Rotation

Enrollment key rotation generates new key material in-place. The usage counter is reset to zero. You can optionally update maxUsage and expiresAt during rotation.

Terminal window
curl -X POST https://breeze.yourdomain.com/api/v1/enrollment-keys/KEY_UUID/rotate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"maxUsage": 100,
"expiresAt": "2026-04-01T00:00:00Z"
}'

If maxUsage or expiresAt are omitted from the rotation request, the existing values are preserved.

Deletion

Unlike API keys (which are soft-deleted), enrollment keys are hard deleted from the database:

Terminal window
curl -X DELETE https://breeze.yourdomain.com/api/v1/enrollment-keys/KEY_UUID \
-H "Authorization: Bearer $TOKEN"

Multi-Tenant Access Control

Both key types enforce the Breeze multi-tenant hierarchy:

ScopeAPI KeysEnrollment Keys
OrganizationCan only manage keys for their own org.Can only manage keys for their own org. orgId is inferred if omitted.
PartnerCan manage keys for any org they have access to.Must provide orgId. Limited to accessible orgs.
SystemCan manage keys for any org.Must provide orgId. Unrestricted.

All management endpoints require the organizations:read permission for listing/viewing and organizations:write for creating, updating, rotating, and deleting.


API Reference

API Key Endpoints

All routes are prefixed with /api/v1/api-keys. All routes require JWT authentication and the noted permissions.

GET /api-keys

List API keys for accessible organizations. The keyHash is never returned.

Query parameters:

ParameterTypeDescription
pagestringPage number (default: 1).
limitstringResults per page (default: 50, max: 100).
orgIdstring (uuid)Filter by organization.
statusactive | revoked | expiredFilter by key status.

Required permission: organizations:read


POST /api-keys

Create a new API key. Returns the full key exactly once.

Required permission: organizations:write + MFA

Response (201):

{
"id": "uuid",
"orgId": "uuid",
"name": "My Key",
"key": "brz_...",
"keyPrefix": "brz_aBcDeFgH",
"scopes": ["devices:read"],
"expiresAt": "2026-12-31T23:59:59.000Z",
"rateLimit": 1000,
"createdBy": "uuid",
"createdAt": "2026-02-18T...",
"status": "active",
"warning": "Store this API key securely. It will not be shown again."
}

GET /api-keys/:id

Retrieve metadata for a single API key. The keyHash is not included.

Required permission: organizations:read


PATCH /api-keys/:id

Update an active API key’s name, scopes, or rate limit. Cannot update revoked or expired keys.

Required permission: organizations:write + MFA

Request body (all fields optional):

FieldTypeDescription
namestringNew display name (1-255 characters).
scopesstring[]Replace the scopes array.
rateLimitintegerNew rate limit (1-100,000 requests/hour).

DELETE /api-keys/:id

Revoke an API key (soft delete). Sets status to revoked.

Required permission: organizations:write + MFA


POST /api-keys/:id/rotate

Generate new key material. The old key is immediately invalidated. Usage stats are reset. Returns the new full key once.

Required permission: organizations:write + MFA


Enrollment Key Endpoints

All routes are prefixed with /api/v1/enrollment-keys. All routes require JWT authentication and the noted permissions.

GET /enrollment-keys

List enrollment keys for accessible organizations. The raw key value is never returned in list responses.

Query parameters:

ParameterTypeDescription
pagestringPage number (default: 1).
limitstringResults per page (default: 50, max: 100).
orgIdstring (uuid)Filter by organization.
expiredtrue | falseFilter by expiration status.

Required permission: organizations:read


POST /enrollment-keys

Create a new enrollment key. Returns the raw key exactly once.

Required permission: organizations:write + MFA

Response (201):

{
"id": "uuid",
"orgId": "uuid",
"siteId": "uuid or null",
"name": "NYC Office Deployment",
"usageCount": 0,
"maxUsage": 50,
"expiresAt": "2026-03-01T00:00:00.000Z",
"createdBy": "uuid",
"createdAt": "2026-02-18T...",
"key": "a1b2c3d4e5f6..."
}

GET /enrollment-keys/:id

Retrieve metadata for a single enrollment key. The raw key is not included.

Required permission: organizations:read


POST /enrollment-keys/:id/rotate

Generate new key material in-place. Resets usageCount to zero. Optionally update maxUsage and expiresAt.

Required permission: organizations:write + MFA

Request body (all fields optional):

FieldTypeDescription
maxUsageinteger | nullNew max usage (1-100,000), or null for unlimited.
expiresAtstring (datetime)New expiration timestamp.

DELETE /enrollment-keys/:id

Permanently delete an enrollment key (hard delete).

Required permission: organizations:write + MFA


Audit Logging

Every mutating operation on both key types is recorded in the audit log with:

  • Actor: The user ID and email of the person performing the action.
  • Action: api_key.create, api_key.update, api_key.rotate, api_key.revoke, enrollment_key.create, enrollment_key.rotate, enrollment_key.delete.
  • Resource: The key ID and name.
  • Details: Changed fields, scope updates, usage counts, and expiration changes.
  • Request metadata: IP address (from X-Forwarded-For or X-Real-IP) and user agent.

Security Best Practices

  1. Use scoped keys. Assign the minimum set of scopes needed for each integration. Avoid the wildcard "*" scope in production.
  2. Set expiration dates. Always set expiresAt on API keys to limit the blast radius of a leaked credential.
  3. Rotate keys regularly. Use the /rotate endpoint to generate new key material without changing the key ID or configuration.
  4. Keep enrollment keys short-lived. The default 60-minute TTL is intentional. Only extend it if your deployment workflow requires it.
  5. Set tight maxUsage limits. If you are deploying 10 agents, set maxUsage: 10 rather than leaving it open.
  6. Pin enrollment keys to a site. Use siteId to ensure agents enroll into the correct location.
  7. Store keys in a secrets manager. Never commit API keys to version control or embed them in client-side code.
  8. Monitor usage. Review lastUsedAt, usageCount, and audit logs to detect anomalous key usage.
  9. Revoke unused keys. Periodically review active keys and revoke any that are no longer needed.
  10. Require MFA. Key management endpoints enforce MFA verification. Ensure all administrators have MFA enabled.

Troubleshooting

”Missing X-API-Key header” (401)

The request did not include the X-API-Key header. Ensure the header name is exactly X-API-Key (case-sensitive in some HTTP clients).

”Invalid API key format” (401)

The provided key does not start with brz_. Check that the full key is being sent, not the key prefix or key ID.

”Invalid API key” (401)

The SHA-256 hash of the provided key does not match any record in the database. Possible causes:

  • The key was rotated and the old value is being used.
  • The key was copy-pasted incorrectly (trailing whitespace, truncation).

”API key is revoked” / “API key is expired” (401)

The key exists but is no longer active. Create a new key or rotate an existing active key.

”Rate limit exceeded” (429)

The key has exceeded its configured rateLimit within the current 1-hour window. Check the Retry-After response header for how long to wait. To increase the limit, update the key via PATCH /api-keys/:id with a higher rateLimit value.

”API key does not have required permissions” (403)

The key’s scopes array does not include any of the scopes required by the endpoint. Update the key’s scopes or use a key with broader permissions.

”Organization context required” (403)

An organization-scoped user attempted an operation without a valid orgId in their authentication context. This typically indicates a misconfigured user account.

”Cannot update revoked API key” (400)

Only active keys can be updated or rotated. If you need to restore access, create a new key.

Enrollment key not working during agent enrollment

  • Key expired: Check expiresAt. The default TTL is 60 minutes. Create a new key or rotate the existing one.
  • Usage exhausted: Check usageCount against maxUsage. Rotate the key to reset the counter, or create a new key with a higher maxUsage.
  • Wrong organization: Verify the orgId on the enrollment key matches the target organization.
  • Pepper mismatch: If the server’s pepper environment variable changed since the key was created, existing keys will fail validation. Rotate or recreate affected keys.

”No enrollment key pepper configured” (server startup error)

In production, one of these environment variables must be set: ENROLLMENT_KEY_PEPPER, APP_ENCRYPTION_KEY, SECRET_ENCRYPTION_KEY, or JWT_SECRET. This protects enrollment key hashes from rainbow table attacks.