Datto RMM Job Management
Overview
Jobs in Datto RMM execute component scripts on devices. Quick jobs run immediately; scheduled jobs run at specified times. Each job can accept variables, produces output (stdout/stderr), and has a status lifecycle. This skill covers job execution, monitoring, and results retrieval.
Key Concepts
Job Types
| Type | Description | Use Case | |------|-------------|----------| | Quick Job | Runs immediately | Ad-hoc tasks, troubleshooting | | Scheduled Job | Runs at specified time | Maintenance, recurring tasks | | Policy Job | Runs based on policy | Automated responses |
Job Lifecycle
Created → Queued → Running → Completed/Failed
│
└─→ Timeout
Component Scripts
Components are the scripts/programs that jobs execute:
- Built-in Datto components
- Custom PowerShell/Bash scripts
- Third-party integrations
Field Reference
Job Object
interface Job {
// Identifiers
jobUid: string; // Unique job ID
jobId: number; // Legacy numeric ID
// Target
deviceUid: string; // Target device
hostname: string; // Device hostname
siteUid: string; // Device's site
// Component
componentUid: string; // Component being run
componentName: string; // Component display name
// Status
status: JobStatus; // Current status
startedAt?: number; // Execution start (Unix ms)
completedAt?: number; // Completion time (Unix ms)
// Results
exitCode?: number; // Process exit code
stdout?: string; // Standard output
stderr?: string; // Standard error
// Variables
variables?: Record<string, string>; // Input variables
// Timestamps
createdAt: number; // Job creation time
queuedAt: number; // When queued
}
type JobStatus = 'created' | 'queued' | 'running' | 'completed' | 'failed' | 'timeout';
Component Object
interface Component {
uid: string; // Component UID
name: string; // Display name
description: string; // What the component does
category: string; // Component category
osType: string; // "Windows", "macOS", "Linux"
variables: ComponentVariable[];
}
interface ComponentVariable {
name: string; // Variable name
type: string; // "string", "number", "boolean"
required: boolean; // Is required
defaultValue?: string; // Default if not provided
description: string; // Variable purpose
}
API Patterns
List Components
GET /api/v2/components?max=250
Authorization: Bearer {token}
Response:
{
"components": [
{
"uid": "c1d2e3f4-a5b6-7890-cdef-123456789abc",
"name": "Clear Temp Files",
"description": "Clears Windows temp directories",
"category": "Maintenance",
"osType": "Windows",
"variables": [
{
"name": "days",
"type": "number",
"required": false,
"defaultValue": "7",
"description": "Delete files older than X days"
}
]
}
]
}
Create Quick Job
POST /api/v2/device/{deviceUid}/quickjob
Authorization: Bearer {token}
Content-Type: application/json
{
"componentUid": "c1d2e3f4-a5b6-7890-cdef-123456789abc",
"variables": {
"days": "30"
}
}
Response:
{
"jobUid": "j1k2l3m4-n5o6-7890-pqrs-123456789def",
"status": "queued",
"deviceUid": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
"componentUid": "c1d2e3f4-a5b6-7890-cdef-123456789abc",
"createdAt": 1707991200000
}
Get Job Status
GET /api/v2/job/{jobUid}
Authorization: Bearer {token}
Response (Running):
{
"jobUid": "j1k2l3m4-n5o6-7890-pqrs-123456789def",
"status": "running",
"startedAt": 1707991260000,
"deviceUid": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
"hostname": "ACME-DC01",
"componentName": "Clear Temp Files"
}
Response (Completed):
{
"jobUid": "j1k2l3m4-n5o6-7890-pqrs-123456789def",
"status": "completed",
"startedAt": 1707991260000,
"completedAt": 1707991320000,
"exitCode": 0,
"stdout": "Deleted 156 files totaling 2.3 GB",
"stderr": "",
"deviceUid": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
"hostname": "ACME-DC01",
"componentName": "Clear Temp Files"
}
Get Jobs for Device
GET /api/v2/device/{deviceUid}/jobs?max=50
Authorization: Bearer {token}
Get Jobs for Site
GET /api/v2/site/{siteUid}/jobs?max=50
Authorization: Bearer {token}
Workflows
Run Job and Wait for Completion
async function runJobAndWait(client, deviceUid, componentUid, variables = {}, options = {}) {
const { timeoutMs = 300000, pollIntervalMs = 5000 } = options;
// Create the job
const createResponse = await client.request(
`/api/v2/device/${deviceUid}/quickjob`,
{
method: 'POST',
body: JSON.stringify({ componentUid, variables })
}
);
const jobUid = createResponse.jobUid;
const startTime = Date.now();
// Poll for completion
while (true) {
const job = await client.request(`/api/v2/job/${jobUid}`);
if (job.status === 'completed' || job.status === 'failed' || job.status === 'timeout') {
return {
success: job.status === 'completed' && job.exitCode === 0,
job
};
}
// Check timeout
if (Date.now() - startTime > timeoutMs) {
return {
success: false,
job,
error: 'Job polling timeout exceeded'
};
}
await sleep(pollIntervalMs);
}
}
Find Component by Name
async function findComponentByName(client, name) {
const response = await client.request('/api/v2/components?max=250');
const components = response.components || [];
// Exact match
const exact = components.find(c =>
c.name.toLowerCase() === name.toLowerCase()
);
if (exact) return { found: true, component: exact };
// Partial match
const matches = components.filter(c =>
c.name.toLowerCase().includes(name.toLowerCase())
);
if (matches.length === 0) {
return { found: false, suggestions: [] };
}
if (matches.length === 1) {
return { found: true, component: matches[0] };
}
return {
found: false,
ambiguous: true,
suggestions: matches.map(c => ({
name: c.name,
uid: c.uid,
category: c.category
}))
};
}
Batch Job Execution
async function runJobOnMultipleDevices(client, deviceUids, componentUid, variables = {}) {
const jobs = [];
for (const deviceUid of deviceUids) {
try {
const response = await client.request(
`/api/v2/device/${deviceUid}/quickjob`,
{
method: 'POST',
body: JSON.stringify({ componentUid, variables })
}
);
jobs.push({
deviceUid,
jobUid: response.jobUid,
status: 'queued'
});
} catch (error) {
jobs.push({
deviceUid,
error: error.message,
status: 'failed'
});
}
// Respect rate limits
await sleep(100);
}
return jobs;
}
Monitor Running Jobs
async function monitorJobs(client, jobUids, options = {}) {
const { onUpdate, timeoutMs = 600000, pollIntervalMs = 10000 } = options;
const startTime = Date.now();
const results = new Map();
// Initialize tracking
jobUids.forEach(uid => results.set(uid, { status: 'unknown' }));
while (true) {
let allComplete = true;
for (const jobUid of jobUids) {
const current = results.get(jobUid);
if (['completed', 'failed', 'timeout'].includes(current.status)) {
continue;
}
try {
const job = await client.request(`/api/v2/job/${jobUid}`);