Skip to main content

Write Evidence Records

This guide walks you through posting a release-evidence record back into CertHub from your CI pipeline or release process. This is the main pattern for connecting test or build results back to CertHub without copying the entire artifact tree into it.

When to use this​

Use this when your engineering process produces artifacts outside CertHub and you want CertHub to keep a short controlled record that answers:

  • what version was built
  • which evidence belongs to it
  • where the full evidence lives

Prerequisites​

  • A CertHub API key (Authentication)
  • The IDs you will need (IDs and relationships):
    • KT revision ID of your Release Evidence knowledge topic
    • KU revision ID resolved from the Tech Doc API
    • Product history ID from the dashboard URL
  • A multi-record knowledge topic in CertHub for the evidence you want to track

Step 1: Create a Release Evidence KT in CertHub​

Before you can write records via the API, create a knowledge topic in CertHub with the fields you actually want to review.

Field (UI label)certhub-keyPurpose
Release numberrelease-numberVersion identifier
Release IDrelease-idGit commit SHA or build identifier
Generated atgenerated-atUTC ISO timestamp
Evidence URLevidence-urlLink to the full evidence
Notes (or Details)detailsShort plain-text summary

The UI label can be Notes; the stable certhub-key must be details. Set certhub-key on each field so your integration can resolve form keys from the schema instead of hardcoding opaque widget IDs.

For more detail on that mapping, see Form field keys.

Step 2: Resolve the form keys from the schema​

# pip install requests
import requests

API_KEY = "YOUR_API_KEY"
TECHDOC_BASE = "https://techdoc.prod.certhub-containers.containers.certhub.tech"
RECORDS_BASE = "https://records.prod.certhub-containers.containers.certhub.tech"
HEADERS = {"X-API-Key": API_KEY}

KT_REVISION_ID = "YOUR_RELEASE_EVIDENCE_KT_REVISION_ID"

resp = requests.get(f"{TECHDOC_BASE}/kt/{KT_REVISION_ID}", headers=HEADERS)
resp.raise_for_status()
kt = resp.json()
schema = kt.get("knowledge_topic_schema", {})
components = schema.get("components", [])

certhub_key_to_form_key = {}
for component in components:
form_key = component.get("key")
if not form_key:
continue
props = component.get("properties", {})
ck = props.get("certhub-key")
if ck:
certhub_key_to_form_key[ck] = form_key

Step 3: Get the KU revision ID​

KU_HISTORY_ID = "YOUR_KU_HISTORY_ID"

resp = requests.get(f"{TECHDOC_BASE}/ku/{KU_HISTORY_ID}", headers=HEADERS)
resp.raise_for_status()
ku = resp.json()
ku_revision_id = ku["id"]

Step 4: Build and POST the record​

PRODUCT_HISTORY_ID = "YOUR_PRODUCT_HISTORY_ID"

def form_key(certhub_key):
fk = certhub_key_to_form_key.get(certhub_key)
if not fk:
raise KeyError(f"No form field with certhub-key '{certhub_key}' in schema")
return fk

payload = {
"name": "Release Evidence 1.0.0",
"form": {},
"data": {
form_key("release-number"): "1.0.0",
form_key("release-id"): "abc123def456",
form_key("generated-at"): "2026-08-19T09:00:00Z",
form_key("evidence-url"): "https://github.com/your-org/your-repo/actions/runs/123456789",
form_key("details"): "VERIFIED\nSystem Requirements: 4\nVerified: 4/4\nPassed: 4 Failed: 0",
},
"context": {
"knowledge_unit_topic_id": KT_REVISION_ID,
"knowledge_unit_id": ku_revision_id,
"linked_product": PRODUCT_HISTORY_ID,
},
"read_only": True,
}

response = requests.post(f"{RECORDS_BASE}/records/", json=payload, headers=HEADERS)
response.raise_for_status()
created = response.json()
print(f"Created record: {created['_id']}")

Setting read_only: true prevents accidental edits in the UI. The record becomes a controlled evidence entry.

When to write and when not to​

A sensible default:

  • write to CertHub for full releases
  • build evidence for release candidates, but do not post it yet
  • keep pull request and branch evidence in CI only
Working example

Cadence (CertHub’s public engineering example) follows this release-only pattern. See Working example.

Next steps​