Create a draft tenant
curl --request POST \
--url https://api.example.com/admin/tenants \
--header 'Content-Type: application/json' \
--data '
{
"slug": "<string>",
"name": "<string>",
"industry": "<string>",
"region": "<string>",
"data_residency_notes": "<string>",
"primary_contact_email": "<string>"
}
'import requests
url = "https://api.example.com/admin/tenants"
payload = {
"slug": "<string>",
"name": "<string>",
"industry": "<string>",
"region": "<string>",
"data_residency_notes": "<string>",
"primary_contact_email": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
slug: '<string>',
name: '<string>',
industry: '<string>',
region: '<string>',
data_residency_notes: '<string>',
primary_contact_email: '<string>'
})
};
fetch('https://api.example.com/admin/tenants', 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.example.com/admin/tenants",
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([
'slug' => '<string>',
'name' => '<string>',
'industry' => '<string>',
'region' => '<string>',
'data_residency_notes' => '<string>',
'primary_contact_email' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"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.example.com/admin/tenants"
payload := strings.NewReader("{\n \"slug\": \"<string>\",\n \"name\": \"<string>\",\n \"industry\": \"<string>\",\n \"region\": \"<string>\",\n \"data_residency_notes\": \"<string>\",\n \"primary_contact_email\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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.example.com/admin/tenants")
.header("Content-Type", "application/json")
.body("{\n \"slug\": \"<string>\",\n \"name\": \"<string>\",\n \"industry\": \"<string>\",\n \"region\": \"<string>\",\n \"data_residency_notes\": \"<string>\",\n \"primary_contact_email\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/admin/tenants")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"slug\": \"<string>\",\n \"name\": \"<string>\",\n \"industry\": \"<string>\",\n \"region\": \"<string>\",\n \"data_residency_notes\": \"<string>\",\n \"primary_contact_email\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"slug": "<string>",
"name": "<string>",
"industry": "<string>",
"region": "<string>",
"lifecycle_state": "<string>",
"share_onboarding_outputs": true,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"model_route": "<string>",
"model_trained_at": "2023-11-07T05:31:56Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Staff Onboarding
Create a draft tenant
Requires the onboarding_admin control role; unlike every other mutating route on this router, this one does not enforce CSRF. Validates the slug format (422 if invalid) and returns 409 if the slug is already taken, then inserts a draft control.tenants row and writes a control.audit_log row (tenant_created). Does not provision the tenant’s Postgres schema — call POST /admin/tenants/{tenant_id}/provision next. Returns 201 with the created tenant’s summary.
POST
/
admin
/
tenants
Create a draft tenant
curl --request POST \
--url https://api.example.com/admin/tenants \
--header 'Content-Type: application/json' \
--data '
{
"slug": "<string>",
"name": "<string>",
"industry": "<string>",
"region": "<string>",
"data_residency_notes": "<string>",
"primary_contact_email": "<string>"
}
'import requests
url = "https://api.example.com/admin/tenants"
payload = {
"slug": "<string>",
"name": "<string>",
"industry": "<string>",
"region": "<string>",
"data_residency_notes": "<string>",
"primary_contact_email": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
slug: '<string>',
name: '<string>',
industry: '<string>',
region: '<string>',
data_residency_notes: '<string>',
primary_contact_email: '<string>'
})
};
fetch('https://api.example.com/admin/tenants', 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.example.com/admin/tenants",
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([
'slug' => '<string>',
'name' => '<string>',
'industry' => '<string>',
'region' => '<string>',
'data_residency_notes' => '<string>',
'primary_contact_email' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"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.example.com/admin/tenants"
payload := strings.NewReader("{\n \"slug\": \"<string>\",\n \"name\": \"<string>\",\n \"industry\": \"<string>\",\n \"region\": \"<string>\",\n \"data_residency_notes\": \"<string>\",\n \"primary_contact_email\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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.example.com/admin/tenants")
.header("Content-Type", "application/json")
.body("{\n \"slug\": \"<string>\",\n \"name\": \"<string>\",\n \"industry\": \"<string>\",\n \"region\": \"<string>\",\n \"data_residency_notes\": \"<string>\",\n \"primary_contact_email\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/admin/tenants")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"slug\": \"<string>\",\n \"name\": \"<string>\",\n \"industry\": \"<string>\",\n \"region\": \"<string>\",\n \"data_residency_notes\": \"<string>\",\n \"primary_contact_email\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"slug": "<string>",
"name": "<string>",
"industry": "<string>",
"region": "<string>",
"lifecycle_state": "<string>",
"share_onboarding_outputs": true,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"model_route": "<string>",
"model_trained_at": "2023-11-07T05:31:56Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Body
application/json
Response
Successful Response