Copy a site
Copies the published content of a site into a new draft site the member owns. The copy holds the site’s published layouts, sections and snippets, its published pages, its published settings, its published access declaration, its text assets and its menus. The declaration becomes a draft of the copy, so it is not published until the copy is. It never holds a draft above a published file, a file that was never published, or a page that is not published. A menu item that links to a page that was not copied is left out with the items below it. The copy is always private, is not published, and is not required; it counts toward the 25-site limit. The original is not changed, and the copy shares no rows with it: the content of each is independent, so changing either one never changes the other, and deleting either one never deletes the other. The new site’s copied_from_id is the original’s id; deleting the original sets the copy’s copied_from_id to null and keeps the copy.
A member can copy a site they own, and a published site that a share with copy permission opens to them; when several shares match, the highest permission counts. A member who can open the site, such as a published public site with no share for them or one shared with them to view, but may not copy it gets 403. A site the member cannot open, such as another member’s draft, a draft or taken-down site shared with them, a private site no share matches, a site in another company and an unknown id, returns 404, whatever the body holds. A member who owns a site an admin took down gets 422. The body is optional.
curl --request POST \
--url https://api.fluid.app/api/member/v2026-10/sites/{id}/copy \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"name": "Riley's Fall Shop",
"handle": "fall-shop"
}
EOFimport requests
url = "https://api.fluid.app/api/member/v2026-10/sites/{id}/copy"
payload = {
"name": "Riley's Fall Shop",
"handle": "fall-shop"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Riley\'s Fall Shop', handle: 'fall-shop'})
};
fetch('https://api.fluid.app/api/member/v2026-10/sites/{id}/copy', 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://api.fluid.app/api/member/v2026-10/sites/{id}/copy",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Riley\'s Fall Shop',
'handle' => 'fall-shop'
]),
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://api.fluid.app/api/member/v2026-10/sites/{id}/copy"
payload := strings.NewReader("{\n \"name\": \"Riley's Fall Shop\",\n \"handle\": \"fall-shop\"\n}")
req, _ := http.NewRequest("POST", 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.post("https://api.fluid.app/api/member/v2026-10/sites/{id}/copy")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Riley's Fall Shop\",\n \"handle\": \"fall-shop\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/member/v2026-10/sites/{id}/copy")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Riley's Fall Shop\",\n \"handle\": \"fall-shop\"\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"data": {
"id": "0192f4e5-6f7a-7b8c-9d0e-1f2a3b4c5e01",
"handle": "fall-shop",
"name": "Riley's Fall Shop",
"visibility": "private",
"status": "draft",
"owner_kind": "member",
"copied_from_id": "0192f4e5-6f7a-7b8c-9d0e-1f2a3b4c5d67",
"access": {
"declared": false,
"uses": []
},
"layout_name": null,
"settings": {
"primary_color": "#112233"
},
"content_version": "0192f4e5-6f7a-7b8c-9d0e-1f2a3b4c5e02",
"lock_version": 0,
"created_at": "2026-09-02T10:00:00.000000Z",
"updated_at": "2026-09-02T10:00:00.000000Z"
},
"meta": {
"request_uuid": "0192f4c8-5b6c-7d74-a3e8-0f1a2b3c4d5e",
"timestamp": "2026-09-30T15:08:21Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}Authorizations
A member credential in the Authorization: Bearer header: a member
session JWT, a portal JWT whose login is linked to a membership in
that company, an opaque member token, or a verified One Auth identity
access token. The credential alone identifies the member and company.
Member session and portal JWTs are read-only and are refused with 403
on every write.
Path Parameters
Public id of one of the member's sites.
Body
Optional choices for a copy. Any other field, such as visibility, is refused as an unknown field.
Display name of the copy. Omit it to use "Copy of" followed by the original's name.
1 - 255^[^\u0000]*[^\s\u0000][^\u0000]*$Lowercase letters and digits separated by single hyphens, not reserved and not used by another site in the company. Omit it to have one proposed from the name.
1 - 40^[a-z0-9]+(-[a-z0-9]+)*$curl --request POST \
--url https://api.fluid.app/api/member/v2026-10/sites/{id}/copy \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"name": "Riley's Fall Shop",
"handle": "fall-shop"
}
EOFimport requests
url = "https://api.fluid.app/api/member/v2026-10/sites/{id}/copy"
payload = {
"name": "Riley's Fall Shop",
"handle": "fall-shop"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Riley\'s Fall Shop', handle: 'fall-shop'})
};
fetch('https://api.fluid.app/api/member/v2026-10/sites/{id}/copy', 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://api.fluid.app/api/member/v2026-10/sites/{id}/copy",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Riley\'s Fall Shop',
'handle' => 'fall-shop'
]),
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://api.fluid.app/api/member/v2026-10/sites/{id}/copy"
payload := strings.NewReader("{\n \"name\": \"Riley's Fall Shop\",\n \"handle\": \"fall-shop\"\n}")
req, _ := http.NewRequest("POST", 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.post("https://api.fluid.app/api/member/v2026-10/sites/{id}/copy")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Riley's Fall Shop\",\n \"handle\": \"fall-shop\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/member/v2026-10/sites/{id}/copy")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Riley's Fall Shop\",\n \"handle\": \"fall-shop\"\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"data": {
"id": "0192f4e5-6f7a-7b8c-9d0e-1f2a3b4c5e01",
"handle": "fall-shop",
"name": "Riley's Fall Shop",
"visibility": "private",
"status": "draft",
"owner_kind": "member",
"copied_from_id": "0192f4e5-6f7a-7b8c-9d0e-1f2a3b4c5d67",
"access": {
"declared": false,
"uses": []
},
"layout_name": null,
"settings": {
"primary_color": "#112233"
},
"content_version": "0192f4e5-6f7a-7b8c-9d0e-1f2a3b4c5e02",
"lock_version": 0,
"created_at": "2026-09-02T10:00:00.000000Z",
"updated_at": "2026-09-02T10:00:00.000000Z"
},
"meta": {
"request_uuid": "0192f4c8-5b6c-7d74-a3e8-0f1a2b3c4d5e",
"timestamp": "2026-09-30T15:08:21Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}