curl --request POST \
--url https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate \
--header 'Content-Type: application/json' \
--data '
{
"seed_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"triage_note": "<string>"
}
'import requests
url = "https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate"
payload = {
"seed_ids": ["3c90c3cc-0d44-4b50-8888-8dd25736052a"],
"triage_note": "<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({seed_ids: ['3c90c3cc-0d44-4b50-8888-8dd25736052a'], triage_note: '<string>'})
};
fetch('https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate', 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.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate",
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([
'seed_ids' => [
'3c90c3cc-0d44-4b50-8888-8dd25736052a'
],
'triage_note' => '<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.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate"
payload := strings.NewReader("{\n \"seed_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"triage_note\": \"<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.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate")
.header("Content-Type", "application/json")
.body("{\n \"seed_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"triage_note\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate")
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 \"seed_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"triage_note\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"succeeded": [
{
"seed_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tenant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"slug": "<string>",
"state_changed_at": "2023-11-07T05:31:56Z",
"promoted_project_id": "<string>",
"triaged_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"triage_note": "<string>",
"dramatic_premise": "<string>",
"dramatic_situation": "<string>",
"central_tension": {},
"human_tension_grounding": {},
"protagonist_shape": {},
"stakes": {},
"tone": "<string>",
"pack_affinity": [
{}
],
"risk_flags": [],
"provenance": {},
"quality_scores": {},
"durability_class": "<string>",
"freshness_ts": "2023-11-07T05:31:56Z",
"stale_after": "2023-11-07T05:31:56Z",
"resonance_score": 123,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
}
],
"failed": [
{
"seed_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"error": "<string>",
"detail": "<string>"
}
]
}{
"error": "Forbidden",
"code": "forbidden",
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"message": "Your API key does not have the required scope.",
"doc_url": "https://docs.kitefrost.ai/errors/forbidden"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"error": "Rate limit exceeded",
"code": "rate_limited",
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"message": "You have exceeded the rate limit for your plan.",
"doc_url": "https://docs.kitefrost.ai/errors/rate_limited"
}{
"error": "Internal server error",
"code": "internal_error",
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"message": "An unexpected error occurred.",
"doc_url": "https://docs.kitefrost.ai/errors/internal_error"
}Bulk validate (promote) seeds to this project
Bulk version of POST /seeds/{seed_id}/promote. For each supplied seed UUID: resolve tenant-scoped, then dispatch through IdeaPoolAPI.assign (canonical promote - R127-C6) to transition TRIAGED → PROMOTED and stamp promoted_project_id. Per-item failures are isolated; the batch returns 200 with failed[] entries describing the rejections.
curl --request POST \
--url https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate \
--header 'Content-Type: application/json' \
--data '
{
"seed_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"triage_note": "<string>"
}
'import requests
url = "https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate"
payload = {
"seed_ids": ["3c90c3cc-0d44-4b50-8888-8dd25736052a"],
"triage_note": "<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({seed_ids: ['3c90c3cc-0d44-4b50-8888-8dd25736052a'], triage_note: '<string>'})
};
fetch('https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate', 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.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate",
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([
'seed_ids' => [
'3c90c3cc-0d44-4b50-8888-8dd25736052a'
],
'triage_note' => '<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.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate"
payload := strings.NewReader("{\n \"seed_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"triage_note\": \"<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.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate")
.header("Content-Type", "application/json")
.body("{\n \"seed_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"triage_note\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.kitefrost.ai/v1/projects/{project_id}/seeds/batch/validate")
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 \"seed_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"triage_note\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"succeeded": [
{
"seed_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tenant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"slug": "<string>",
"state_changed_at": "2023-11-07T05:31:56Z",
"promoted_project_id": "<string>",
"triaged_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"triage_note": "<string>",
"dramatic_premise": "<string>",
"dramatic_situation": "<string>",
"central_tension": {},
"human_tension_grounding": {},
"protagonist_shape": {},
"stakes": {},
"tone": "<string>",
"pack_affinity": [
{}
],
"risk_flags": [],
"provenance": {},
"quality_scores": {},
"durability_class": "<string>",
"freshness_ts": "2023-11-07T05:31:56Z",
"stale_after": "2023-11-07T05:31:56Z",
"resonance_score": 123,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
}
],
"failed": [
{
"seed_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"error": "<string>",
"detail": "<string>"
}
]
}{
"error": "Forbidden",
"code": "forbidden",
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"message": "Your API key does not have the required scope.",
"doc_url": "https://docs.kitefrost.ai/errors/forbidden"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"error": "Rate limit exceeded",
"code": "rate_limited",
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"message": "You have exceeded the rate limit for your plan.",
"doc_url": "https://docs.kitefrost.ai/errors/rate_limited"
}{
"error": "Internal server error",
"code": "internal_error",
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"message": "An unexpected error occurred.",
"doc_url": "https://docs.kitefrost.ai/errors/internal_error"
}Body
Body of POST .../seeds/batch/{validate,reject}.
A flat list of seed UUIDs plus an optional shared note that gets
stamped on every successful transition. The size cap is enforced
inside the handler (explicit 400) rather than via Pydantic
max_length so the failure mode is a clean 400 Bad Request
with a stable schema, not the framework's default 422.
Response
Batch processed (per-id verdicts in body)
Response shape for both batch endpoints.
succeeded carries the per-id verdicts that landed; failed
carries per-id reasons (not_found, cross_tenant,
invalid_transition, error). The HTTP status code is always
200 once the request has cleared validation - per-item failure
does NOT abort the batch (per-item error isolation per design §
Batch).