Training API
Programmatic control over the full Guardian training lifecycle: retrain existing Guardians, monitor job progress, compare versions, run multi-Guardian campaigns, and generate or densify synthetic training data.
Authentication: Authorization: Bearer <session_token | api_key>. Most endpoints require guardians:write (or guardians:admin for campaigns).
The Guardian CRUD surface (/v1/guardians/*) uses snake_case (guardian_id, guardian_name). The Training API uses camelCase request bodies on retrain and campaign endpoints (guardianId, goodExamples, versionNotes). Responses and other training endpoints use snake_case. Each route's example below shows the actual shape.
Endpoints
Retraining
| Method | Path | Purpose |
|---|---|---|
POST | /v1/training/retrain | Retrain an existing Guardian |
GET | /v1/training/jobs | List active training jobs |
GET | /v1/training/jobs/{job_id} | Get a training job's status |
DELETE | /v1/training/jobs/{job_id} | Cancel a queued or in-progress job |
GET | /v1/training/guardians/{guardian_id}/history | Version history |
GET | /v1/training/guardians/{guardian_id}/compare | Compare two versions |
Campaigns
| Method | Path | Purpose |
|---|---|---|
POST | /v1/training/campaigns | Train multiple Guardians in one coordinated run |
GET | /v1/training/campaigns/{campaignId} | Poll campaign progress |
Synthetic data generation
| Method | Path | Purpose |
|---|---|---|
POST | /v1/training/generate | Start a synthetic training data generation job |
GET | /v1/training/generate/jobs/{job_id} | Get generation job status |
GET | /v1/training/generate/jobs/{job_id}/data | Retrieve generated examples |
POST | /v1/training/generate/validate | Validate a candidate dataset |
Densification
| Method | Path | Purpose |
|---|---|---|
POST | /v1/training/densify | Enrich sparse trajectories with intermediate reasoning |
GET | /v1/training/densify/jobs/{job_id} | Densification job status |
GET | /v1/training/densify/jobs/{job_id}/data | Retrieve densified trajectories |
POST /v1/training/retrain
Trigger a new training run for an existing Guardian. You can supply updated examples, a revised rubric, or leave them unset to re-run training on the existing dataset.
Request body — camelCase
{
"guardianId": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"versionNotes": "Stricter PII redaction rules for GDPR.",
"rubric": "Block any response that contains a social security number, account number, or date of birth.",
"goodExamples": [
{ "log": "User asked for account balance. Guardian responded with [REDACTED].", "reason": "PII redacted before response." }
],
"badExamples": [
{ "log": "Guardian responded with raw account number 4111-1111-1111-1111.", "reason": "Raw account number not redacted." }
],
"incremental": false
}
| Field | Type | Required | Description |
|---|---|---|---|
guardianId | string | Yes | ID of the Guardian to retrain |
versionNotes | string | No | Notes describing the new version |
rubric | string | No | Updated rubric. Omitted: existing rubric is retained |
goodExamples | array | No | New compliant examples |
badExamples | array | No | New violation examples |
incremental | boolean | No | If true, layer new examples on existing training. Default false |
Response — 202 Accepted
{
"job_id": "job_01JF8R3M5Z6N7Q8T9V0W1Y2Z3C",
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"status": "queued",
"version": 4,
"version_notes": "Stricter PII redaction rules for GDPR.",
"created_at": "2026-05-01T15:00:00Z",
"estimated_completion": "2026-05-01T15:45:00Z"
}
- cURL
- Python
- JavaScript
curl -X POST https://api.trinitite.ai/v1/training/retrain \
-H "Authorization: Bearer $TRINITITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"guardianId": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"versionNotes": "Stricter PII redaction.",
"rubric": "Block SSNs, account numbers, dates of birth.",
"goodExamples": [
{ "log": "Guardian responded with [REDACTED].", "reason": "PII redacted." }
],
"badExamples": [
{ "log": "Account number 4111-1111-1111-1111 in response.", "reason": "Raw account number." }
],
"incremental": false
}'
import os, requests
response = requests.post(
"https://api.trinitite.ai/v1/training/retrain",
headers={"Authorization": f"Bearer {os.environ['TRINITITE_API_KEY']}"},
json={
"guardianId": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"versionNotes": "Stricter PII redaction.",
"rubric": "Block SSNs, account numbers, dates of birth.",
"goodExamples": [
{"log": "Guardian responded with [REDACTED].", "reason": "PII redacted."},
],
"badExamples": [
{"log": "Account number 4111-1111-1111-1111 in response.", "reason": "Raw account number."},
],
"incremental": False,
},
)
data = response.json()
print(f"Training job queued: {data['job_id']}")
const response = await fetch('https://api.trinitite.ai/v1/training/retrain', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TRINITITE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
guardianId: 'gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A',
versionNotes: 'Stricter PII redaction.',
rubric: 'Block SSNs, account numbers, dates of birth.',
goodExamples: [{ log: 'Guardian responded with [REDACTED].', reason: 'PII redacted.' }],
badExamples: [
{ log: 'Account number 4111-1111-1111-1111 in response.', reason: 'Raw account number.' },
],
incremental: false,
}),
});
const data = await response.json();
GET /v1/training/jobs
List active (queued or in-progress) training jobs for your organization.
{
"jobs": [
{
"job_id": "job_01JF8R3M5Z6N7Q8T9V0W1Y2Z3C",
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"guardian_name": "PII-Redactor",
"status": "training",
"version": 4,
"progress": 0.62,
"created_at": "2026-05-01T15:00:00Z",
"estimated_completion": "2026-05-01T15:45:00Z"
}
]
}
GET /v1/training/jobs/{job_id}
Poll a specific training job. progress is 0.0–1.0.
{
"job_id": "job_01JF8R3M5Z6N7Q8T9V0W1Y2Z3C",
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"guardian_name": "PII-Redactor",
"status": "completed",
"version": 4,
"progress": 1.0,
"version_notes": "Stricter PII redaction rules for GDPR.",
"created_at": "2026-05-01T15:00:00Z",
"completed_at": "2026-05-01T15:42:00Z",
"error": null
}
| Status | Meaning |
|---|---|
queued | Awaiting a training slot |
training | Actively in progress |
completed | New version is live |
failed | See error for details |
cancelled | Cancelled before completion |
Recommended polling cadence: 2 s initial interval, linear backoff to 10 s.
DELETE /v1/training/jobs/{job_id}
Cancel a queued or in-progress training job.
{
"message": "Training job cancelled successfully.",
"job_id": "job_01JF8R3M5Z6N7Q8T9V0W1Y2Z3C",
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"cancelled_at": "2026-05-01T15:20:00Z"
}
GET /v1/training/guardians/{guardian_id}/history
Full version history for a Guardian.
| Query | Type | Description |
|---|---|---|
status | string | Filter to all, completed, failed, training |
limit | integer | Default 50, max 200 |
offset | integer | Default 0 |
{
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"guardian_name": "PII-Redactor",
"versions": [
{ "version": 4, "status": "completed", "version_notes": "GDPR refinements.", "created_at": "2026-05-01T15:00:00Z", "completed_at": "2026-05-01T15:42:00Z" },
{ "version": 3, "status": "completed", "version_notes": "Credit card patterns.", "created_at": "2026-04-15T10:00:00Z", "completed_at": "2026-04-15T10:38:00Z" }
],
"pagination": { "total": 4, "limit": 50, "offset": 0 }
}
GET /v1/training/guardians/{guardian_id}/compare
Compare two versions of a Guardian.
| Query | Required | Description |
|---|---|---|
version1 | Yes | First version (integer) |
version2 | Yes | Second version (integer) |
{
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"guardian_name": "PII-Redactor",
"version1": {
"version": 3,
"version_notes": "Credit card patterns.",
"completed_at": "2026-04-15T10:38:00Z",
"metrics": { "correction_rate": 0.12, "block_rate": 0.03, "avg_latency_ms": 92 }
},
"version2": {
"version": 4,
"version_notes": "GDPR refinements.",
"completed_at": "2026-05-01T15:42:00Z",
"metrics": { "correction_rate": 0.18, "block_rate": 0.05, "avg_latency_ms": 88 }
},
"delta": { "correction_rate": "+0.06", "block_rate": "+0.02", "avg_latency_ms": "-4" }
}
Training Campaigns
A campaign coordinates training runs across multiple Guardians, applying the same rubric updates, base hyperparameters, and test-suite gates to a fleet at once. Useful when an updated regulatory rule must roll across every Guardian in a project.
POST /v1/training/campaigns — camelCase
{
"campaignName": "GDPR-Q2-2026",
"description": "Apply Q2 GDPR rubric refinements to all PII Guardians.",
"guardianIds": [
"gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"gov_01JF8R3M4Y5N6Q7T8V9W0Y1Z2B"
],
"rubric": "Block SSN, IBAN, BSN, NINO, CPF; redact partial PII.",
"testSuiteIds": ["ts_01JF8R3M9N0Q1T2V3W4Y5Z6A7B"],
"incremental": true
}
Returns 202 Accepted with a campaign_id. Each Guardian in the campaign produces its own training job, polled either via the campaign endpoint or via /v1/training/jobs/{job_id}.
GET /v1/training/campaigns/{campaignId}
{
"campaign_id": "cmp_01JF8RC1Q2W3E4R5T6Y7U8I9O0",
"campaign_name": "GDPR-Q2-2026",
"status": "running",
"started_at": "2026-05-01T20:00:00Z",
"completed_at": null,
"guardians": [
{ "guardian_id": "gov_01JF8...A", "job_id": "job_01...", "status": "completed", "version": 4 },
{ "guardian_id": "gov_01JF8...B", "job_id": "job_02...", "status": "training", "progress": 0.74 }
],
"test_results": null
}
When all Guardians reach a terminal state and any attached test suites have run, status transitions to completed (or failed if any Guardian failed).
Synthetic data generation
POST /v1/training/generate
{
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"count": 100,
"context": "Customer support assistant for a financial services company."
}
Response — 202 Accepted:
{
"job_id": "job_01JF8RGEN1A2B3C4D5E6F7G8H9I",
"status": "queued",
"count_requested": 100,
"created_at": "2026-05-01T16:00:00Z",
"estimated_completion": "2026-05-01T16:15:00Z"
}
GET /v1/training/generate/jobs/{job_id}
{
"job_id": "job_01JF8RGEN1A2B3C4D5E6F7G8H9I",
"status": "completed",
"count_requested": 100,
"count_generated": 100,
"created_at": "2026-05-01T16:00:00Z",
"completed_at": "2026-05-01T16:12:00Z"
}
GET /v1/training/generate/jobs/{job_id}/data
| Query | Type | Default | Description |
|---|---|---|---|
split | boolean | true | Pre-split into train + validation sets |
{
"job_id": "job_01JF8RGEN1A2B3C4D5E6F7G8H9I",
"total_examples": 100,
"train": {
"good_examples": [
{ "log": "...", "reason": "..." }
],
"bad_examples": [
{ "log": "...", "reason": "..." }
]
},
"validation": {
"good_examples": [ /* ... */ ],
"bad_examples": [ /* ... */ ]
}
}
POST /v1/training/generate/validate
{
"good_examples": [ { "log": "...", "reason": "..." } ],
"bad_examples": [ { "log": "...", "reason": "..." } ]
}
{
"valid": true,
"total_examples": 120,
"good_count": 60,
"bad_count": 60,
"warnings": [],
"errors": []
}
Densification
Densification enriches sparse conversation trajectories with intermediate reasoning steps, improving training signal for complex governance scenarios.
POST /v1/training/densify
{
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"trajectories": [
[
{ "role": "user", "content": "..." },
{ "role": "assistant", "content": "..." }
]
]
}
Response — 202 Accepted:
{
"job_id": "job_01JF8RDS1Y2X3W4V5U6T7S8R9Q",
"status": "queued",
"trajectory_count": 50,
"created_at": "2026-05-01T17:00:00Z",
"estimated_completion": "2026-05-01T17:20:00Z"
}
GET /v1/training/densify/jobs/{job_id}
{
"job_id": "job_01JF8RDS1Y2X3W4V5U6T7S8R9Q",
"status": "completed",
"trajectory_count": 50,
"densified_count": 50,
"created_at": "2026-05-01T17:00:00Z",
"completed_at": "2026-05-01T17:18:00Z"
}
GET /v1/training/densify/jobs/{job_id}/data
| Query | Type | Default | Description |
|---|---|---|---|
include_metadata | boolean | true | Include per-trajectory metadata (loss surface, density score) |
{
"job_id": "job_01JF8RDS1Y2X3W4V5U6T7S8R9Q",
"trajectory_count": 50,
"densified": [
{
"original": [ { "role": "user", "content": "..." }, { "role": "assistant", "content": "..." } ],
"densified": [
{ "role": "user", "content": "..." },
{ "role": "assistant", "content": "...intermediate reasoning..." },
{ "role": "assistant", "content": "..." }
],
"metadata": { "density_score": 0.87, "added_steps": 3 }
}
]
}
AutoResearch — Document to Guardian Pipeline
The AutoResearch surface gives you the "drop a PDF" → Guardian workflow programmatically: ingest a policy document, let the Teleological Data Generator synthesize adversarial training data, and optionally trigger a Guardian retrain — all in one pipeline.
See Policy Intelligence for the conceptual overview of how policy documents become enforcement geometry.
POST /v1/research/ingest
Ingest a policy document and queue synthetic data generation. Returns 202 Accepted with a research_job_id.
{
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"source": {
"type": "url",
"url": "https://gdpr-info.eu/art-33-gdpr/",
"format": "html"
},
"auto_retrain": true,
"version_notes": "Q2 2026 — GDPR Art. 33 policy update"
}
| Field | Type | Required | Description |
|---|---|---|---|
guardian_id | string | Yes | Guardian to update with the synthesized data |
source | object | Yes | Same discriminated union as the Policies API (inline / url / integration) |
auto_retrain | boolean | No | If true, a retrain job starts automatically after data generation completes. Default false |
version_notes | string | No | Stored on the resulting training job |
Response — 202 Accepted
{
"research_job_id": "rj_01JF8RRES1A2B3C4D5E6F7G8H9I",
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"status": "ingesting",
"auto_retrain": true,
"created_at": "2026-05-01T18:00:00Z",
"estimated_completion": "2026-05-01T18:45:00Z"
}
GET /v1/research/jobs/{research_job_id}
Poll the pipeline. status progresses through: ingesting → extracting_nodes → generating_data → retraining (if auto_retrain) → completed | failed.
{
"research_job_id": "rj_01JF8RRES1A2B3C4D5E6F7G8H9I",
"guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A",
"status": "completed",
"stages": {
"ingestion": "completed",
"node_extraction": "completed",
"data_generation": "completed",
"retrain": "completed"
},
"examples_generated": 5241,
"training_job_id": "job_01JF8R3M5Z6N7Q8T9V0W1Y2Z3C",
"policy_document_id": "pol_01JF8RPDC1A2B3C4D5E6F7G8H9I",
"created_at": "2026-05-01T18:00:00Z",
"completed_at": "2026-05-01T18:42:00Z"
}
Start at 10 s intervals for the research pipeline; use exponential backoff up to 60 s. A typical full pipeline (ingest → generate → retrain) completes in 45–90 minutes on an A100. The training_job_id field populates when the retrain starts — you can track that job separately via GET /v1/training/jobs/{job_id}.
Errors
| HTTP | error.code | Cause |
|---|---|---|
400 | validation_error | Body or query failed schema validation |
400 | bad_request | Missing required field, invalid incremental value |
401 | unauthenticated | Missing or invalid credential |
403 | forbidden | Caller lacks guardians:write |
404 | not_found | Guardian or job not found in your organization |
409 | conflict | A training job is already running for this Guardian |
422 | unprocessable_entity | Domain validation failed (e.g. Guardian not in ready/failed state) |
429 | rate_limited | Per-organization rate limit exceeded |
{
"error": {
"code": "conflict",
"message": "A training job is already running for guardian gov_01JF8...",
"details": { "guardian_id": "gov_01JF8R3M3X4N5Q6T7V8W9Y0Z1A", "active_job_id": "job_01..." },
"request_id": "req_01J..."
}
}
Next steps
- Manage Guardians → Guardians API
- Try a Guardian without writing logs → Simulation API
- Build regression test suites → Test Suites API
- Ingest compliance documents → Policies API
- Understand the policy → Guardian pipeline → Policy Intelligence