Span Coding Hooks: IT Admin User Guide Jamf Pro (MDM)

Last updated: May 13, 2026

This guide is for IT administrators deploying Span Coding Hooks to macOS devices via Jamf Pro.


What This Package Does

Span Coding Hooks captures AI coding activity (prompts, file edits, tool use) from supported IDEs (currently Cursor and Claude Code) and sends telemetry to Span’s analytics backend. This data appears in your Span dashboard as AI tool usage metrics.

The package installs silently. Employees will not see any notifications or UI changes. Their IDEs continue to work exactly as before.


Prerequisites

  1. A Span account with access to the integrations settings page.

  2. A Jamf Pro tenant managing the target macOS devices, with each device’s Email Address populated under User and Location in its inventory record. This is typically synced from your directory provider (LDAP, Cloud Identity Provider, Jamf Connect, or Single Sign-On). Without it, the configuration script cannot determine the work email per device.

  3. The coding-hooks-{version}.pkg file from your Span account team.


Deployment Steps

Step 1: Enable the Integration and Get Your Token (this needs to be done once)

This step is the same regardless of your MDM.

Head to the AI tool settings dashboard (https://span.app/_/settings/integrations) and under IDE & CLI AI Tool Integrations, enable the tools your engineers use. Here is what each tool supports:

Tool

What’s captured

Claude Code

OTEL metrics + hooks

Cursor

Hooks

Codex CLI

OTEL metrics

Note: The Cursor integration does not yet have a toggle in the dashboard. If your engineers use Cursor, enable Claude Code or Codex CLI to generate the token — the token is shared across all tools and is required by the package regardless of which IDEs you are deploying for.

Once you enable a tool, you’ll be able to access the token that you have to use to authenticate against our Otel backend:


Step 2: Deploy the Email Configuration Profile (one-time)

Jamf Pro policy scripts run locally on the device (as root) and do not have direct access to User and Location inventory data, which is stored in the Jamf Pro server-side database. To make each device’s work email available to the script, deploy a Configuration Profile that uses Jamf Pro’s $EMAIL payload variable. Jamf substitutes the assigned user’s email server-side and writes it to a managed plist on disk.

This only needs to be done once and must be installed on devices before Step 5.

On your local machine, save the following as span-email.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "<http://www.apple.com/DTDs/PropertyList-1.0.dtd>">
<plist version="1.0">
<dict>
    <key>EMAIL</key>
    <string>$EMAIL</string>
</dict>
</plist>
  1. In Jamf Pro: Computers → Configuration Profiles → New.

  2. In the General payload, set Name to Span Email Variable.

  3. In the left sidebar, scroll to Application & Custom Settings → click Configure → choose External Applications.

  4. Click Add, then set:

    • Source: Upload File

    • Preference Domain: app.span.coding-hooks

    • Click Upload PLIST File and select span-email.plist from step 1.

  5. Click the Scope tab. Add the Smart Computer Group (or static group) of devices that should receive Span Coding Hooks. Save.

Jamf substitutes $EMAIL with the value from each device’s User and Location → Email Address at deployment time and writes the resulting plist to /Library/Managed Preferences/app.span.coding-hooks.plist.

Once devices check in, verify on a test device:

/usr/libexec/PlistBuddy -c 'print :EMAIL' '/Library/Managed Preferences/app.span.coding-hooks.plist'

This should print the user’s work email. If the output is empty, confirm an Email Address is populated in Computers → select device → Inventory → User and Location.


Step 3: Add the Span Configuration Script

This script writes the credentials file that the package reads at install time. You will reference it from the Policy in Step 5.

In Jamf Pro: Settings → Computer Management → Scripts → New.

  1. In the General tab, set Display Name to Span Coding Hooks Config.

  2. Click the Script tab and paste the script below.

  3. Click Save.

#!/bin/bash
set -e

# REPLACE THIS with your Organization OTEL Token from Step 1
SPAN_AUTH_TOKEN="<YOUR_TOKEN>"

export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

if [ "$SPAN_AUTH_TOKEN" = "<YOUR_TOKEN>" ] || [ -z "$SPAN_AUTH_TOKEN" ]; then
  echo "ERROR: SPAN_AUTH_TOKEN placeholder was not replaced in the script body."
  echo "Edit the Span Coding Hooks Config script in Jamf Pro and paste the real token from Step 1."
  exit 1
fi

PLIST="/Library/Managed Preferences/app.span.coding-hooks.plist"

if [ ! -f "$PLIST" ]; then
  echo "ERROR: Email variable profile not found at$PLIST"
  echo "Complete Step 2 and wait for the Configuration Profile to install on this device, then re-run the policy."
  exit 1
fi

WORK_EMAIL=$(/usr/libexec/PlistBuddy -c 'print :EMAIL' "$PLIST" 2>/dev/null || true)

if [ -z "$WORK_EMAIL" ] || [[ "$WORK_EMAIL" != *"@"* ]]; then
  echo "ERROR: EMAIL value in$PLIST is empty or not a valid email (got: '$WORK_EMAIL')"
  echo "Confirm Email Address is populated in Computers > select device > Inventory > User and Location, then re-run the policy."
  exit 1
fi

json_escape() {
  printf '%s' "$1" | sed 's/\\\\/\\\\\\\\/g; s/"/\\\\"/g'
}

SPAN_AUTH_TOKEN_JSON=$(json_escape "$SPAN_AUTH_TOKEN")
WORK_EMAIL_JSON=$(json_escape "$WORK_EMAIL")

mkdir -p '/Library/Application Support/app.span.coding-hooks'
printf '{\\n  "span_auth_token": "%s",\\n  "work_email": "%s"\\n}\\n' \\
  "$SPAN_AUTH_TOKEN_JSON" "$WORK_EMAIL_JSON" \\
  > '/Library/Application Support/app.span.coding-hooks/span-config.json'

chown root:staff '/Library/Application Support/app.span.coding-hooks/span-config.json'
chmod 640 '/Library/Application Support/app.span.coding-hooks/span-config.json'

echo "Success: span-config.json written for$WORK_EMAIL"

Why hardcode the token: Jamf script-policy parameters ($4 through $11) are stored in plaintext on the Policy record, the same way the script body is. Hardcoding keeps a single source of truth for the token. The script is org-scoped, not employee-scoped.


Step 4: Upload the Package

Ask the Span team to share the latest coding-hooks-{version}.pkg for this step.

In Jamf Pro: Settings → Computer Management → Packages → New.

  1. In the General tab, set Display Name to Span Coding Hooks.

  2. Click Upload Package and select coding-hooks-{version}.pkg. Your principal distribution point must be configured; a Jamf Cloud Distribution Point works without further setup.

  3. Click Save.


Step 5: Create the Deployment Policy

This Policy bundles the script (priority Before) and the package, so the credentials file is written immediately before the package installs.

In Jamf Pro: Computers → Policies → New.

  1. In the General payload, set:

    • Display Name: Span Coding Hooks Install

    • Trigger: check Recurring Check-in

    • Execution Frequency: Once per computer

  2. In the left sidebar, click Scripts → Configure. Add the Span Coding Hooks Config script from Step 3 and set its Priority to Before.

  3. Click Packages → Configure. Add the Span Coding Hooks package from Step 4. Leave the action as Install.

  4. Click the Scope tab. Add the same Smart Computer Group (or static group) used in Step 2’s Configuration Profile.

  5. Click Save.

Jamf will run the policy on assigned devices at their next recurring check-in. The Scripts payload with priority Before runs the configuration script before the package installs.


Step 6: Smoke Test (before fleet rollout)

Before scoping the Configuration Profile (Step 2) and the Policy (Step 5) to your full fleet, validate the full flow on 1–2 pilot devices. This helps catch token typos, missing User and Location data, and distribution point issues with minimal impact.

  1. In Jamf Pro: Computers → Smart Computer Groups → New (or Static Computer Groups → New). Name it Span Coding Hooks Pilot and add 1–2 test machines, ideally yours and one teammate's.

  2. Edit the Span Email Variable Configuration Profile from Step 2 and the Span Coding Hooks Install policy from Step 5. For each, replace the production scope with Span Coding Hooks Pilot. Save both.

  3. On each pilot device, force a check-in to apply the changes immediately:

sudo jamf policy
  1. Confirm the Configuration Profile installed on each pilot device:

/usr/libexec/PlistBuddy -c 'print :EMAIL' '/Library/Managed Preferences/app.span.coding-hooks.plist'

This should print the pilot user's email.

  1. Confirm the Policy ran. In Computers → select device → History → Policy Logs, the Span Coding Hooks Install entry should show Completed, and the script output should end with Success: span-config.json written for <email>.

  2. Confirm the package installed on each pilot device:

pkgutil --pkg-info app.span.coding-hooks

This should print a package-id, version, and install-time. If it errors with "No receipt for 'app.span.coding-hooks' found", the package did not install. Check the Span Coding Hooks Install policy logs for installer errors before continuing.

  1. Run the health check on each pilot device and confirm every component is green. All checks should pass, and the exit code should be 0:

~/.span/bin/span-health
  1. Once both pilot devices pass all steps above, edit the Configuration Profile and the Policy and replace the pilot scope with your full target Smart Computer Group. Save.

If any step fails, see the Troubleshooting section before proceeding to fleet rollout.

What Employees Will See

Nothing 🙂,  the installation is completely silent:

  • No installer window or prompts appear.

  • No new applications or icons are added.

  • Cursor and Claude Code continue working exactly as before.

The OTel Collector runs as a background system service (LaunchDaemon) and is not visible in the macOS menu bar or Dock.


Verifying the Deployment

In Jamf Pro, go to Computers → search for a device → History tab → Policy Logs:

  • The Span Coding Hooks Install policy should show Completed.

  • Expand the row to see script and installer output. The configuration script should end with: Success: span-config.json written for <email>

To run a full health check on the device:

~/.span/bin/span-health

Troubleshooting

1. Policy log shows ERROR: SPAN_AUTH_TOKEN placeholder was not replaced

The <YOUR_TOKEN> placeholder in the script body was not replaced. Fix it:

  1. In Jamf Pro: Settings → Computer Management → Scripts, open Span Coding Hooks Config.

  2. On the Script tab, replace <YOUR_TOKEN> with the real token from Step 1, then click Save.

  3. Go to Computers → search for the affected device → History tab → Policy Logs, find Span Coding Hooks Install, and click Flush. The policy becomes eligible to re-run at the next recurring check-in. To trigger it immediately, run sudo jamf policy on the device.

If the script succeeded but the integration still is not working, the token value may be incorrect rather than missing. Confirm what was written to disk:

cat '/Library/Application Support/app.span.coding-hooks/span-config.json'

Compare span_auth_token to the value shown under View token on the Span integrations page. If it does not match, update the script in Jamf Pro and re-run as above.

2. EMAIL is empty or invalid

The device’s User and Location → Email Address is blank or malformed, so the $EMAIL substitution wrote an unusable value. Fix it:

  1. Go to Computers → search for the device → Inventory tab → User and Location → Edit.

  2. Set the Email Address field to a valid work email, then click Save.

  3. If you sync user data from a directory service, confirm the upstream record is populated and trigger a recon: on the same device page, click the Management tab → Update Inventory.

  4. Once the email is set, go to the History tab → Policy Logs, click Flush on Span Coding Hooks Install, then run sudo jamf policy on the device (or wait for the next recurring check-in).

  5. Confirm the script output now shows Success: span-config.json written for <email>.

3. OTel Collector not running after install

The package likely installed before the config file was ready. This most commonly happens when the Before script exits with an ERROR: line but the package still runs. Confirm span-config.json exists and contains a real token and email, then re-deliver the policy:

  1. Check the config: cat '/Library/Application Support/app.span.coding-hooks/span-config.json'

  2. If span_auth_token is still <YOUR_TOKEN> or work_email is empty, fix the root cause via Troubleshooting item 1 or 2.

  3. Once the config is correct, go to Computers → search for the device → History tab → Policy Logs, click Flush on Span Coding Hooks Install, then run sudo jamf policy on the device. The installer is safe to run multiple times.

If you have any issues with the installation or have any questions, please reach out so we can assist.


Upgrading

When a new package version is available:

  1. In Jamf Pro: Settings → Computer Management → Packages, open the existing Span Coding Hooks package.

  2. Click Upload Package, select the new coding-hooks-{version}.pkg, then click Save.

  3. The existing Span Coding Hooks Install policy already references this package by name, so no policy edit is required.

Because Execution Frequency is Once per computer, the policy will not re-run automatically on devices that already installed a prior version. To roll out the upgrade:

Open the Span Coding Hooks Install policy → Logs tab → click Flush All. Jamf will re-deliver the policy to in-scope devices at their next recurring check-in.

The config script does not need to be redeployed unless your token changes.


Uninstalling

In Jamf Pro: Settings → Computer Management → Scripts → New.

  1. In the General tab, set Display Name to Span Coding Hooks Uninstall.

  2. Click the Script tab, paste the script below, and click Save.

#!/bin/bash
set -e

UNINSTALLER="/Library/Application Support/app.span.coding-hooks/uninstall.sh"

if [ -f "$UNINSTALLER" ]; then
  bash "$UNINSTALLER"
else
  echo "Warning: uninstall.sh not found at$UNINSTALLER; skipping package uninstall step."
fi

rm -f '/Library/Application Support/app.span.coding-hooks/span-config.json'

Then create a one-off uninstall policy: Computers → Policies → New.

  • General → Display Name: Span Coding Hooks Uninstall

  • General → Trigger: Recurring Check-in

  • General → Execution Frequency: Once per computer

  • Scripts → Configure → add Span Coding Hooks Uninstall (priority After is fine).

  • Scope tab → add the target devices or group.

  • Click Save.

User self-uninstall (for individual opt-out or troubleshooting)

bash "/Library/Application Support/app.span.coding-hooks/uninstall.sh"

User-defined hooks in Cursor and Claude Code are preserved.


Support

For issues not covered here, contact the Span team with the following from the affected device:

# Health check
~/.span/bin/span-health

# Installer log
cat /var/log/span-coding-hooks-install.log

# Config file — redact the token value before sharing
cat '/Library/Application Support/app.span.coding-hooks/span-config.json'