curl --request POST \
--url http://localhost:8080/api/v1/catalog/{name}/restore \
--header 'Content-Type: multipart/form-data' \
--header 'x-api-key: <api-key>' \
--form file=@example-fileimport requests
url = "http://localhost:8080/api/v1/catalog/{name}/restore"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
options.body = form;
fetch('http://localhost:8080/api/v1/catalog/{name}/restore', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "8080",
CURLOPT_URL => "http://localhost:8080/api/v1/catalog/{name}/restore",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"x-api-key: <api-key>"
],
]);
$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 := "http://localhost:8080/api/v1/catalog/{name}/restore"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://localhost:8080/api/v1/catalog/{name}/restore")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:8080/api/v1/catalog/{name}/restore")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"restored": {
"taps": [
"<string>"
],
"pipelines": [
"<string>"
]
},
"skipped": [
"<string>"
],
"failed": [
{
"name": "<string>",
"error": "<string>"
}
],
"missingSecrets": [
{
"name": "<string>",
"usedBy": [
"<string>"
]
}
],
"warnings": [
"<string>"
],
"tapsDisabled": true,
"stateRestored": true
}{
"restored": {
"taps": [
"<string>"
],
"pipelines": [
"<string>"
]
},
"skipped": [
"<string>"
],
"failed": [
{
"name": "<string>",
"error": "<string>"
}
],
"missingSecrets": [
{
"name": "<string>",
"usedBy": [
"<string>"
]
}
],
"warnings": [
"<string>"
],
"tapsDisabled": true,
"stateRestored": true
}{
"error": "<string>",
"clashes": [
"<string>"
]
}Restore a catalog from a zip backup
Restores an archive produced by GET /api/v1/catalog/{name}/export as catalog name (the same or a new name, on the same or another install). A restore never merges: with mode=new (default) it is refused with 409 when catalog name already exists or any archived tap or pipeline name exists anywhere on the install (any catalog, either type), and nothing is written. With mode=overwrite (requires confirm equal to name) items already in name may be replaced; a name in another catalog is still a 409. When tap scripts are stored in a code repository, a script file already at branch head with different content is also a clash; identical content is reused without a new commit. Pipelines are saved exactly as POST /api/v1/pipeline saves them; tap scripts go to the install’s default script storage; taps are saved with enabled=false and their run status cleared, so nothing runs until a user tests and enables them. Secrets are never written: missingSecrets lists the secrets the archived items reference that are absent on this install. Secrets are only checked where the key may read them: a key without secret read access gets a <name>: not checked, this key may not read secrets warning instead, whether or not the secret exists. Each tap’s incremental state is restored for taps that did not exist before, unless restoreState=false. An archive with no members creates the empty catalog. Not transactional: failed members are listed under failed (207). Requires pipeline:create. Every member is scope-checked before anything is written: tap:create / pipeline:create in the target catalog as the caller, and for an item that already exists (overwrite) also tap:update / pipeline:update against its current catalog and owner. A refused member is listed under failed and nothing of it, including its validation schema, is written. Inline credentials (preprocessor and REST destination bearerToken/apiKey, restEndpoint row-function token parameters) are removed at export; the restore response’s warnings names each one to re-enter.
curl --request POST \
--url http://localhost:8080/api/v1/catalog/{name}/restore \
--header 'Content-Type: multipart/form-data' \
--header 'x-api-key: <api-key>' \
--form file=@example-fileimport requests
url = "http://localhost:8080/api/v1/catalog/{name}/restore"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
options.body = form;
fetch('http://localhost:8080/api/v1/catalog/{name}/restore', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "8080",
CURLOPT_URL => "http://localhost:8080/api/v1/catalog/{name}/restore",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"x-api-key: <api-key>"
],
]);
$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 := "http://localhost:8080/api/v1/catalog/{name}/restore"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://localhost:8080/api/v1/catalog/{name}/restore")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:8080/api/v1/catalog/{name}/restore")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"restored": {
"taps": [
"<string>"
],
"pipelines": [
"<string>"
]
},
"skipped": [
"<string>"
],
"failed": [
{
"name": "<string>",
"error": "<string>"
}
],
"missingSecrets": [
{
"name": "<string>",
"usedBy": [
"<string>"
]
}
],
"warnings": [
"<string>"
],
"tapsDisabled": true,
"stateRestored": true
}{
"restored": {
"taps": [
"<string>"
],
"pipelines": [
"<string>"
]
},
"skipped": [
"<string>"
],
"failed": [
{
"name": "<string>",
"error": "<string>"
}
],
"missingSecrets": [
{
"name": "<string>",
"usedBy": [
"<string>"
]
}
],
"warnings": [
"<string>"
],
"tapsDisabled": true,
"stateRestored": true
}{
"error": "<string>",
"clashes": [
"<string>"
]
}Authorizations
Optional API key for authentication (enabled via application.yaml)
Path Parameters
Target catalog name. Uncataloged (any case) is refused.
^[A-Za-z0-9_-]+$Query Parameters
new, overwrite Required for mode=overwrite; must equal the catalog name.
false skips every tap's incremental state (for a copy that should start from scratch).
Body
The catalog archive (zip, at most 1000 entries and 50 MB uncompressed).
Response
Every member restored
Show child attributes
Show child attributes
Validation-schema objects that already existed on this install and were kept.
Show child attributes
Show child attributes
Secrets the archive's items reference that do not exist on this install. Create them before enabling the items. Only secrets the key may read are checked; the others are named in warnings as not checked.
Show child attributes
Show child attributes
Always true; restored taps are saved disabled.
True when at least one tap's incremental state was restored.
