API conventions
Authentication, the error envelope, tasks, versions, compare-and-set and paging of the native REST API. English only.
This page describes what every operation of the native REST API has in common. The operations themselves are in the generated pages of the REST API reference. The API reference pages are in English in every language.
Requests and data
- The controller serves the API over HTTPS on the address of
[http] listenincontroller.toml(port 8443 in the examples of this documentation). Every operation is under the path prefix/v1/. - Bodies are JSON. Field names are camelCase.
- Object ids are strings such as
vm-42orhost-7. Timestamps are RFC 3339 in UTC. - Every request may carry the header
x-request-id. The controller uses its value as the correlation id of the request, or generates one. The id appears in the logs, in audit records and in thecorrelationIdof an error, and an error response carries it in the headerx-request-id.
Authentication
Every operation except the sign-in calls needs a bearer token in the Authorization header:
GET /v1/about HTTP/1.1
Host: controller.example.com:8443
Authorization: Bearer <token>Without a token, or with an invalid one, the API answers 401 with the message id error.unauthenticated.
Programmatic login
POST /v1/identity/login takes a user name and a password and returns the session token:
{"username": "admin", "password": "<password>", "kind": "interactive"}kind is interactive (the default) or api. The field outcome of the answer is one of:
| Outcome | Meaning |
|---|---|
session | The answer carries session, with the bearer token (shown once), sessionId, expiresAt and the user. |
mfa_required | The account has two-step verification. The answer carries pendingToken; send it with the code to POST /v1/identity/mfa/complete. |
mfa_enrolment_required | The session exists but is restricted until two-step verification is set up. |
A session is restricted while the password must be changed or two-step verification must be set up. Other operations then fail with error.password_rotation_required or error.mfa_required, and the field passwordRotationRequired or mfaEnrolmentRequired of the session says which step is open. The self-service operations of the account, such as the password change and the set-up of two-step verification, still work.
kyvernon login is the same call. Login attempts are limited: too many attempts fail with 429, the message id error.auth.rate_limited and the header Retry-After.
API tokens
An API token is a long-lived credential for automation that cannot enter a code. Its form is kyvt_<id>_<secret>. An account has at most 16 tokens. A token is created with POST /v1/auth/api-tokens for the caller, or for another user with POST /v1/identity/users/{username}/api-tokens, and is shown once. To use it, send it as the password of POST /v1/identity/login with kind set to api; the answer is a session like any other. See Portal integration for a service account that holds a token.
Browser sessions
The web console signs in through the operations under /v1/auth/. They keep the access token in memory, a refresh token in the cookie kyv_refresh and check the header X-Kyvernon-CSRF. Clients other than a browser use the programmatic login.
Errors
A failed request returns an HTTP status and one flat JSON body, for example:
{
"code": "NOT_FOUND",
"message": "virtual machine vm-99 not found",
"messageId": "error.not_found",
"args": {},
"correlationId": "9d1f4c0e-7a53-4a8e-b2c4-3f6b8f0e7a11"
}| Field | Meaning |
|---|---|
code | A stable code from the table below. Clients branch on it. |
message | An English text for developers. Do not show it to end users or parse it. |
messageId | The key of the message, for example error.not_found. Console and clients localise on it. A client treats an id it does not know like its code. It can be absent. |
args | The arguments of the message, as strings. The meaning of each is in the error codes. |
correlationId | The correlation id of the request. Give it to the administrator who reads the logs. |
| Code | HTTP status | Meaning |
|---|---|---|
INVALID_ARGUMENT | 400 | The request is malformed or a value is out of range. |
UNAUTHENTICATED | 401 | The caller is not authenticated or the session expired. |
PERMISSION_DENIED | 403 | The caller lacks a required privilege. |
NOT_FOUND | 404 | The object does not exist, or the caller may not know it does. |
ALREADY_EXISTS | 409 | An object with the same unique key exists. |
CONFLICT | 409 | The object changed since it was read (see compare-and-set below). |
FAILED_PRECONDITION | 412 | The object is in a state that does not allow the operation. |
RESOURCE_EXHAUSTED | 429 | A quota, a queue bound or a rate limit was reached. Try again later. |
CANCELLED | 499 | The caller cancelled the operation. |
INTERNAL | 500 | A bug or an I/O failure. The details are logged, not returned. |
UNIMPLEMENTED | 501 | The operation is not implemented. |
UNAVAILABLE | 503 | The service cannot answer now. Retrying is safe. |
DEADLINE_EXCEEDED | 504 | The operation did not finish in time. |
A few message ids use another status than their code: error.datastore_upload_too_large answers 413 and error.datastore_range_invalid answers 416. The full list of message ids, their arguments and their status is in the error codes. The gRPC API reports the same codes as gRPC status codes.
Tasks
An operation that takes time does not wait for the result. It answers 202 with a task reference, and the work continues in the controller:
{"taskId": "task-17"}Follow the task with these operations:
| Operation | Use |
|---|---|
GET /v1/tasks/{id} | The task as it is now. |
GET /v1/tasks/{id}/wait | Long polling. The answer comes when the task has ended or after timeout seconds (default 30, at most 300). Either way the answer is 200 with the task, so a client asks again while the state is queued or running. |
POST /v1/tasks/{id}/cancel | Cancels a task of the caller, or any task with the privilege Global.CancelTask. |
GET /v1/tasks | A page of tasks. Filters: target, state (comma-separated), user, chainId, since; order is asc (default) or desc. |
The state of a task is queued, running, success, error or cancelled. A task has a progress from 0 to 100. On success result holds the payload of the operation, and some operations leave it empty. On failure error holds an error body as above, and retryable says whether the same request may succeed later. chainId groups the tasks and events of one action. The kyvernon command-line client does the polling with --wait.
A request is refused with RESOURCE_EXHAUSTED when the queue of the controller is full. The limits are in Configuration.
Versions and compare-and-set
- The path prefix
/v1/is the version of the API. A client ignores fields and message ids it does not know. GET /v1/aboutreturnsversion(the build),apiVersion, whether the node is the leader and the currentstoreVersion. The OpenAPI document of each release carries its own version number, and every release of the documentation has the document to download in the REST API reference.- Every stored object has a
version. An update that replaces or changes an object carriesexpectedVersion, the version the client read. When the object has changed since, the controller refuses the update with409, the codeCONFLICTand the message iderror.conflict. Read the object again, apply the change to the new state and send it again. Updates of a virtual machine configuration are merged when the changes do not touch the same settings; when they cannot be merged the answer is409witherror.vm.concurrent_modification. - Listings carry the store
versionthey were read at. The change feedGET /v1/inventory/changestakes that value assince, and a client withresync: truein the answer lists again.
Paging
Operations that list many items return a page:
{"items": [], "version": 4182, "next": "<cursor>"}limit is the largest number of items; the default is 100 and the maximum is 1000 for the operations that state it. next is a cursor. Send it as after in the next request, with the same filters and the same order. The last page has no next.
Version 0.1. Last changed: 2026-11-13