Change the SSH keys that can log in to a VPS
PATCH /api/v2/vps/{id}/ssh-access
Removes chosen key lines from ~/.ssh/authorized_keys on the running VPS and adds saved account keys to it. Other lines stay as they are, options included. Read GET /api/v2/vps/{id}/ssh-access first. Send its revision and the entries[].id values to remove. If the file changed since that read, nothing is written and the call returns 409 ssh_access_changed. The file is read back after the write. The response is the new list with lastOperation. Removing every key needs confirmRemoveAll: true. With no keys left, the login user cannot log in with an SSH key.
Authentication: An API key with the scope write:vm. About keys and scopes
Request
Path parameters
idstringrequiredThe VPS ID (
vps_…).Body
revisionstringrequiredrevision from the latest GET.removeEntryIdsarray of stringrequiredentries[].id values to remove. Send [] to remove nothing.addSshKeyIdsarray of stringrequiredSaved SSH key IDs (
ssh_…) to add. A key that is already in the file is not added twice. Send [] to add nothing.confirmRemoveAllbooleanSet to true when the change leaves no keys in the file. Default false.
Example
curl -X PATCH "https://cloud.hostup.se/api/v2/vps/vps_01hxa3b4c5d6e7f8g9h0j1k2m3/ssh-access" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "revision": "9b74c9897bac770ffc029102a200c5de6e2c7e1c4f2a3b8d0e5f6a7b8c9d0e1f", "removeEntryIds": [ "ak_3f9a1c7e5b2d8a4c6e0f1b3d5a" ], "addSshKeyIds": [ "ssh_01hxa3b4c5d6e7f8g9h0j1k2m3" ] }'Responses
200 The change is written and read back. Same shape as the GET, plus lastOperation.
{ "available": true, "unavailableReason": null, "unavailableMessage": null, "loginUser": "root", "checkedAt": "2026-08-13T10:01:00.000Z", "revision": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", "entries": [ { "id": "ak_7c9e1f3a5b2d8a4c6e0f1b3d5a", "keyType": "ssh-ed25519", "fingerprint": "SHA256:exampleFingerprint", "comment": "alex@laptop", "restricted": false, "restrictions": [], "savedKeyId": "ssh_01hxa3b4c5d6e7f8g9h0j1k2m3", "savedKeyName": "Laptop" } ], "savedKeys": [ { "id": "ssh_01hxa3b4c5d6e7f8g9h0j1k2m3", "name": "Laptop", "fingerprint": "SHA256:exampleFingerprint", "active": true, "activeEntryIds": [ "ak_7c9e1f3a5b2d8a4c6e0f1b3d5a" ] } ], "lastOperation": { "added": 1, "removed": 1, "verified": true, "metadataSynced": true }}Response fields
availablebooleanAlways true after a change. One of
true.unavailableReasonnullAlways null after a change.
unavailableMessagenullAlways null after a change.
loginUserstringThe user whose
~/.ssh/authorized_keys was read. The VPS’s default login user, usually root.checkedAtstringWhen the file was read.
revisionstringThe new version of the file. Send it with the next PATCH.
entriesarray of objectEvery public key line in the file, including keys added inside the VPS.
idstringID of this line in the file. It changes when the line or its position changes. Use it in
removeEntryIds.keyTypestringKey algorithm.
fingerprintstringSHA256 fingerprint of the public key.
commentstring or nullComment at the end of the key line.
restrictedbooleanTrue when the line has OpenSSH options that limit the key.
restrictionsarray of stringThe options on the line.
forced_command is command=, source_restricted is from=, expires is expiry-time=, restricted is restrict. custom_options covers any other option. The values of the options are not returned.savedKeyIdstring or nullThe saved SSH key on your account with the same public key. Null when the key is not saved on the account.
savedKeyNamestring or nullName of that saved key.
savedKeysarray of objectThe SSH keys saved on your account and whether each one is in the file.
idstringSaved SSH key ID. Use it in
addSshKeyIds.namestringName of the saved key.
fingerprintstring or nullSHA256 fingerprint of the key.
activebooleanTrue when the key is in the file now.
activeEntryIdsarray of stringThe
entries[].id values that hold this key.lastOperationobjectWhat this change did.
addedintegerNumber of saved key IDs sent in
addSshKeyIds. At least 0.removedintegerNumber of lines removed. At least 0.
verifiedbooleanAlways true. The file was read back and matches the change. One of
true.metadataSyncedbooleanTrue when the VPS’s cloud-init key list was also set to the saved keys now in the file. A rebuild then installs the same keys.
Errors
Errors are Problem Details with a stable code. Branch on the code, not on the text.
| 400 | Invalid body. code is invalid_request. It is confirmation_required when the change would leave no keys and confirmRemoveAll is not true. |
| 401 | Unauthorized. Authentication is required. |
| 403 | Forbidden. The caller lacks a required scope or does not own the resource. |
| 404 | Not found. The resource does not exist or is not owned by the caller; code is vps_not_found. |
| 409 | The change was not made or could not be read back. code is ssh_access_unavailable (the file cannot be read, for example the VPS is stopped), ssh_access_changed (the file changed since your GET: read it again), ssh_access_update_failed (the write failed and the old file is kept) or ssh_access_verification_failed (the write ran but could not be read back: check access through the console). |
| 429 | Rate limited. Retry after the limit resets. 429 responses include Retry-After seconds plus X-RateLimit-* headers. |
| 500 | Internal error. Retry later or contact support if the issue persists. |