Connecting n8n to NinjaOne
OAuth2 setup, API applications, credential configuration, and undocumented API gotchas.
What You'll Learn
- Understand why n8n fills the automation gaps that NinjaOne cannot handle natively
- Create two types of API applications in NinjaOne: client_credentials (read-only) vs authorization_code (full access)
- Configure a Generic OAuth2 API credential in n8n with the correct authentication method and scopes
- Make your first successful API call to NinjaOne from an n8n workflow
- Avoid the undocumented API gotchas that waste hours of debugging time
Why Automate NinjaOne with n8n
NinjaOne is a powerful RMM platform, but it has significant gaps when it comes to automation. There are no native scheduled or recurring tickets. You cannot create a monthly patching ticket that auto-populates with your current device inventory. There is no built-in way to generate custom reports beyond the canned options. Cross-platform integrations (NinjaOne to Slack, NinjaOne to Jira, NinjaOne to Power BI) do not exist out of the box.
This is where n8n comes in. n8n is an open-source workflow automation platform that can connect to any API, including NinjaOne's REST API. With n8n, you can:
- Create scheduled tickets that run on any cadence (weekly, monthly, quarterly, annually) with dynamic content pulled from the NinjaOne API
- Build custom reporting pipelines that extract device data, transform it into CSV/Excel, push it to SharePoint, and feed Power BI dashboards
- Set up smart alerting that consolidates 50 individual "device offline" emails into a single daily digest sent to Slack or Teams
- Automate onboarding/offboarding workflows that create checklists, assign tickets, and update multiple systems simultaneously
What makes this combination unique: No one else has documented how to build these integrations. If you search "NinjaOne scheduled tickets" or "NinjaOne recurring tickets," you will find nothing useful. MSPs and IT teams are building these solutions in isolation, reinventing the wheel each time. This playbook gives you production-tested patterns you can deploy immediately.
Prerequisites: A self-hosted or cloud-hosted n8n instance (see the n8n Administration playbook for setup), and admin access to your NinjaOne portal.
Quick Test: Check Your NinjaOne API Access
Step 1: Log into your NinjaOne admin portal.
Step 2: Navigate to Administration > Apps > API.
Step 3: If you do not see this menu, your account may not have the required permissions. You need Organization Administrator or a custom role with API application management rights.
Step 4: Verify this access before proceeding to the credential setup steps.
NinjaOne API Applications
NinjaOne supports two OAuth2 grant types, and choosing the wrong one is the most common mistake people make. Each grant type has different capabilities and limitations.
Creating an API Application:
- Go to Administration > Apps > API in NinjaOne
- Click "Add" to create a new application
- Give it a descriptive name (e.g., "n8n Automation" or "n8n Ticketing")
- Select the appropriate grant type (see below)
- Configure the redirect URI if using Authorization Code flow
- Save and copy the Client ID and Client Secret immediately
Grant Type Comparison:
| Feature | Client Credentials | Authorization Code |
|---|---|---|
| Auth flow | Machine-to-machine, no user login | User must authorize via browser |
| Token refresh | Request new token each time | Refresh token for auto-renewal |
| Read devices | Yes | Yes |
| Read organizations | Yes | Yes |
| Create tickets | No (returns 403) | Yes |
| Create/update tickets | No | Yes |
| Run scripts | Yes | Yes |
| Best for | Read-only monitoring, reporting | Full automation including ticketing |
Critical distinction: client_credentials cannot create tickets. The NinjaOne API returns a 403 Forbidden error with no explanation. This is not documented anywhere in their API docs. If your automation needs to create, update, or assign tickets, you must use the authorization_code grant type.
Authorization Code flow requires a Redirect URI. When setting up the API application, enter your n8n instance's OAuth2 callback URL:
https://<your-n8n-domain>/rest/oauth2-credential/callback
For example: https://n8n.yourdomain.com/rest/oauth2-credential/callback
Scopes: Request these scopes for full access:
monitoring- read device data, alerts, activitiesmanagement- manage organizations, policies, devicescontrol- run scripts, reboot devicesoffline_access- enables refresh tokens for long-lived access
Client Credentials Cannot Create Tickets
This is the single biggest gotcha with the NinjaOne API. If you set up a client_credentials application and try to POST to /v2/ticketing/ticket, you will get a 403 Forbidden error. NinjaOne does not document this limitation. You must use authorization_code grant type for any ticket operations.
Setting Up the OAuth2 Credential in n8n
n8n's Generic OAuth2 API credential handles the entire OAuth2 flow, including automatic token refresh. Here is how to set it up for NinjaOne.
Step 1: Create the credential in n8n
- Go to Credentials > Add Credential > Search for "Generic OAuth2 API"
- Select "Predefined Credential Type" and choose "OAuth2 API"
Step 2: Configure the OAuth2 settings
Fill in the following fields:
| Field | Value |
|---|---|
| Grant Type | Authorization Code |
| Authorization URL | https://app.ninjarmm.com/ws/oauth/authorize |
| Access Token URL | https://app.ninjarmm.com/ws/oauth/token |
| Client ID | Your NinjaOne API application Client ID |
| Client Secret | Your NinjaOne API application Client Secret |
| Scope | monitoring management control offline_access |
| Authentication | Body (not Header) |
Step 3: Connect the credential
Click "Sign in with Generic OAuth2 API" (or "Connect my account"). This opens a NinjaOne authorization page in your browser. Log in with your NinjaOne admin account, review the requested permissions, check "Remember my decision," and click Authorize.
Once authorized, n8n stores the access token and refresh token. The offline_access scope ensures n8n can automatically refresh the token when it expires (every 3600 seconds / 1 hour) without requiring you to re-authorize.
Why Body authentication, not Header: NinjaOne's token endpoint expects client_id and client_secret in the POST body as form parameters. If you select "Header" authentication, n8n sends them as a Base64-encoded Authorization header, which NinjaOne silently rejects. This is another undocumented behavior. Always use Body.
Test Token Refresh
After connecting the credential, wait 1 hour (or manually expire the token by editing it in n8n) and run a test workflow. If the credential auto-refreshes and the API call succeeds, your OAuth2 setup is correct. If it fails, check that your scope includes "offline_access" and that authentication is set to "Body."
Your First API Call
With the OAuth2 credential connected, let us make a simple API call to verify everything works.
Workflow: List all organizations
-
Add a Manual Trigger node
-
Add an HTTP Request node
-
Configure the HTTP Request:
- Method: GET
- URL:
https://app.ninjarmm.com/api/v2/organizations - Authentication: Predefined Credential Type > OAuth2 API
- Credential: Select your NinjaOne OAuth2 credential
- Send Query Parameters: Add
pageSize=100
-
Click "Test step" and verify you get a JSON response with your organizations
Expected response structure:
[
{
"id": 1,
"name": "Your Organization",
"description": "",
"nodeApprovalMode": "AUTOMATIC"
},
{
"id": 2,
"name": "Client Organization",
"description": "Managed client",
"nodeApprovalMode": "AUTOMATIC"
}
]
Key endpoints you will use throughout this playbook:
| Endpoint | Method | Description |
|---|---|---|
/api/v2/organizations | GET | List all organizations |
/api/v2/devices | GET | List all devices (supports filters) |
/api/v2/devices-detailed | GET | Devices with full details (OS, status, etc.) |
/api/v2/organization/{id}/devices | GET | Devices for a specific organization |
/api/v2/ticketing/ticket | POST | Create a new ticket |
/api/v2/ticketing/ticket/{id} | GET | Get ticket details |
/api/v2/queries/devices | GET | Advanced device queries |
Pagination: Most NinjaOne API endpoints return paginated results. Use the pageSize query parameter (max 1000) to control how many results per page. For endpoints that support cursor-based pagination, check the response for a cursor field and pass it as a query parameter in subsequent requests.
Build Your First NinjaOne Workflow
Create a workflow with Manual Trigger > HTTP Request (GET /api/v2/organizations). Run it and verify you see your organizations in the output. Then change the URL to /api/v2/devices and add pageSize=10 as a query parameter. You should see your managed devices. This confirms your OAuth2 credential is working correctly.
API Gotchas That Will Waste Your Time
The NinjaOne API has several undocumented behaviors that will cost you hours of debugging if you are not aware of them. These are hard-won lessons from production use.
1. htmlBody vs body (Ticket Creation)
When creating a ticket via POST /api/v2/ticketing/ticket, the request body includes a description object. This object accepts two fields: body and htmlBody.
{
"clientId": 2,
"subject": "Monthly Patching",
"description": {
"body": "This is plain text",
"htmlBody": "<h3>This is HTML</h3><p>With formatting</p>"
}
}
The catch: if you include htmlBody, the body field is silently ignored. NinjaOne displays the HTML version in the ticket UI. If you only provide body, NinjaOne uses that as plain text. There is no error or warning when both are provided - it just picks htmlBody and discards body.
Best practice: Always use htmlBody for ticket descriptions. NinjaOne's ticket viewer renders HTML, so you get proper formatting with headers, lists, tables, and bold text. Use <h3>, <ul>, <li>, <strong>, and <table> tags for structured ticket content.
2. client_credentials Cannot Create Tickets
As covered earlier, the client_credentials grant type returns 403 Forbidden when trying to create tickets. The error response gives no indication that the grant type is the issue. You must use authorization_code.
3. DELETE Returns 405 for Tickets
The NinjaOne API does not support deleting tickets via the API. A DELETE request to /api/v2/ticketing/ticket/{id} returns 405 Method Not Allowed. You can close tickets by updating their status, but you cannot delete them programmatically.
4. Device Endpoint Variations
/api/v2/devicesreturns a minimal device list (ID, system name, organization)/api/v2/devices-detailedreturns full details including OS version, last contact time, and IP addresses/api/v2/organization/{orgId}/devicesreturns devices for a specific organization/api/v2/queries/devicessupports advanced filtering with query parameters
Use devices-detailed when you need OS version or online/offline status. The basic devices endpoint does not include these fields.
5. Ticket Form and User IDs
When creating tickets, you can assign them using assignedAppUserId and specify a ticket form with ticketFormId. These IDs are not the same as the user's email or the form's display name. You need to look them up via the API:
- Users: GET
/api/v2/usersreturns all NinjaOne users with their IDs - Ticket Forms: GET
/api/v2/ticketing/ticket-formreturns available forms with IDs
Hardcode these IDs in your workflows after looking them up once, or query them dynamically if your team changes frequently.
The htmlBody Trap
If your tickets are showing up with blank descriptions, check whether you are using "body" instead of "htmlBody" in the description object. The NinjaOne API silently ignores the plain text body when htmlBody is present, and will also silently ignore body in some contexts. Always use htmlBody with HTML-formatted content.
Core Insights
- NinjaOne has no native scheduled tickets, custom reporting, or cross-platform integrations - n8n fills all three gaps
- Use authorization_code grant type, not client_credentials, if your automation needs to create or update tickets
- Set the OAuth2 credential authentication to "Body" in n8n - NinjaOne silently rejects Header authentication
- Include "offline_access" in your scopes to enable automatic token refresh without manual re-authorization
- Always use htmlBody (not body) in ticket descriptions - NinjaOne silently ignores the plain text field when HTML is present
- The NinjaOne API does not support deleting tickets (DELETE returns 405) - you can only close them by updating status