HTTP API
Base URL: your Paraph host (e.g. http://localhost:3000). All requests authenticate with a
Paraph API key as a Bearer token:
Authorization: Bearer <api-key>
The @paraph/sdk client wraps these endpoints; use them directly for other languages or an
OTel exporter.
Record an event
POST /api/v1/events
{
"run": "loan-4821",
"agent": "underwriter-agent",
"event": {
"kind": "tool_call",
"role": "tool",
"name": "pull_credit_report",
"content": "score=690",
"metadata": { "bureau": "Experian" }
}
}
Ensures the run exists (by its run external id) and appends the event to the run's hash
chain.
- Event
kind:message·model_call·tool_call·decision·error. - Response
200:{ "runId": "<internal id>", "seq": 0 }.
Open an approval gate
POST /api/v1/runs/{run}/approvals
{ "action": "Approve €40,000 loan for applicant #4821", "policy": "block" }
Creates a pending approval and records an approval_requested decision event into the run.
- Response
200:{ "run": "loan-4821", "seq": 0, "status": "pending" }.
Poll an approval
GET /api/v1/runs/{run}/approvals/{seq}
The waiting agent polls this until status is no longer pending.
- Response
200:
{
"seq": 0,
"action": "Approve €40,000 loan for applicant #4821",
"status": "approved",
"reviewer": "Jane Okafor",
"note": "Income verified; within policy",
"requestedAt": 1782278196255,
"resolvedAt": 1782278237000
}
status: pending · approved · rejected · expired.
Close a run
POST /api/v1/runs/{run}/end
{ "status": "complete" }
Marks the run finished (status: complete · error; defaults to complete on an empty
body). The SDKs call this from end() — the Python client's context manager does it
automatically, closing as error if the block raised. The record itself stays append-only;
status is presentation.
- Response
200:{ "run": "loan-4821", "status": "complete" }.
OTel-GenAI ingestion
POST /api/v1/otel/v1/traces
Point any OpenTelemetry exporter at …/api/v1/otel (it appends /v1/traces), with the API
key as a Bearer token. Spans carrying GenAI semantics (gen_ai.*) become run events —
chat/completion/embeddings → model_call, execute_tool → tool_call — in start-time
order. The run is keyed by the paraph.run attribute, else gen_ai.conversation.id, else the
trace id; non-GenAI spans are ignored. This lets any OTel-instrumented agent — any language or
framework, including LangChain — feed the record without the SDK.
Both OTLP/HTTP encodings are accepted: http/protobuf (the exporter default — no
configuration needed) and http/json. The mapping → record path is verified end-to-end
against Postgres.
Evidence package (period export)
GET /api/evidence?days=90 · GET /api/evidence?from=<ms>&to=<ms> (session-authenticated,
from the console)
Returns one JSON package containing every run in the window as a full evidence bundle,
a summary (runCount, allVerified, brokenRuns, signoffs), and a single RFC 3161
anchor over the whole package (anchor.digest = SHA-256 of the package canonicalized
without its anchor field). Exports are audit-logged.
Webhooks (outbound)
Register endpoints in Settings → Webhooks. A gate opening or resolving POSTs:
{
"type": "approval.requested",
"run": "loan-4821",
"seq": 0,
"action": "Approve €40,000 consumer loan for applicant #4821",
"agent": "underwriter-agent",
"at": 1782278196255
}
approval.resolved adds "status" (approved · rejected) and "reviewer". Headers:
x-paraph-event (the type) and x-paraph-signature — verify authenticity:
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
if (expected !== req.headers["x-paraph-signature"]) reject();
Delivery is best-effort with a 3-second timeout — a slow receiver never blocks a gate.
Errors
| Status | When |
|---|---|
400 |
Malformed body, missing action, or invalid seq. |
401 |
Missing or invalid API key. |
404 |
Unknown approval (or one belonging to another tenant). |
Approvals are resolved by a human in the dashboard control room (/dashboard/approvals),
not via the public API — the reviewer's identity comes from their authenticated session, and
the resolution is recorded as an approval_resolved decision event.