curl --request PATCH \
--url https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"description": "Nightly snapshots of the database volume",
"enabled": false,
"interval_minutes": 1440,
"retention_count": 7,
"retention_days": 30,
"tags": {
"environment": "production",
"team": "backend"
}
}
'import requests
url = "https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}"
payload = {
"description": "Nightly snapshots of the database volume",
"enabled": False,
"interval_minutes": 1440,
"retention_count": 7,
"retention_days": 30,
"tags": {
"environment": "production",
"team": "backend"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
description: 'Nightly snapshots of the database volume',
enabled: false,
interval_minutes: 1440,
retention_count: 7,
retention_days: 30,
tags: {environment: 'production', team: 'backend'}
})
};
fetch('https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'description' => 'Nightly snapshots of the database volume',
'enabled' => false,
'interval_minutes' => 1440,
'retention_count' => 7,
'retention_days' => 30,
'tags' => [
'environment' => 'production',
'team' => 'backend'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}"
payload := strings.NewReader("{\n \"description\": \"Nightly snapshots of the database volume\",\n \"enabled\": false,\n \"interval_minutes\": 1440,\n \"retention_count\": 7,\n \"retention_days\": 30,\n \"tags\": {\n \"environment\": \"production\",\n \"team\": \"backend\"\n }\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"description\": \"Nightly snapshots of the database volume\",\n \"enabled\": false,\n \"interval_minutes\": 1440,\n \"retention_count\": 7,\n \"retention_days\": 30,\n \"tags\": {\n \"environment\": \"production\",\n \"team\": \"backend\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"description\": \"Nightly snapshots of the database volume\",\n \"enabled\": false,\n \"interval_minutes\": 1440,\n \"retention_count\": 7,\n \"retention_days\": 30,\n \"tags\": {\n \"environment\": \"production\",\n \"team\": \"backend\"\n }\n}"
response = http.request(request)
puts response.read_body{
"snapshot_policy": {
"faults": [
{
"code": "BACKUP_FAILED",
"severity": "error",
"message": "Backup upload failed.",
"details": {},
"first_at": "2023-11-07T05:31:56Z",
"last_at": "2023-11-07T05:31:56Z",
"occurrences": 1
}
],
"id": "3c9f1b2e-6d4a-4e8b-9f1c-2a3b4c5d6e7f",
"crn": "crn:storage:sa-saopaulo-1:my-account:volume/data-vol-01/snapshot-policy/nightly",
"name": "nightly",
"description": "Nightly snapshots of the database volume",
"enabled": true,
"interval_minutes": 1440,
"retention_count": 7,
"retention_days": 30,
"next_run_at": "2026-01-16T00:00:00Z",
"last_run_at": "2026-01-15T00:00:00Z",
"tags": {
"environment": "production",
"team": "backend"
},
"created_at": "2026-01-15T09:30:00Z",
"updated_at": "2026-01-15T09:30:00Z"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}Update snapshot policy
Change the schedule, the retention window, or pause the policy.
Changing interval_minutes re-bases the next run off now.
curl --request PATCH \
--url https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"description": "Nightly snapshots of the database volume",
"enabled": false,
"interval_minutes": 1440,
"retention_count": 7,
"retention_days": 30,
"tags": {
"environment": "production",
"team": "backend"
}
}
'import requests
url = "https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}"
payload = {
"description": "Nightly snapshots of the database volume",
"enabled": False,
"interval_minutes": 1440,
"retention_count": 7,
"retention_days": 30,
"tags": {
"environment": "production",
"team": "backend"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
description: 'Nightly snapshots of the database volume',
enabled: false,
interval_minutes: 1440,
retention_count: 7,
retention_days: 30,
tags: {environment: 'production', team: 'backend'}
})
};
fetch('https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'description' => 'Nightly snapshots of the database volume',
'enabled' => false,
'interval_minutes' => 1440,
'retention_count' => 7,
'retention_days' => 30,
'tags' => [
'environment' => 'production',
'team' => 'backend'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}"
payload := strings.NewReader("{\n \"description\": \"Nightly snapshots of the database volume\",\n \"enabled\": false,\n \"interval_minutes\": 1440,\n \"retention_count\": 7,\n \"retention_days\": 30,\n \"tags\": {\n \"environment\": \"production\",\n \"team\": \"backend\"\n }\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"description\": \"Nightly snapshots of the database volume\",\n \"enabled\": false,\n \"interval_minutes\": 1440,\n \"retention_count\": 7,\n \"retention_days\": 30,\n \"tags\": {\n \"environment\": \"production\",\n \"team\": \"backend\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://storage.{region}.basaltic.sh/v1/volumes/{volume_id}/snapshot-policies/{policy_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"description\": \"Nightly snapshots of the database volume\",\n \"enabled\": false,\n \"interval_minutes\": 1440,\n \"retention_count\": 7,\n \"retention_days\": 30,\n \"tags\": {\n \"environment\": \"production\",\n \"team\": \"backend\"\n }\n}"
response = http.request(request)
puts response.read_body{
"snapshot_policy": {
"faults": [
{
"code": "BACKUP_FAILED",
"severity": "error",
"message": "Backup upload failed.",
"details": {},
"first_at": "2023-11-07T05:31:56Z",
"last_at": "2023-11-07T05:31:56Z",
"occurrences": 1
}
],
"id": "3c9f1b2e-6d4a-4e8b-9f1c-2a3b4c5d6e7f",
"crn": "crn:storage:sa-saopaulo-1:my-account:volume/data-vol-01/snapshot-policy/nightly",
"name": "nightly",
"description": "Nightly snapshots of the database volume",
"enabled": true,
"interval_minutes": 1440,
"retention_count": 7,
"retention_days": 30,
"next_run_at": "2026-01-16T00:00:00Z",
"last_run_at": "2026-01-15T00:00:00Z",
"tags": {
"environment": "production",
"team": "backend"
},
"created_at": "2026-01-15T09:30:00Z",
"updated_at": "2026-01-15T09:30:00Z"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}storage:UpdateSnapshotPolicy. See STORAGE permissions for the full list, what each one covers, and an example policy.Authorizations
An OAuth 2.0 bearer token, sent as Authorization: Bearer <token>.
This is the recommended way to authenticate.
Get one by exchanging a service account's access key pair at
POST /v1/oauth/token with grant_type=client_credentials. It is the
standard client-credentials grant, so any OAuth-aware library will
obtain and refresh it for you.
curl -s -u "$KEY_ID:$SECRET" -d grant_type=client_credentials \
https://iam.basaltic.sh/v1/oauth/token
Tokens last an hour by default. The same access key pair is separately your AWS SigV4 credential for the S3-compatible object endpoint, which speaks nothing else.
Path Parameters
Volume ID
Snapshot policy ID
Body
Names are fixed at creation because they form the CRN used by IAM policies.
Sending name in an update, including an unchanged, empty or null value, returns a validation error.
PATCH semantics — an omitted field keeps its current value.
Changing interval_minutes re-bases the next run off now, so
shortening a daily schedule to hourly takes effect within the hour.
"Nightly snapshots of the database volume"
false pauses the policy, true resumes it. Pausing stops the
whole policy — no snapshots are taken and none are deleted, so
a paused schedule cannot lose you history. Resuming applies the
retention window again on the next run, so anything sitting
outside it by then — because you lowered retention_count
while paused, say — is reaped on that run.
false
Minutes between snapshots — a minimum gap, not an exact cadence. A
periodic pass takes whatever has come due and re-bases each policy's
next run off the moment it ran, so a snapshot lands at or after
interval_minutes and never before, and can land a minute or two
later when the pass is busy. A window the pass misses costs one
snapshot rather than producing a catch-up burst afterwards.
The floor is one minute, because that pass is what evaluates the
schedule and nothing finer can be honoured; the ceiling is 30 days.
Sub-hourly intervals multiply snapshot churn and count against
the snapshots quota, so pick the largest interval that meets your
recovery point objective.
1 <= x <= 432001440
How many of this policy's snapshots to keep. When a fire takes the count past this, the oldest go first.
1 <= x <= 2567
Optional age bound, applied on top of retention_count: a
snapshot outside EITHER window is reaped. 0 means no age bound.
The single newest snapshot is exempt from the age bound, so a
volume that could not be snapshotted for longer than the window
never loses its whole history.
0 <= x <= 365030
Show child attributes
Show child attributes
{
"environment": "production",
"team": "backend"
}
Response
Snapshot policy updated
A schedule attached to one volume: take a snapshot every
interval_minutes, then keep at most retention_count of the
snapshots this policy created.
Retention only ever deletes snapshots the policy itself created
(those carrying its snapshot_policy reference) — a snapshot taken by
hand is never reaped. It also never deletes a snapshot something
depends on: a snapshot a volume was created from, including a
restore still in progress, is skipped and re-examined later.
Show child attributes
Show child attributes

