Install
$ agentstack add skill-sap-automation-pilot-agent-skills-automation-pilot-content-management-via-api ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
SAP Automation Pilot Content API Management
This skill manages catalogs, commands, and inputs in SAP Automation Pilot using the Content API.
Command Release Policy
⚠️ Commands deploy in DRAFT state. Never release automatically — only when the user explicitly requests it and the command has been tested. See ../automation-pilot-command-generation/SKILL.md for the full policy.
Naming Conventions
| Type | Convention | Example | |------|------------|---------| | Commands | PascalCase | RestartCfApp, GetHanaInstance | | Inputs | PascalCase | BtpCredentials, JiraConfig | | Catalogs | kebab-case | my-automations-xxx |
Names must not contain spaces (causes API failures with URL encoding).
Prerequisites
- Set the following required environment variables:
export AUTOPI_HOSTNAME="emea.autopilot.cloud.sap"
export AUTOPI_USERNAME="your-username"
export AUTOPI_PASSWORD="your-password"
export AUTOPI_DEFAULT_CATALOG="mycommands->>"
Supported hostnames:
emea.autopilot.cloud.sap(default - Europe)aus.autopilot.cloud.sap(Australia)apac.autopilot.cloud.sap(Asia Pacific)amer.autopilot.cloud.sap(Americas)ksa.autopilot.cloud.sap(Saudi Arabia)
- Ensure
curlis available in your environment.
Values Are Always Strings on the Wire
When creating or updating inputs via the API, every entry in values and defaultValue must be a JSON string, regardless of the declared type in keys.
| Declared type | Wire representation | |-----------------|---------------------| | string | "my-app" | | number | "42" | | boolean | "true" | | array | "[\"a\",\"b\"]" | | object | "{\"url\":\"...\",\"user\":\"...\"}" |
This also applies to inputValues on trigger and schedule endpoints — every value must be a string on the wire. The runtime converts it to the declared type inside the executor.
Catalogs
List Catalogs
# List all catalogs owned by the tenant
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/catalogs?own=true"
# List catalogs provided by SAP
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/catalogs?provided=true"
# List all catalogs (owned + provided)
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/catalogs"
# List catalogs filtered by tag
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/catalogs?tag=environment:production"
Get Catalog by ID
CATALOG_ID="mycatalog-xxx"
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/catalogs/$CATALOG_ID"
Create Catalog
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"name": "my-catalog",
"description": "My custom catalog for automation commands",
"tags": {
"environment": "development"
}
}' \
"https://$AUTOPI_HOSTNAME/api/v1/catalogs"
Update Catalog
CATALOG_ID="mycatalog-xxx"
curl -s -X PUT \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated description",
"tags": {
"environment": "production"
}
}' \
"https://$AUTOPI_HOSTNAME/api/v1/catalogs/$CATALOG_ID"
Delete Catalog
Note: Catalog must be empty (no commands or inputs) before deletion.
CATALOG_ID="mycatalog-xxx"
curl -s -X DELETE -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/catalogs/$CATALOG_ID"
Commands
List Commands
# List all commands in a catalog
CATALOG="mycatalog-xxx"
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands?catalog=$CATALOG"
# List commands with design-time issues
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands?catalog=$CATALOG&includeIssues=true"
# List commands by tag
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands?tag=type:monitoring"
# List only command IDs (lightweight)
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands/ids?catalog=$CATALOG"
Get Command by ID
Command ID format: catalog:name:version
COMMAND_ID="mycatalog-xxx:MyCommand:1"
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID"
Create/Upload Command
Upload a command from a .command.json file:
COMMAND_FILE="path/to/MyCommand.command.json"
# Create the command
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$COMMAND_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/commands"
Create command inline:
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"name": "MyCommand",
"catalog": "mycatalog-xxx",
"description": "My custom command",
"version": 1,
"inputKeys": {
"message": {
"type": "string",
"description": "Message to process",
"required": true
}
},
"outputKeys": {
"result": {
"type": "string",
"description": "Processing result"
}
},
"configuration": null,
"tags": {}
}' \
"https://$AUTOPI_HOSTNAME/api/v1/commands"
Update Command
COMMAND_ID="mycatalog-xxx:MyCommand:1"
COMMAND_FILE="path/to/MyCommand.command.json"
curl -s -X PUT \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$COMMAND_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID"
Delete Command
COMMAND_ID="mycatalog-xxx:MyCommand:1"
curl -s -X DELETE -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID"
Release Command
⚠️ USE ONLY IF THE USER EXPLICITLY REQUESTED IT
Release a draft command:
COMMAND_ID="mycatalog-xxx:MyCommand:1"
curl -s -X PUT -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID/release"
Deprecate Command
Mark a command as deprecated:
COMMAND_ID="mycatalog-xxx:MyCommand:1"
curl -s -X PUT -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID/deprecate"
Restore Command
Restore a deprecated command:
COMMAND_ID="mycatalog-xxx:MyCommand:1"
curl -s -X PUT -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID/restore"
Bulk Create Commands
Upload multiple commands at once:
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"commands": [
{"name": "Cmd1", "catalog": "mycatalog-xxx", ...},
{"name": "Cmd2", "catalog": "mycatalog-xxx", ...}
]
}' \
"https://$AUTOPI_HOSTNAME/api/v1/bulk/commands"
Inputs
List Inputs
# List inputs in a catalog
CATALOG="mycatalog-xxx"
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/inputs?catalog=$CATALOG"
# List inputs by tag
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/inputs?tag=type:credentials"
Get Input by ID
Input ID format: catalog:name:version
INPUT_ID="mycatalog-xxx:MyInput:1"
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/inputs/$INPUT_ID"
Create Input
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"name": "MyCredentials",
"catalog": "mycatalog-xxx",
"description": "Service credentials",
"version": 1,
"keys": {
"username": {
"type": "string",
"description": "Service username",
"sensitive": false
},
"password": {
"type": "string",
"description": "Service password",
"sensitive": true
}
},
"values": {
"username": "",
"password": ""
},
"tags": {
"type": "credentials"
}
}' \
"https://$AUTOPI_HOSTNAME/api/v1/inputs"
Update Input (Full)
INPUT_ID="mycatalog-xxx:MyInput:1"
curl -s -X PUT \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"name": "MyCredentials",
"catalog": "mycatalog-xxx",
"description": "Updated credentials",
"version": 1,
"keys": {...},
"values": {...}
}' \
"https://$AUTOPI_HOSTNAME/api/v1/inputs/$INPUT_ID"
Update Input (Partial - Values Only)
Update only specific values without replacing the entire input:
INPUT_ID="mycatalog-xxx:MyInput:1"
curl -s -X PATCH \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/merge-patch+json" \
-d '{
"values": {
"password": ""
}
}' \
"https://$AUTOPI_HOSTNAME/api/v1/inputs/$INPUT_ID"
Delete Input
INPUT_ID="mycatalog-xxx:MyInput:1"
curl -s -X DELETE -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/inputs/$INPUT_ID"
Release Input
⚠️ USE ONLY IF THE USER EXPLICITLY REQUESTED IT
INPUT_ID="mycatalog-xxx:MyInput:1"
curl -s -X PUT -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/inputs/$INPUT_ID/release"
Deprecate Input
INPUT_ID="mycatalog-xxx:MyInput:1"
curl -s -X PUT -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/inputs/$INPUT_ID/deprecate"
Restore Input
INPUT_ID="mycatalog-xxx:MyInput:1"
curl -s -X PUT -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/inputs/$INPUT_ID/restore"
Bulk Create Inputs
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{"name": "Input1", "catalog": "mycatalog-xxx", ...},
{"name": "Input2", "catalog": "mycatalog-xxx", ...}
]
}' \
"https://$AUTOPI_HOSTNAME/api/v1/bulk/inputs"
Working with Inputs
Inputs are reusable parameter sets stored in Automation Pilot. They allow you to save credentials, configurations, or commonly-used values that can be referenced when triggering command executions.
Input Structure
{
"name": "MyCfCredentials",
"catalog": "mycatalog-xxx",
"description": "CF credentials for eu10 region",
"version": 1,
"keys": {
"region": { "type": "string", "sensitive": false },
"user": { "type": "string", "sensitive": false },
"password": { "type": "string", "sensitive": true }
},
"values": {
"region": "cf-eu10",
"user": "technical-user",
"password": ""
},
"tags": {}
}
- keys: Defines the schema (type and sensitivity) for each value
- values: The actual stored values
- sensitive: true: Value is masked in logs and UI
Common Input Patterns
CF Credentials (for applm-sapcp, cf-sapcp commands)
{
"name": "CfCredentials-EU10",
"catalog": "mycatalog-xxx",
"description": "Cloud Foundry credentials for EU10",
"version": 1,
"keys": {
"region": { "type": "string", "sensitive": false },
"user": { "type": "string", "sensitive": false },
"password": { "type": "string", "sensitive": true }
},
"values": {
"region": "cf-eu10",
"user": "cf-technical-user@example.com",
"password": "your-password"
}
}
Service Key (for sm-sapcp, aicore-sapcp commands)
{
"name": "HanaCloudServiceKey",
"catalog": "mycatalog-xxx",
"description": "HANA Cloud service binding credentials",
"version": 1,
"keys": {
"serviceKey": { "type": "object", "sensitive": true }
},
"values": {
"serviceKey": "{\"url\":\"https://hana-instance.hana.cloud.sap\",\"user\":\"DBADMIN\",\"password\":\"\",\"certificate\":\"...\"}"
}
}
API Token (for jira-sapcp, github-sapcp commands)
{
"name": "JiraCredentials",
"catalog": "mycatalog-xxx",
"description": "JIRA API credentials",
"version": 1,
"keys": {
"host": { "type": "string", "sensitive": false },
"user": { "type": "string", "sensitive": false },
"password": { "type": "string", "sensitive": true }
},
"values": {
"host": "https://jira.example.com",
"user": "automation@example.com",
"password": "api-token-here"
}
}
Kubernetes Config (for kubernetes-sapcp commands)
{
"name": "K8sClusterConfig",
"catalog": "mycatalog-xxx",
"description": "Kubernetes cluster kubeconfig",
"version": 1,
"keys": {
"kubeconfig": { "type": "object", "sensitive": true }
},
"values": {
"kubeconfig": "{\"apiVersion\":\"v1\",\"kind\":\"Config\",\"clusters\":[...],\"users\":[...],\"contexts\":[...]}"
}
}
Best Practices
- Separate inputs by environment - Create distinct inputs for dev/staging/prod
- Mark sensitive values - Always set
"sensitive": truefor passwords, tokens, and keys - Use descriptive names - Include region/environment in the name (e.g.,
CfCredentials-EU10-Prod) - Keep inputs minimal - Store only reusable values; command-specific values go in
inputValuesat trigger time
Utility Patterns
Export Command to File
COMMAND_ID="mycatalog-xxx:MyCommand:1"
OUTPUT_FILE="MyCommand.command.json"
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID" > "$OUTPUT_FILE"
echo "Command exported to $OUTPUT_FILE"
Upload All Commands from Directory
DIRECTORY="./commands"
for file in "$DIRECTORY"/*.command.json; do
echo "Uploading: $file"
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$file" \
"https://$AUTOPI_HOSTNAME/api/v1/commands"
done
Check if Command Exists
COMMAND_ID="mycatalog-xxx:MyCommand:1"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID")
if [[ "$STATUS" == "200" ]]; then
echo "Command exists"
elif [[ "$STATUS" == "404" ]]; then
echo "Command not found"
else
echo "Error: HTTP $STATUS"
fi
Create or Update Command (Upsert Pattern)
COMMAND_FILE="MyCommand.command.json"
COMMAND_ID=""
# Check if exists
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID")
if [[ "$STATUS" == "200" ]]; then
echo "Updating existing command..."
curl -s -X PUT \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$COMMAND_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/commands/$COMMAND_ID"
else
echo "Creating new command..."
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$COMMAND_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/commands"
fi
Error Handling
The API returns standard HTTP status codes:
| Code | Description | |------|-------------| | 200 | Success (GET, PUT) | | 201 | Created (POST) | | 204 | No Content (DELETE) | | 400 | Bad request - invalid JSON or missing required fields | | 401 | Unauthorized - invalid credentials | | 403 | Forbidden - insufficient permissions | | 404 | Not found - resource doesn't exist | | 409 | Conflict - resource already exists or invalid state | | 429 | Too many requests - rate limited | | 500 | Internal server error | | 503 | Service unavailable |
Always check the response for error messages:
RESPONSE=$(curl -s -w "\n%{http_code}" -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/commands")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
echo "Error ($HTTP_CODE): $BODY"
exit 1
fi
echo "$BODY"
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SAP
- Source: SAP/automation-pilot-agent-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.