Everything on this page is self-serve — no email, no call. The client surface is one HTTP endpoint, the SDK and integration adapters are Apache-2.0, and your chat and ticketing tools connect per-organization from the dashboard.
01 · The one call
Nothing organizational happened unless it's in the ledger. Agents report by appending typed events; the ledger is append-only, so evidence is never refused and never edited. Here's an agent submitting completed work:
curl -X POST https://YOUR-PROVOSTRY/api/v1/events \
-H 'Content-Type: application/json' \
-H 'x-provostry-key: <agent key>' \
-d '{
"type": "evidence_bundle",
"actor": { "id": "builder-3", "kind": "agent", "stance": "builder" },
"initiativeId": "capsule",
"workItemId": "cap-01",
"payload": {
"summary": "Passkey enrollment implemented, verified against acceptance criteria",
"testsPassed": 34, "testsFailed": 0,
"verified": true, "verifier": "verifier-1",
"rollback": "git revert + redeploy previous tag"
}
}'
Every event is validated against a typed schema. The vocabulary an agent may append directly:
initiativework_packagestatus_updateevidence_bundleescalationverification_contractorg_change_proposalincidentmetric_sampleagent_heartbeatvalidation_runfindingregression_testcheck_definitionTen more types exist and are refused on this endpoint by design: decision_record and check_decision come only from the decision routes, so a decision is always attributed to the caller's own credential; phase_ready, experiment_proposal, experiment_result, verification_observation, verification_verdict and ci_attestation are engine-authored — an agent cannot hand-write its own verdict or attest its own commit; budget_reservation comes only from the reservation route, so an agent cannot authorize its own spend; and milestone_config comes only from an organization owner, so an agent cannot decide what the organization's milestones are.
02 · Identity
Writes require x-provostry-key. Keys are issued per agent and stored hashed, so agents cannot impersonate each other and a leaked key revokes cleanly — the audit trail always names the real actor.
curl -X POST https://YOUR-PROVOSTRY/api/v1/agents/builder-1/keys \
-H "x-provostry-key: $ADMIN" -H 'Content-Type: application/json' -d '{}'
03 · Verification
Two independent paths make work verified, and an agent controls neither. Work with a verification contract — observable pass/fail clauses written down first — is verified when the server has probed the clauses and written a passing verdict, and a human has approved the contract if it required approval. Work with no contract is verified when CI attests the commit: register the repositories whose CI may vouch for your work from the dashboard's Integrations panel (or PUT /api/v1/integrations/github), name the checks that must succeed, and have your agent cite the commit on its evidence:
{ "type": "evidence_bundle", "actor": { "id": "builder-1", "kind": "agent" }, "workItemId": "wp-42",
"payload": { "summary": "checkout retries on 5xx", "verified": true,
"commit": "9f3c2ab…", "repo": "acme/shop" } }
The server fetches that commit's check runs from GitHub itself — with a fine-grained token (Contents: read, Actions: read; GitHub grants check runs only to apps) it reads the commit's workflow runs instead, one per workflow — and writes a ci_attestation. Only the commit's author may claim it — a human session by the email the server verified at sign-in, an agent by the name or login your sync uses — or a trusted poster you listed for the repository, such as your CI bot. A skipped-only run certifies nothing; an unregistered repository is never fetched; a contract, when present, stays authoritative. Work with neither shows as self-reported, and velocity does not move on it. The design, in full.
Every contract card has a Receipt: the goal verbatim, the contract version and hash, who approved it (or that no approval was needed, or that one is still pending), the run and its timestamps, each clause with what the server observed, the verdict, and the ledger's integrity and coverage — readable in the product, downloadable as a PDF, with the raw ledger records as a separate export. Generating one runs nothing and replaces nothing; a failed, incomplete or superseded result says so in its first line.
GET /api/v1/verify/vc:…/receipt the receipt (JSON) GET /api/v1/verify/vc:…/receipt.pdf the PDF GET /api/v1/verify/vc:…/receipt.json the raw records
An organization owner registers an evidence source of their own — an https base URL, optionally with a credential header the server stores encrypted and never returns — and the server proves it can reach it before saving. A contract comes from a template over the closed probe vocabulary, bound to a work item; a run probes the source and writes the verdict. The dashboard's Verify panel walks these three steps; the routes are the same ones it calls:
PUT /api/v1/verify/sources/shop-api { "baseUrl": "https://api.acme.example", "authHeader": { "header": "x-api-key", "value": "…" } }
GET /api/v1/verify/templates
POST /api/v1/verify/contracts { "templateId": "http-ok", "params": { "path": "/health" }, "source": "shop-api", "workItemId": "wp-42" }
POST /api/v1/verify/vc:…/run
Budgets are tracked against every work item as agents report cost, and crossing the ceiling raises an escalation to a human. A runner that wants a hard stop asks first: a reservation is a pre-spend authorization the server grants only while spent + reserved + the ask fit the budget. A refusal opens the escalation for you and returns 409; the SDK's reserve() throws BudgetRefusedError on it, so a runner that asks cannot miss the answer. Settle the hold by naming it on the evidence that reports the real cost, or release it.
POST /api/v1/budgets/wp-42/reserve { "amountUsd": 25, "ttlSec": 900 } → 201 { "reservationId": "…" } or 409
POST /api/v1/events { "type": "evidence_bundle", …, "payload": { "costUsd": 18, "reservationId": "…" } }
POST /api/v1/budgets/reservations/:id/release
A runner that never asks is not stopped — Provostry is a ledger and a decision port, not a proxy between your agent and its tools — it is escalated the moment its reported spend crosses the line, with the context attached.
04 · Customer integrations
Each org connects its own tools from the dashboard (account menu → Integrations, owner/admin). Secrets are encrypted at rest (AES-256-GCM) and never returned by any API. There is no deployment-wide fallback: an org that hasn't connected gets nothing — that absence is what stops one tenant's escalations reaching another tenant's workspace. Connecting posts a real test message, so a broken connection is refused with the vendor's own reason instead of failing silently at your first escalation.
Escalations post with Approve / Reject buttons. A click is verified against your workspace's signing secret (HMAC over the raw body, replay-window, fail-closed) and recorded as a decision_record attributed to the human who clicked. You bring your own Slack app: webhook URL + signing secret + workspace ID.
Escalations arrive as an Adaptive Card via a Power Automate Workflows webhook — not the legacy O365 connector, which Microsoft disabled in May 2026. Workflows webhooks are outbound-only, so Teams can't sign a click back to us; the card deep-links into Provostry, where the approver is authenticated. One extra click, zero ambiguity about who approved — we won't render a button we can't attribute to a real human.
Validation findings become issues with evidence verbatim — one ticket per distinct defect, deduped so a recurring regression check never spams duplicates. One-way on purpose: Provostry creates issues and never reads Jira back, so the ledger stays the system of record.