API: SSH keys and own backup storage
Manage an account's SSH keys and SSH access, and its own S3 or SFTP backup storage, through the token API.
These endpoints do what the SSH / SFTP keys and Own backup storage screens in Control Panel do, with the same checks (the customer's hosting plan and feature list, the licence's Backup storage module), activity log lines and event hooks.
A=https://api.yourdomain.com:2087/v1
H="Authorization: Bearer hf_xxx"
The paths name the account (/v1/accounts/{username}/…); the rules are those of every
per-account endpoint.
SSH keys
curl -s -H "$H" $A/accounts/sarahbakes/ssh/keys
curl -s -X POST -H "$H" -H "Content-Type: application/json" \
-d '{"name":"Laptop","key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA… sarah@laptop"}' $A/accounts/sarahbakes/ssh/keys # 201
curl -s -X DELETE -H "$H" $A/accounts/sarahbakes/ssh/keys/12
{
"ok": true,
"data": [
{
"id": 12,
"name": "Laptop",
"type": "ssh-ed25519",
"bits": 256,
"fingerprint": "SHA256:Xq3…",
"comment": "sarah@laptop",
"weak": false,
"created_at": "2026-10-25"
}
]
}
Keys must be ed25519, ECDSA or RSA of 3072 bits or more, without options; a refused key answers 422 with the
reason. weak marks an older key below what is accepted now.
| Endpoint | What it does |
|---|---|
POST …/ssh/import |
keys: several key lines at once; answers added and skipped (each with its line and reason) |
POST …/ssh/generate |
type (ed25519, rsa or ecdsa), comment, add (default true): a key pair made on the server as the account; answers public_key, private_key and fingerprint (201). The private key is in this answer only and is not kept. |
GET …/ssh/access |
enabled (SSH and SFTP logins are on) and can_switch (the plan lets the customer switch them) |
POST …/ssh/access |
enabled: true or false; 403 when the plan doesn't allow it |
Every change writes the account's ~/.ssh/authorized_keys again, as the account.
Own backup storage
curl -s -H "$H" $A/accounts/sarahbakes/backup-targets
curl -s -X POST -H "$H" -H "Content-Type: application/json" -d '{"kind":"s3","name":"My S3",
"s3_endpoint":"https://s3.eu-west-2.amazonaws.com","s3_region":"eu-west-2","s3_bucket":"sarah-backups",
"s3_access_key":"AKIA…","s3_secret_key":"…","freq":"daily","hour":3,"keep_days":30}' $A/accounts/sarahbakes/backup-targets
A place is answered without its secrets: has_s3_secret_key, has_sftp_password and has_sftp_key say which are
saved, and sftp_host_key is the pinned host key's fingerprint. Saving tests the connection; the result is in test.
| Endpoint | What it does |
|---|---|
GET …/backup-targets/{id} |
one place |
PATCH …/backup-targets/{id} |
change it; a secret left out stays as it is; kind can't change |
DELETE …/backup-targets/{id} |
remove it from the panel; the copies on the storage stay |
POST …/backup-targets/{id}/test |
test the connection: ok, message |
POST …/backup-targets/{id}/run |
copy a fresh backup now, or backup: one already on the server; answers backup |
GET …/backup-targets/{id}/copies |
the account's copies there (name, size, created_at), newest first |
POST …/backup-targets/{id}/restore |
backup and what (all, files, databases, mail or cron): bring the copy back and restore it |
A plan without own backup storage, or with no more places, answers 403 or 422. When a whole restore needs approval, the answer is 403 and the restore must be done in the panel.
Events
These actions raise the hook events ssh.key_added, ssh.key_removed, ssh.access_changed, backup.target_added,
backup.target_removed, backup.remote_uploaded, backup.remote_failed and backup.remote_restored; see
Event hooks.