Skip to content
Back to skills

Jobs

ASecurity

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.

  • 48 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
data-aijavascripttypescriptgojavashellbashapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add wyre-technology/msp-claude-plugins --skill jobs --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Jobs?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Jobs
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wyre-technology-jobs/badge)](https://www.skillsdirectory.com/skills/wyre-technology-jobs)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.
when_to_use: "When running quick jobs, scheduling jobs, monitoring job status, and viewing results"
triggers:
  - datto job
  - rmm job
  - quick job
  - run script
  - component job
  - job status
  - job results
  - scheduled job
  - 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

```http
GET /api/v2/site/{siteUid}/jobs?max=50
Authorization: Bearer {token}
```

## 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}`);

  // Check required variables
  const missingVars = component.variables
    .filter(v => v.required && !variables[v.name])
    .map(v => v.name);

  if (missingVars.length > 0) {
    return {
      success: false,
      error: `Missing required variables: ${missingVars.join(', ')}`
    };
  }

  // 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 };
  }
}
```

## Best Practices

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

## Job Status Interpretation

| 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) |

## 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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…