Webhooks
Trinitite can push event payloads to your endpoints as things happen — verdicts land, training jobs finish, policies are finalized, governance controls trip. Webhooks are the lowest-latency way to react to the platform without polling.
Register an endpoint
POST /v1/webhooks
Authorization: Bearer <api_key>
Content-Type: application/json
{
"url": "https://your.app/webhooks/trinitite",
"events": ["verdict.created", "training.job.completed", "policy.finalized", "governance.shutdown_engaged"],
"secret": "whsec_..."
}
| Field | Description |
|---|---|
url | HTTPS endpoint on your side. Must respond 2xx within 10 s. |
events | List of event types to subscribe to (see § Event types). |
secret | Used to sign payloads. Rotatable; never reuse across endpoints. |
The platform returns a webhook record with an id (wh_01J…) and the active secret hash. Manage endpoints via GET / PATCH / DELETE /v1/webhooks/:id.
Event types
| Event | When it fires | Payload root |
|---|---|---|
verdict.created | Every governance verdict is written to the ledger. | verdict |
training.job.completed | A training/LoRA job reaches a terminal state. | job |
policy.finalized | A policy document is locked. | policy |
governance.shutdown_engaged | An emergency kill-switch is engaged. | control |
governance.breaker_tripped | A circuit breaker trips. | control |
attestation.ready | An attestation report finishes generating. | attestation |
eval.run.completed | An Eval run finishes. | eval_run |
Payload shape
Every delivery uses one envelope:
{
"id": "evt_01J9X…",
"type": "verdict.created",
"created_at": "2026-05-01T12:00:00Z",
"data": {
"verdict": "corrected",
"guardian": "pii-redactor",
"ledger_id": "led_01J9X…",
"policy_hash": "sha256:7f3a…",
"correction_diff": [
{ "op": "replace", "path": "/choices/0/message/content", "value": "Customer SSN: [REDACTED]" }
]
},
"previous_attempt": null,
"attempt": 1
}
The data object mirrors the resource documented on the relevant endpoint page — verdict.created matches the verdict vocabulary receipt, training.job.completed matches the Training job shape, and so on.
Signature verification
Every delivery is signed with your endpoint's secret using HMAC-SHA256. Two headers travel with the POST:
| Header | Description |
|---|---|
X-Trinitite-Signature | t=<unix_ts>,v1=<hex_hmac> |
X-Trinitite-Event-Id | The evt_… id (for idempotency on your side). |
Verify on your end:
import hmac, hashlib, time
def verify(payload_body: bytes, sig_header: str, secret: str, tolerance_sec: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in sig_header.split(","))
t, v1 = parts["t"], parts["v1"]
if abs(time.time() - int(t)) > tolerance_sec:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + payload_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(v1, expected)
Reject any delivery whose signature fails or whose timestamp is outside your replay tolerance.
Retries & idempotency
- The platform considers a delivery successful when your endpoint returns any
2xxstatus. Anything else (including timeouts and3xx) triggers a retry. - Retry backoff: exponential, up to 24 hours, then the delivery is marked
failedand is available viaGET /v1/webhooks/:id/events. - Redeliveries carry the same
X-Trinitite-Event-Idand the samedata— deduplicate on the event id on your side. previous_attemptis set on redeliveries so you can distinguish a fresh event from a retry.
Ordering
Events for a single resource are delivered in order. Events across different resources are not strictly ordered — rely on created_at and the resource's own version/sequence fields, not arrival order, for cross-resource correlation.
→ Verdict vocabulary — the shape of the verdict.created payload.
→ Errors, rate limits & conventions — idempotency and retry semantics.
→ Logs — pull historical verdicts when a webhook was missed.