Modulify

Restore a storage backup

restore_storage_backup

Puts files back into a site's storage from one of its backups, after a first call that only answers the plan.

POST /v1/restore_storage_backupScopedata:writeDestructive

This is a two call tool, and the first call never restores anything. mode full makes the storage match the backup exactly and deletes every file added since, uploads from the CMS included. mode selected writes back only the paths you name, up to 200 of them, where a path ending in a slash is a folder and anything else is one file, both taken from browse_storage_backup. The only thing a selected restore deletes is the other copy of a restored file that has since been made public or private.

Request

Call it with a POST to https://api.modulify.ai/v1/restore_storage_backup, sending the inputs below as a JSON object. The token needs the data:write scope.

This method is marked destructive: it deletes or overwrites data. Check the inputs before you call it, and send an Idempotency-Key header whenever you might retry it.

curl -X POST https://api.modulify.ai/v1/restore_storage_backup \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"PROJECT_ID","backupId":"BACKUP_ID","mode":"MODE"}'

Over MCP, the same method is the restore_storage_backup tool.

The plan, then the restore

Call it first with backupId, mode and, for selected, paths, and without confirm. It answers Nothing was restored, this is the plan. with started false, a plan and a confirm value, and changes nothing at all. The plan counts the files that come back, as filesAdded for those not in the storage now, filesReplaced for those already there at a different size or visibility and filesUnchanged for those already there that are written over anyway, the files deleted in filesRemoved and the bytes that move in bytesWritten and bytesRemoved. safetyBackup says whether a Before restore backup of the current storage is taken first, and filesSkipped counts files whose path storage no longer accepts, which are left as they are.

The tool tells the client to show you that plan, say plainly what is overwritten and what is deleted, and wait for you to agree in your own words. Only then does it call again with the same backupId, mode and paths plus confirm set to the exact value the plan returned, and that call starts the restore. The client is told never to make up a confirm value, never to send one in the same breath as the plan it just read, and never to restore because it seems to follow from the request.

The confirm value is worked out from the plan, so it is pinned to it. If the storage changed in between, the second call is refused with a 409 and The storage changed since this plan was made, so this confirmation no longer matches. Check the new plan and confirm again!, carrying the new plan and a new confirm, so the new numbers have to be shown to you and agreed again. A file rewritten to the same size and visibility leaves the plan as it was, so that change is not caught.

Once it starts

The confirmed call answers started true with the restore row, whose _id is the restoreId for get_storage_restore_status. The restore runs in the background and is not done when this answers, so the tool tells the client to say it has started, never that the files are back, and to poll get_storage_restore_status until it reads completed. Whenever the restore would overwrite or delete anything, a Before restore backup of the whole current storage is taken first, so a restore can itself be undone by restoring that row. The restored files are what the live site serves straight away, with nothing to publish.

Both calls need a paid plan and the Delete projects permission in the workspace, so even the plan is refused without them. Only one restore runs per site at a time, and a second one is refused with A restore of this storage is already running!, carrying the running restore as activeRestore. While a restore runs, the storage tools that change files, create_storage_backup, clear_storage_backups and deleting the backups the restore uses are refused with a 409, and a restore cannot start while a backup of the same storage is still being archived.

A backup still being archived, one that failed and one taken in an older format cannot be restored. When you only want to see an old version of a file, read_storage_backup_file is the lighter answer. See Restore a storage backup.

Inputs

Input Type Required Description
projectId string Yes The site whose storage should be restored.
backupId string Yes The backup to restore from, from list_storage_backups.
mode string Yes full makes the whole storage match the backup and deletes every file added since. selected puts back only the paths you name.
paths array of strings No For selected only: the files and folders to put back, from browse_storage_backup, at most 200. A path ending in a slash is a folder. Ignored for full.
confirm string No The exact value the plan call returned. Leave it out on the first call to get the plan, since only a call carrying it starts the restore.

Response

Every call answers with the JSON envelope of success, message, data, code and version. data holds the result described above, and on a method that returns a total, count carries it. The response headers carry the call's X-Request-Id and what is left of your per-minute budget in X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Errors explains every status code a call can answer with.