Test Results API
Live accepts test execution evidence from CI pipelines and test runners. Results are stored in the graph, rolled up per requirement, and written back to the workbook's RTMify Verification column.
Using this path for production test?
ATE stations and ATP workflows use this same JSON contract. For the manufacturing and as-built framing, including serial-number and product-anchor guidance, see ATE / ATP Results.
Two ingestion paths
HTTP POST
Send a JSON payload to the local API endpoint. Best for CI pipelines, test runners, and scripts that run on the same machine as Live.
POST http://127.0.0.1:8000/api/v1/test-results File-drop inbox
Drop JSON files into the inbox directory. Live polls every 5 seconds and ingests automatically. Best for tools that can write files but not make HTTP requests.
~/.rtmify/inbox/ Authentication
HTTP ingestion requires a bearer token. The token is stored at:
~/.rtmify/api-token
Include it in the Authorization header:
Authorization: Bearer <your-token>
The dashboard's Guide → MCP & AI tab shows your current token and lets you regenerate it. You can also regenerate it via the API:
POST /api/v1/test-results/token/regenerate
File-drop ingestion does not require a bearer token — it uses filesystem access control instead.
Payload structure
The payload is a JSON object describing a test execution. Both the HTTP and file-drop paths accept the same format.
{
"execution_id": "ci-run-20260313-001",
"executed_at": "2026-03-13T14:00:00Z",
"serial_number": "UNIT-042", // optional
"executor": "pytest", // optional
"source": "ci/github-actions", // optional
"test_cases": [
{
"result_id": "result-001",
"test_case_ref": "TG-007/TC-001",
"status": "passed",
"duration_ms": 142,
"notes": "All assertions passed",
"measurements": [], // optional structured data
"attachments": [] // optional file references
},
{
"result_id": "result-002",
"test_case_ref": "TG-007/TC-002",
"status": "failed",
"duration_ms": 89,
"notes": "Expected 200, got 422"
}
]
} | Field | Required | Description |
|---|---|---|
| execution_id | Required | Unique identifier for this test run. Used for idempotent replacement. |
| executed_at | Required | ISO 8601 timestamp of when the execution occurred. |
| serial_number | Optional | Physical unit or device identifier, for hardware/embedded test evidence. |
| executor | Optional | Name of the test framework or runner. |
| source | Optional | CI system or execution context label. |
| test_cases[].result_id | Required | Unique identifier for this individual test case result. |
| test_cases[].test_case_ref | Required | Reference to a test case in your Tests tab. Dangling refs are accepted as warnings. |
| test_cases[].status | Required | One of: passed, failed, error, blocked, skipped. |
| test_cases[].duration_ms | Optional | Execution time in milliseconds. |
| test_cases[].notes | Optional | Free-text notes or failure message. |
| test_cases[].measurements | Optional | Array of structured measurement objects for quantitative evidence. |
| test_cases[].attachments | Optional | Array of file references (logs, screenshots, etc.). |
What happens on ingestion
- Validates the JSON payload structure
- Computes an execution-level rollup status from the individual test case statuses
- Creates or updates
TestExecutionandTestResultgraph nodes - Creates
HAS_RESULTandEXECUTION_OFedges linking results to test cases - Accepts dangling
test_case_refvalues as warnings rather than rejecting the request - On the next workbook sync, computes requirement verification rollups and writes the RTMify Verification column
Execution rollup statuses
| Rollup | Meaning |
|---|---|
| passed | All test cases in the execution passed. |
| failed | At least one test case failed, errored, or was blocked. |
| partial | Mixed results — some passed, some did not reach a concrete outcome. |
Idempotency
Submissions are idempotent by execution_id. Re-sending the same execution ID replaces the previous results, unless a newer execution has already superseded it (returns 409).
File-drop inbox
Drop JSON files into ~/.rtmify/inbox/. Live polls every 5 seconds and processes them automatically.
processed/ # successfully ingested files moved here
rejected/ # invalid or unprocessable files moved here
The inbox directory can be changed with --inbox-dir <path> at startup. The current inbox path is shown in the dashboard Guide → MCP & AI tab.
API error codes
| Code | Meaning |
|---|---|
| 200 | Ingestion successful. |
| 401 | Missing or invalid bearer token. |
| 400 | Invalid payload structure. Response body contains structured error detail. |
| 409 | Execution ID conflict — a newer execution has already superseded this ID. |
| 413 | Payload too large. |