curl --request POST \
--url https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"config": {},
"graphId": "<string>",
"description": "<string>",
"metadata": {}
}
'import requests
url = "https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create"
payload = {
"name": "<string>",
"config": {},
"graphId": "<string>",
"description": "<string>",
"metadata": {}
}
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: '<string>',
config: {},
graphId: '<string>',
description: '<string>',
metadata: {}
})
};
fetch('https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create', 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.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create",
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' => '<string>',
'config' => [
],
'graphId' => '<string>',
'description' => '<string>',
'metadata' => [
]
]),
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.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"config\": {},\n \"graphId\": \"<string>\",\n \"description\": \"<string>\",\n \"metadata\": {}\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.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"config\": {},\n \"graphId\": \"<string>\",\n \"description\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create")
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\": \"<string>\",\n \"config\": {},\n \"graphId\": \"<string>\",\n \"description\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{}{
"error": "Forbidden: token holds no claim for the requested account"
}{
"error": "Forbidden: token holds no claim for the requested account"
}{
"error": "Forbidden: token holds no claim for the requested account"
}{
"error": "Forbidden: token holds no claim for the requested account"
}Propose an agent create
First step of a safe two-step create — nothing is created yet. Send the full create body (for a knowledge-base Q&A agent: deployment: react, graphId: qna_agent, and knowledge_base_ids inside config): config takes the shape listAssistants and getAssistant return for an existing agent of the same deployment, and must carry project. metadata.productionAssistantId is the ONLY metadata key accepted here — set it to anchor the new draft to a PUBLISHED agent of the same deployment on this tenant, which is the pairing proposeAssistantEdits coaches you toward when a published agent has no draft yet; any other metadata key is refused. Returns message, diff, preview and confirmationId: diff renders the whole state as + lines with prompt bodies STUBBED to their real length ([N chars — use readAssistantText]) — the full text is exactly what you authored and is what gets created — while preview is that same state as it will persist (server defaults and coordinates applied), prompt bodies stubbed the same way. Review the diff, then call confirmAssistantCreate with just that confirmationId; no other body field is read. The agent is ALWAYS created as a DRAFT — a human reviews and publishes it in the ClaudIA app, and this surface has no opt-out.
curl --request POST \
--url https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"config": {},
"graphId": "<string>",
"description": "<string>",
"metadata": {}
}
'import requests
url = "https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create"
payload = {
"name": "<string>",
"config": {},
"graphId": "<string>",
"description": "<string>",
"metadata": {}
}
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: '<string>',
config: {},
graphId: '<string>',
description: '<string>',
metadata: {}
})
};
fetch('https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create', 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.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create",
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' => '<string>',
'config' => [
],
'graphId' => '<string>',
'description' => '<string>',
'metadata' => [
]
]),
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.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"config\": {},\n \"graphId\": \"<string>\",\n \"description\": \"<string>\",\n \"metadata\": {}\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.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"config\": {},\n \"graphId\": \"<string>\",\n \"description\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloudhumans.com/claudia/v1/tenants/{tenant}/assistants/propose-create")
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\": \"<string>\",\n \"config\": {},\n \"graphId\": \"<string>\",\n \"description\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{}{
"error": "Forbidden: token holds no claim for the requested account"
}{
"error": "Forbidden: token holds no claim for the requested account"
}{
"error": "Forbidden: token holds no claim for the requested account"
}{
"error": "Forbidden: token holds no claim for the requested account"
}If-Match (a create has no prior state to pin), and metadata.productionAssistantId
is how you anchor the new draft to a published agent — the way to edit a
published agent through this API, since edits only ever land on drafts.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Tenant whose agents you are managing. Discover the tenants your credentials cover with listMyClaudiaProjects. A tenant your credentials do not cover is indistinguishable from one that does not exist.
Body
The agent to create. config takes the shape listAssistants/getAssistant return for an existing agent of the same deployment, and must carry project. metadata accepts ONLY productionAssistantId — the id of a published agent on this tenant to anchor the new draft to.
The agent to create. config must carry project. metadata accepts only productionAssistantId.
Target deployment role: supervisor for an orchestrator agent, react for a specialist one. Any other value answers 400. There is no qna deployment to send: a knowledge-base Q&A agent is created as react with graphId set to qna_agent, and the platform derives its deployment_role: qna from that graph.
supervisor, react Human-readable agent name.
The config.configurable object. Build it in the shape listAssistants/getAssistant return for an existing agent of the same deployment — read one first and follow it. Send it as a JSON OBJECT, never as a JSON-encoded string, and always carrying config.project — a propose without it is rejected with 400.
LangGraph graph id. agent is the graph both the supervisor and react deployments host, for orchestrators and regular specialists — the deployment field selects the target. qna_agent, together with deployment: react, creates a knowledge-base Q&A agent: its config carries knowledge_base_ids — ids from listMyKnowledgeBases, copied verbatim — instead of tool whitelists, and the platform reports it back with deployment_role: qna. Routing caveat: the supervisor's judge never offers a qna_agent as a routing option — a Q&A agent listed in a supervisor's agents[] is reached only through the start-with-agentic bypass on the customer's first message, not by regular routing on later turns.
This assistant's own routing description. For a sub-agent it is what supervisors use to decide when to route to it, so make it rich and specific.
Optional. productionAssistantId is the ONLY key this endpoint accepts — set it to anchor the created draft to a PUBLISHED agent of the same deployment on this tenant. Any other key is rejected with 422. Unlike plain createAssistant (where metadata is dropped entirely), propose-create reads and re-verifies this one key at confirm time.
Response
The confirmation contract: message (review + confirm instructions), diff (git unified format, all + lines, prompt bodies in full), preview (the final persisted state, with long prompt bodies stubbed) and confirmationId — send it back unchanged to confirmAssistantCreate.
The response is of type object.