Cursor Setup Guide

Send the prompts your developers submit to Cursor's AI to Axia, so sensitive data pasted into an AI coding agent is detected.

What you'll have at the end

A Cursor hook on each developer machine that forwards every submitted prompt to Axia, using the Webhook URL and Webhook Secret you get on the Connect page in Axia.

Prerequisites

Tell your developers

Capturing prompts is monitoring of what employees type. Treat it as a policy and consent matter, tell developers before you turn it on, and check it fits how you use Cursor Privacy Mode.


1 Connect Cursor in Axia

  1. In Axia, open Connect and find Cursor under AI coding.
  2. Click Connect.
  3. Axia shows a Webhook URL and a Webhook Secret. Copy both.
Value to copyLooks like
Webhook URLhttps://<your-axia-host>/api/v1/webhooks/cursor
Webhook SecretA long random string
The secret is shown once

Save the Webhook Secret in your password manager now. If you lose it, open the connection on the Connect page and click Regenerate secret, then update every machine: the old secret stops working.


2 Check the Webhook URL is reachable

From a developer machine, send a test prompt. Replace the two values with yours:

curl -i -X POST "https://<your-axia-host>/api/v1/webhooks/cursor" \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Secret: <webhook-secret>" \
  -d '{"event":"user_prompt_submit","timestamp":"2026-01-01T00:00:00Z","project_dir":"/tmp",
       "data":{"user_prompt":"Axia connectivity test","session_id":"setup-test","prompt_id":"1","user":"you@yourcompany.com"}}'

You should get HTTP 202 with "status":"accepted". If not, see Troubleshooting.


3 Set the URL and secret on each machine

The hook reads two environment variables. Cursor must see them when it starts, so set them for the user's login session, not only in a shell profile: Cursor opened from the Dock, Start menu or an app launcher does not read ~/.zshrc, ~/.bashrc or a PowerShell profile.

VariableValue
AXIA_WEBHOOK_URLThe Webhook URL from step 1
AXIA_WEBHOOK_SECRETThe Webhook Secret from step 1
AXIA_USER_EMAIL (optional)The developer's work email. See Which user is the prompt attributed to?

macOS

Push a LaunchAgent with your MDM that runs at login, for example /Library/LaunchAgents/com.yourcompany.axia-env.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>Label</key><string>com.yourcompany.axia-env</string>
  <key>RunAtLoad</key><true/>
  <key>ProgramArguments</key><array>
    <string>/bin/sh</string><string>-c</string>
    <string>launchctl setenv AXIA_WEBHOOK_URL "https://<your-axia-host>/api/v1/webhooks/cursor"; launchctl setenv AXIA_WEBHOOK_SECRET "<webhook-secret>"</string>
  </array>
</dict></plist>

Linux

Add the variables to /etc/environment (or a file in /etc/environment.d/), then have the user log out and back in.

Windows

Set them as system environment variables, with Intune or another MDM, or from an Administrator command prompt:

setx /M AXIA_WEBHOOK_URL "https://<your-axia-host>/api/v1/webhooks/cursor"
setx /M AXIA_WEBHOOK_SECRET "<webhook-secret>"

Have the user sign out and back in, so Cursor starts with the new variables.


4 Deploy the hook

Cursor reads hooks.json from several places. Use the enterprise location for production: developers cannot remove it. Use the per-user location to try it on your own machine first.

ScopePathUse for
Enterprise (macOS)/Library/Application Support/Cursor/hooks.jsonFleet rollout by MDM
Enterprise (Linux)/etc/cursor/hooks.jsonFleet rollout by MDM
Enterprise (Windows)C:\ProgramData\Cursor\hooks.jsonFleet rollout by MDM
TeamCursor dashboard (Cursor Enterprise, see below)Fleet rollout without MDM
User~/.cursor/hooks.jsonTesting on one machine

The file contents differ by operating system, because Cursor runs hooks with PowerShell on Windows. If a hooks.json already exists there, add the beforeSubmitPrompt entry to it.

macOS and Linux

