procurement-decision-api
The machine that produces buyer-side AI procurement decisions, schema-conformant and ready to publish.
A FastAPI service that ingests a buyer's evaluation rubric plus a set of vendor Kinetic Gain Protocol Suite declarations and returns a draft AI Procurement Decision Card (spec #11 of the Suite). The Decision Card is the canonical machine-readable carrier for NIST AI RMF-aligned procurement outcomes under OMB M-24-10 — see the crosswalk doc.
The cross-ecosystem bridge
This is the first repo that composes the Kinetic Gain Protocol Suite with the Decision Intelligence Engines portfolio:
Vendor publishes: Buyer publishes (this service produces):
───────────────────────── ────────────────────────────────────────
AEO Protocol Card ┐
Tool Disclosure │
Clinical AI Card ├──> AI Procurement Decision Card
Student AI Disclosure │ (status / rubric / conditions /
Agent Card │ documents reviewed / rationale)
…the other six specs… ┘
Quick start
pip install procurement-decision-api
procurement-decision-api # listens on http://0.0.0.0:8088
Or via Docker:
docker run -p 8088:8088 ghcr.io/mizcausevic-dev/procurement-decision-api:latest
Then draft a decision:
curl -s http://localhost:8088/decisions/draft \
-H 'content-type: application/json' \
-d '{
"decision_id": "SPRINGFIELD-DEC-2026-001",
"buyer": {
"name": "Springfield Unified School District",
"type": "school-district",
"jurisdiction": "US-CA"
},
"decision_maker": {
"role": "Director of Educational Technology",
"name": "Dr. Jane Doe",
"authority": "Board Resolution 2026-04"
},
"vendor_name": "AcmeTutor Inc.",
"product_name": "AcmeTutor 3.0",
"vendor_id": "https://acmetutor.example/.well-known/aeo.json",
"fetch_targets": [
{ "type": "aeo", "url": "https://acmetutor.example/.well-known/aeo.json" },
{ "type": "tutor-card", "url": "https://acmetutor.example/.well-known/tutor-card.json" },
{ "type": "student-ai-disclosure", "url": "https://acmetutor.example/.well-known/student-ai-disclosure.json" }
],
"policy_uris": [
"https://springfield.edu/.well-known/aup.json"
],
"rubric": [
{ "id": "ferpa-compliance", "result": "pass", "weight": 1.0 },
{ "id": "coppa-compliance", "result": "pass", "weight": 1.0 },
{ "id": "no-training-on-student-data", "result": "pass-with-condition", "weight": 1.0,
"notes": "Disclosure asserts no-training; require contractual confirmation." },
{ "id": "bias-audit-completed", "result": "partial", "weight": 0.8,
"notes": "Audit current but due for refresh by 2026-09." }
],
"conditions": [
{ "id": "no-training-restriction",
"description": "Vendor SHALL NOT use Springfield USD student-provided content for model training.",
"enforcement": "contractual" },
{ "id": "bias-audit-refresh",
"description": "Vendor SHALL deliver a refreshed third-party bias audit by 2026-12-01.",
"enforcement": "audit" }
]
}' | jq
The response includes:
draft— the full, schema-conformant Decision Card (ready to sign + publish at/.well-known/decisions/<id>.json)documents_fetched[]— each vendor URL with its retrieval timestamp + sha256 content hashfetch_errors[]— per-target retrieval errors (the draft doesn't fail wholesale on one missing URL)inferred_status—trueif the service inferred the decision status from the rubric
What the service does
- Fetches every URL in
fetch_targetsconcurrently with httpx, capped at 2 MB / 10 s per document, and computes a canonical sha256 hash over each (sorted keys, no whitespace). - Infers
decision.statusfrom the rubric if you didn't supplyproposed_status. The inference rules:- Any
fail→rejected-with-remediation - Any
partialorpass-with-condition→approved-with-conditions - All
pass→approved - Empty / all
n/a→pending
- Any
- Composes a default rationale from the rubric results if you didn't supply
rationale_template. - Validates the Decision Card against the same conditional rules the upstream zod schema enforces:
status∈ {approved-with-conditions,rejected-with-remediation} →conditionsmust be non-emptystatus=withdrawn→withdrawalblock requiredpublication.is_public=true→publication_urirequired
- Returns the Draft Decision Card. Review, edit, sign, publish.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | / |
Service info + relevant links |
| GET | /healthz |
Liveness probe (always 200 if the process is running) |
| POST | /decisions/draft |
Produce a Draft Decision Card |
| POST | /decisions/validate |
Validate an existing Decision Card against the v0.1 schema |
| GET | /docs |
Interactive OpenAPI documentation (Swagger UI) |
| GET | /openapi.json |
Machine-readable API schema |
Why this matters
AI procurement under OMB M-24-10 and NIST AI RMF requires agencies to publish reviewable decisions about vendor AI systems. Today, those decisions sit in PDFs and procurement databases — invisible to vendors trying to win future RFPs and invisible to citizens whose data is being processed.
The AI Procurement Decision Card spec defines a machine-readable carrier for those decisions. This service is the tool that produces them at scale: a reviewer fills in the rubric, points at the vendor's published declarations, and gets back a schema-valid card ready to publish at /.well-known/decisions/<decision_id>.json.
For procurement teams, this means a decision becomes a queryable, searchable, audit-friendly artifact — and the vendor's published declarations are cited by URL and content hash, so any drift after the decision is detectable.
Architecture
┌────────────────────────────────────────────────────────────┐
│ FastAPI app (lifespan-managed) │
│ │
│ POST /decisions/draft │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ fetcher.fetch_documents (async, httpx) │ │
│ │ - timeout 10s per doc │ │
│ │ - 2 MB size cap │ │
│ │ - canonical sha256 hash │ │
│ │ - per-target error collection │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ rubric.infer_status │ │
│ │ rubric.compose_rationale │ │
│ │ rubric.weighted_score │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ drafter.draft_decision_card │ │
│ │ - validates conditional rules │ │
│ │ - assembles history events │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ DraftResponse │
└────────────────────────────────────────────────────────────┘
Pydantic v2 models mirror the JSON Schema 2020-12 spec exactly, including the conditional rules (which run as @model_validator(mode="after") hooks).
Development
git clone https://github.com/mizcausevic-dev/procurement-decision-api
cd procurement-decision-api
pip install -e ".[dev]"
# Run the test suite (mocks the vendor HTTP layer; no internet required)
pytest -q
# Lint, format, typecheck
ruff check src tests
ruff format src tests
mypy src
# Run the service
python -m procurement_decision_api
# or
uvicorn procurement_decision_api.app:app --reload --port 8088
Composability
This service composes naturally with the rest of the Kinetic Gain ecosystem:
- Input documents can be fetched directly from any vendor's
/.well-known/paths, or validated first viakg-validate-actionin your CI. - Output Decision Cards can be inspected by
mcp-kinetic-gain(tools:decision_card_inspect,decision_card_validate). - Inline validation in the browser is available at validator.kineticgain.com — paste the produced draft, get inline error markers.
License
MIT. The Kinetic Gain Protocol Suite specifications this service produces are also MIT; reference implementations like mcp-kinetic-gain are AGPL-3.0.
Related
- Spec repo:
ai-procurement-decision-spec - Hosted validator: validator.kineticgain.com
- MCP server:
mcp-kinetic-gain— install withnpx -y mcp-kinetic-gain - GitHub Action:
kg-validate-action - NIST AI RMF crosswalk: suite.kineticgain.com/docs/nist-rmf-crosswalk.md
- Apex: kineticgain.com
Metadata
Release files for procurement-decision-api 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| procurement_decision_api-0.1.1.tar.gz | 20.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| procurement_decision_api-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.7 kB
Release files / procurement_decision_api-0.1.1.tar.gz
| Download URL | procurement_decision_api-0.1.1.tar.gz |
|---|---|
| Size | 20.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
23ba3e358a31b2a4c045f16e5076597a9bd028f0b44c85247b645688b99dcf75
|
|
BLAKE2b-256 checksum How to use checksums |
b06f0af95cc6d69d891024206a5950c8fb46ebdee8d44ecc813a9b60beea4a2b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 15, 2026.
Transparency logRelease files / procurement_decision_api-0.1.1-py3-none-any.whl
| Download URL | procurement_decision_api-0.1.1-py3-none-any.whl |
|---|---|
| Size | 18.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8c66d3106c9ae64f1e2ed6d1bb4ad91334dd67dea7e0060e4478440a49196510
|
|
BLAKE2b-256 checksum How to use checksums |
002dc9d74a6f3924a3174612ec224bbfac796f62fdaa3be3d174eb81e5f928b2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 15, 2026.
Transparency log