Onboard privileged accounts at scale with CyberArk discovery API
For developers automating privileged account onboarding into CyberArk, this guide shows a repeatable API-driven flow: authenticate, run account discovery, inspect results, and onboard accounts in bulk. You will leave with working curl and jq commands, verification checks, and the failure modes that usually trigger support tickets.
TL;DR — Use the CyberArk REST API to authenticate, trigger account discovery on your target platforms, filter the discovered accounts you actually want, and then create managed accounts in the correct Safe and platform. The most common failure is mixing up platform IDs, Safe permissions, or authentication token scope; verify those first before debugging payloads. Reading time: ~5 min
Goal
When you finish, you will have a repeatable scriptable workflow that discovers privileged accounts, filters the results, and onboards the selected accounts into CyberArk so they appear as managed accounts in the target Safe and can be verified with API calls.
Prerequisites
- A CyberArk Privilege Cloud or self-hosted PVWA environment URL, for example
https://pvwa.example.com - An API-capable CyberArk user with permission to authenticate, view platforms, run discovery, and add accounts to the target Safe
- The target Safe name, for example
Unix-ProdorWindows-Servers - The platform ID you will onboard against; get it from API instead of guessing
- A list of target systems or an account discovery source already configured in your CyberArk environment
curl >= 8— check withcurl --versionjq >= 1.6— check withjq --version- Bash or a POSIX shell
- Network access from your workstation or runner to the PVWA URL over HTTPS
- If your org uses SSO/RADIUS/SAML for API auth, the exact authentication type accepted by your CyberArk deployment
Steps
Step 1: Set environment variables
Run these commands and replace the values literally.
export CYBERARK_BASE_URL="https://pvwa.example.com"
export CYBERARK_AUTH_TYPE="cyberark"
export CYBERARK_USERNAME="svc_api_onboarding"
export CYBERARK_PASSWORD='REPLACE_WITH_REAL_PASSWORD'
export SAFE_NAME="Unix-Prod"
export PLATFORM_ID="UnixSSH"
What you should see when this succeeds: no output, and echo "$CYBERARK_BASE_URL $SAFE_NAME $PLATFORM_ID" prints your values.
Step 2: Authenticate and store the session token
Use the authentication endpoint your environment accepts. For standard CyberArk auth, this shape is common.
TOKEN=$(curl -sS -X POST "${CYBERARK_BASE_URL}/PasswordVault/API/Auth/${CYBERARK_AUTH_TYPE}/Logon" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${CYBERARK_USERNAME}\",\"password\":\"${CYBERARK_PASSWORD}\"}")
echo "$TOKEN"
Typical success output shape:
"v9f7m2...very-long-session-token...Q=="
Normalize it for later calls:
TOKEN=$(echo "$TOKEN" | jq -r '.')
printf '%s\n' "$TOKEN" | wc -c
What you should see when this succeeds: wc -c returns a non-trivial length, usually dozens to hundreds of characters.
Step 3: Confirm the API is reachable and your token works
Do this before trying discovery or onboarding.
curl -sS -H "Authorization: ${TOKEN}" "${CYBERARK_BASE_URL}/PasswordVault/API/Platforms" | jq '.value[0:5] | map({ID, Name})'
Typical success output shape:
[
{
"ID": "UnixSSH",
"Name": "Unix via SSH"
},
{
"ID": "WinServerLocal",
"Name": "Windows Server Local"
}
]
What you should see when this succeeds: a JSON array of platform IDs and names, not an HTML login page and not 401 Unauthorized.
Step 4: Verify the platform ID and Safe before bulk work
Do not hardcode guessed values from memory.
curl -sS -H "Authorization: ${TOKEN}" "${CYBERARK_BASE_URL}/PasswordVault/API/Platforms" | jq -r --arg p "$PLATFORM_ID" '.value[] | select(.ID==$p) | .ID'
curl -sS -H "Authorization: ${TOKEN}" "${CYBERARK_BASE_URL}/PasswordVault/API/Safes/${SAFE_NAME}" | jq '{safeName: .safeName, numberOfVersionsRetention, managingCPM}'
What you should see when this succeeds: the first command prints your exact platform ID, and the second returns JSON for the Safe instead of 404 or 403.
Step 5: Trigger or retrieve account discovery results
CyberArk discovery implementations vary by deployment and connector, but the API pattern is the same: query discovered accounts, then filter. If your environment exposes discovered accounts directly, start here.
curl -sS -H "Authorization: ${TOKEN}" "${CYBERARK_BASE_URL}/PasswordVault/API/DiscoveredAccounts" | jq '.value[0:10]'
If your environment requires a filtered query by address or username, use query parameters your deployment supports. Example shape:
curl -sS -G -H "Authorization: ${TOKEN}" "${CYBERARK_BASE_URL}/PasswordVault/API/DiscoveredAccounts" \
--data-urlencode "search=prod-unix-01" | jq '.value[] | {id, userName, address, platformType}'
What you should see when this succeeds: one or more discovered account records with an internal ID and target address.
Step 6: Build the onboarding payload from discovered results
For bulk onboarding, generate payloads with jq instead of hand-editing JSON. This example turns discovered accounts into account-create payloads for Unix SSH.
curl -sS -H "Authorization: ${TOKEN}" "${CYBERARK_BASE_URL}/PasswordVault/API/DiscoveredAccounts" | \
jq -c --arg safe "$SAFE_NAME" --arg platform "$PLATFORM_ID" '
.value[]
| select(.platformType=="Unix")
| {
name: (.userName + "@" + .address),
address: .address,
userName: .userName,
platformId: $platform,
safeName: $safe,
secretType: "password"
}
' > accounts-to-onboard.jsonl
head -n 3 accounts-to-onboard.jsonl | jq .
What you should see when this succeeds: newline-delimited JSON objects with name, address, userName, platformId, safeName, and secretType.
Step 7: Create managed accounts in bulk
⚠️ This step creates managed accounts in the target Safe. If your CPM policy auto-rotates on add, onboarding the wrong accounts can cause access disruption. Test with 1-2 accounts first.
Single account test:
head -n 1 accounts-to-onboard.jsonl > one-account.json
curl -sS -X POST "${CYBERARK_BASE_URL}/PasswordVault/API/Accounts" \
-H "Authorization: ${TOKEN}" \
-H "Content-Type: application/json" \
-d @one-account.json | jq .
Bulk create with per-line status capture:
while IFS= read -r payload; do
response=$(curl -sS -w '\n%{http_code}' -X POST "${CYBERARK_BASE_URL}/PasswordVault/API/Accounts" \
-H "Authorization: ${TOKEN}" \
-H "Content-Type: application/json" \
-d "$payload")
body=$(printf '%s' "$response" | sed '$d')
code=$(printf '%s' "$response" | tail -n1)
printf 'HTTP %s %s\n' "$code" "$(echo "$payload" | jq -r '.name')"
printf '%s\n' "$body" | jq -c '{id, name, address, userName, platformId, safeName, ErrorCode, ErrorMessage}'
done < accounts-to-onboard.jsonl
What you should see when this succeeds: HTTP 201 lines and response bodies containing a new account id.
Step 8: Log off the API session
Do not leave long-lived sessions open.
curl -sS -X POST "${CYBERARK_BASE_URL}/PasswordVault/API/Auth/Logoff" \
-H "Authorization: ${TOKEN}"
What you should see when this succeeds: an empty body or a simple success response, depending on deployment.
Verify it works
Run an end-to-end check against the managed accounts API.
export TOKEN=$(curl -sS -X POST "${CYBERARK_BASE_URL}/PasswordVault/API/Auth/${CYBERARK_AUTH_TYPE}/Logon" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${CYBERARK_USERNAME}\",\"password\":\"${CYBERARK_PASSWORD}\"}" | jq -r '.')
curl -sS -G -H "Authorization: ${TOKEN}" "${CYBERARK_BASE_URL}/PasswordVault/API/Accounts" \
--data-urlencode "search=${SAFE_NAME}" | jq '.value[] | {id, name, address, userName, safeName, platformId}'
Expected result: the accounts you created are returned with the correct safeName and platformId.
If you get redirected instead of JSON, inspect headers:
curl -sSI "${CYBERARK_BASE_URL}/PasswordVault/API/Platforms"
A misconfigured reverse proxy often looks like this:
HTTP/1.1 302 Found
Location: /PasswordVault/v10/logon
Content-Type: text/html; charset=utf-8
That means your API base URL or proxy path handling is wrong; fix the URL before debugging auth.
Common pitfalls
Wrong platform ID
Mistake: using a human-readable platform name like Unix via SSH instead of the platform ID like UnixSSH.
Symptom: 400 Bad Request or an error body indicating invalid platformId.
Fix: run GET /PasswordVault/API/Platforms and copy the exact .ID value.
Safe exists but your API user cannot add accounts
Mistake: the Safe name is correct, but the API user lacks add-account rights on that Safe.
Symptom: 403 Forbidden on POST /PasswordVault/API/Accounts while GET /Safes/<name> may still work.
Fix: grant the API user or its group add-account permissions on the target Safe, then retry the same payload.
Token includes quotes or newline garbage
Mistake: storing the raw JSON string token without stripping quotes.
Symptom: every authenticated request returns 401 Unauthorized even though logon looked successful.
Fix: normalize with TOKEN=$(... | jq -r '.') and pass it exactly as Authorization: ${TOKEN}.
Posting discovered accounts without filtering duplicates
Mistake: onboarding every discovered record, including accounts already managed.
Symptom: repeated 409 Conflict, duplicate-name errors, or partial bulk success.
Fix: query existing managed accounts first and exclude matches on address + userName + platformId before POSTing.
Reverse proxy or load balancer rewrites API paths
Mistake: calling a base URL that serves the web UI but rewrites or redirects API routes.
Symptom: curl returns HTML, 302 Found, or a login page instead of JSON.
Fix: test with curl -sSI and use the exact PVWA API base path that returns JSON on /PasswordVault/API/....
Auto-management side effects after onboarding
Mistake: bulk onboarding accounts into a platform policy that immediately verifies or rotates passwords.
Symptom: accounts appear in CyberArk, then verification failures or unexpected password changes hit downstream systems.
Fix: test one account on the target platform first, confirm CPM behavior, then bulk onboard the rest.
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI