Datto RMM Jobs · diff
git:20260713.1fbd3f3 to git:20260728.b7e78fc
30 added, 399 removed. Audit A to A.
---
name: "Datto RMM Jobs"
description: >
- Use this skill when working with Datto RMM jobs - running quick jobs,
- scheduling jobs, monitoring job status, and viewing results. Covers
- component scripts, job variables, execution status, stdout/stderr output,
- and job management workflows.
+ Datto RMM job execution: quick jobs vs. scheduled vs. policy jobs, the
+ job status lifecycle, component scripts and their variables, and
+ stdout/stderr/exit-code result handling.
when_to_use: >-
When running quick jobs, scheduling jobs, monitoring job status, and viewing results. Use when:
datto job, rmm job, quick job, run script, component job, job status, job results, scheduled
job, or remote execution.
---
# 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
-
- ```typescript
- 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
-
- ```typescript
- 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
-
- ```http
- GET /api/v2/components?max=250
- Authorization: Bearer {token}
- ```
-
- **Response:**
- ```json
- {
- "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
-
- ```http
- POST /api/v2/device/{deviceUid}/quickjob
- Authorization: Bearer {token}
- Content-Type: application/json
-
- {
- "componentUid": "c1d2e3f4-a5b6-7890-cdef-123456789abc",
- "variables": {
- "days": "30"
- }
- }
- ```
-
- **Response:**
- ```json
- {
- "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
-
- ```http
- GET /api/v2/job/{jobUid}
- Authorization: Bearer {token}
- ```
-
- **Response (Running):**
- ```json
- {
- "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):**
- ```json
- {
- "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
-
- ```http
- GET /api/v2/device/{deviceUid}/jobs?max=50
- Authorization: Bearer {token}
- ```
-
- ### Get Jobs for Site
+ ### Field Reference
- ```http
- GET /api/v2/site/{siteUid}/jobs?max=50
- Authorization: Bearer {token}
- ```
+ A `Job` carries identifiers, target device/site, component, status,
+ start/complete timestamps, `exitCode`/`stdout`/`stderr` results, and input
+ `variables`. A `Component` declares its own typed `ComponentVariable[]`
+ (name, type, required, default). See
+ [references/fields.md](references/fields.md) for the complete interfaces.
- ## Workflows
+ ## Common Workflows
### Run Job and Wait for Completion
```javascript
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
-
- ```javascript
- 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
-
- ```javascript
- 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
-
- ```javascript
- 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}`);
- results.set(jobUid, job);
-
- if (!['completed', 'failed', 'timeout'].includes(job.status)) {
- allComplete = false;
- }
-
- if (onUpdate) {
- onUpdate(jobUid, job);
- }
- } catch (error) {
- results.set(jobUid, { status: 'error', error: error.message });
- }
- }
-
- if (allComplete) break;
-
- if (Date.now() - startTime > timeoutMs) {
- break;
- }
-
- await sleep(pollIntervalMs);
- }
-
- return Array.from(results.entries()).map(([jobUid, data]) => ({
- jobUid,
- ...data
- }));
- }
- ```
-
- ### Job Result Summary
-
- ```javascript
- function summarizeJobResult(job) {
- const duration = job.completedAt && job.startedAt
- ? Math.round((job.completedAt - job.startedAt) / 1000)
- : null;
-
- return {
- jobUid: job.jobUid,
- device: job.hostname,
- component: job.componentName,
- status: job.status,
- exitCode: job.exitCode,
- duration: duration ? `${duration}s` : 'N/A',
- success: job.status === 'completed' && job.exitCode === 0,
- output: job.stdout?.substring(0, 500) || '',
- errors: job.stderr?.substring(0, 500) || ''
- };
- }
- ```
-
- ## Error Handling
-
- ### Common Job API Errors
-
- | Error | Status | Cause | Resolution |
- |-------|--------|-------|------------|
- | Device offline | 400 | Device not online | Wait for device or use scheduled job |
- | Component not found | 404 | Invalid componentUid | Verify component exists |
- | Missing variable | 400 | Required variable not provided | Include all required variables |
- | Job not found | 404 | Invalid jobUid | Verify job was created |
- | Permission denied | 403 | API restrictions | Check component permissions |
-
- ### Error Response Example
-
- ```json
- {
- "errorCode": "DEVICE_OFFLINE",
- "message": "Cannot run quick job on offline device",
- "details": {
- "deviceUid": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
- "lastSeen": 1707900000000
- }
- }
- ```
-
- ### Safe Job Execution
-
- ```javascript
- async function safeRunJob(client, deviceUid, componentUid, variables = {}) {
- // Verify device is online
- const device = await client.request(`/api/v2/device/${deviceUid}`);
-
- if (device.status !== 'online') {
- return {
- success: false,
- error: `Device is ${device.status}`,
- lastSeen: new Date(device.lastSeen).toISOString()
- };
- }
-
- // Verify component exists and get required variables
- const component = await client.request(`/api/v2/component/${componentUid}`);
+ Related helpers - component lookup by name, batch execution across
+ multiple devices, multi-job monitoring, result summarization, and a
+ "safe run" wrapper that pre-validates device status and required
+ variables - are in [references/examples.md](references/examples.md).
- // Check required variables
- const missingVars = component.variables
- .filter(v => v.required && !variables[v.name])
- .map(v => v.name);
+ ## API Patterns
- if (missingVars.length > 0) {
- return {
- success: false,
- error: `Missing required variables: ${missingVars.join(', ')}`
- };
- }
+ - `GET /api/v2/components?max=250` - list components (with their variable schemas)
+ - `POST /api/v2/device/{deviceUid}/quickjob` - create a quick job (`componentUid` + `variables`)
+ - `GET /api/v2/job/{jobUid}` - poll job status/results
+ - `GET /api/v2/device/{deviceUid}/jobs?max=50` - job history for a device
+ - `GET /api/v2/site/{siteUid}/jobs?max=50` - job history for a site
- // Run the job
- try {
- const response = await client.request(
- `/api/v2/device/${deviceUid}/quickjob`,
- {
- method: 'POST',
- body: JSON.stringify({ componentUid, variables })
- }
- );
- return { success: true, jobUid: response.jobUid };
- } catch (error) {
- return { success: false, error: error.message };
- }
- }
- ```
+ See [references/api.md](references/api.md) for full request/response examples.
- ## Best Practices
+ ## Gotchas
- 1. **Verify device online** - Check status before quick jobs
- 2. **Provide all variables** - Include required and optional variables
- 3. **Poll with backoff** - Don't poll too frequently
- 4. **Handle long-running jobs** - Set appropriate timeouts
- 5. **Log job results** - Store stdout/stderr for troubleshooting
- 6. **Use meaningful variable values** - Document what each variable does
- 7. **Test components first** - Validate on test devices
- 8. **Monitor exit codes** - 0 typically means success
- 9. **Handle stderr** - Warnings may appear in stderr even on success
- 10. **Batch carefully** - Respect rate limits when running on multiple devices
+ - **Quick jobs fail with 400 `DEVICE_OFFLINE` if the target device isn't online** - check `device.status` first, or fall back to a scheduled job.
+ - **Missing required component variables fail with 400**, not a partial run - fetch the component's `variables` schema and validate before submitting.
+ - **`exitCode === 0` is the actual success signal**, not `status === 'completed'` alone - a component can complete with a nonzero exit code.
+ - **stderr may contain warnings even on success** - don't treat any stderr output as failure by itself.
+ - Poll with backoff/interval rather than tight-looping; respect rate limits when running jobs across multiple devices (see batch example in references).
- ## Job Status Interpretation
+ ### Exit Code Reference
| Exit Code | Typical Meaning |
|-----------|-----------------|
| 0 | Success |
| 1 | General error |
| 2 | Misuse of command |
| 126 | Permission denied |
| 127 | Command not found |
| 130 | Script terminated (Ctrl+C) |
| 137 | Killed (SIGKILL) |
| 143 | Terminated (SIGTERM) |
+
+ See [references/errors.md](references/errors.md) for the full job API error table.
## Related Skills
- [Datto RMM Devices](../devices/SKILL.md) - Target device management
- [Datto RMM Variables](../variables/SKILL.md) - Job variables
- [Datto RMM Alerts](../alerts/SKILL.md) - Alert-triggered jobs
- [Datto RMM API Patterns](../api-patterns/SKILL.md) - Authentication and pagination