Skip to content

Deutsch

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):

  1. Fetch the packages: GET /v1/profile/packages, then the content of each one with GET /v1/profile/packages/{id} (The package list, One package). Note profile_version.
  2. Wait for a change: GET /v1/events?profile_version=PROFILE_VERSION answers 200 with the new version when the configuration changes, or 204 after 60 seconds; ask again (Wait for a change). On 200 there is a new configuration: fetch it (step 1) and confirm it (step 3).
  3. Confirm: once a package is applied, POST /v1/profile/packages/{id}/ack with {"profile_version": PROFILE_VERSION, "applied": true} (Acknowledge a package), with the version you fetched. An ack with an older version gets 409: fetch again. When every package is acknowledged, the agent sends confirmed.

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

curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/status | jq .
{
  "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:

curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/status | jq -r .state
ready

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:

curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/profile/packages | jq .
{
  "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)"'
check_in_interval_seconds EXAMPLE-HASH-HEX
log_level EXAMPLE-HASH-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 .
"warning"
300

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
HTTP/1.1 404 Not Found
Content-Type: application/json

{"code":"NOT_FOUND"}

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
HTTP/1.1 304 Not Modified
Etag: "EXAMPLE-ETAG"
X-Profile-Version: 42

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
HTTP/1.1 204 No Content

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
HTTP/1.1 400 Bad Request
Content-Type: application/json

{"code":"MALFORMED_REQUEST"}

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
HTTP/1.1 409 Conflict
Content-Type: application/json

{"code":"STALE_PROFILE_VERSION"}

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
HTTP/1.1 404 Not Found
Content-Type: application/json

{"code":"NOT_FOUND"}

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"
HTTP/1.1 204 No Content

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 .
{
  "profile_version": 42
}

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
HTTP/1.1 404 Not Found
Content-Type: application/json

{"code":"NOT_FOUND"}

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.