Local API examples¶
Every call of the agent's local interface, with the curl command and the
answer the agent gave. The calls ran on the lab gateway against
cproto-v0.2.0-rc.2. The summary of the interface is on
Local API reference.
Values replaced. Every command on this page was pasted as it is into a
shell on the lab gateway. The status codes, the error codes, the headers and
the shape of each answer are real. The profile version (42), the ETags, the
hashes and the time stamp are replaced; lists are shortened. The Date and
Content-Length headers are left out.
Short flow¶
For software that confirms over the API instead of the marker file (the marker file is recommended, see Two ways to confirm):
- Fetch the packages:
GET /v1/profile/packages, then the content of each one withGET /v1/profile/packages/{id}(The package list, One package). Noteprofile_version. - Wait for a change:
GET /v1/events?profile_version=PROFILE_VERSIONanswers200with the new version when the configuration changes, or204after 60 seconds; ask again (Wait for a change). On200there is a new configuration: fetch it (step 1) and confirm it (step 3). - Confirm: once a package is applied,
POST /v1/profile/packages/{id}/ackwith{"profile_version": PROFILE_VERSION, "applied": true}(Acknowledge a package), with the version you fetched. An ack with an older version gets409: fetch again. When every package is acknowledged, the agent sendsconfirmed.
Run the commands on the gateway as root or as a member of the group
bootstrap-agent (see Transport and access).
The agent answers with one line of JSON; | jq . prints it indented, one field
per line, and jq -r reduces it to one value. On the lab gateway jq was
installed. Where it is missing, | python3 -m json.tool indents as well.
Calls that print private keys are at the end of this page
The full profile and the packages with credentials contain private keys. They are only in Calls that return secrets, behind a warning. Everything before that section prints no secret.
Status¶
{
"interface_version": 1,
"interface_minor_version": 6,
"agent_version": "cproto-v0.2.0-rc.2",
"state": "ready",
"attestation_mode": "tpm",
"profile_version": 42,
"last_checkin": "2026-01-01T12:00:00Z",
"tpm_transient_avail": 3,
"tpm_loaded_sessions": 0,
"tpm_transient_capacity": 3,
"tpm_transient_used": 0,
"identity_cert_state": "not_configured",
"auth_mode": "tpm",
"auth_mode_source": "server",
"activation": "listen",
"profile_ready_marker": "/etc/iot-config/profile-ready"
}
One field only:
last_error_code is missing above because there was none. The fields are
explained under State.
The package list¶
The list names every package with its version, hash and whether it is acknowledged. It never contains the content:
{
"profile_version": 42,
"packages": [
{
"id": "check_in_interval_seconds",
"version": 42,
"sha256": "EXAMPLE-HASH-BASE64=",
"acked": true
},
{
"id": "log_level",
"version": 42,
"sha256": "EXAMPLE-HASH-BASE64=",
"acked": true
}
]
}
The lab gateway's list had six packages; two are shown.
Why the hash looks like that. sha256 is the SHA-256 of the package's
content, written in Base64 (44 characters ending in =), as the platform sends
it. ?pretty=1 adds the same hash in hex as sha256_hex (64 characters), for
comparing with sha256sum. It does not indent; jq does:
curl -s --unix-socket /run/bootstrap-agent/api.sock 'http://localhost/v1/profile/packages?pretty=1' | jq -r '.packages[] | "\(.id) \(.sha256_hex)"'
One package without secrets¶
The answer is the package's content without an envelope: a string, a number or an object. These two packages hold no secret:
curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/profile/packages/log_level | jq .
curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/profile/packages/check_in_interval_seconds | jq .
Which packages hold secrets depends on your template; on the lab gateway they
were target-credential (the broker credential with its private key) and
target-connection. Read those only as in Calls that return secrets.
A package that does not exist (-i prints the status line and the headers):
curl -s -i --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/profile/packages/does-not-exist
Nothing changed: 304¶
Send the ETag of your last answer in If-None-Match. While the
configuration is the same, the answer is 304 without a body. The first line
reads the current ETag:
E=$(curl -s -I --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/profile/packages | sed -n 's/^[Ee][Tt]ag: //p' | tr -d '\r')
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -H "If-None-Match: $E" http://localhost/v1/profile/packages
Acknowledge a package¶
You normally leave this to the confirmation marker, see
Two ways to confirm. Each call below
changes the package's state on the gateway. The first line reads the current
profile version into PV; the calls use it:
PV=$(curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/status | jq -r .profile_version)
Recorded. With the current version and applied: true, the answer is
204 without a body:
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -X POST -d "{\"profile_version\":$PV,\"applied\":true}" http://localhost/v1/profile/packages/log_level/ack
Without applied. The answer is also 204, and the call counts as
applied: false: the package shows "acked": false in the package list
afterwards. Send the call above again to acknowledge it again.
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -X POST -d "{\"profile_version\":$PV}" http://localhost/v1/profile/packages/log_level/ack
Malformed body: 400.
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -X POST -d '{' http://localhost/v1/profile/packages/log_level/ack
Old version: 409. The version is checked before the package, so an
unknown package with an old version gets 409 too:
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -X POST -d '{"profile_version":1,"applied":true}' http://localhost/v1/profile/packages/log_level/ack
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -X POST -d '{"profile_version":1,"applied":true}' http://localhost/v1/profile/packages/nope/ack
Unknown package with the current version: 404.
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -X POST -d "{\"profile_version\":$PV,\"applied\":true}" http://localhost/v1/profile/packages/nope/ack
Wait for a change¶
With the current version, the call waits. If nothing changes, it answers
204 without a body after 60 seconds; ask again. PV is set as under
Acknowledge a package.
curl -s -i --unix-socket /run/bootstrap-agent/api.sock "http://localhost/v1/events?profile_version=$PV"
With an old version, it answers at once with the current one:
curl -s --unix-socket /run/bootstrap-agent/api.sock 'http://localhost/v1/events?profile_version=1' | jq .
Unknown path or method¶
An unknown path, and a method that a path does not take, get 404, not
405:
curl -s -i --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/nope
curl -s -i --unix-socket /run/bootstrap-agent/api.sock -X DELETE http://localhost/v1/status
POST /v1/status and GET /v1/profile/packages/{id}/ack answer 404 in the
same way.
Calls that return secrets¶
Prints private keys. Do not copy it, do not paste it into tickets or chats.
GET /v1/profile returns the whole configuration, and
GET /v1/profile/packages/{id} returns a package's content. For a package
with a credential, such as target-credential on the lab gateway, that
content is the private key of the broker access, in clear text. ?pretty=1
does not hide it. Send the package list instead when you
share output.
To check the profile without seeing its content, read only the headers, or only the package names:
curl -s -I --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/profile
curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/profile | jq -r '.packages[].id'
HTTP/1.1 200 OK
Content-Type: application/json
Etag: "EXAMPLE-ETAG"
X-Profile-Version: 42
check_in_interval_seconds
log_level
target-credential
The full body, and the content of a credential package, are not shown on this page. Their shape, with invented values, is on Local API reference. Read them only from your own software, which needs them.