AAP 2.6 REST API Guide: Automate the Automation Platform
By Luca Berton · Published 2024-01-01 · Category: troubleshooting
Master the AAP 2.6 REST API for programmatic management. Launch jobs, manage inventories, create credentials, query job results, and integrate AAP with CI/CD.
AAP 2.6 API Overview
Every action available in the AAP web UI is also available through the REST API. The API enables: • Programmatic job execution — trigger automation from scripts, CI/CD, or external tools • Integration with ITSM platforms — ServiceNow, Jira, PagerDuty • Self-service portals — build custom UIs on top of AAP • Bulk operations — manage hundreds of hosts, credentials, or templates • Monitoring and reporting — extract job data for dashboards
See also: AAP 2.6 CI/CD Pipeline Integration: GitOps Workflows with Jenkins, GitLab, and GitHub Actions
Authentication
Personal Access Tokens (PAT)
The recommended authentication method:
# Create a token via API
curl -s -k -X POST \
-H "Content-Type: application/json" \
-u "admin:password" \
"https://gateway.example.org/api/controller/v2/tokens/" \
-d '{"description": "CI/CD Pipeline Token", "scope": "write"}'
# Response
{
"id": 1,
"token": "abc123def456...",
"description": "CI/CD Pipeline Token",
"scope": "write"
}
Using Tokens
# Bearer token (recommended)
curl -s -k -H "Authorization: Bearer abc123def456..." \
"https://gateway.example.org/api/controller/v2/ping/"
# Or via OAuth2 header
curl -s -k -H "Authorization: Bearer abc123def456..." \
"https://gateway.example.org/api/controller/v2/me/"
OAuth2 Application Tokens
For service-to-service integration:
- name: Create OAuth2 application
ansible.builtin.uri:
url: "https://gateway.example.org/api/controller/v2/applications/"
method: POST
headers:
Authorization: "Bearer {{ admin_token }}"
Content-Type: "application/json"
body_format: json
body:
name: "ServiceNow Integration"
organization: 1
authorization_grant_type: "password"
client_type: "confidential"
validate_certs: false
API Endpoints Reference
Core Resources
| Endpoint | Description |
|----------|-------------|
| /api/controller/v2/ping/ | Health check (no auth required) |
| /api/controller/v2/me/ | Current user info |
| /api/controller/v2/config/ | Platform configuration |
| /api/controller/v2/dashboard/ | Summary statistics |
Automation Resources
| Endpoint | Description |
|----------|-------------|
| /api/controller/v2/job_templates/ | Job templates CRUD |
| /api/controller/v2/workflow_job_templates/ | Workflow templates |
| /api/controller/v2/jobs/ | Job history and status |
| /api/controller/v2/workflow_jobs/ | Workflow job history |
| /api/controller/v2/inventories/ | Inventories |
| /api/controller/v2/hosts/ | Hosts |
| /api/controller/v2/groups/ | Host groups |
| /api/controller/v2/projects/ | SCM projects |
| /api/controller/v2/credentials/ | Credentials (metadata only) |
| /api/controller/v2/schedules/ | Job schedules |
| /api/controller/v2/notification_templates/ | Notifications |
Platform Resources
| Endpoint | Description |
|----------|-------------|
| /api/controller/v2/organizations/ | Organizations |
| /api/controller/v2/teams/ | Teams |
| /api/controller/v2/users/ | Users |
| /api/controller/v2/roles/ | RBAC roles |
| /api/controller/v2/instances/ | Mesh instances |
| /api/controller/v2/instance_groups/ | Instance groups |
| /api/controller/v2/execution_environments/ | EE images |
| /api/controller/v2/activity_stream/ | Audit log |
See also: Ansible Tower API: Automate Jobs, Inventories & Workflows via REST (Guide)
Common API Operations
Launch a Job Template
# Launch with defaults
curl -s -k -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://gateway.example.org/api/controller/v2/job_templates/15/launch/"
# Launch with parameters
curl -s -k -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"extra_vars": {"app_version": "2.4.1", "deploy_env": "staging"},
"limit": "webservers",
"inventory": 5,
"credentials": [3, 7]
}' \
"https://gateway.example.org/api/controller/v2/job_templates/15/launch/"
# Response includes job ID
{
"id": 423,
"url": "/api/controller/v2/jobs/423/",
"status": "pending",
...
}
Wait for Job Completion
# Poll job status
JOB_ID=423
while true; do
STATUS=$(curl -s -k -H "Authorization: Bearer $TOKEN" \
"https://gateway.example.org/api/controller/v2/jobs/$JOB_ID/" | \
jq -r '.status')
echo "Job $JOB_ID: $STATUS"
case $STATUS in
successful|failed|error|canceled) break ;;
esac
sleep 5
done
Get Job Output
# Job stdout
curl -s -k -H "Authorization: Bearer $TOKEN" \
"https://gateway.example.org/api/controller/v2/jobs/423/stdout/?format=txt"
# Job events (structured)
curl -s -k -H "Authorization: Bearer $TOKEN" \
"https://gateway.example.org/api/controller/v2/jobs/423/job_events/?page_size=50" | \
jq '.results[] | {counter: .counter, event: .event, task: .task, host: .host_name, changed: .changed}'
Create a Host
curl -s -k -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "web05.example.com",
"inventory": 3,
"enabled": true,
"variables": "{\"ansible_host\": \"192.168.1.15\", \"http_port\": 8080}"
}' \
"https://gateway.example.org/api/controller/v2/hosts/"
Search and Filter
# Search job templates by name
curl -s -k -H "Authorization: Bearer $TOKEN" \
"https://gateway.example.org/api/controller/v2/job_templates/?search=patching"
# Filter hosts by group
curl -s -k -H "Authorization: Bearer $TOKEN" \
"https://gateway.example.org/api/controller/v2/hosts/?group__name=webservers"
# Filter jobs by status and date
curl -s -k -H "Authorization: Bearer $TOKEN" \
"https://gateway.example.org/api/controller/v2/jobs/?status=failed&finished__gte=2026-04-01"
# Order by field
curl -s -k -H "Authorization: Bearer $TOKEN" \
"https://gateway.example.org/api/controller/v2/jobs/?order_by=-finished&page_size=10"
Python API Client
Using requests
#!/usr/bin/env python3
"""AAP API client example"""
import requests
import time
import json
class AAPClient:
def __init__(self, gateway_url, token, verify_ssl=False):
self.base_url = f"{gateway_url}/api/controller/v2"
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
})
self.session.verify = verify_ssl
def launch_job(self, template_id, extra_vars=None, limit=None):
"""Launch a job template and return job ID"""
payload = {}
if extra_vars:
payload["extra_vars"] = extra_vars
if limit:
payload["limit"] = limit
resp = self.session.post(
f"{self.base_url}/job_templates/{template_id}/launch/",
json=payload
)
resp.raise_for_status()
return resp.json()
def wait_for_job(self, job_id, timeout=600, interval=5):
"""Wait for job completion, return final status"""
elapsed = 0
while elapsed < timeout:
resp = self.session.get(f"{self.base_url}/jobs/{job_id}/")
resp.raise_for_status()
job = resp.json()
status = job["status"]
if status in ("successful", "failed", "error", "canceled"):
return job
time.sleep(interval)
elapsed += interval
raise TimeoutError(f"Job {job_id} did not complete in {timeout}s")
def get_job_output(self, job_id):
"""Get job stdout"""
resp = self.session.get(
f"{self.base_url}/jobs/{job_id}/stdout/",
params={"format": "txt"}
)
return resp.text
def list_inventories(self):
"""List all inventories"""
resp = self.session.get(f"{self.base_url}/inventories/")
resp.raise_for_status()
return resp.json()["results"]
def add_host(self, inventory_id, hostname, variables=None):
"""Add a host to an inventory"""
payload = {
"name": hostname,
"inventory": inventory_id,
"enabled": True
}
if variables:
payload["variables"] = json.dumps(variables)
resp = self.session.post(f"{self.base_url}/hosts/", json=payload)
resp.raise_for_status()
return resp.json()
# Usage
client = AAPClient("https://gateway.example.org", "your-token")
# Launch and wait
job = client.launch_job(15, extra_vars={"app_version": "2.4.1"})
print(f"Launched job {job['id']}")
result = client.wait_for_job(job["id"])
print(f"Job finished: {result['status']}")
if result["status"] == "failed":
print(client.get_job_output(job["id"]))
Using awxkit (Official SDK)
from awxkit import api
from awxkit.config import config
config.base_url = "https://gateway.example.org"
config.credentials = {"default": {"username": "admin", "password": "password"}}
root = api.Api()
root.load_session().get()
# Launch job
jt = root.get("/api/controller/v2/job_templates/15/").results
job = jt.launch(extra_vars={"env": "staging"})
job.wait_until_completed()
print(f"Status: {job.status}")
See also: Automate Ansible Collection Testing with GitHub Actions
CI/CD Integration Examples
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy via AAP
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Trigger AAP deployment
run: |
RESPONSE=$(curl -s -k -X POST \
-H "Authorization: Bearer ${{ secrets.AAP_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"extra_vars": {"app_version": "${{ github.sha }}"}}' \
"${{ secrets.AAP_URL }}/api/controller/v2/job_templates/${{ vars.DEPLOY_TEMPLATE_ID }}/launch/")
JOB_ID=$(echo $RESPONSE | jq -r '.id')
echo "Launched AAP job $JOB_ID"
# Wait for completion
while true; do
STATUS=$(curl -s -k \
-H "Authorization: Bearer ${{ secrets.AAP_TOKEN }}" \
"${{ secrets.AAP_URL }}/api/controller/v2/jobs/$JOB_ID/" | \
jq -r '.status')
echo "Status: $STATUS"
case $STATUS in
successful) exit 0 ;;
failed|error|canceled) exit 1 ;;
esac
sleep 10
done
GitLab CI
# .gitlab-ci.yml
deploy:
stage: deploy
script:
- |
JOB_ID=$(curl -s -k -X POST \
-H "Authorization: Bearer $AAP_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"extra_vars\": {\"version\": \"$CI_COMMIT_SHA\"}}" \
"$AAP_URL/api/controller/v2/job_templates/$DEPLOY_TEMPLATE_ID/launch/" | \
jq -r '.id')
echo "AAP Job: $JOB_ID"
Jenkins Pipeline
pipeline {
agent any
stages {
stage('Deploy via AAP') {
steps {
script {
def response = httpRequest(
url: "${AAP_URL}/api/controller/v2/job_templates/${DEPLOY_TEMPLATE_ID}/launch/",
httpMode: 'POST',
customHeaders: [[name: 'Authorization', value: "Bearer ${AAP_TOKEN}"]],
contentType: 'APPLICATION_JSON',
requestBody: '{"extra_vars": {"version": "' + env.BUILD_TAG + '"}}',
ignoreSslErrors: true
)
def jobId = readJSON(text: response.content).id
echo "Launched AAP job ${jobId}"
}
}
}
}
}
ServiceNow Integration
Trigger AAP from ServiceNow Incident
# ServiceNow Business Rule or Flow Designer script
import requests
AAP_URL = "https://gateway.example.org"
AAP_TOKEN = "your-service-token"
def trigger_remediation(incident):
"""Auto-remediate based on incident category"""
template_map = {
"disk_full": 15, # Disk Cleanup template
"service_down": 22, # Service Restart template
"cert_expiry": 31, # Certificate Renewal template
}
template_id = template_map.get(incident.category)
if not template_id:
return
response = requests.post(
f"{AAP_URL}/api/controller/v2/job_templates/{template_id}/launch/",
headers={"Authorization": f"Bearer {AAP_TOKEN}"},
json={
"extra_vars": {
"incident_number": incident.number,
"target_host": incident.cmdb_ci,
"priority": incident.priority
}
},
verify=False
)
# Update incident with AAP job link
job_id = response.json()["id"]
incident.work_notes = f"Automated remediation triggered: AAP Job #{job_id}"
incident.update()
Webhook Receivers
AAP can receive webhooks from GitHub, GitLab, and other systems:
- name: Configure GitHub webhook on job template
ansible.platform.job_template:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Deploy on Push"
webhook_service: "github"
webhook_credential: "GitHub Webhook Secret"
state: present
The webhook URL is:
POST https://gateway.example.org/api/controller/v2/job_templates/{id}/github/
Configure this URL in GitHub repository Settings → Webhooks.
Pagination and Bulk Operations
Handle Paginated Results
def get_all_results(client, endpoint):
"""Fetch all pages of a paginated API response"""
results = []
url = f"{client.base_url}{endpoint}"
while url:
resp = client.session.get(url)
resp.raise_for_status()
data = resp.json()
results.extend(data["results"])
url = data.get("next")
return results
# Get all hosts across all pages
all_hosts = get_all_results(client, "/hosts/?page_size=200")
print(f"Total hosts: {len(all_hosts)}")
FAQ
What is the API rate limit?
AAP does not enforce a hard rate limit by default, but rapid API calls can impact Controller performance. For bulk operations, add small delays between requests. For monitoring, scrape the metrics endpoint instead of polling job status.
Can I use the API to export/import configurations?
Yes. Use the API to export job templates, inventories, credentials (metadata only), and workflows as JSON. The awx-manage export_assets and awx-manage import_assets CLI commands provide bulk export/import functionality.
Is the API backward compatible?
The /api/v2/ endpoint has been stable since Ansible Tower 3.x. AAP 2.6 adds new endpoints under /api/controller/v2/ but maintains backward compatibility. Check the API changelog in the release notes for deprecations.
How do I find the template ID for API calls?
Browse the API in your browser at https://gateway.example.org/api/controller/v2/ — it provides a browsable interface. Or search by name: /api/controller/v2/job_templates/?search=patching.
Can I use GraphQL instead of REST?
No. AAP provides a REST API only. There is no GraphQL endpoint.
Conclusion
The AAP 2.6 REST API transforms the platform from a UI-driven tool into a programmable automation engine. Whether integrating with CI/CD pipelines, ITSM platforms, or building custom self-service portals, the API provides complete programmatic access to every AAP capability.
Related Articles
• AAP 2.6 Architecture and Components: Complete Guide • AAP 2.6 RBAC and Gateway API • AAP 2.6 Configuration as Code with ansible.platform • AAP 2.6 Workflow Templates: Advanced Multi-Step Automation Guide • AAP 2.6 Job Templates and Inventories: Complete Configuration GuideCategory: troubleshooting