curl --request POST \
--url https://api.textsetu.com/api/v1/projects/{projectId}/keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "home.hero.title",
"description": "Headline shown on the landing page hero.",
"tags": [
"marketing",
"landing"
],
"isPlural": true,
"values": {
"en": "Welcome",
"fr": "Bienvenue"
}
}
'import requests
url = "https://api.textsetu.com/api/v1/projects/{projectId}/keys"
payload = {
"key": "home.hero.title",
"description": "Headline shown on the landing page hero.",
"tags": ["marketing", "landing"],
"isPlural": True,
"values": {
"en": "Welcome",
"fr": "Bienvenue"
}
}
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({
key: 'home.hero.title',
description: 'Headline shown on the landing page hero.',
tags: ['marketing', 'landing'],
isPlural: true,
values: {en: 'Welcome', fr: 'Bienvenue'}
})
};
fetch('https://api.textsetu.com/api/v1/projects/{projectId}/keys', 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.textsetu.com/api/v1/projects/{projectId}/keys",
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([
'key' => 'home.hero.title',
'description' => 'Headline shown on the landing page hero.',
'tags' => [
'marketing',
'landing'
],
'isPlural' => true,
'values' => [
'en' => 'Welcome',
'fr' => 'Bienvenue'
]
]),
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.textsetu.com/api/v1/projects/{projectId}/keys"
payload := strings.NewReader("{\n \"key\": \"home.hero.title\",\n \"description\": \"Headline shown on the landing page hero.\",\n \"tags\": [\n \"marketing\",\n \"landing\"\n ],\n \"isPlural\": true,\n \"values\": {\n \"en\": \"Welcome\",\n \"fr\": \"Bienvenue\"\n }\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.textsetu.com/api/v1/projects/{projectId}/keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"home.hero.title\",\n \"description\": \"Headline shown on the landing page hero.\",\n \"tags\": [\n \"marketing\",\n \"landing\"\n ],\n \"isPlural\": true,\n \"values\": {\n \"en\": \"Welcome\",\n \"fr\": \"Bienvenue\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.textsetu.com/api/v1/projects/{projectId}/keys")
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 \"key\": \"home.hero.title\",\n \"description\": \"Headline shown on the landing page hero.\",\n \"tags\": [\n \"marketing\",\n \"landing\"\n ],\n \"isPlural\": true,\n \"values\": {\n \"en\": \"Welcome\",\n \"fr\": \"Bienvenue\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"key": {
"id": "a1b2c3d4-0000-4000-8000-000000000001",
"projectId": "proj_9f8e7d6c",
"key": "home.hero.title",
"description": "Headline shown on the landing page hero.",
"tags": [
"marketing",
"landing"
],
"isPlural": false,
"screenshotUrl": null,
"screenshotFileId": null,
"keySource": "main",
"createdAt": "2026-01-15T09:24:00.000Z",
"updatedAt": "2026-02-02T14:05:00.000Z"
}
}
}Create a key (write)
Add a new key to the project. You may seed its values in one call via the values map. On a protected main branch, target a branch with ?branch= instead — direct main writes are rejected.
Constraints:
- The key must be well-formed: each segment (split on the project’s key separator) may contain only letters, digits,
_and-. - Creating a key that already exists returns 409.
curl --request POST \
--url https://api.textsetu.com/api/v1/projects/{projectId}/keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "home.hero.title",
"description": "Headline shown on the landing page hero.",
"tags": [
"marketing",
"landing"
],
"isPlural": true,
"values": {
"en": "Welcome",
"fr": "Bienvenue"
}
}
'import requests
url = "https://api.textsetu.com/api/v1/projects/{projectId}/keys"
payload = {
"key": "home.hero.title",
"description": "Headline shown on the landing page hero.",
"tags": ["marketing", "landing"],
"isPlural": True,
"values": {
"en": "Welcome",
"fr": "Bienvenue"
}
}
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({
key: 'home.hero.title',
description: 'Headline shown on the landing page hero.',
tags: ['marketing', 'landing'],
isPlural: true,
values: {en: 'Welcome', fr: 'Bienvenue'}
})
};
fetch('https://api.textsetu.com/api/v1/projects/{projectId}/keys', 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.textsetu.com/api/v1/projects/{projectId}/keys",
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([
'key' => 'home.hero.title',
'description' => 'Headline shown on the landing page hero.',
'tags' => [
'marketing',
'landing'
],
'isPlural' => true,
'values' => [
'en' => 'Welcome',
'fr' => 'Bienvenue'
]
]),
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.textsetu.com/api/v1/projects/{projectId}/keys"
payload := strings.NewReader("{\n \"key\": \"home.hero.title\",\n \"description\": \"Headline shown on the landing page hero.\",\n \"tags\": [\n \"marketing\",\n \"landing\"\n ],\n \"isPlural\": true,\n \"values\": {\n \"en\": \"Welcome\",\n \"fr\": \"Bienvenue\"\n }\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.textsetu.com/api/v1/projects/{projectId}/keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"home.hero.title\",\n \"description\": \"Headline shown on the landing page hero.\",\n \"tags\": [\n \"marketing\",\n \"landing\"\n ],\n \"isPlural\": true,\n \"values\": {\n \"en\": \"Welcome\",\n \"fr\": \"Bienvenue\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.textsetu.com/api/v1/projects/{projectId}/keys")
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 \"key\": \"home.hero.title\",\n \"description\": \"Headline shown on the landing page hero.\",\n \"tags\": [\n \"marketing\",\n \"landing\"\n ],\n \"isPlural\": true,\n \"values\": {\n \"en\": \"Welcome\",\n \"fr\": \"Bienvenue\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"key": {
"id": "a1b2c3d4-0000-4000-8000-000000000001",
"projectId": "proj_9f8e7d6c",
"key": "home.hero.title",
"description": "Headline shown on the landing page hero.",
"tags": [
"marketing",
"landing"
],
"isPlural": false,
"screenshotUrl": null,
"screenshotFileId": null,
"keySource": "main",
"createdAt": "2026-01-15T09:24:00.000Z",
"updatedAt": "2026-02-02T14:05:00.000Z"
}
}
}Authorizations
API token: tsu_pat_… (PAT) or tsu_proj_… (project token). The token's granted RBAC permissions determine access; see each operation's x-permission.
Path Parameters
Project id (UUID) or id-embedded path slug.
Query Parameters
Target an open translation branch instead of main. Required when the project's main branch is protected.
Body
Lookup identifier. Segments (split on the project separator) may contain only letters, digits, _ and -.
1 - 500"home.hero.title"
Optional translator-facing context for the key.
1000"Headline shown on the landing page hero."
Up to 20 labels for filtering and organizing keys.
2050["marketing", "landing"]
Set true to make this a plural key (values are keyed by CLDR plural form).
Optional initial values, keyed by language code. Singular: { "fr": "…" }; plural: { "fr": { "one": "…", "other": "…" } }.
Show child attributes
Show child attributes
{ "en": "Welcome", "fr": "Bienvenue" }