{
  "version": 1,
  "hooks": {
    "beforeSubmitPrompt": [
      {
        "timeout": 10,
        "failClosed": false,
        "command": "python3 -c \"import os,sys,json,shutil,subprocess,getpass,datetime as dt;s=os.environ.get('AXIA_WEBHOOK_SECRET','');url=os.environ.get('AXIA_WEBHOOK_URL','');(s and url) or sys.exit(0);d=json.load(sys.stdin);e=(subprocess.run(['git','config','user.email'],capture_output=True,text=True).stdout.strip() if shutil.which('git') else '');r=d.get('workspace_roots') or [''];u=os.environ.get('AXIA_USER_EMAIL') or e or d.get('user_email') or getpass.getuser();body=json.dumps({'event':'user_prompt_submit','timestamp':dt.datetime.now(dt.timezone.utc).isoformat(),'project_dir':(r[0] if r else ''),'data':{'user_prompt':d.get('prompt',''),'session_id':d.get('conversation_id',''),'prompt_id':d.get('generation_id',''),'user':u}});p=subprocess.Popen([sys.executable,'-c','import sys,json,urllib.request as r;m=json.loads(sys.stdin.buffer.readline());r.urlopen(r.Request(m[0],data=sys.stdin.buffer.read(),headers=m[1]),timeout=5)'],stdin=subprocess.PIPE,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,**({'creationflags':0x00000208} if os.name=='nt' else {'start_new_session':True}));p.stdin.write(json.dumps([url,{'Content-Type':'application/json','X-Webhook-Secret':s}]).encode()+bytes([10])+body.encode());p.stdin.close()\" 2>/dev/null; echo '{\"continue\": true}'"
      }
    ]
  }
}

Windows

{
  "version": 1,
  "hooks": {
    "beforeSubmitPrompt": [
      {
        "timeout": 10,
        "failClosed": false,
        "command": "python -c 'import os,sys,json,shutil,subprocess,getpass,datetime as dt;s=os.environ.get(''AXIA_WEBHOOK_SECRET'','''');url=os.environ.get(''AXIA_WEBHOOK_URL'','''');(s and url) or sys.exit(0);d=json.load(sys.stdin);e=(subprocess.run([''git'',''config'',''user.email''],capture_output=True,text=True).stdout.strip() if shutil.which(''git'') else '''');r=d.get(''workspace_roots'') or [''''];u=os.environ.get(''AXIA_USER_EMAIL'') or e or d.get(''user_email'') or getpass.getuser();body=json.dumps({''event'':''user_prompt_submit'',''timestamp'':dt.datetime.now(dt.timezone.utc).isoformat(),''project_dir'':(r[0] if r else ''''),''data'':{''user_prompt'':d.get(''prompt'',''''),''session_id'':d.get(''conversation_id'',''''),''prompt_id'':d.get(''generation_id'',''''),''user'':u}});p=subprocess.Popen([sys.executable,''-c'',''import sys,json,urllib.request as r;m=json.loads(sys.stdin.buffer.readline());r.urlopen(r.Request(m[0],data=sys.stdin.buffer.read(),headers=m[1]),timeout=5)''],stdin=subprocess.PIPE,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,**({''creationflags'':0x00000208} if os.name==''nt'' else {''start_new_session'':True}));p.stdin.write(json.dumps([url,{''Content-Type'':''application/json'',''X-Webhook-Secret'':s}]).encode()+bytes([10])+body.encode());p.stdin.close()' 2>$null; Write-Output '{\"continue\": true}'"
      }
    ]
  }
}
Keep failClosed off

With "failClosed": false, a network error or a missing Python never stops a developer's prompt. The hook also always prints {"continue": true}, so Axia only observes: it never blocks.

Cursor Enterprise: deploy from the Cursor dashboard

On Cursor Enterprise you can add the hook in the Cursor web dashboard instead of deploying a file. Cursor syncs it to every team member automatically, about every 30 minutes. You still need to set the variables on each machine (step 3).

Open Cursor team hooks ↗

  1. Sign in to the Cursor dashboard as a team admin. The button above opens the hooks section.
  2. Add a hook on the beforeSubmitPrompt step for each operating system your developers use, targeted to that system, with the script below.

macOS and Linux:

