Span Coding Hooks: IT Admin User Guide for IRU (MDM)
Last updated: May 5, 2026
This guide is for IT administrators deploying Span Coding Hooks to macOS devices via IRU (formerly Kandji). If your console still shows “Kandji” branding, these steps should be identical.
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
A Span account with access to the integrations settings page.
IRU managing the target macOS devices, with devices enrolled via your IdP (Google Workspace, Okta, Azure AD, etc.) and a user assigned to each device.
The
coding-hooks-{version}.pkgfile 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 Global Variables Profile (one-time)
IRU scripts run as root and do not have access to user session variables. To get each device’s user email, you must first deploy IRU’s Global Variables profile, which writes user identity data from your IdP to a file on disk.
This only needs to be done once and must be synced to devices before Step 3.
Download
Global Variables.mobileconfigfrom the IRU support repo.In the IRU console: Library → Add Library Item → Custom Profile → upload the
Global Variables.mobileconfigfileAssign to the Blueprint(s) that will receive the Span package. Save and activate. (A Blueprint in IRU is the collection of apps, scripts, and profiles assigned to a group of devices, select whichever Blueprint covers your target machines.)
Once devices check in, verify it is working on a test device:
/usr/libexec/PlistBuddy -c 'print :EMAIL' "/Library/Managed Preferences/io.kandji.globalvariables.plist"
This should print the user’s work email. If the output is empty, confirm the device has an IdP user assigned in IRU (Devices → select device → check assigned user).
Step 3: Deploy the Span Configuration Script
This script writes the credentials file that the package reads at install time.
NOTE: Replace <YOUR_TOKEN> with the Span Token from Step 1
#!/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
PLIST="/Library/Managed Preferences/io.kandji.globalvariables.plist"
if [ ! -f "$PLIST" ]; then
echo "ERROR: Global Variables profile not found at $PLIST"
echo "Complete Step 2 and wait for device check-in before running this script."
exit 1
fi
WORK_EMAIL=$(/usr/libexec/PlistBuddy -c 'print :EMAIL' "$PLIST" 2>/dev/null || true)
if [ -z "$WORK_EMAIL" ]; then
echo "ERROR: EMAIL key is empty in Global Variables plist."
echo "Confirm an IdP user is assigned to this device in IRU (Devices > select device)."
exit 1
fi
mkdir -p '/Library/Application Support/app.span.coding-hooks'
printf '{\\n "span_auth_token": "%s",\\n "work_email": "%s"\\n}\\n' \\
"$SPAN_AUTH_TOKEN" "$WORK_EMAIL" \\
> '/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"
In the IRU console: Library → Add Library Item → Custom Script, then:
Shell:
/bin/bashExecution frequency: Install once per device
Script body: paste the script above
Assign to the same Blueprint(s) as Step 2. Save and activate.
Step 4: Deploy the Package
Ask the Span team to share the latest coding-hooks-{version}.pkg for this step.
In the IRU console: Library → Add Library Item → Custom App → upload
coding-hooks-{version}.pkg.In the Pre-install Script field, paste the same script from Step 3. This guarantees the credentials file is written immediately before the package installs, even if the Step 3 script has not yet run on a device.
Set Installation Type to Install once per device.
Assign to the same Blueprint(s) as Steps 2 and 3. Save and activate.
IRU will install the package on assigned devices at next check-in.
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 the IRU console, go to Devices → select a device → Library tab:
The Custom Script (Step 2b) should show:
Success: config written for <email>The Custom App (Step 3) should show: Installed
To run a full health check on the device:
~/.span/bin/span-health
Troubleshooting
Script shows “success” but the integration isn’t working
Check what was actually written to disk:
cat '/Library/Application Support/app.span.coding-hooks/span-config.json'
If span_auth_token still shows <YOUR_TOKEN>, the placeholder was not replaced before deploying. Fix it:
In the IRU console, go to Library and open the Custom Script from Step 3.
Replace
<YOUR_TOKEN>in the script body with your actual token, then save.Go to Devices → select the affected device → Library tab, find the Custom Script, and click Re-run.
Once the script shows success, find the Custom App (Step 4) on the same Library tab and click Reinstall.
EMAIL is empty
The device does not have an IdP user assigned in IRU. Fix it:
Go to Devices → select the device and confirm a user is assigned. Assign one if missing.
Trigger a check-in. On the device page, click Actions → Send MDM Command → Check In.
After the device checks in, go to the Library tab, find the Custom Script from Step 3, and click Re-run.
Confirm the script output shows
Success: span-config.json written for <email>, then click Reinstall on the Custom App.
Upgrading
In the IRU console, open your existing Span Custom App in Library, upload the new coding-hooks-{version}.pkg as a new version. IRU pushes the update at next check-in. The config script does not need to be redeployed unless your token changes.
Uninstalling
Deploy this as a Custom Script in IRU (same settings as Step 2b):
#!/bin/bash
set -e
bash "/Library/Application Support/app.span.coding-hooks/uninstall.sh"
rm -f '/Library/Application Support/app.span.coding-hooks/span-config.json'
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 machine:
Health check output:
~/.span/bin/span-health
Installation log:
cat /var/log/span-coding-hooks-install.log
Span configuration file (redact the token value before sharing):
cat '/Library/Application Support/app.span.coding-hooks/span-config.json'