70 KiB
TTSCM API Routes Documentation
This document provides a comprehensive overview of all API routes available in the TTSCM API.
Base URL
http://localhost:3000/v1
Authentication Routes
Get Authentication URI
GET /auth/uri
Get the Microsoft OAuth authentication URI for user login.
Authentication Required: No
Response:
{
"status": 200,
"message": "Successfully fetch Auth URI",
"data": {
"uri": "https://login.microsoftonline.com/...",
"callbackKey": "ck123..."
},
"successful": true
}
OAuth Redirect Handler
GET /auth/redirect
Handles the OAuth redirect callback from Microsoft. This endpoint processes the authorization code and creates a user session.
Authentication Required: No
Query Parameters:
code- Authorization code from Microsoftstate- Callback key for WebSocket notification
Response: Closes the browser window and emits authentication tokens via WebSocket.
Refresh Access Token
POST /auth/refresh
Refresh an expired access token using a valid refresh token.
Authentication Required: Yes (Refresh Token)
Headers:
x-refresh-token- The refresh token
Response:
{
"status": 201,
"message": "Token refreshed successfully!",
"data": {
"accessToken": "eyJhbGc...",
"refreshToken": "eyJhbGc..."
},
"successful": true
}
User Routes
Get Current User
GET /user/@me
Fetch the currently authenticated user's information.
Authentication Required: Yes
Required Scopes: user.read
Response:
{
"status": 200,
"message": "Fetched user.",
"data": {
"id": "ckx...",
"name": "John Doe",
"email": "john.doe@example.com",
"login": "john.doe",
"image": "https://...",
"roles": ["admin"],
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-02-14T00:00:00.000Z"
},
"successful": true
}
Update Current User
PATCH /user/@me
Update the currently authenticated user's information.
Authentication Required: Yes
Required Scopes: user.write
Request Body:
{
"name": "Jane Doe",
"image": "https://example.com/avatar.jpg"
}
Response:
{
"status": 200,
"message": "Successfully updated user.",
"data": {
"id": "ckx...",
"name": "Jane Doe",
"email": "jane.doe@example.com",
"image": "https://example.com/avatar.jpg"
},
"successful": true
}
Check User Permissions
POST /user/@me/check-permission
Check if the currently authenticated user has specific permissions. Accepts an array of permission nodes and returns the status for each.
Authentication Required: Yes
Required Scopes: user.read
Request Body:
{
"permissions": ["user.read", "company.create", "credential.write"]
}
Response:
{
"status": 200,
"message": "Permission check completed.",
"data": {
"results": [
{
"permission": "user.read",
"hasPermission": true
},
{
"permission": "company.create",
"hasPermission": false
},
{
"permission": "credential.write",
"hasPermission": true
}
]
},
"successful": true
}
Other User Routes
Get All Users
GET /user/users
Fetch a list of all users.
Authentication Required: Yes
Required Permissions: user.read.other, user.list.other
Response:
{
"status": 200,
"message": "Users Fetched Successfully!",
"data": [
{
"id": "ckx...",
"name": "John Doe",
"email": "john.doe@example.com",
"login": "john.doe",
"image": "https://...",
"roles": ["admin"]
}
],
"successful": true
}
Get User by ID
GET /user/users/:identifier
Fetch a specific user by their ID.
Authentication Required: Yes
Required Permissions: user.read.other
Path Parameters:
identifier- The user's ID
Response:
{
"status": 200,
"message": "User Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "John Doe",
"email": "john.doe@example.com",
"login": "john.doe",
"image": "https://...",
"roles": ["admin"]
},
"successful": true
}
Error Response (404):
{
"status": 404,
"message": "User with identifier 'ckx...' was not found.",
"error": "UserNotFound",
"successful": false
}
Update User by ID
PATCH /user/users/:identifier
Update a specific user's information. Supports updating profile fields, roles, and direct permissions.
Authentication Required: Yes
Required Permissions: user.write.other
Conditional Permissions:
- If
rolesis included in the body:user.roles.otheris also required - If
permissionsis included in the body:user.permissions.otheris also required
Path Parameters:
identifier- The user's ID
Request Body:
All fields are optional. Include only the fields you want to update.
{
"name": "Jane Doe",
"image": "https://example.com/avatar.jpg",
"roles": ["admin", "moderator"],
"permissions": ["credential.fetch", "company.fetch"]
}
| Field | Type | Description |
|---|---|---|
name |
string |
The user's display name |
image |
string |
URL to the user's avatar image |
roles |
string[] |
Array of role ids or monikers to assign (replaces all roles) |
permissions |
string[] |
Array of permission nodes to assign (replaces all permissions) |
Response:
{
"status": 200,
"message": "User Updated Successfully!",
"data": {
"id": "ckx...",
"name": "Jane Doe",
"email": "jane.doe@example.com",
"login": "jane.doe",
"image": "https://example.com/avatar.jpg",
"roles": ["admin", "moderator"]
},
"successful": true
}
Error Response (403 - Missing role permission):
{
"status": 403,
"message": "You do not have permission to modify roles on another user.",
"error": "InsufficientPermission",
"successful": false
}
Delete User by ID
DELETE /user/users/:identifier
Delete a specific user.
Authentication Required: Yes
Required Permissions: user.delete.other
Path Parameters:
identifier- The user's ID
Response:
{
"status": 200,
"message": "User Deleted Successfully!",
"data": {
"id": "ckx...",
"name": "John Doe",
"email": "john.doe@example.com",
"login": "john.doe",
"image": "https://...",
"roles": ["admin"]
},
"successful": true
}
Get User Roles
GET /user/users/:identifier/roles
Fetch all roles assigned to a specific user.
Authentication Required: Yes
Required Permissions: user.read.other, role.read
Path Parameters:
identifier- The user's ID
Response:
{
"status": 200,
"message": "User Roles Fetched Successfully!",
"data": [
{
"id": "uuid...",
"title": "Administrator",
"moniker": "admin",
"permissions": ["*"]
}
],
"successful": true
}
Check User Permissions (Other User)
POST /user/users/:identifier/check-permission
Check if a specific user has certain permissions.
Authentication Required: Yes
Required Permissions: user.read.other
Path Parameters:
identifier- The user's ID
Request Body:
{
"permissions": ["user.read", "company.fetch", "credential.write"]
}
Response:
{
"status": 200,
"message": "Permission check completed.",
"data": {
"results": [
{
"permission": "user.read",
"hasPermission": true
},
{
"permission": "company.fetch",
"hasPermission": false
},
{
"permission": "credential.write",
"hasPermission": true
}
]
},
"successful": true
}
Company Routes
Get All Companies
GET /company/companies
Fetch a paginated list of all companies with optional search functionality.
Authentication Required: Yes
Required Permissions: company.fetch.many
Query Parameters:
page(optional) - Page number (default: 1)rpp(optional) - Records per page (default: 30)search(optional) - Search query to filter companies
Response:
{
"status": 200,
"message": "Companies Fetched Successfully!",
"data": [
{
"id": "ckx...",
"name": "Acme Corp",
"cw_CompanyId": 12345,
"cw_Identifier": "AcmeCorp"
}
],
"pagination": {
"previousPage": null,
"currentPage": 1,
"nextPage": 2,
"totalPages": 10,
"totalRecords": 300,
"listedRecords": 30
},
"successful": true
}
Get Company by ID
GET /company/companies/:identifier
Fetch a single company by its ID. Automatically fetches fresh data from ConnectWise and returns it along with internal company data.
Authentication Required: Yes
Required Permissions:
company.fetch(base permission)company.fetch.address(required whenincludeAddress=true)company.fetch.contacts(required whenincludeAllContacts=true)
URL Parameters:
identifier- Company ID (internal database ID)
Query Parameters:
includeAddress(optional) - Set to "true" to include full address information. Requirescompany.fetch.addresspermission. (default: false)includePrimaryContact(optional) - Set to "true" to include the company's default contact from ConnectWise. (default: false)includeAllContacts(optional) - Set to "true" to include all contacts for the company from ConnectWise. Requirescompany.fetch.contactspermission. (default: false)
Response (without optional query params):
{
"status": 200,
"message": "Company Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "Acme Corp",
"cw_CompanyId": 12345,
"cw_Identifier": "AcmeCorp",
"cw_Data": {}
},
"successful": true
}
Response (with includeAddress=true):
{
"status": 200,
"message": "Company Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "Acme Corp",
"cw_CompanyId": 12345,
"cw_Identifier": "AcmeCorp",
"cw_Data": {
"address": {
"line1": "123 Main St",
"line2": null,
"city": "Springfield",
"state": "IL",
"zip": "62701",
"country": "United States"
}
}
},
"successful": true
}
Response (with includePrimaryContact=true):
{
"status": 200,
"message": "Company Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "Acme Corp",
"cw_CompanyId": 12345,
"cw_Identifier": "AcmeCorp",
"cw_Data": {
"primaryContact": {
"firstName": "John",
"lastName": "Doe",
"cwId": 456,
"inactive": false,
"title": "IT Manager",
"phone": "555-0123",
"email": "john.doe@acmecorp.com"
}
}
},
"successful": true
}
Response (with includeAllContacts=true):
{
"status": 200,
"message": "Company Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "Acme Corp",
"cw_CompanyId": 12345,
"cw_Identifier": "AcmeCorp",
"cw_Data": {
"allContacts": [
{
"firstName": "John",
"lastName": "Doe",
"cwId": 456,
"inactive": false,
"title": "IT Manager",
"phone": "555-0123",
"email": "john.doe@acmecorp.com"
},
{
"firstName": "Jane",
"lastName": "Smith",
"cwId": 789,
"inactive": false,
"title": "CTO",
"phone": "555-0456",
"email": "jane.smith@acmecorp.com"
}
]
}
},
"successful": true
}
Get Company Configurations
GET /company/companies/:identifier/configurations
Fetch configurations for a specific company from ConnectWise.
Authentication Required: Yes
Required Permissions: company.fetch, company.fetch.configurations
URL Parameters:
identifier- Company ID
Response:
{
"status": 200,
"message": "Company Configurations Fetched Successfully!",
"data": {
// ConnectWise configuration data
},
"successful": true
}
Get Company UniFi Sites
GET /company/companies/:identifier/unifi/sites
Fetch all UniFi sites linked to a specific company.
Authentication Required: Yes
Required Permissions: unifi.access, company.fetch
URL Parameters:
identifier- Company ID
Response:
{
"status": 200,
"message": "Company UniFi Sites Fetched Successfully!",
"data": [
{
"id": "ckx...",
"name": "Main Office",
"siteId": "abc123",
"companyId": "ckx...",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
}
],
"successful": true
}
Credential Routes
Get Value Types
GET /credential/valuetypes
Returns all available field value types for credential type fields.
Authentication Required: Yes
Response:
{
"status": 200,
"message": "Value Types Fetched Successfully!",
"data": [
"plain_text",
"license_key",
"ip_address",
"generic_secret",
"bitlocker_key",
"password",
"multi_credential"
],
"successful": true
}
Get Credential by ID
GET /credential/credentials/:id
Fetch a single credential by its ID.
Authentication Required: Yes
Required Permissions: credential.fetch
URL Parameters:
id- Credential ID
Response:
{
"status": 200,
"message": "Credential Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "AWS Credentials",
"notes": null,
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {
"id": "cky...",
"name": "AWS",
"fields": [...],
"permissionScope": "aws.credentials"
},
"company": {
"id": "ckz...",
"name": "Acme Corp"
},
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-02-14T00:00:00.000Z"
},
"successful": true
}
Get Credentials by Company
GET /credential/credentials/company/:companyId
Fetch all credentials associated with a specific company.
Authentication Required: Yes
Required Permissions: credential.fetch.many
URL Parameters:
companyId- Company ID
Response:
{
"status": 200,
"message": "Company Credentials Fetched Successfully!",
"data": [
{
"id": "ckx...",
"name": "AWS Credentials",
"notes": null,
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {...},
"company": {...}
}
],
"successful": true
}
Create Credential
POST /credential/credentials
Create a new credential with validated and encrypted fields.
Authentication Required: Yes
Required Permissions: credential.create
Request Body:
{
"name": "Production AWS Credentials",
"notes": "Used for production S3 access",
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "ckx1...",
"fieldId": "accessKeyId",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "ckx2...",
"fieldId": "secretAccessKey",
"value": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
],
"subCredentials": {
"tunnels": [
{
"name": "Tunnel 1",
"fields": [
{ "fieldId": "server", "value": "vpn1.example.com" },
{ "fieldId": "port", "value": "443" }
]
}
]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Display name for the credential |
notes |
string |
No | Optional notes |
typeId |
string |
Yes | The credential type ID |
companyId |
string |
Yes | The company ID this credential belongs to |
fields |
array |
Yes | Array of field values ({ fieldId, value }) |
subCredentials |
object |
No | Keyed by multi-credential field ID. Each value is an array of { name, fields } objects for inline sub-credentials |
**Response:**
```json
{
"status": 201,
"message": "Credential Created Successfully!",
"data": {
"id": "ckx...",
"name": "Production AWS Credentials",
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {...},
"company": {...}
},
"successful": true
}
Update Credential
PATCH /credential/credentials/:id
Update a credential's basic properties (name, notes) and/or field values. Secure fields are automatically encrypted.
Authentication Required: Yes
Required Permissions: credential.update
URL Parameters:
id- Credential ID
Request Body:
All properties are optional. Include only the properties you want to update.
{
"name": "Updated Credential Name",
"notes": "Updated notes for this credential",
"fields": [
{
"fieldId": "accessKeyId",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"fieldId": "secretAccessKey",
"value": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
]
}
Response:
{
"status": 200,
"message": "Credential Updated Successfully!",
"data": {
"id": "ckx...",
"name": "Updated Credential Name",
"notes": "Updated notes for this credential",
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
}
],
"type": {...},
"company": {...}
},
"successful": true
}
Update Credential Fields
PUT /credential/credentials/:id/fields
Validate and update credential field values. Secure fields are automatically encrypted.
Authentication Required: Yes
Required Permissions: credential.update, credential.fields.update
URL Parameters:
id- Credential ID
Request Body:
{
"fields": [
{
"fieldId": "accessKeyId",
"value": "AKIAIOSFODNN7NEWVALUE"
},
{
"fieldId": "secretAccessKey",
"value": "newSecretKeyValue123"
}
]
}
Response:
{
"status": 200,
"message": "Credential Fields Updated Successfully!",
"data": {
"id": "ckx...",
"name": "Production AWS Credentials",
"notes": null,
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7NEWVALUE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {...},
"company": {...}
},
"successful": true
}
Get Credential Fields
GET /credential/credentials/:id/fields
Fetch all field values for a credential (secure fields returned encrypted).
Authentication Required: Yes
Required Permissions: credential.fetch, credential.fields.fetch
URL Parameters:
id- Credential ID
Response:
{
"status": 200,
"message": "Credential Fields Fetched Successfully!",
"data": [
{
"id": "ckx-accessKeyId",
"fieldId": "accessKeyId",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "ckx1...",
"fieldId": "secretAccessKey",
"value": "base64EncryptedValue=="
}
],
"successful": true
}
Read Secure Values
GET /credential/credentials/:id/secure-values
Decrypt and return all secure field values for a credential.
Authentication Required: Yes
Required Permissions: credential.fetch, credential.secure_values.read
URL Parameters:
id- Credential ID
Response:
{
"status": 200,
"message": "Secure Values Fetched Successfully!",
"data": {
"secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"apiKey": "sk_live_123456789abcdef"
},
"successful": true
}
Read Single Secure Value
GET /credential/credentials/:id/secure-values/:fieldId
Decrypt and return a single secure field value for a credential.
Authentication Required: Yes
Required Permissions: credential.fetch, credential.secure_values.read
URL Parameters:
id- Credential IDfieldId- The field ID of the secure value to read
Response:
{
"status": 200,
"message": "Secure Value Fetched Successfully!",
"data": {
"fieldId": "secretAccessKey",
"value": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
},
"successful": true
}
Error Response (404):
{
"status": 404,
"message": "Secure field not found: unknownField",
"error": "SecureFieldNotFound",
"successful": false
}
Delete Credential
DELETE /credential/credentials/:id
Delete a credential and all associated secure values. Sub-credentials are cascade-deleted automatically.
Authentication Required: Yes
Required Permissions: credential.delete
URL Parameters:
id- Credential ID
Response:
{
"status": 200,
"message": "Credential Deleted Successfully!",
"data": null,
"successful": true
}
Get Sub-Credentials
GET /credential/credentials/:id/sub-credentials
Fetch all sub-credentials that belong to a specific parent credential.
Authentication Required: Yes
Required Permissions: credential.fetch, credential.sub_credentials.fetch
URL Parameters:
id- Parent Credential ID
Response:
{
"status": 200,
"message": "Sub-Credentials Fetched Successfully!",
"data": [
{
"id": "ckx1...",
"name": "Tunnel 1",
"notes": null,
"typeId": "cky...",
"companyId": "ckz...",
"subCredentialOfId": "ckx...",
"fields": [
{ "id": "server", "value": "vpn1.example.com", "secure": false },
{ "id": "port", "value": "443", "secure": false }
],
"type": { "..." },
"company": { "..." },
"createdAt": "2026-02-20T00:00:00.000Z",
"updatedAt": "2026-02-20T00:00:00.000Z"
}
],
"successful": true
}
Add Sub-Credential
POST /credential/credentials/:id/sub-credentials
Create a new sub-credential under an existing parent credential for a specific multi-credential field.
Authentication Required: Yes
Required Permissions: credential.fetch, credential.sub_credentials.create
URL Parameters:
id- Parent Credential ID
Request Body:
{
"fieldId": "tunnels",
"name": "Tunnel 2",
"fields": [
{ "fieldId": "server", "value": "vpn2.example.com" },
{ "fieldId": "port", "value": "1194" }
]
}
| Field | Type | Required | Description |
|---|---|---|---|
fieldId |
string |
Yes | The multi-credential field ID on the parent credential type |
name |
string |
Yes | Display name for the sub-credential |
fields |
array |
Yes | Array of field values matching the multi-credential's subFields |
Response:
{
"status": 201,
"message": "Sub-Credential Created Successfully!",
"data": {
"id": "ckx2...",
"name": "Tunnel 2",
"typeId": "cky...",
"companyId": "ckz...",
"subCredentialOfId": "ckx...",
"fields": [
{ "id": "server", "value": "vpn2.example.com", "secure": false },
{ "id": "port", "value": "1194", "secure": false }
],
"type": { "..." },
"company": { "..." },
"createdAt": "2026-02-20T00:00:00.000Z",
"updatedAt": "2026-02-20T00:00:00.000Z"
},
"successful": true
}
Remove Sub-Credential
DELETE /credential/credentials/:id/sub-credentials/:subId
Delete a sub-credential and remove its reference from the parent credential's multi-credential field.
Authentication Required: Yes
Required Permissions: credential.fetch, credential.sub_credentials.delete
URL Parameters:
id- Parent Credential IDsubId- Sub-Credential ID to remove
Response:
{
"status": 200,
"message": "Sub-Credential Removed Successfully!",
"data": null,
"successful": true
}
Error Response (404):
{
"status": 404,
"message": "Sub-credential not found",
"error": "SubCredentialNotFound",
"successful": false
}
Credential Type Routes
Get Credential Type by ID or Name
GET /credential-type/:identifier
Fetch a single credential type by its ID or name.
Authentication Required: Yes
Required Permissions: credential_type.fetch
URL Parameters:
identifier- Credential Type ID or name
Response:
{
"status": 200,
"message": "Credential Type Fetched Successfully!",
"data": {
"id": "cky...",
"name": "AWS",
"permissionScope": "aws.credentials",
"icon": "https://aws.amazon.com/favicon.ico",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"required": true,
"secure": false,
"valueType": "plain_text"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"required": true,
"secure": true,
"valueType": "password"
}
],
"credentialCount": 5,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-02-14T00:00:00.000Z"
},
"successful": true
}
Get All Credential Types
GET /credential-type
Fetch all credential types in the system.
Authentication Required: Yes
Required Permissions: credential_type.fetch.many
Response:
{
"status": 200,
"message": "Credential Types Fetched Successfully!",
"data": [
{
"id": "cky...",
"name": "AWS",
"permissionScope": "aws.credentials",
"icon": "https://aws.amazon.com/favicon.ico",
"fields": [...],
"credentialCount": 5
},
{
"id": "ckz...",
"name": "Azure",
"permissionScope": "azure.credentials",
"icon": "https://azure.microsoft.com/favicon.ico",
"fields": [...],
"credentialCount": 3
}
],
"successful": true
}
Create Credential Type
POST /credential-type
Create a new credential type with field definitions.
Authentication Required: Yes
Required Permissions: credential_type.create
Request Body:
{
"name": "GitHub",
"permissionScope": "github.credentials",
"icon": "https://github.com/favicon.ico",
"fields": [
{
"id": "username",
"name": "Username",
"required": true,
"secure": false,
"valueType": "plain_text"
},
{
"id": "personalAccessToken",
"name": "Personal Access Token",
"required": true,
"secure": true,
"valueType": "password"
}
]
}
Multi-Credential Example:
Fields with valueType: "multi_credential" support an optional subFields array that defines the field structure for each sub-credential entry. Sub-fields use the same schema and can be nested recursively.
{
"name": "VPN Config",
"permissionScope": "vpn.credentials",
"icon": "https://example.com/vpn.ico",
"fields": [
{
"id": "hostname",
"name": "Hostname",
"required": true,
"secure": false,
"valueType": "plain_text"
},
{
"id": "tunnels",
"name": "Tunnels",
"required": false,
"secure": false,
"valueType": "multi_credential",
"subFields": [
{
"id": "server",
"name": "Server",
"required": true,
"secure": false,
"valueType": "plain_text"
},
{
"id": "psk",
"name": "Pre-Shared Key",
"required": true,
"secure": true,
"valueType": "password"
}
]
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
fields[].id |
string |
Yes | Unique identifier for the field |
fields[].name |
string |
Yes | Display name for the field |
fields[].required |
bool |
Yes | Whether the field is required when creating a credential |
fields[].secure |
bool |
Yes | Whether the field value should be encrypted at rest |
fields[].valueType |
string |
Yes | One of the supported value types (see GET /credential/valuetypes) |
fields[].subFields |
array |
No | Only for multi_credential fields. Defines the field structure for each nested sub-credential |
Response:
{
"status": 201,
"message": "Credential Type Created Successfully!",
"data": {
"id": "ck1...",
"name": "GitHub",
"permissionScope": "github.credentials",
"icon": "https://github.com/favicon.ico",
"fields": [...]
},
"successful": true
}
Update Credential Type
PATCH /credential-type/:id
Update a credential type's properties or field definitions.
Authentication Required: Yes
Required Permissions: credential_type.update
URL Parameters:
id- Credential Type ID
Request Body:
{
"name": "GitHub Enterprise",
"icon": "https://github.enterprise.com/favicon.ico",
"fields": [
{
"id": "username",
"name": "Username",
"required": true,
"secure": false,
"valueType": "plain_text"
},
{
"id": "personalAccessToken",
"name": "Personal Access Token",
"required": true,
"secure": true,
"valueType": "password"
},
{
"id": "enterpriseUrl",
"name": "Enterprise URL",
"required": true,
"secure": false,
"valueType": "plain_text"
}
]
}
Note: Fields with
valueType: "multi_credential"support an optionalsubFieldsarray — see the Create Credential Type section for the full schema and example.
Response:
{
"status": 200,
"message": "Credential Type Updated Successfully!",
"data": {
"id": "ck1...",
"name": "GitHub Enterprise",
"fields": [...]
},
"successful": true
}
Delete Credential Type
DELETE /credential-type/:id
Delete a credential type. This will cascade delete all credentials of this type.
Authentication Required: Yes
Required Permissions: credential_type.delete
URL Parameters:
id- Credential Type ID
Response:
{
"status": 200,
"message": "Credential Type Deleted Successfully!",
"data": null,
"successful": true
}
Get Credentials by Type
GET /credential-type/:id/credentials
Fetch all credentials that use a specific credential type.
Authentication Required: Yes
Required Permissions: credential_type.fetch, credential.fetch.many
URL Parameters:
id- Credential Type ID
Response:
{
"status": 200,
"message": "Credentials Fetched Successfully!",
"data": [
{
"id": "ckx...",
"name": "Production AWS",
"typeId": "cky...",
"companyId": "ckz...",
"fields": {...}
},
{
"id": "ck2...",
"name": "Staging AWS",
"typeId": "cky...",
"companyId": "ckz...",
"fields": {...}
}
],
"successful": true
}
Role Routes
Create Role
POST /role
Create a new role with a title, moniker, and optional permissions.
Authentication Required: Yes
Required Permissions: role.create
Request Body:
{
"title": "System Administrator",
"moniker": "system_admin",
"permissions": [
"user.read",
"user.write",
"company.fetch",
"credential.create"
]
}
Response:
{
"status": 201,
"message": "Role Created Successfully!",
"data": {
"id": "ckx...",
"title": "System Administrator",
"moniker": "system_admin",
"permissions": [
"user.read",
"user.write",
"company.fetch",
"credential.create"
],
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T00:00:00.000Z"
},
"successful": true
}
Get Role by ID or Moniker
GET /role/:identifier
Fetch a single role by its ID or moniker.
Authentication Required: Yes
Required Permissions: role.read
URL Parameters:
identifier- Role ID or moniker
Response:
{
"status": 200,
"message": "Role Fetched Successfully!",
"data": {
"id": "ckx...",
"title": "System Administrator",
"moniker": "system_admin",
"permissions": [
"user.read",
"user.write",
"company.fetch",
"credential.create"
],
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T00:00:00.000Z"
},
"successful": true
}
Get All Roles
GET /role
Fetch all roles in the system.
Authentication Required: Yes
Required Permissions: role.read, role.list
Response:
{
"status": 200,
"message": "Roles Fetched Successfully!",
"data": [
{
"id": "ckx...",
"title": "System Administrator",
"moniker": "system_admin",
"permissions": ["user.read", "user.write"],
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T00:00:00.000Z"
},
{
"id": "cky...",
"title": "Viewer",
"moniker": "viewer",
"permissions": ["user.read", "company.fetch"],
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T00:00:00.000Z"
}
],
"successful": true
}
Update Role
PATCH /role/:identifier
Update a role's title, moniker, or permissions.
Authentication Required: Yes
Required Permissions: role.modify
URL Parameters:
identifier- Role ID or moniker
Request Body:
{
"title": "Super Administrator",
"moniker": "super_admin",
"permissions": ["*"]
}
Response:
{
"status": 200,
"message": "Role Updated Successfully!",
"data": {
"id": "ckx...",
"title": "Super Administrator",
"moniker": "super_admin",
"permissions": ["*"],
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T12:00:00.000Z"
},
"successful": true
}
Delete Role
DELETE /role/:identifier
Delete a role. This will remove the role from all users that have it assigned.
Authentication Required: Yes
Required Permissions: role.delete
URL Parameters:
identifier- Role ID or moniker
Response:
{
"status": 200,
"message": "Role Deleted Successfully!",
"data": {
"id": "ckx...",
"title": "System Administrator",
"moniker": "system_admin",
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T12:00:00.000Z"
},
"successful": true
}
Add Permissions to Role
POST /role/:identifier/permissions
Add one or more permissions to an existing role. The new permissions will be merged with existing permissions.
Authentication Required: Yes
Required Permissions: role.modify
URL Parameters:
identifier- Role ID or moniker
Request Body:
{
"permissions": ["credential.update", "credential.delete"]
}
Response:
{
"status": 200,
"message": "Permissions Added Successfully!",
"data": {
"id": "ckx...",
"title": "System Administrator",
"moniker": "system_admin",
"permissions": [
"user.read",
"user.write",
"company.fetch",
"credential.create",
"credential.update",
"credential.delete"
],
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T12:30:00.000Z"
},
"successful": true
}
Remove Permissions from Role
DELETE /role/:identifier/permissions
Remove one or more permissions from an existing role.
Authentication Required: Yes
Required Permissions: role.modify
URL Parameters:
identifier- Role ID or moniker
Request Body:
{
"permissions": ["credential.delete"]
}
Response:
{
"status": 200,
"message": "Permissions Removed Successfully!",
"data": {
"id": "ckx...",
"title": "System Administrator",
"moniker": "system_admin",
"permissions": [
"user.read",
"user.write",
"company.fetch",
"credential.create",
"credential.update"
],
"createdAt": "2026-02-17T00:00:00.000Z",
"updatedAt": "2026-02-17T12:45:00.000Z"
},
"successful": true
}
Get Users with Role
GET /role/:identifier/users
Fetch all users that have been assigned a specific role.
Authentication Required: Yes
Required Permissions: role.read, user.read
URL Parameters:
identifier- Role ID or moniker
Response:
{
"status": 200,
"message": "Users Fetched Successfully!",
"data": [
{
"id": "cku...",
"name": "John Doe",
"login": "john.doe",
"roles": ["ckx..."],
"createdAt": "2026-01-15T00:00:00.000Z",
"updatedAt": "2026-02-10T00:00:00.000Z"
},
{
"id": "ckv...",
"name": "Jane Smith",
"login": "jane.smith",
"roles": ["ckx...", "cky..."],
"createdAt": "2026-01-20T00:00:00.000Z",
"updatedAt": "2026-02-12T00:00:00.000Z"
}
],
"successful": true
}
Permission Routes
Get All Permission Nodes (Categorized)
GET /permissions
Fetch all permission nodes organized by category. Returns the full permission node definition object with categories as keys.
Authentication Required: Yes
Required Permissions: role.read
Response:
{
"status": 200,
"message": "Permission Nodes Fetched Successfully!",
"data": {
"global": {
"name": "Global Permissions",
"description": "Global wildcard permissions that grant access to all resources",
"permissions": [
{
"node": "*",
"description": "Full access to all resources and actions (administrator role)",
"usedIn": []
}
]
},
"company": { "..." },
"credential": { "..." },
"...additional categories": { "..." }
},
"successful": true
}
Get All Permission Nodes (Flat)
GET /permissions/nodes
Fetch a flat array of all permission nodes across all categories.
Authentication Required: Yes
Required Permissions: role.read
Response:
{
"status": 200,
"message": "All Permission Nodes Fetched Successfully!",
"data": [
{
"node": "*",
"description": "Full access to all resources and actions (administrator role)",
"usedIn": []
},
{
"node": "company.fetch",
"description": "Fetch a single company",
"usedIn": ["src/api/companies/[id]/fetch.ts"]
},
"...additional nodes"
],
"successful": true
}
Get Permission Nodes by Category
GET /permissions/:category
Fetch all permission nodes for a specific category.
Authentication Required: Yes
Required Permissions: role.read
Path Parameters:
category- The category key (e.g.,global,company,credential,credentialType,role,user,permission,uiNavigation,adminUI)
Response (Success):
{
"status": 200,
"message": "Permission Category Fetched Successfully!",
"data": {
"name": "Company Permissions",
"description": "Permissions for accessing and managing company resources",
"permissions": [
{
"node": "company.fetch",
"description": "Fetch a single company",
"usedIn": ["src/api/companies/[id]/fetch.ts"]
},
{
"node": "company.fetch.address",
"description": "View company address information",
"usedIn": ["src/api/companies/[id]/fetch.ts"],
"dependencies": ["company.fetch"]
}
]
},
"successful": true
}
Response (Not Found):
{
"status": 404,
"message": "Permission category \"invalidCategory\" not found",
"error": "NotFound",
"successful": false
}
Utility Routes
Teapot
GET /teapot
A fun Easter egg endpoint that returns HTTP 418 (I'm a teapot).
Authentication Required: No
Response:
{
"status": 418,
"message": "I'm a teapot",
"successful": false
}
UniFi Routes
All UniFi routes require the unifi.access permission in addition to their route-specific permission. This acts as a gate for the entire UniFi API.
Get All UniFi Sites
GET /unifi/sites
Fetch all UniFi site records from the database.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.sites.fetch.many
Response:
{
"status": 200,
"message": "UniFi Sites Fetched Successfully!",
"data": [
{
"id": "ckx...",
"name": "Total Tech - Murray Office",
"siteId": "km9b1v8i",
"companyId": "ckx...",
"company": {
"id": "ckx...",
"name": "Acme Corp"
},
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
}
],
"successful": true
}
Sync UniFi Sites
POST /unifi/sites/sync
Synchronize sites from the UniFi controller into the database. Creates new records for sites not yet tracked and updates names for existing ones.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.sites.sync
Response:
{
"status": 200,
"message": "UniFi Sites Synced Successfully!",
"data": [
{
"id": "ckx...",
"name": "Total Tech - Murray Office",
"siteId": "km9b1v8i",
"companyId": null,
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
}
],
"successful": true
}
Create UniFi Site
POST /unifi/sites/create
Create a new site on the UniFi controller and track it in the database.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.sites.create
Request Body:
{
"description": "New Office Site"
}
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | Yes | Human-readable name / description for the site |
Response:
{
"status": 200,
"message": "UniFi Site Created Successfully!",
"data": {
"id": "ckx...",
"name": "New Office Site",
"siteId": "abc123",
"companyId": null,
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
"successful": true
}
Get UniFi Site
GET /unifi/site/:id
Fetch a single UniFi site record from the database by its internal ID.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.sites.fetch
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi Site Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "Total Tech - Murray Office",
"siteId": "km9b1v8i",
"companyId": "ckx...",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
"successful": true
}
Get Site Overview
GET /unifi/site/:id/overview
Fetch live site overview data from the UniFi controller, including health status, system info, and site information.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.overview
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi Site Overview Fetched Successfully!",
"data": {
"health": [
{
"subsystem": "wan",
"status": "ok",
"numAdopted": 1,
"numGateway": 1
}
],
"sysInfo": {
"timezone": "America/Denver",
"hostname": "UniFi-Controller",
"version": "8.x.x"
},
"siteInfo": {
"description": "Total Tech - Murray Office",
"name": "km9b1v8i"
}
},
"successful": true
}
Get Site Devices
GET /unifi/site/:id/devices
Fetch live device list from the UniFi controller for a specific site.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.devices
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi Devices Fetched Successfully!",
"data": [
{
"id": "abc123...",
"mac": "00:11:22:33:44:55",
"model": "U6-Pro",
"name": "Office AP",
"type": "uap",
"state": "connected",
"ip": "192.168.1.10",
"version": "6.x.x",
"uptime": 123456,
"radios": [],
"uplink": {}
}
],
"successful": true
}
Get Site WiFi Networks
GET /unifi/site/:id/wifi
Fetch live WiFi network (WLAN) configurations from the UniFi controller for a specific site.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.wifi
Field-Level Gating: unifi.site.wifi.read.<field>
This route uses processObjectValuePerms to filter each WLAN object on a per-field basis. Only fields whose corresponding unifi.site.wifi.read.<field> permission the user holds are included in the response. For example, a user with unifi.site.wifi.read.name and unifi.site.wifi.read.enabled but without unifi.site.wifi.read.passphrase will receive objects containing only name and enabled. Use unifi.site.wifi.read.* to grant access to all fields.
All available field-level permission nodes
| Permission Node | Field |
|---|---|
unifi.site.wifi.read.id |
id |
unifi.site.wifi.read.name |
name |
unifi.site.wifi.read.siteId |
siteId |
unifi.site.wifi.read.enabled |
enabled |
unifi.site.wifi.read.security |
security |
unifi.site.wifi.read.wpaMode |
wpaMode |
unifi.site.wifi.read.wpaEnc |
wpaEnc |
unifi.site.wifi.read.wpa3Support |
wpa3Support |
unifi.site.wifi.read.wpa3Transition |
wpa3Transition |
unifi.site.wifi.read.wpa3FastRoaming |
wpa3FastRoaming |
unifi.site.wifi.read.wpa3Enhanced192 |
wpa3Enhanced192 |
unifi.site.wifi.read.passphrase |
passphrase |
unifi.site.wifi.read.passphraseAutogenerated |
passphraseAutogenerated |
unifi.site.wifi.read.hideSSID |
hideSSID |
unifi.site.wifi.read.isGuest |
isGuest |
unifi.site.wifi.read.band |
band |
unifi.site.wifi.read.bands |
bands |
unifi.site.wifi.read.networkconfId |
networkconfId |
unifi.site.wifi.read.usergroupId |
usergroupId |
unifi.site.wifi.read.apGroupIds |
apGroupIds |
unifi.site.wifi.read.apGroupMode |
apGroupMode |
unifi.site.wifi.read.pmfMode |
pmfMode |
unifi.site.wifi.read.groupRekey |
groupRekey |
unifi.site.wifi.read.dtimMode |
dtimMode |
unifi.site.wifi.read.dtimNg |
dtimNg |
unifi.site.wifi.read.dtimNa |
dtimNa |
unifi.site.wifi.read.dtim6e |
dtim6e |
unifi.site.wifi.read.l2Isolation |
l2Isolation |
unifi.site.wifi.read.fastRoamingEnabled |
fastRoamingEnabled |
unifi.site.wifi.read.bssTransition |
bssTransition |
unifi.site.wifi.read.uapsdEnabled |
uapsdEnabled |
unifi.site.wifi.read.iappEnabled |
iappEnabled |
unifi.site.wifi.read.proxyArp |
proxyArp |
unifi.site.wifi.read.mcastenhanceEnabled |
mcastenhanceEnabled |
unifi.site.wifi.read.macFilterEnabled |
macFilterEnabled |
unifi.site.wifi.read.macFilterPolicy |
macFilterPolicy |
unifi.site.wifi.read.macFilterList |
macFilterList |
unifi.site.wifi.read.radiusDasEnabled |
radiusDasEnabled |
unifi.site.wifi.read.radiusMacAuthEnabled |
radiusMacAuthEnabled |
unifi.site.wifi.read.radiusMacaclFormat |
radiusMacaclFormat |
unifi.site.wifi.read.minrateSettingPreference |
minrateSettingPreference |
unifi.site.wifi.read.minrateNgEnabled |
minrateNgEnabled |
unifi.site.wifi.read.minrateNgDataRateKbps |
minrateNgDataRateKbps |
unifi.site.wifi.read.minrateNgAdvertisingRates |
minrateNgAdvertisingRates |
unifi.site.wifi.read.minrateNaEnabled |
minrateNaEnabled |
unifi.site.wifi.read.minrateNaDataRateKbps |
minrateNaDataRateKbps |
unifi.site.wifi.read.minrateNaAdvertisingRates |
minrateNaAdvertisingRates |
unifi.site.wifi.read.settingPreference |
settingPreference |
unifi.site.wifi.read.no2ghzOui |
no2ghzOui |
unifi.site.wifi.read.privatePreSharedKeysEnabled |
privatePreSharedKeysEnabled |
unifi.site.wifi.read.privatePreSharedKeys |
privatePreSharedKeys |
unifi.site.wifi.read.saeGroups |
saeGroups |
unifi.site.wifi.read.saePsk |
saePsk |
unifi.site.wifi.read.schedule |
schedule |
unifi.site.wifi.read.scheduleWithDuration |
scheduleWithDuration |
unifi.site.wifi.read.bcFilterList |
bcFilterList |
unifi.site.wifi.read.externalId |
externalId |
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi WiFi Networks Fetched Successfully!",
"data": [
{
"id": "66eb36e54bb53f0ae3fb82bd",
"name": "TTAD",
"siteId": "61ae856a8eae567c3905c54f",
"enabled": true,
"security": "wpapsk",
"wpaMode": "wpa2",
"wpaEnc": "ccmp",
"wpa3Support": false,
"wpa3Transition": false,
"wpa3FastRoaming": false,
"wpa3Enhanced192": false,
"passphrase": "S3cur3Alarm!",
"passphraseAutogenerated": false,
"hideSSID": false,
"isGuest": false,
"band": "2g",
"bands": ["2g"],
"networkconfId": "66eb368d4bb53f0ae3fb7e0d",
"usergroupId": "61ae856a8eae567c3905c55c",
"apGroupIds": ["66eb36e44bb53f0ae3fb82bc"],
"apGroupMode": "devices",
"pmfMode": "disabled",
"groupRekey": 0,
"dtimMode": "default",
"dtimNg": 1,
"dtimNa": 3,
"dtim6e": 3,
"l2Isolation": false,
"fastRoamingEnabled": false,
"bssTransition": true,
"uapsdEnabled": false,
"iappEnabled": true,
"proxyArp": false,
"mcastenhanceEnabled": false,
"macFilterEnabled": false,
"macFilterPolicy": "allow",
"macFilterList": [],
"radiusDasEnabled": false,
"radiusMacAuthEnabled": false,
"radiusMacaclFormat": "none_lower",
"minrateSettingPreference": "auto",
"minrateNgEnabled": true,
"minrateNgDataRateKbps": 1000,
"minrateNgAdvertisingRates": false,
"minrateNaEnabled": false,
"minrateNaDataRateKbps": 6000,
"minrateNaAdvertisingRates": false,
"settingPreference": "manual",
"no2ghzOui": true,
"privatePreSharedKeysEnabled": false,
"privatePreSharedKeys": [],
"saeGroups": [],
"saePsk": [],
"schedule": [],
"scheduleWithDuration": [],
"bcFilterList": [],
"externalId": "ba0f579c-6b21-4dfc-8d03-5a1a09243328"
}
],
"successful": true
}
Update WiFi Network
PATCH /unifi/site/:id/wifi/:wlanId
Update a WiFi network configuration on the UniFi controller.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.wifi, unifi.site.wifi.update
URL Parameters:
id- Internal UniFi site ID (database ID)wlanId- UniFi WLAN ID
Request Body:
{
"name": "NewSSIDName",
"x_passphrase": "NewPassword123",
"enabled": true,
"security": "wpapsk",
"wpa_mode": "wpa2",
"hide_ssid": false,
"is_guest": false,
"band": "both"
}
All fields are optional. Valid values:
security:"wpapsk","wpaeap","open"wpa_mode:"wpa2","wpa3","wpa2wpa3"band:"both","2g","5g"
Response:
{
"status": 200,
"message": "UniFi WiFi Network Updated Successfully!",
"data": {
"id": "66eb36e54bb53f0ae3fb82bd",
"name": "NewSSIDName",
"siteId": "61ae856a8eae567c3905c54f",
"enabled": true,
"security": "wpapsk",
"wpaMode": "wpa2",
"wpaEnc": "ccmp",
"wpa3Support": false,
"wpa3Transition": false,
"wpa3FastRoaming": false,
"wpa3Enhanced192": false,
"passphrase": "NewPassword123",
"passphraseAutogenerated": false,
"hideSSID": false,
"isGuest": false,
"band": "both",
"bands": ["2g", "5g"],
"networkconfId": "66eb368d4bb53f0ae3fb7e0d",
"usergroupId": "61ae856a8eae567c3905c55c",
"apGroupIds": ["66eb36e44bb53f0ae3fb82bc"],
"apGroupMode": "devices",
"pmfMode": "disabled",
"groupRekey": 0,
"dtimMode": "default",
"dtimNg": 1,
"dtimNa": 3,
"dtim6e": 3,
"l2Isolation": false,
"fastRoamingEnabled": false,
"bssTransition": true,
"uapsdEnabled": false,
"iappEnabled": true,
"proxyArp": false,
"mcastenhanceEnabled": false,
"macFilterEnabled": false,
"macFilterPolicy": "allow",
"macFilterList": [],
"radiusDasEnabled": false,
"radiusMacAuthEnabled": false,
"radiusMacaclFormat": "none_lower",
"minrateSettingPreference": "auto",
"minrateNgEnabled": true,
"minrateNgDataRateKbps": 1000,
"minrateNgAdvertisingRates": false,
"minrateNaEnabled": false,
"minrateNaDataRateKbps": 6000,
"minrateNaAdvertisingRates": false,
"settingPreference": "manual",
"no2ghzOui": true,
"privatePreSharedKeysEnabled": false,
"privatePreSharedKeys": [],
"saeGroups": [],
"saePsk": [],
"schedule": [],
"scheduleWithDuration": [],
"bcFilterList": [],
"externalId": "ba0f579c-6b21-4dfc-8d03-5a1a09243328"
},
"successful": true
}
Get Site Networks
GET /unifi/site/:id/networks
Fetch live network configurations from the UniFi controller for a specific site.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.networks
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi Networks Fetched Successfully!",
"data": [
{
"id": "abc123...",
"name": "LAN",
"purpose": "corporate",
"subnet": "192.168.1.0/24",
"vlanId": null,
"dhcpEnabled": true,
"dhcpStart": "192.168.1.100",
"dhcpStop": "192.168.1.254",
"domainName": "localdomain",
"isNat": true,
"enabled": true
}
],
"successful": true
}
Get WLAN Groups
GET /unifi/site/:id/wlan-groups
Fetch WLAN groups (AP groups) from the UniFi controller for a specific site. WLAN groups define which WLANs are broadcast on which access points.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.wlan-groups
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi WLAN Groups Fetched Successfully!",
"data": [
{
"id": "61ae856a8eae567c3905c55d",
"name": "Default",
"siteId": "61ae856a8eae567c3905c550",
"noDelete": true,
"noEdit": false,
"hidden": false
}
],
"successful": true
}
Create WLAN Group
POST /unifi/site/:id/wlan-groups
Create a new WLAN group (AP broadcasting group) on the UniFi controller. WLAN groups control which WLANs are broadcast on which access points.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.wlan-groups, unifi.site.wlan-groups.create
URL Parameters:
id- Internal UniFi site ID (database ID)
Request Body:
{
"name": "Lobby APs"
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Name of the WLAN group |
Response (201):
{
"status": 201,
"message": "UniFi WLAN Group Created Successfully!",
"data": {
"id": "abc123...",
"name": "Lobby APs",
"siteId": "61ae856a8eae567c3905c550",
"noDelete": false,
"noEdit": false,
"hidden": false
},
"successful": true
}
Get AP Groups
GET /unifi/site/:id/ap-groups
Fetch AP groups from the UniFi controller for a specific site. AP groups define collections of access points — individual WLANs can target specific AP groups to control which APs broadcast a given SSID.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.ap-groups
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi AP Groups Fetched Successfully!",
"data": [
{
"id": "61ae856a8eae567c3905c565",
"name": "All APs",
"deviceMacs": [
"18:e8:29:59:25:bc",
"78:45:58:29:fa:87",
"68:d7:9a:73:9a:28"
],
"noDelete": true
},
{
"id": "63c5be017e957d08189ed997",
"name": "301Andrus",
"deviceMacs": ["78:45:58:29:fa:87", "18:e8:29:59:25:bc"],
"noDelete": false
}
],
"successful": true
}
Get Access Points
GET /unifi/site/:id/access-points
Fetch access points (UAPs only) from the UniFi controller for a specific site. This filters the full device list to only return wireless access points.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.access-points
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi Access Points Fetched Successfully!",
"data": [
{
"id": "abc123...",
"mac": "00:11:22:33:44:55",
"model": "U6-Pro",
"type": "uap",
"name": "Office AP",
"state": 1,
"adopted": true,
"ip": "192.168.1.10",
"version": "7.1.68"
}
],
"successful": true
}
Get WiFi Limits
GET /unifi/site/:id/wifi-limits
Check the WiFi SSID limits per access point per radio band. UniFi access points support a maximum of 8 SSIDs per radio. This endpoint shows how many SSIDs are active on each radio and how many more can be added.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.wifi-limits
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi WiFi Limits Fetched Successfully!",
"data": [
{
"apId": "abc123...",
"apName": "Office AP",
"mac": "00:11:22:33:44:55",
"model": "U6-Pro",
"radios": [
{
"radio": "ng",
"band": "2g",
"activeWlans": 3,
"limit": 8,
"remaining": 5,
"wlanNames": ["Corporate", "Guest", "IoT"]
},
{
"radio": "na",
"band": "5g",
"activeWlans": 3,
"limit": 8,
"remaining": 5,
"wlanNames": ["Corporate", "Guest", "IoT"]
}
]
}
],
"successful": true
}
Get Speed Profiles
GET /unifi/site/:id/speed-profiles
Fetch speed limit profiles (user groups) from the UniFi controller for a specific site. Speed profiles define bandwidth limits that can be applied to WiFi networks.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.speed-profiles
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi Speed Profiles Fetched Successfully!",
"data": [
{
"id": "61ae856a8eae567c3905c55e",
"name": "Default",
"siteId": "61ae856a8eae567c3905c550",
"noDelete": true,
"downloadLimitKbps": -1,
"uploadLimitKbps": -1
},
{
"id": "abc123...",
"name": "Guest 10Mbps",
"siteId": "61ae856a8eae567c3905c550",
"noDelete": false,
"downloadLimitKbps": 10000,
"uploadLimitKbps": 5000
}
],
"successful": true
}
Note: A value of
-1fordownloadLimitKbpsoruploadLimitKbpsmeans unlimited.
Create Speed Profile
POST /unifi/site/:id/speed-profiles
Create a new speed limit profile (user group) on the UniFi controller.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.speed-profiles, unifi.site.speed-profiles.create
URL Parameters:
id- Internal UniFi site ID (database ID)
Request Body:
{
"name": "Guest 10Mbps",
"downloadLimitKbps": 10000,
"uploadLimitKbps": 5000
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Name of the speed profile |
downloadLimitKbps |
number | No | Download limit in Kbps (-1 or omit = unlimited) |
uploadLimitKbps |
number | No | Upload limit in Kbps (-1 or omit = unlimited) |
Response (201):
{
"status": 201,
"message": "UniFi Speed Profile Created Successfully!",
"data": {
"id": "abc123...",
"name": "Guest 10Mbps",
"siteId": "61ae856a8eae567c3905c550",
"noDelete": false,
"downloadLimitKbps": 10000,
"uploadLimitKbps": 5000
},
"successful": true
}
Get Private PSKs
GET /unifi/site/:id/wifi/:wlanId/ppsk
Fetch private pre-shared keys (PPSKs) for a specific WiFi network. PPSKs allow different devices to connect to the same SSID with unique passwords.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.wifi, unifi.site.wifi.ppsk
URL Parameters:
id- Internal UniFi site ID (database ID)wlanId- UniFi WLAN configuration ID
Response:
{
"status": 200,
"message": "UniFi Private PSKs Fetched Successfully!",
"data": [
{
"key": "mySecurePassword123",
"name": "John's Laptop",
"mac": null,
"vlanId": null
},
{
"key": "anotherPassword456",
"name": "IoT Device",
"mac": "AA:BB:CC:DD:EE:FF",
"vlanId": 100
}
],
"successful": true
}
Create Private PSK
POST /unifi/site/:id/wifi/:wlanId/ppsk
Create a private pre-shared key on a specific WiFi network. This adds a new PPSK to the WLAN's list and enables PPSK mode if not already enabled.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.site.wifi, unifi.site.wifi.ppsk, unifi.site.wifi.ppsk.create
URL Parameters:
id- Internal UniFi site ID (database ID)wlanId- UniFi WLAN configuration ID
Request Body:
{
"key": "mySecurePassword123",
"name": "John's Laptop",
"mac": "AA:BB:CC:DD:EE:FF",
"vlanId": 100
}
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | The pre-shared key (min 8 characters) |
name |
string | Yes | Descriptive name for this PSK |
mac |
string | No | MAC address to lock this PSK to a specific device |
vlanId |
number | No | VLAN ID to assign to clients using this PSK |
Response (201):
{
"status": 201,
"message": "UniFi Private PSK Created Successfully!",
"data": [
{
"key": "mySecurePassword123",
"name": "John's Laptop",
"mac": "AA:BB:CC:DD:EE:FF",
"vlanId": 100
}
],
"successful": true
}
Note: The response returns the full updated list of all PPSKs on the WLAN after creation.
Link Site to Company
POST /unifi/site/:id/link
Link a UniFi site to a company.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.sites.link
URL Parameters:
id- Internal UniFi site ID (database ID)
Request Body:
{
"companyId": "ckx..."
}
Response:
{
"status": 200,
"message": "UniFi Site Linked to Company Successfully!",
"data": {
"id": "ckx...",
"name": "Total Tech - Murray Office",
"siteId": "km9b1v8i",
"companyId": "ckx...",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
"successful": true
}
Unlink Site from Company
POST /unifi/site/:id/unlink
Unlink a UniFi site from its associated company.
Authentication Required: Yes
Required Permissions: unifi.access, unifi.sites.link
URL Parameters:
id- Internal UniFi site ID (database ID)
Response:
{
"status": 200,
"message": "UniFi Site Unlinked from Company Successfully!",
"data": {
"id": "ckx...",
"name": "Total Tech - Murray Office",
"siteId": "km9b1v8i",
"companyId": null,
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
"successful": true
}
Error Responses
All endpoints may return error responses in the following format:
400 Bad Request
{
"status": 400,
"message": "Validation error",
"errors": [
{
"path": ["field"],
"message": "Field is required"
}
],
"successful": false
}
401 Unauthorized
{
"status": 401,
"message": "Unauthorized",
"successful": false
}
403 Forbidden
{
"status": 403,
"message": "Insufficient permissions",
"successful": false
}
404 Not Found
{
"status": 404,
"message": "Resource not found",
"successful": false
}
500 Internal Server Error
{
"status": 500,
"message": "Internal server error",
"successful": false
}
Authentication
Most endpoints require authentication via an access token in the request headers:
Authorization: Bearer <access_token>
Tokens are obtained through the Microsoft OAuth flow:
- Call
GET /auth/urito get the authentication URL - Redirect user to the Microsoft login page
- User authenticates and is redirected to
/auth/redirect - Access and refresh tokens are provided via WebSocket or response
- Use the access token in subsequent API requests
When the access token expires, use POST /auth/refresh with the refresh token to obtain a new access token.
Permission System
The API uses a granular permission system. Each endpoint requires specific permissions that are checked via the authMiddleware. Permissions are granted through roles assigned to users.
Common permission patterns:
resource.fetch- Read a single resourceresource.fetch.many- Read multiple resourcesresource.create- Create a new resourceresource.update- Update a resourceresource.delete- Delete a resourceresource.field.action- Perform specific field operations
Users can have multiple roles, and permissions are accumulated from all assigned roles.