Sealed artifact endpoints

Four endpoints, one per outcome state, each returning the cryptographically sealed artifact a third party needs to replay the decision without trusting Verdifax.

See Outcome states & sealed artifacts for the conceptual overview and field-level schema.

Endpoints

GET /runs/{id}/allow-token         → AllowToken      (PEPG admit + nine-stage pipeline)
GET /runs/{id}/deny-receipt        → DenyReceipt     (PEPG)
GET /runs/{id}/ccv-halt-receipt    → CCVHaltReceipt  (CCV)
GET /runs/{id}/macc-halt-receipt   → MACCHaltReceipt (MACC)

Each endpoint returns the sealed artifact for run {id} if and only if the run resolved to the matching outcome state. Otherwise it returns 404 Not Found with a clear error explaining which state the run is in.

How to pick the right endpoint: Use the outcome_kind field from the run summary (GET /runs/{id}):

  • ok/runs/{id}/allow-token
  • pepg_deny/runs/{id}/deny-receipt
  • ccv_halt/runs/{id}/ccv-halt-receipt
  • macc_halt/runs/{id}/macc-halt-receipt
  • stage_error → No artifact endpoint exists (run record only)

Authentication

Same as every other run endpoint, pass X-Verdifax-Key: $VERDIFAX_KEY. The orchestrator scopes results to the API key that authored the run, so one customer cannot retrieve another customer's artifacts.

Response shape

All four return the same envelope:

{
  "ok": true,
  "run_id": 42,
  "<artifact_key>": { ... }
}

Where <artifact_key> is one of:

  • allow_token
  • deny_receipt
  • ccv_halt_receipt
  • macc_halt_receipt

The artifact object itself is a canonical-bytes record (RFC 8785 + SHA-256) whose fields are documented in Outcome states. The terminal hash field is the SHA-256 of the canonical preimage and is the value verdifax-pepg-verify recomputes.

Example, fetching a DenyReceipt

curl -H "X-Verdifax-Key: $VERDIFAX_KEY" \
  https://api.verdifax.com/runs/42/deny-receipt
{
  "ok": true,
  "run_id": 42,
  "deny_receipt": {
    "envelope_id": "env-7a3c…",
    "fired_rule_id": "rule_classification_exceeded",
    "decision_reason_code": "CLASSIFICATION_EXCEEDED",
    "policy_hash": "8f1d…",
    "evaluation_hash": "b2e0…",
    "mcd_finding_hash": "",
    "deny_clock": "2026-04-30T17:23:48.124Z",
    "evaluator_version": "pepg-0.3.0",
    "version": "vfa.deny_receipt.v1",
    "hash": "9e7f…"
  }
}

Independent verification

The audit dashboard and PDF emit a copy-pasteable command for each run. The same command works against any of the four endpoints, only the path and the jq selector change:

curl -H "X-Verdifax-Key: $VERDIFAX_KEY" \
  https://api.verdifax.com/runs/42/deny-receipt \
  | jq .deny_receipt | verdifax-pepg-verify

The verdifax-pepg-verify CLI is open-source and MIT-licensed. It re-derives the canonical preimage, recomputes SHA-256, and compares against the hash field. Exit code 0 confirms the seal; non-zero indicates tampering.

When you'll see 404

{ "ok": false, "error": "run 42 is \"ok\", not \"pepg_deny\", no deny_receipt exists" }

The error message names both the actual outcome state of the run and the state required for the requested artifact. The dashboard handles this automatically by inspecting outcome_kind on the run summary before choosing which endpoint to call.

Outcome discriminator on run summaries

Every entry in GET /runs and GET /runs/search carries an outcome_kind field with one of: ok, pepg_deny, ccv_halt, macc_halt, stage_error. Use it to branch on which artifact to fetch without parsing the manifest.