API keys and the REST API
Scripts, pipelines, monitoring tools and the gdn command-line tool talk to Gardience with an API key. A key always acts as the person who created it, called its owner, and can never do more than that person.
Create a key
- Sign out of the console and sign back in. Gardience only lets a session that started on its own sign-in page create keys.
- Open Settings and find API keys.
- Enter a name for the key, for example
PrometheusorNightly patch job. Choose a scope:
- Read only: sees everything the API offers, changes nothing.
- Operate (request runbooks): can also request runbook runs and upload Ansible playbooks.
- Configure (Terraform, admins): for Terraform and other infrastructure-as-code tools. Manages enrollments, custom node groups, saved states and event rules, and removes machines. It cannot request runs on the existing fleet. It can, however, decide what happens to new machines: an enrollment lets them join without approval, and a join rule applies a saved state to them (states are still checked by the config keeper, and cannot run commands while command execution is off). Treat it like an admin credential. See Terraform.
- Leave Require approval ticked unless you have a reason not to (see below).
- Click Create key.
- Copy the key. It starts with
gdn_and is shown once. Store it in your secrets manager.
Gardience keeps only a SHA-256 hash of the key. The list shows the first 10 characters so you can tell keys apart, plus the owner, scope, approval setting, last use and expiry date.
Who can do what with keys:
| Action | Who |
|---|---|
| Create a read key | Anyone who can sign in |
| Create an operate key | Anyone whose role can change something. A viewer cannot. |
| Create an operate key that skips approval | Admins only |
| Create a configure key | Admins only. It cannot be limited to runbooks or node groups, and stops working the moment its owner is no longer an admin. |
| See a key in the list | Its owner, and all admins |
| Revoke a key | Its owner, and all admins |
Creating and revoking keys are recorded in the audit log (apikey.created, apikey.revoked).
Approval
When an operate key has Require approval on, each run it requests waits for a second person. Someone with the admin or approver role approves or rejects it in the console under Workflows. The key's owner cannot approve their own request.
The API answers at once with the run's ID and the status awaiting approval. Poll GET /runs/{id} to follow it.
With approval off, the run starts at once with the status running.
What a key is allowed to run
Even an operate key can only:
- Request runbooks, the predefined actions listed by
GET /runbooks, with inputs that pass the runbook's checks. A key can never run an arbitrary Salt function, and cannot launch workflows. - Run a runbook on nodes where its owner's own Salt permissions allow that function. Gardience checks this for every target node before anything is queued. A refusal is recorded as
api.run_denied. - Run uploaded playbooks that its owner uploaded.
A key also needs a valid license to request runs. See Licensing.
When a key stops working
A key is refused with HTTP 401 when any of these is true:
- It was revoked.
- It has expired. Keys created in the console expire 90 days after creation. Create a new one before then.
- Its owner was removed with
gardience user del. This takes effect at once. Its owner has not signed in to the console recently. This is the idle pause:
- 30 days for an owner with a local or directory account.
- 7 days for an owner who signs in through single sign-on, because Gardience cannot see when someone is removed from the identity provider. To change this, add
GARDIENCE_SSO_KEY_DAYS=Nto the install's.envfile and rungardience start.
A paused key works again as soon as its owner signs in. Keys used by monitoring or automation therefore need an owner who signs in regularly.
A read or configure key that tries to request a run gets HTTP 403. A read or operate key that tries to change configuration gets HTTP 403, "This needs a configure key".
Call the REST API
The base URL is https://<console>/gardience/api/v1. Send the key in the Authorization header:
curl -s -H "Authorization: Bearer gdn_..." https://gardience.example.com/gardience/api/v1/fleetInteractive documentation, generated from the running server, is at /gardience/api/v1/docs, and the OpenAPI description at /gardience/api/v1/openapi.json. The console links to it from Settings > API keys (API reference).
Endpoints
All paths are relative to /gardience/api/v1.
| Method and path | Scope | Returns or does |
|---|---|---|
GET /fleet | read | Every node: OS, Salt agent version, pending updates, reboot pending, baseline score, failing controls, compliant, stale. Also the last scan time. |
GET /nodes/{node} | read | One node: pending updates, every baseline control, open drift and when drift was last checked. 404 if there is no such node. |
GET /drift | read | Differences between each node and its desired state, plus history. |
GET /audit?limit=50&q=text | read | Recent audit entries, newest last. limit is 1 to 500. q matches any text in the entry. Searches the most recent 20,000 entries. |
GET /audit/verify | read | Recomputes the audit log's hash chain and reports whether it is intact. |
GET /runbooks | read | Runbooks, the inputs each takes, and the valid node groups. |
GET /runs | read | The 50 most recent runbook and workflow runs. |
GET /runs/{id} | read | One run with its steps, log and approval decision. |
POST /runs | operate | Requests a runbook run. See below. |
POST /playbooks | operate | Uploads an Ansible playbook for the ansible-upload runbook. See below. |
GET /groups | read | Node groups (built-in, Ansible and custom) with their online members. |
GET /inventory | read | Asset inventory: domain, BitLocker, .NET, SELinux, disk, local admins, last boot. |
GET /license | read | License state, type, node limit, expiry and nodes in use, with a message when something needs attention. |
GET /savings | read | Automation hours and money saved this month. |
GET /metrics | read | Prometheus metrics. See Monitoring. |
GET /enrollments | configure | Enrollments, newest first. Tokens are never listed. |
POST /enrollments | configure | Opens an enrollment: label, hours (1 to 2160), groups, site, proxy. Returns its token once. |
GET /enrollments/{id} | configure | One enrollment and its state (active, expired or revoked). |
DELETE /enrollments/{id} | configure | Closes an enrollment. Machines that joined stay. |
GET /enrollments/{id}/script?os=linux&node=NAME&groups=a,b | configure | A first-boot script for one machine (os is linux or windows). Contains the token. |
GET /groups/{name} | read | One node group: its Salt expression and members. |
PUT /groups/{name} | configure | Creates or replaces a custom group: kind (nodes, os, match or compound) and its value. |
DELETE /groups/{name} | configure | Deletes a custom group. |
GET /states, GET /states/{name} | read | Saved states as published, with a short SHA-256 of each. |
PUT /states/{name} | configure | Saves a state (content). The configuration keeper checks it first; it is published within seconds or refused. |
DELETE /states/{name} | configure | Deletes a saved state. |
GET /rules, GET /rules/{id} | read | Event rules. |
PUT /rules/{id} | configure | Creates or replaces an event rule. See Drift checks and event rules. |
DELETE /rules/{id} | configure | Deletes an event rule. |
GET /machines/{node} | read | Whether a machine has joined: its key state (accepted, pending, rejected, denied or none), OS and compliance. |
DELETE /machines/{node} | configure | Removes a machine from the fleet. Answers 202: the config keeper checks the request and the hourly limit, then the Salt master moves the machine's key to a holding folder for 30 days (the web service never deletes keys), so root can put it back with gardience machine restore NAME. At most 50 removals an hour by default (gardience machine limit N); over that, 429. Not available in attach mode. If the name joins again later it counts as a new machine. |
Changes made with a configure key are recorded in the audit log under the key's owner, with "via API key NAME".
Request a run
curl -s -X POST https://gardience.example.com/gardience/api/v1/runs \
-H "Authorization: Bearer gdn_..." -H "Content-Type: application/json" \
-d '{"runbook": "run-command", "inputs": {"nodegroup": "Linux", "command": "uptime"}, "reason": "check load"}'{"run_id": "...", "status": "awaiting approval", "console": "/gardience/workflows#run-..."}The body takes runbook (a runbook ID from GET /runbooks), inputs (an object) and reason (shown to the approver and kept in the audit log). Every request is recorded as api.run_requested.
Upload a playbook
POST /playbooks takes name and content:
namemay use letters, digits,.,-and_, up to 64 characters, and must end in.ymlor.yaml.contentmust be a YAML list of plays, no larger than 200 KB.
The answer gives a playbook value to pass as the playbook input of the ansible-upload runbook. Uploads are kept per owner and recorded as playbook.uploaded. The gdn playbook command does both steps for you.
Errors
Errors come back as JSON with a detail message:
| Code | Meaning |
|---|---|
| 400 | A bad input, for example an unknown runbook input or a node group with no online node. |
| 401 | No key, an unknown, revoked or expired key, or a paused key. |
| 402 | The license does not allow changes right now. |
| 403 | A read key asked for a change, or the owner is not allowed to run that function on those nodes. |
| 404 | No such node or run. |
| 503 | The Salt master is starting or unreachable. Try again in a few seconds. |
The MCP server
Gardience is also a Model Context Protocol (MCP) server. MCP is an open protocol that lets other tools, such as code editors, call a service's functions. Any MCP client that supports the HTTP transport with a custom header can connect.
- Address:
https://<console>/gardience/mcp - Authentication: the same API key, in an
Authorization: Bearer gdn_...header. - Transport: JSON-RPC 2.0 over HTTP POST, with JSON responses. Batches are accepted.
GETreturns 405, because the server never starts a stream of its own.
A typical client configuration looks like this (the exact file depends on your client):
{ "servers": { "gardience": { "type": "http",
"url": "https://gardience.example.com/gardience/mcp",
"headers": { "Authorization": "Bearer gdn_..." } } } }The console shows ready-made examples for common clients under Settings.
Tools
| Tool | Scope | What it does |
|---|---|---|
fleet_status | read | Every node with pending updates, reboot status, baseline score, Salt agent version and compliance. |
node_details | read | One node in full. Takes node. |
compliance_failures | read | Only the nodes out of compliance, with failing controls and pending updates. |
drift_report | read | Open drift per node, plus the 10 most recent automatic corrections. |
search_audit_log | read | Searches the audit log. Takes query and limit (default 25). |
verify_audit_chain | read | Recomputes the audit hash chain. |
list_runbooks | read | Runbooks, their inputs and the valid node groups. |
inventory | read | Asset inventory for every node. |
run_status | read | Status, steps, approver and log of a run. Takes run_id. |
request_runbook | operate | Requests a runbook run. Takes runbook, reason and inputs. |
A read key does not see request_runbook in the tool list. request_runbook follows exactly the same rules as POST /runs, including approval, and each call is recorded in the audit log as mcp.request_runbook. A tool's answer is cut off after 60,000 characters.