#!/bin/sh
python3 -c "import os,sys,json,shutil,subprocess,getpass,datetime as dt;s=os.environ.get('AXIA_WEBHOOK_SECRET','');url=os.environ.get('AXIA_WEBHOOK_URL','');(s and url) or sys.exit(0);d=json.load(sys.stdin);e=(subprocess.run(['git','config','user.email'],capture_output=True,text=True).stdout.strip() if shutil.which('git') else '');r=d.get('workspace_roots') or [''];u=os.environ.get('AXIA_USER_EMAIL') or e or d.get('user_email') or getpass.getuser();body=json.dumps({'event':'user_prompt_submit','timestamp':dt.datetime.now(dt.timezone.utc).isoformat(),'project_dir':(r[0] if r else ''),'data':{'user_prompt':d.get('prompt',''),'session_id':d.get('conversation_id',''),'prompt_id':d.get('generation_id',''),'user':u}});p=subprocess.Popen([sys.executable,'-c','import sys,json,urllib.request as r;m=json.loads(sys.stdin.buffer.readline());r.urlopen(r.Request(m[0],data=sys.stdin.buffer.read(),headers=m[1]),timeout=5)'],stdin=subprocess.PIPE,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,**({'creationflags':0x00000208} if os.name=='nt' else {'start_new_session':True}));p.stdin.write(json.dumps([url,{'Content-Type':'application/json','X-Webhook-Secret':s}]).encode()+bytes([10])+body.encode());p.stdin.close()" 2>/dev/null; echo '{"continue": true}'

Windows (PowerShell):

python -c 'import os,sys,json,shutil,subprocess,getpass,datetime as dt;s=os.environ.get(''AXIA_WEBHOOK_SECRET'','''');url=os.environ.get(''AXIA_WEBHOOK_URL'','''');(s and url) or sys.exit(0);d=json.load(sys.stdin);e=(subprocess.run([''git'',''config'',''user.email''],capture_output=True,text=True).stdout.strip() if shutil.which(''git'') else '''');r=d.get(''workspace_roots'') or [''''];u=os.environ.get(''AXIA_USER_EMAIL'') or e or d.get(''user_email'') or getpass.getuser();body=json.dumps({''event'':''user_prompt_submit'',''timestamp'':dt.datetime.now(dt.timezone.utc).isoformat(),''project_dir'':(r[0] if r else ''''),''data'':{''user_prompt'':d.get(''prompt'',''''),''session_id'':d.get(''conversation_id'',''''),''prompt_id'':d.get(''generation_id'',''''),''user'':u}});p=subprocess.Popen([sys.executable,''-c'',''import sys,json,urllib.request as r;m=json.loads(sys.stdin.buffer.readline());r.urlopen(r.Request(m[0],data=sys.stdin.buffer.read(),headers=m[1]),timeout=5)''],stdin=subprocess.PIPE,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,**({''creationflags'':0x00000208} if os.name==''nt'' else {''start_new_session'':True}));p.stdin.write(json.dumps([url,{''Content-Type'':''application/json'',''X-Webhook-Secret'':s}]).encode()+bytes([10])+body.encode());p.stdin.close()' 2>$null; Write-Output '{"continue": true}'

Dashboard hooks don't take a failClosed option. They fail open by default, so the prompt still goes through if the hook fails.

Restart Cursor after deploying. In Cursor, the Hooks tab under Customize lists the hook and shows its output if it fails.

Which user is the prompt attributed to?

The hook works out the developer's identity in this order:

  1. The AXIA_USER_EMAIL environment variable, if set
  2. The developer's git config user.email
  3. The email of the account signed in to Cursor
  4. The operating system login name

Git email comes before the Cursor account so that one person's Cursor and Claude Code prompts match up in Axia.


5 Verify

  1. On a machine with the hook, open Cursor and send any prompt to the AI.
  2. In Axia, the Cursor connection on the Connect page shows a recent event. A prompt containing test sensitive data (for example a fake card number) should open a case.

Troubleshooting

SymptomCauseFix
Test returns 404 Unknown sourceCursor is not connected in Axia, or the URL is wrongConnect Cursor (step 1) and copy the Webhook URL exactly
Test returns 401Wrong or missing secretPaste the secret again, or regenerate it on the Connect page and update every machine
Test times outThe machine can't reach AxiaCheck VPN, proxy and firewall rules for the Axia host
Test works, but no prompts arriveCursor started without the variables, or the hook isn't loadedCheck step 3 applied to the login session (on macOS: launchctl getenv AXIA_WEBHOOK_URL; on Windows: echo %AXIA_WEBHOOK_URL% in a new command prompt), quit and reopen Cursor, check Customize > Hooks
No prompts arrive from Windows machinesPython is missing, or python opens the Microsoft Store placeholderInstall Python 3 from python.org with Add python.exe to PATH, then check python --version in a new terminal
Prompts are attributed to a login name, not an emailNo AXIA_USER_EMAIL, git email or Cursor account emailSet AXIA_USER_EMAIL or the developer's git email

Verification Checklist