curl --request POST \
--url https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"version": "<string>",
"percent": 123
}
'import requests
url = "https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary"
payload = {
"version": "<string>",
"percent": 123
}
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({version: '<string>', percent: 123})
};
fetch('https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary', 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.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary",
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([
'version' => '<string>',
'percent' => 123
]),
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.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary"
payload := strings.NewReader("{\n \"version\": \"<string>\",\n \"percent\": 123\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.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": \"<string>\",\n \"percent\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary")
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 \"version\": \"<string>\",\n \"percent\": 123\n}"
response = http.request(request)
puts response.read_body{
"key": "<string>",
"name": "<string>",
"type": "<string>",
"gpu_type": "<string>",
"enabled": true,
"status": "<string>",
"est_hourly_cost": 123,
"capacity_per_pod": 123,
"datacenter": "<string>",
"live_url": "<string>",
"est_hourly_cost_source": "registry_estimate",
"error": "<string>",
"desired_count": 1,
"auto_scale": false,
"pods": [],
"canary": {
"version": "<string>",
"percent": 123,
"pods": 123,
"healthy": 123
},
"workers_min": 123,
"workers_max": 123,
"co_hosted_on": "<string>",
"pod_image": "<string>",
"blockers": [],
"bake": {
"status": "<string>",
"image": "<string>",
"built_at": "<string>",
"added": [],
"removed": [],
"changed": [],
"summary": "<string>"
},
"pinned_datacenter": "<string>",
"pinned_gpu_type": "<string>",
"pinned_gpu_forced": false,
"hardware_source": "auto",
"hardware_pinned_at": "2023-11-07T05:31:56Z",
"hardware_drift": "<string>",
"hardware_warning": "<string>",
"direct_url": "<string>",
"direct_url_in_use": false
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Start Canary
Start a CANARY rollout on a POD plane: launch ONE extra pod running
version (the operator’s label tying the rollout to the model/adapter
they just trained — pods sync adapters from S3 at boot, so the new pod
comes up serving it) and route percent% (1..50) of NEW selections to
it once healthy. Sticky calls never switch mid-call.
202 on launch (the pod pool-joins when the health loop promotes it).
Idempotent: re-POSTing the SAME version only re-weights percent (200,
no second pod); a DIFFERENT version while one is active is a 409 (promote
or abort the running canary first). A RunPod launch failure is recorded
in-band (status=error) and the canary is NOT armed.
curl --request POST \
--url https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"version": "<string>",
"percent": 123
}
'import requests
url = "https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary"
payload = {
"version": "<string>",
"percent": 123
}
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({version: '<string>', percent: 123})
};
fetch('https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary', 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.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary",
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([
'version' => '<string>',
'percent' => 123
]),
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.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary"
payload := strings.NewReader("{\n \"version\": \"<string>\",\n \"percent\": 123\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.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": \"<string>\",\n \"percent\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.peachdeskai.com/api/v1/sa/gpu/planes/{key}/canary")
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 \"version\": \"<string>\",\n \"percent\": 123\n}"
response = http.request(request)
puts response.read_body{
"key": "<string>",
"name": "<string>",
"type": "<string>",
"gpu_type": "<string>",
"enabled": true,
"status": "<string>",
"est_hourly_cost": 123,
"capacity_per_pod": 123,
"datacenter": "<string>",
"live_url": "<string>",
"est_hourly_cost_source": "registry_estimate",
"error": "<string>",
"desired_count": 1,
"auto_scale": false,
"pods": [],
"canary": {
"version": "<string>",
"percent": 123,
"pods": 123,
"healthy": 123
},
"workers_min": 123,
"workers_max": 123,
"co_hosted_on": "<string>",
"pod_image": "<string>",
"blockers": [],
"bake": {
"status": "<string>",
"image": "<string>",
"built_at": "<string>",
"added": [],
"removed": [],
"changed": [],
"summary": "<string>"
},
"pinned_datacenter": "<string>",
"pinned_gpu_type": "<string>",
"pinned_gpu_forced": false,
"hardware_source": "auto",
"hardware_pinned_at": "2023-11-07T05:31:56Z",
"hardware_drift": "<string>",
"hardware_warning": "<string>",
"direct_url": "<string>",
"direct_url_in_use": false
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Authorizations
Interactive user session: the login JWT, sent as Authorization: Bearer <token>.
Path Parameters
Body
Response
Successful Response
One plane's card for the SA GPU screen.
Show child attributes
Show child attributes
The plane's active canary (Phase H6), or absent when none is configured.
version is the OPERATOR'S label tying the rollout to the model/adapter
they just trained (it must match the model_version the canary pod was
launched with); pods/healthy count the plane's pods at that
version.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
The pod's baked-store freshness verdict
(services/voice-engine/scripts/bake_manifest.py).
A baked image pins its model set at BUILD time and the warm-store skip
deliberately declines to re-sync it, so a pod can serve last week's weights
indefinitely with no symptom but behaviour. status is fresh |
stale | unknown (the bucket could not be listed — never guessed).
Show child attributes
Show child attributes
Whether live traffic is actually being ROUTED at the direct address (VOICE_POD_DIRECT_URL). Knowing the address and using it are different facts, and this one is OFF by default on purpose. A RunPod direct TCP port is PLAINTEXT — the engine terminates no TLS, so the hop is plain HTTP across the public internet — and the bearer sent on it is the RunPod ACCOUNT api key, which creates and terminates pods and whose GET /v1/pods returns every pod's env including the workers' S3 credentials. Turning this on leaks that key, and the call audio and transcripts beside it, to any observer on the path, ~3 times per conversational turn. PREREQUISITES before it may carry live traffic: (1) the hop is encrypted — the engine terminating TLS on the direct port, or a mutually-authenticated tunnel (WireGuard/Tailscale) between the api task and the pod; and (2) the bearer is a NARROW, pod-scoped token instead of the account key. Neither is in place today. Read pod_pool.prefer_direct_pod_urls before flipping it; use pod_rtt_probe compare to measure without routing anything.

