Translate every missing value of a field with AI
Token-authenticated. Queues a background run that translates, with the
company’s AI translation service, the value of one translatable Product
metafield into lang for every product whose translation is missing,
that is, has none or a blank one (status missing in the catalog). A
non-blank stored translation is never overwritten, outdated or not. Each
translation is saved with the digest of the original it was made from.
Products whose original is blank, or whose AI answer is unusable, are
left as they are. The run schedules one purge of the company’s cached
pages when it ends, not one per product.
Answers 202 as soon as the run is queued. Asking again while a run for
the same field and language is queued or running queues nothing and
returns state: already_running. The field and lang are refused with a
422 as in the catalog. Answers 404 while the translatable-metafields
feature is off for the company.
Uses the company’s AI spending allowance. The run stops when the allowance
is used up; the products it translated before keep their translations.
Because of that, it needs the permission to update products and also the
one POST /api/v202604/ai/translate needs (application_themes.update);
without either it answers 403.
curl --request POST \
--url https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"namespace": "custom",
"key": "how_to_use",
"lang": "es"
}
'import requests
url = "https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing"
payload = {
"namespace": "custom",
"key": "how_to_use",
"lang": "es"
}
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({namespace: 'custom', key: 'how_to_use', lang: 'es'})
};
fetch('https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing', 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/v202604/company/metafield-translations/ai-translate-missing",
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([
'namespace' => 'custom',
'key' => 'how_to_use',
'lang' => 'es'
]),
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/v202604/company/metafield-translations/ai-translate-missing"
payload := strings.NewReader("{\n \"namespace\": \"custom\",\n \"key\": \"how_to_use\",\n \"lang\": \"es\"\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/v202604/company/metafield-translations/ai-translate-missing")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"namespace\": \"custom\",\n \"key\": \"how_to_use\",\n \"lang\": \"es\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing")
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 \"namespace\": \"custom\",\n \"key\": \"how_to_use\",\n \"lang\": \"es\"\n}"
response = http.request(request)
puts response.read_body{
"metafield_translation_ai_run": {
"namespace": "custom",
"key": "how_to_use",
"lang": "es",
"state": "queued"
},
"status": 202,
"meta": {
"request_uuid": "4c9fc097-1f78-43b8-b8e0-ef3bb7163381",
"timestamp": "2026-11-13T18:24:12Z"
}
}{
"message": "Invalid credentials."
}{
"error": "Not authorized."
}{
"error": {
"message": "Not found",
"details": {}
},
"status": 404,
"meta": {
"request_uuid": "4c9fc097-1f78-43b8-b8e0-ef3bb7163381",
"timestamp": "2026-11-13T18:24:12Z"
}
}{
"error": {
"message": "Field cannot be translated",
"details": {
"key": [
"custom.config_text is not a translatable Product metafield"
]
}
},
"status": 422,
"meta": {
"request_uuid": "4c9fc097-1f78-43b8-b8e0-ef3bb7163381",
"timestamp": "2026-11-13T18:24:12Z"
}
}Authorizations
Bearer token authentication. Accepts company-admin tokens with
storefront.view for read operations and storefront.update
for writes and scan triggers (legacy per-resource grants like
categories.update / products.view / etc. are still accepted
during the migration), required on every company operation.
Storefront public operations under /api/v202604/{resource} and
/api/v202604/{resource}/{slug} are unauthenticated
(security: []).
Body
curl --request POST \
--url https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"namespace": "custom",
"key": "how_to_use",
"lang": "es"
}
'import requests
url = "https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing"
payload = {
"namespace": "custom",
"key": "how_to_use",
"lang": "es"
}
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({namespace: 'custom', key: 'how_to_use', lang: 'es'})
};
fetch('https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing', 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/v202604/company/metafield-translations/ai-translate-missing",
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([
'namespace' => 'custom',
'key' => 'how_to_use',
'lang' => 'es'
]),
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/v202604/company/metafield-translations/ai-translate-missing"
payload := strings.NewReader("{\n \"namespace\": \"custom\",\n \"key\": \"how_to_use\",\n \"lang\": \"es\"\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/v202604/company/metafield-translations/ai-translate-missing")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"namespace\": \"custom\",\n \"key\": \"how_to_use\",\n \"lang\": \"es\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/v202604/company/metafield-translations/ai-translate-missing")
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 \"namespace\": \"custom\",\n \"key\": \"how_to_use\",\n \"lang\": \"es\"\n}"
response = http.request(request)
puts response.read_body{
"metafield_translation_ai_run": {
"namespace": "custom",
"key": "how_to_use",
"lang": "es",
"state": "queued"
},
"status": 202,
"meta": {
"request_uuid": "4c9fc097-1f78-43b8-b8e0-ef3bb7163381",
"timestamp": "2026-11-13T18:24:12Z"
}
}{
"message": "Invalid credentials."
}{
"error": "Not authorized."
}{
"error": {
"message": "Not found",
"details": {}
},
"status": 404,
"meta": {
"request_uuid": "4c9fc097-1f78-43b8-b8e0-ef3bb7163381",
"timestamp": "2026-11-13T18:24:12Z"
}
}{
"error": {
"message": "Field cannot be translated",
"details": {
"key": [
"custom.config_text is not a translatable Product metafield"
]
}
},
"status": 422,
"meta": {
"request_uuid": "4c9fc097-1f78-43b8-b8e0-ef3bb7163381",
"timestamp": "2026-11-13T18:24:12Z"
}
}