Convert a company site into a Mist app
Starts turning one public company site into a Mist app, for a project that has outgrown sites: it needs its own domain, server rendering or heavier server code. The answer is 202 with the queued conversion; the app is built in the background and its first deployment follows, usually within a few minutes. The site is archived and its address redirected only once that deployment is ready. Read its progress with GET on the same path.
The app starts from the site’s code: its own source repository when it has one, otherwise the files of its current bundle, which become the whole of the app’s repository and are served as a static project. Once the app is built the site is archived (not deleted), and /sites/<handle> sends visitors to the app’s address at the same path.
Only a public company site converts, because an app has no Fluid sign-in to keep a private or members-only site closed. What is refused, with details.reason: a member’s site (not_company_site, 403); a branch (branch), a taken-down site (taken_down), an archived site (archived), a site that is private, made private by the company or declares visitor data (not_public), a site not published through the sites gateway (not_published), a site with a database (has_database: its rows can’t be moved into an app yet; download them with the database export) and no usable Mist template (template_unavailable), all 422; a site being converted (conversion_in_progress) or already converted (already_converted), 409; and a company with no credits for a new app (credits_exhausted), 402. Nothing is created for a refusal. Env vars are not carried yet: sites have none until they are added.
Needs application_themes.update, mist.create and mist.code, because the app is built from the site’s code, and a signed-in admin. A site converted from its bundle is served as built files and never built again: its files go under public/, and Fluid’s own vercel.json turns off install and build, so no script from the bundle runs. A droplet installation token and an OAuth client token are refused with 403. It answers 404 for every caller that passes those checks until the sites gateway is released to the company.
curl --request POST \
--url https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion', 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/company/v2026-10/member-sites/{id}/conversion",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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/company/v2026-10/member-sites/{id}/conversion")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"conversion": {
"id": "019a6f00-0000-7000-8000-000000000001",
"state": "queued",
"mist_id": null,
"source": null,
"redirect_url": null,
"error": null,
"created_at": "2026-11-10T10:00:00.000Z",
"finished_at": null
},
"status": 202,
"meta": {
"request_uuid": "3b8f1d27-6c4e-4a91-8e5d-2f7c9a1b0e64",
"timestamp": "2026-11-10T10:00:00Z"
}
}{}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "Only a public site can become an app. An app can't keep a private or members-only site closed to the public, so make the site public first, or keep it as a site",
"details": {
"reason": "not_public"
}
},
"status": 422,
"meta": {
"request_uuid": "3b8f1d27-6c4e-4a91-8e5d-2f7c9a1b0e64",
"timestamp": "2026-11-10T10:00:00Z"
}
}Authorizations
A company admin credential for your company, or a droplet installation token with the website scope for reads, except on the share routes, which refuse a droplet token.
Path Parameters
The site's id.
curl --request POST \
--url https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion', 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/company/v2026-10/member-sites/{id}/conversion",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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/company/v2026-10/member-sites/{id}/conversion")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/company/v2026-10/member-sites/{id}/conversion")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"conversion": {
"id": "019a6f00-0000-7000-8000-000000000001",
"state": "queued",
"mist_id": null,
"source": null,
"redirect_url": null,
"error": null,
"created_at": "2026-11-10T10:00:00.000Z",
"finished_at": null
},
"status": 202,
"meta": {
"request_uuid": "3b8f1d27-6c4e-4a91-8e5d-2f7c9a1b0e64",
"timestamp": "2026-11-10T10:00:00Z"
}
}{}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "<string>",
"details": {}
},
"status": 123,
"meta": {
"request_uuid": "<string>",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": {
"message": "Only a public site can become an app. An app can't keep a private or members-only site closed to the public, so make the site public first, or keep it as a site",
"details": {
"reason": "not_public"
}
},
"status": 422,
"meta": {
"request_uuid": "3b8f1d27-6c4e-4a91-8e5d-2f7c9a1b0e64",
"timestamp": "2026-11-10T10:00:00Z"
}
}