# Job: Minimal macOS

**Audience:** Integrators deploying a custom executable on macOS endpoints.

This example is the macOS variant of the [Minimal Windows Job](/keeperpam/endpoint-privilege-manager/integrations/examples/job-minimal-windows.md). The structure is identical — the differences are the binary path, the `osFilter`, and signing expectations specific to macOS. If you are deploying to a mixed fleet, pair this file with the Windows and Linux variants.

## The Job JSON

```json
{
  "id": "my-tool",
  "name": "My Tool",
  "description": "Runs MyTool on a 60-minute interval.",
  "enabled": true,

  "schedule": {
    "intervalMinutes": 60
  },

  "osFilter": {
    "windows": false,
    "linux": false,
    "macOS": true
  },

  "mqttTopics": {
    "allowedPublications": ["KeeperLogger"],
    "allowedSubscriptions": []
  },

  "parameters": [],

  "tasks": [
    {
      "id": "run-tool",
      "name": "Run tool",
      "ExecutionType": "Service",
      "command": "MyTool",
      "executablePath": "/usr/local/KeeperPrivilegeManager/Jobs/bin/MyTool/MyTool",
      "arguments": "--keeper-api-base={KeeperApiBaseUrl}",
      "timeoutSeconds": 3600,
      "continueOnFailure": false,
      "scriptType": "Auto"
    }
  ]
}
```

## What to Change

<table data-header-hidden="false" data-header-sticky><thead><tr><th>Field</th><th>What to Put Here</th></tr></thead><tbody><tr><td><code>id</code></td><td>A unique identifier for this job. Use hyphens — no underscores. The filename must match: <code>my-tool.json</code> for <code>"id": "my-tool"</code>. Keep this consistent with your Windows and Linux variants if deploying to a mixed fleet.</td></tr><tr><td><code>name</code></td><td>A human-readable name shown in logs and the admin view.</td></tr><tr><td><code>tasks[0].command</code></td><td>The name of your binary without a path or extension.</td></tr><tr><td><code>tasks[0].executablePath</code></td><td>Full path to your binary. The default agent install root on macOS is <code>/usr/local/KeeperPrivilegeManager</code> — confirm with your administrator if your deployment uses a different path.</td></tr><tr><td><code>tasks[0].arguments</code></td><td>Any flags your binary accepts. Keep <code>{KeeperApiBaseUrl}</code> — the agent substitutes the local HTTPS API base URL here at run time.</td></tr><tr><td><code>schedule.intervalMinutes</code></td><td>How often to run, in minutes.</td></tr><tr><td><code>tasks[0].timeoutSeconds</code></td><td>Maximum run time before the agent kills the task.</td></tr></tbody></table>

**No `.exe` extension.** macOS executables have no extension. Both `command` and `executablePath` use the plain binary name.

**File permissions.** The binary must be executable by the agent service account:

```bash
chmod +x /usr/local/KeeperPrivilegeManager/Jobs/bin/MyTool/MyTool
```

**Signing and notarization.** Apple requires binaries distributed outside the Mac App Store to be signed with an Apple Developer ID certificate and notarized with Apple. An unsigned or unnotarized binary will be blocked by Gatekeeper before the agent can run it as a job task. Sign and notarize your binary as part of your release process — this is a macOS platform requirement, not a KEPM-specific one.

Once signed, add your certificate thumbprint to `Settings:AlternativeSignatures` in `appsettings.json` if MQTT or Plugin Settings access must work outside a job-launched context. For standard job task execution, the agent's process trust handles authentication automatically when it starts your binary.

**`osFilter` and validation.** Agents on Windows and Linux will skip this job entirely. The validator also skips binary existence checks when `osFilter` does not match the current OS, so you can register this job from a non-macOS host without it looking for the macOS binary path.

## Before You Deploy

1. **Sign and notarize the binary** before deploying to any macOS endpoint. Gatekeeper will block unsigned binaries at the OS level.
2. **Deploy the binary first.** Place it at `executablePath` before calling `POST /api/Jobs` — the validator checks that it exists at the time of the call.
3. **Set executable permissions.** Run `chmod +x` on the binary after copying it.
4. **Confirm the install root.** `/usr/local/KeeperPrivilegeManager` is the typical macOS default — verify with your administrator before deploying.
5. **Filename must match `id`.** Save this file as `my-tool.json` if `"id"` is `"my-tool"`.

## Deploy

Validate before saving:

```bash
curl -s -X POST https://127.0.0.1:6889/api/Jobs/validate \
  --cert /path/to/client.pem \
  --key /path/to/client.key \
  --cacert /path/to/ca.pem \
  -H "Content-Type: application/json" \
  -d @my-tool.json
```

Create the job:

```bash
curl -s -X POST https://127.0.0.1:6889/api/Jobs \
  --cert /path/to/client.pem \
  --key /path/to/client.key \
  --cacert /path/to/ca.pem \
  -H "Content-Type: application/json" \
  -d @my-tool.json
```

Trigger a manual run to confirm:

```bash
curl -s -X POST https://127.0.0.1:6889/api/Jobs/my-tool/trigger \
  --cert /path/to/client.pem \
  --key /path/to/client.key \
  --cacert /path/to/ca.pem \
  -H "Content-Type: application/json" \
  -d "{}"
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeper.io/keeperpam/endpoint-privilege-manager/integrations/examples/job-minimal-macos.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
