Skip to main content
Version: 4.x

Open API

Overview

Besides the web UI, Spug exposes a small set of HTTP interfaces so that a CI job, an in-house platform or any automation script can trigger it directly:

CapabilityEndpointNotes
Run an execution templatePOST /api/apis/exec/<template id>/Runs the command of a template on the given hosts
Query the execution resultGET /api/apis/exec/result/<token>/Status and output of the run above
Trigger a pipelinePOST /api/apis/pipeline/<pipeline id>/Triggers a pipeline
Query the pipeline resultGET /api/apis/pipeline/result/<token>/Status and per-node output of the run above
Fetch the host inventoryGET /api/apis/host/Read-only host list, usable as an Ansible dynamic inventory
Fetch application configsGET /api/apis/config/See configuration API
Trigger a deployPOST /api/apis/deploy/<deploy id>/<kind>/See webhook auto deploy

Authentication

Every open endpoint uses the access token from System / Settings / Open service. Pass it either way:

# Option 1: request header (recommended)
curl -H "X-Api-Key: JLV8IGO0DhoxcM7I" https://spug.example.com/api/apis/host/

# Option 2: query parameter
curl "https://spug.example.com/api/apis/host/?apiKey=JLV8IGO0DhoxcM7I"

A wrong or missing token returns 401. Responses use the {"data": ..., "error": null} shape; on failure error holds the message.

Caution

The access token is effectively a key that can run commands on all of your hosts. Never commit it to a public repository or ship it in frontend code — keep it in the encrypted variables of your CI and rotate it regularly.

Run an execution template

  • Endpoint: /api/apis/exec/<template id>/

  • Method: POST

  • The template id is visible in Batch execution / Templates

  • Body (JSON, may be omitted entirely):

    NameTypeRequiredNotes
    host_idsarrayNoHost ids to run on. Defaults to the hosts configured in the template; returns 400 when the template has none either
    paramsobjectNoTemplate parameters keyed by variable name. Defaults defined in the template are applied; a missing required parameter returns 400
  • Returns 202 and a token used to query the result

curl -X POST -H "X-Api-Key: JLV8IGO0DhoxcM7I" -H "Content-Type: application/json" \
-d '{"host_ids": [1, 2], "params": {"who": "nolan"}}' \
https://spug.example.com/api/apis/exec/1/
{"data": {"token": "8a35cc6f1a0043bab1964148c977176b"}, "error": null}

Query the execution result

  • Endpoint: /api/apis/exec/result/<token>/
  • Method: GET
  • Results are kept for one hour; an expired or unknown token returns 404
{
"data": {
"status": "running",
"hosts": [
{
"id": 1,
"title": "web-01(10.0.0.11:22)",
"status": "success",
"exit_code": 0,
"output": "hello from web-01\r\n"
},
{
"id": 2,
"title": "web-02(10.0.0.12:22)",
"status": "running",
"exit_code": null,
"output": ""
}
]
},
"error": null
}

The top-level status aggregates the run: running while any host is still executing, success when all hosts finished without failure, failed otherwise. Per-host status uses the same three values and exit_code is the exit code of the command (Spug itself reports 130 for a timeout and 131 for an exception).

A typical polling loop:

TOKEN=$(curl -s -X POST -H "X-Api-Key: $SPUG_API_KEY" -H "Content-Type: application/json" \
-d '{"params": {"version": "v1.2.3"}}' "$SPUG_URL/api/apis/exec/1/" | jq -r .data.token)

while true; do
STATUS=$(curl -s -H "X-Api-Key: $SPUG_API_KEY" "$SPUG_URL/api/apis/exec/result/$TOKEN/" | jq -r .data.status)
[ "$STATUS" = "running" ] || break
sleep 3
done
echo "result: $STATUS"
[ "$STATUS" = "success" ] || exit 1

Trigger a pipeline

  • Endpoint: /api/apis/pipeline/<pipeline id>/

  • Method: POST

  • Body (JSON, may be omitted entirely):

    NameTypeRequiredNotes
    paramsobjectNoDynamic parameters declared in the parameter node, plus _spug_git_tag / _spug_git_commit for build nodes. Array values are joined with ,. A missing required parameter returns 400
  • Returns 202 and a token

curl -X POST -H "X-Api-Key: JLV8IGO0DhoxcM7I" -H "Content-Type: application/json" \
-d '{"params": {"ver": "v1.2.3"}}' \
https://spug.example.com/api/apis/pipeline/11/
Note

A pipeline containing a data upload node cannot be triggered through the API because that node needs a file picked in the browser; such a request returns 400. Runs triggered through the API are recorded under the pipeline's creator.

Query the pipeline result

  • Endpoint: /api/apis/pipeline/result/<token>/
  • Method: GET
  • Results are kept for one hour
{
"data": {
"pipeline_id": 11,
"status": "success",
"nodes": {
"p1": {"status": "success", "output": "parsing parameters ..."},
"s1": {"status": "success", "output": ""},
"s1.1": {"status": "success", "output": "started ... finished"}
}
},
"error": null
}

The top-level status is running, success or failed. Keys of nodes are node ids; a key shaped like node id.host id carries the output of that node on that host. Node status values match the UI (processing / success / error).

Fetch the host inventory

  • Endpoint: /api/apis/host/

  • Method: GET

  • Parameters:

    NameTypeRequiredDefaultNotes
    formatstringNoansibleansible returns an Ansible dynamic inventory, json returns a plain array of hosts

The endpoint is read-only and returns host names, addresses, ports, usernames, groups and the asset information collected by host management. It never returns private keys, passwords or any other credential.

{
"_meta": {
"hostvars": {
"web-01": {
"ansible_host": "10.0.0.11",
"ansible_port": 22,
"ansible_user": "root",
"spug_id": 1,
"spug_groups": ["web"],
"spug_os_name": "CentOS 7.9",
"spug_private_ip_address": ["10.0.0.11"]
}
}
},
"prod": {"hosts": [], "children": ["web"]},
"web": {"hosts": ["web-01"], "children": []},
"all": {"children": ["ungrouped", "prod"]},
"ungrouped": {"hosts": []}
}

Use it as an Ansible dynamic inventory

Create an executable script that simply forwards the response:

spug_inventory.sh
#!/bin/sh
case "$1" in
--host) echo '{}' ;;
*) curl -sf -H "X-Api-Key: ${SPUG_API_KEY}" "${SPUG_URL}/api/apis/host/" ;;
esac
chmod +x spug_inventory.sh
export SPUG_API_KEY=JLV8IGO0DhoxcM7I SPUG_URL=https://spug.example.com

ansible-inventory -i ./spug_inventory.sh --graph
ansible -i ./spug_inventory.sh web -m ping
ansible-playbook -i ./spug_inventory.sh deploy.yml --limit prod

The group hierarchy of host management maps to Ansible groups and children; hosts without a group land in ungrouped. Group names may repeat in Spug but not in Ansible, so duplicated names get the group id appended (for example web_6).

Tip

Ansible only accepts group names made of letters, digits and underscores. Names containing - or non-ASCII characters trigger an Invalid characters were found in group names warning — it is only a warning and targeting still works. Set transform_invalid_group_chars = ignore in ansible.cfg to silence it.

Ansible connects with the SSH key of the machine it runs on; Spug does not hand out its own key. Use --private-key when needed.